Can you test for nullpointers in Fortran? - fortran

I'm trying to learn Fortran2018 using gfortran.
When playing around with pointers I noticed that there doesn't seem to be a facility to test for nullpointers. So I have two questions:
Is there really no (straightforward) way to test if a pointer is a nullpointer or "ready to use" in Fortran?
If there isn't, why wasn't it considered necessary in Fortran so far?
More practically speaking, in the code snippet below: How could we find out at any point during execution if assigning to p was "safe" or not? (Possibly imagining a more complex sequence of allocate, nullify, and deallocate statements.)
program test
implicit none
real, pointer :: p
! p = 333.333 ! Segfault, p is neither defined, nor allocated
! Since p is not defined, checking whether it's
! associated to a target gives arbitrary results:
print *, "Start: Pointer p associated? ", associated(p) ! Result: True or False
nullify(p) ! Now p is defined, but `null`
print *, "Nullyfied: Pointer p associated? ", associated(p) ! False
! p = 123.456 ! Still a segfault
allocate(p) ! Now p can be accessed
print *, "Allocated: Pointer p associated? ", associated(p) ! True
p = 987.654 ! Now assignment is possible
allocate(p) ! Loses the value pointed to by p.
print *, p ! Result: 0.00000000
deallocate(p) ! Now accessing p generates a segfault again.
print *, "Deallocated: Pointer p associated? ", associated(p) ! False
! Never allowed:
! allocated(p)
! p == null()
! p .eqv. null()
end program test

In general, there is no safe way to tell whether a pointer is "ready to use", just as there is no safe way to tell whether a variable is currently defined.
The ASSOCIATED intrinsic function may be used only when the pointer is of defined association status, but there is no comparable way to determine (within the Fortran program itself) whether the pointer is of defined association status.
If the pointer is of defined association status, ASSOCIATED will tell you whether or not the pointer points to something (and can even be used in some cases whether it points to a particular thing) and can be used.
However, if the pointer is not of defined pointer association, an attempt to query its association status is a violation of the Fortran standard (this is stronger than the result being undefined, it means your entire program is broken).
You can help yourself by taking reasonable steps to ensure that the pointer association status does not become, or start as, undefined, but it's entirely your responsibility as a programmer to know whether or not the association status is defined.
One way to help is by setting the initial association status of your pointers:
real, pointer :: p => NULL() ! Defined association status, not-associated
real, pointer :: q ! Undefined association status
print *, ASSOCIATED(p) ! Allowed
print *, ASSOCIATED(q) ! Not allowed
end
(I won't say that ASSOCIATED(p) tells us .FALSE., because that ASSOCIATED(q) means we don't have a valid program.)
To conclude, if you're careful you can use ASSOCIATED reliably to tell whether a pointer is associated, but you must be careful.

One tests for null pointers using the associated() function. It returns true for associated pointers and false for null pointers.
For undefined pointers the result is "undefined behaviour" (one can get anything).

Related

mysterious associated behavior compiled with gfortran

I have the following short fortran code.
!==============================================
MODULE PREC
INTEGER, PARAMETER :: q=8
END MODULE PREC
!==============================================
MODULE MOD_FIT
USE prec ! q
TYPE spec
INTEGER HL,HR
COMPLEX(q), POINTER :: HMAT(:,:) ! (HL,HR)
END TYPE
END MODULE MOD_FIT
!==============================================
PROGRAM MAIN
USE prec
USE MOD_FIT ! spec
IMPLICIT NONE
!
TYPE(spec) SMP
write(*,*)'check associated:',associated(SMP%HMAT)
END
I compiled it with the newest version gfortran, and ran it. The following is what I got
check associated: T
Should it be F as I hadn't initialize it at all?
No, the status of your pointer is undefined. You are not allowed to inquire it using associated() because it can result in anything.
What you should always do is to use default initialization of all pointer components and initialize them to null().
TYPE spec
COMPLEX(q), POINTER :: HMAT(:,:) => null()
END TYPE
After that you are guaranteed to get the expected result false.

Have a function in fortran return a reference that can be placed on the left-hand-side of an assignment

As stated in the title, I want to directly modify data that I access through a pointer retrieved from a function. Having a reference returned by a function appearing on the l.h.s. of an assignment(=) is no issue in C++ but the following minimal example in fortran errors out:
module test_mod
implicit none
integer, target :: a=1, b=2, c=3 ! some member variables
contains
function get(i)
integer, pointer :: get
integer, intent(in) :: i
select case (i)
case (1)
get => a
case (2)
get => b
case (3)
get => c
end select
end function get
end module test_mod
program test
use test_mod
implicit none
integer, pointer :: i_p
!> prints out 1 2 3
print*, get(1), get(2), get(3)
!> this is what I want but I get the error
!> Error: 'get' at (1) is not a variable
get(2) = 5
!> this works but is not what I want
i_p => get(2)
i_p = 5
end program test
Is there any way to accomplish this behaviour; maybe I'm missing some attributes? I would like to bypass writing any setter routines such as
set(i,value)
since it should mimic the appearance of an array.
In my application, the member variables a,b,c are actually arrays of different size
a = [a1, a2, a3]
b = [b1, b2]
c = [c1]
and I want the getter get(i,j) to mimic a matrix of pointers
j = 1 2 3
i = 1: [[a1, a2, a3],
i = 2: [b1, b2, XX],
i = 3: [c1, XX, XX]]
wehre XX would be referencing to null().
Update:
I am using gfortran (version 5.2.0) and the deployment machines would have only versions starting from 4.6.x and upwards. Therefore, the suggested fortran 2008 standard features are unfortunately not available to me. Is it possible to mimic the behaviour described above without having a compiler supporting it out of the box?
Update 2:
So I ended up implementing a structure as follows
type Vec_t
integer, allocatable, dimension(:) :: vec
end type Vec_t
type(Vec_t), allocatable, dimension(:), target :: data
which I initialise like this (my triangular matrix application I mention at the end)
allocate(data(max))
do i=1,max
allocate(data(i)%vec(i))
end do
and I access & write to it through
print*, data(2)%vec(1)
data(2)%vec(1) = 5
which is not precisely what I was after but good enough for my application.
Let's look at what you want to do:
get(2)=5
and the error message
Error: 'get' at (1) is not a variable
That looks pretty comprehensive: you can't do what you want. Or, perhaps...
get(2) is indeed, under the rules of Fortran 2003, not a variable. In Fortran 2003 a variable is given by the rules R601 and R603, which is a list of designators.
The left-hand side of an assignment must be a variable.
But look at Fortran 2008 and its definition of a variable. Now a variable is either one of those same designators (or ones related to coarrays or complex parts), but it could also (C602 to R602) be a function reference which
shall have a data pointer result.
This is summarized in the introduction of Fortran 2008, detailing the extensions over Fortran 2003, as
A pointer function reference can denote a variable in any variable definition context.
get(2) is a reference to a function that has a data pointer result. get(2) then may appear on the left-hand side of an assignment statement under the rules of Fortran 2008.
Alas, this interpretation of Fortran is not widely supported by current compilers: at the time of answering just the Cray compiler.
This means that this answer is really saying that you have two options: switch compiler or wait until this feature is more widespread. As both of these are likely impractical, you probably want another answer which gives something slightly more portable as a workaround.
I prefer my link to that given by innoSPG, as although this latter is based on the former, the description of the appropriate field "Pointer functions - pointer function ref is a variable" is slightly more clear. This is, though, a more accessible document and a viable alternative.

Pointers in Fortran

Im unsure how the below 2 in C can be represented in Fortran.
*VariableOne (Could be used possibly by using 'inout intent' in functions?
&VariableOne (No clue about how I could do this though)
Could someone tell me how I might be able to replicate this in Fortran? I looked up Pointers in Fortran but couldnt clearly understand what relates to the above two. I was hoping someone here would show me an equivalent to help me understand.
You seem to be confusing two different concepts. Values in Fortran act as though they're passed by reference so you can modify a variable within a function and have its change reflected outside. The intent statements are there to specify what you're going to do with that dummy variable. intent(in) doesn't allow you to change that variable, intent(out) you use it to pass data out of a function and its value is undefined on entry, and intent(inout) means that it can both provide data to the function (not undefined on entry) and write to it as well.
But there are also explicit pointers you can use to dynamically allocate memory and act similarly to pointers in C.
Pointers in Fortran can't point to just anything. What they point to must have the target attribute or be a pointer. You also don't need to explicitly dereference a pointer to access its value. Here's a short example of what you can do with pointers and how they work:
real, pointer :: ptr(:) => null() !Can point to an array of reals or be allocated
real, target, allocatable :: trgt(:) !Array of reals
real, allocatable :: array(:)
real, pointer :: scalar => null() !Initially points to null to signify that it's not associated
!I can check if the pointer currently points to anything by using the associated function
!Be careful: the initial associaton status is undefined so you must set it to null or nullify it first
if (associated(ptr)) then
nullify(ptr) !Redundant in this example, same as setting it equal to null()
end if
allocate(ptr(3)) !I can allocate a pointer directly with memory for 3 elements
allocate(trgt(5)) !Allocate the target with 5 elements
allocate(array(6)) !Allocate array
!Assign values to my freshly allocated variables
ptr = 1.0
trgt = 3.0
array = 5.0
deallocate(ptr) !Avoid memory leaks by deallocating memory before pointing to something else
ptr => trgt !Pointer now points to the data stored in trgt. No need to use anything like '&' to reference trgt
ptr => array !Error because array does not have target attribute
scalar => ptr(1) !Both are pointers so no error, now points to a value of 3.0
print *, scalar + 5.0 !I can use this value directly without dereferencing it and prints 8.0

Intel Fortran error "allocatable array or pointer is not allocated"

When I tried to run a huge Fortran code (the code is compiled using Intel compiler version 13.1.3.192), it gave me error message like this:
...
Info[FDFI_Setup]: HPDF code version number is 1.00246
forrtl: severe (153): allocatable array or pointer is not allocated
Image PC Routine Line Source
arts 0000000002AD96BE Unknown Unknown Unknown
arts 0000000002AD8156 Unknown Unknown Unknown
arts 0000000002A87532 Unknown Unknown Unknown
...
Nonetheless, if I insert a small write statement (which is just to check the code, not to disturb the original purpose of the code) in one of the subroutines as the following (I couldn't put all the codes since they are too huge):
...
endif
call GetInputLine(Unit,line,eof,err)
enddo
if(err) return
! - [elfsummer] 20140815 Checkpoint 23
open(unit = 1, file = '/bin/monitor/log_checkpoint',status='old',position='append')
write(1,*) "BEFORE checking required keys: so far so good!"
close(1)
! check required keys
! for modes = 2,3, P and T are the required keys
if(StrmDat%ModeCI==2.or.StrmDat%ModeCI==3) then
...
then suddenly, the error message shown above disappears and the code can run correctly! I also tried to insert such write statements in other locations in the source code but the above error message still exists.
According to Intel's documentation:
severe (153): Allocatable array or pointer is not allocated
FOR$IOS_INVDEALLOC. A Fortran 90 allocatable array or pointer must already be allocated when you attempt to deallocate it. You must allocate the array or pointer before it can again be deallocated.
Note: This error can be returned by STAT in a DEALLOCATE statement.
However, I couldn't see any relations between the error and the "write statements" I added to the code. There is no such "allocate" command in the location I add the write statements.
So I am quite confused. Does anybody know the reasons? Any help is greatly appreciated!!
With traceback option, I could locate the error source directly:
subroutine StringRead(Str,delimiter,StrArray,ns) ! [private] read strings separated by delimiter
implicit none
character*(*),intent(in) :: Str
character*(*),intent(in) :: delimiter
character*(*),pointer :: StrArray(:)
integer,intent(out) :: ns
! - local variables
character(len=len(Str)) :: tline
integer :: nvalue,nvalue_max
character(len=len(StrArray)),pointer:: sarray(:),sarray_bak(:)
integer :: len_a,len_d,i
! deallocate StrArray
if(associated(StrArray)) deallocate(StrArray)
The error, according to the information the traceback gave me, lies in the last statement shown above. If I comment out this statement, then the "forrtl: severe (153)" error would disappear while new errors being generated... But still, I don't think this statement itself could go wrong...It acts as if it just ignores the if... condition and directly reads the deallocate commend, which seems weird to me.
You could have a bug in which you are illegally writing to memory and damaging the structure that stores the allocation information. Changing the code might cause the memory damage to occur elsewhere and that specific error to disappear. Generally, illegal memory accesses typically occur two ways in Fortran. 1) illegal subscripts, 2) mismatch between actual and dummy arguments, i.e., between variables in call and variables as declared in procedures. You can search for the first type of error by using your compiler's option for run-time subscript checking. You can guard against the second by placing all of your procedures in modules and useing those modules so that the compiler can check for argument consistency.
Sounds like some of the earlier comments give the general explanation. However,
1) Is StrArray(:) an Intent(out)? That is, are you reading the file's lines into StrArray() in the s/r, with the hope of returning that as the file's content? If so, declare it as an (Out), or whatever it should be.
2) Why is StrArray() a Pointer? Does it need to be a Pointer? If all you want is file content, you may be better off using a non-Pointer.
You may still need an Allocatable, or Automatic or something, but non-Pointers are easier in many cases.
3) If you must have StrArray(:) as a Pointer, then its size/shape etc must be created prior to use. If the calling sequence ACTUAL Arg is correctly defined (and if StrArray() is Intent(In) or Intent(InOUT), then that might do it.
By contrast, if it is an (Out), then, as with all Pointer arrays, it must be FIRST Allcoated() in the s/r.
If it is not Allocated somewhere early on, then it is undefined, and so the DeAllocate() fails, since it has nothing to DeAlloc, hence Stat = 153.
4) It is possible that you may wish to use this to read files without first knowing the number of lines to read. In that case, you cannot (at least not easily), Allocate StrArray() in advance, since you don't know the Size. In this case, alternate strategies are required.
One possible solution is a loop that simple reads the first char, or advances somehow, for each line in the file. Have the loop track the "sum" of each line read, until EOF. Then, you will know the size of the file (in terms of num lines), and you then allocate StrArray(SumLines) or something. Something like
SumLines = 0
Do i=1, ?? (or use a While)
... test to see if "line i" exists, or EOF, if so, Exit
SumLines = SumLines + 1
End Do
It may be best to do this in a separate s/r, so that the Size etc are known prior to calling the FileRead bits (i.e. that the file size is set prior to the FileRead s/r call).
However, that still leaves you with the problem of what Character(Len) to use. There are many possible solutions to this. Three of which are:
a) Use max length, like Character(Len = 2048), Intent(Out), or better yet, some compile time constant Parameter, call it MaxLineWidth
This has the obvious limitation to lines that <= MaxLineWidth, and that the memory usage may be excessively large when there many "short lines", etc.
b) Use a single char array, like Character(Len = 1), Intent(Out) :: StrArrayChar(:,:)
This is 2-D, since you need 1 D for the chars in each line, and the 2nd D for the lines.
This is a bit better compared to a) since it gives control over line width.
c) A more general approach might rely on a User Defined Type such as:
Type MyFileType
Character(Len=1), Allocatable :: FileLine(:) ! this give variable length lines, but each "line" must be allocated to the length of the line
End Type MyFileType
Then, create an array of this Type, such as:
Type(MyFileType), Allocatable :: MyFile(:) ! or, instead of Allocatable, can use Automatic etc etc
Then, Allocate MyFile to Size = num lines
... anyway, there are various choices, each with its own suitability for varying circumstances (and I have omitted much "housekeeping" re DeAllocs etc, which you will need to implement).
Incidentally, c) is also one possible prototype for "variable length strings" for many Fortran compilers that don't support such explicitly.

Why does the second if-clause even execute?

I have a do while loop in my program, who's condition to continue keeps giving me off-by-one errors and I can't figure out why. It looks like this:
do while (ii .le. nri .and. ed(ii) .le. e1)
! do some stuff...
ii = ii + 1
end do
where ii and nri are scalar integers, e1 is a scalar real, and ed is a real array of length nri. What I expect to happen after the last run is that since ii.le.nri returns .false. the second condition is never tested, and I don't get any off-by-one problems. I've verified with the debugger that ii.le.nri really does return .false. - and yet the program crashes.
To verify my assumption that only one condition is tested, I even wrote a small test program, which I compiled with the same compiler options:
program iftest
implicit none
if (returns_false() .and. returns_true()) then
print *, "in if block"
end if
contains
function returns_true()
implicit none
logical returns_true
print *, "in returns true"
returns_true = .true.
end function
function returns_false()
implicit none
logical returns_false
print *, "in returns false"
returns_false = .false
end function
end program
Running this program outputs, as I expected, only
$ ./iftest
in returns false
and exits. The second test is never run.
Why doesn't this apply to my do while clause?
In contrast to some languages Fortran does not guarantee any particular order of evaluation of compound logical expressions. In the case of your code, at the last go round the while loop the value of ii is set to nri+1. It is legitimate for your compiler to have generated code which tests ed(nri+1)<=e1 and thereby refer to an element outside the bounds of ed. This may well be the cause of your program's crash.
Your expectations are contrary to the Fortran standards prescriptions for the language.
If you haven't already done so, try recompiling your code with array-bounds checking switched on and see what happens.
As to why your test didn't smoke out this issue, well I suspect that all your test really shows is that your compiler generates a different order of execution for different species of condition and that you are not really comparing like-for-like.
Extending the answer High Performance Mark, here is one way to rewrite the loop:
ii_loop: do
if (ii .gt. nri) exit ii_loop
if (ed(ii) .gt. e1) exit ii_loop
! do some stuff
ii = ii + 1
end do ii_loop