3

I want to detect from inside a subroutine that a dummy argument passed with intent(in) is actually a null pointer:

program testPTR
  
implicit none
  
integer, target :: ii
integer, pointer :: iPtr
  
  iPtr => ii
  iPtr = 2
  
  print *, "passing ii"
  call pointer_detect(ii)
  
  print *, "passing iPtr"
  call pointer_detect(iPtr)
  
  iPtr => null()
  print *, "passing iPtr => null()"
  call pointer_detect(iPtr)
  
contains
                                                                                                                                                                      
  subroutine pointer_detect(iVal)
      implicit none
      integer, intent(in), target :: iVal
      integer, pointer :: iPtr
      character(len = *), parameter :: sub_name = 'pointer_detect'
    
      iPtr => iVal
      if (associated(iPtr)) then
        print *, "Pointer associated. Val=", iVal, ", iPtr = ", iPtr
      else
        print *, "Pointer not associated. Val=", iVal, ", iPtr = ", iPtr
       endif
  
  end subroutine pointer_detect
  
end program

To my surprise it works with gfortran-9 and gfortran-12. However I have got a couple of questions:

  1. How legitimate, portable and Fortran-ish the check is?
  2. For some reason it does not segfault on the last print, but rather prints zeros and exits cleanly:
$ gfortan test.f90
$ ./a.out && echo ok
passing ii
 Pointer associated. Val=           2 , iPtr =            2
 passing iPtr
 Pointer associated. Val=           2 , iPtr =            2
 passing iPtr => null()
 Pointer not associated. Val=           0 , iPtr =            0
ok
$ 

Any ideas? Thank you!

Ian Bush
  • 6,996
  • 1
  • 21
  • 27
Roux
  • 435
  • 3
  • 12
  • Probably relevant, maybe not a dupe though, https://stackoverflow.com/questions/72140217/can-you-test-for-nullpointers-in-fortran/72140728 – High Performance Mark Nov 30 '22 at 10:09
  • 2
    The check is not portable at all: your program is not valid Fortran so any Fortran compiler can do whatever it likes and you can't really object. – francescalus Nov 30 '22 at 10:21
  • 1
    Oh, and the obligatory *why are you using pointers in Fortran these days?* question - why are you using pointers in Fortran these days? Or are you just having fun stretching the language into shapes it doesn't like? (Nothing wrong with that, just trying to figure out if there is another question lurking behind what you have asked.) – High Performance Mark Nov 30 '22 at 10:29
  • I agree with @francescalus, the call to the routine is not standard-conforming if the pointer is null(). But it cannot be detected at compile time. It could possibly be detected at runtime with appropriate compilation option (if any). – PierU Nov 30 '22 at 10:55
  • 1
    @HighPerformanceMark There are still quite valid reasons to use pointers in some cases, but I don't think the question is about that. The OP wants to learn about how pointers behave. – PierU Nov 30 '22 at 10:58
  • Thank you! Indeed I have tried to convince my colleague that such checks should not be used ever, and to my surprise the program passed. I just seek for more arguments. (Now I got them!:) – Roux Dec 01 '22 at 15:18

2 Answers2

6

The fragment

  iPtr => null()
  print *, "passing iPtr => null()"
  call pointer_detect(iPtr)

violates the Fortran standard and makes your program invalid (Fortran 2008, 25.5.2.3):

Except in references to intrinsic inquiry functions, a pointer actual argument that corresponds to a nonoptional nonpointer dummy argument shall be pointer associated with a target.

The dummy argument of the non-intrinsic procedure is neither optional, nor a pointer.

Responsibility for avoiding this problem is entirely the programmer's and the compiler has no duty to detect this broken code for you.

A compiler may well be able to detect such bugs, if asked, however (usually at run time):

At line 19 of file brokenpointer.f90
Fortran runtime error: Pointer actual argument 'iptr' is not associated

being the output when using gfortran and the compile option -fcheck=pointer, or

forrtl: severe (408): fort: (7): Attempt to use pointer IPTR when it is not associated with a target

with ifort's -check pointers.

The programmer cannot reliably do similar checks within the procedure itself, because a Fortran compiler is under no obligation to respect the programmer who breaks the rules in this way.

Looking at the procedure's efforts here, for example:

      iPtr => iVal
      if (associated(iPtr)) then

iVal is not a pointer, so iPtr becomes associated with that variable in that pointer assignment. The compiler is allowed to assume you haven't broken the rules of Fortran, so iptr is associated and that test condition is always true. There is no valid Fortran program in which that test condition can resolve to false.

However, not all hope is lost. The text from the standard I quote says something other than "nonpointer": it says "nonoptional". If iVal is instead optional use PRESENT():

  subroutine pointer_detect(iVal)
      implicit none
      integer, intent(in), optional :: iVal
      character(len = *), parameter :: sub_name = 'pointer_detect'
    
      if (present(iVal)) then
        print *, "Actual argument pointer was associated. Val=", iVal
      else
        print *, "Actual argument pointer was not associated."
      endif
  
  end subroutine pointer_detect

A nonpointer, nonallocatable, optional dummy argument will be treated as not present if associated with a disassociated pointer actual argument.

Note, however, that this won't help you if iPtr is of undefined association status. Nothing will.

francescalus
  • 30,576
  • 16
  • 61
  • 96
3

call pointer_detect(iPtr) is not standard-conforming if the pointer is null. The routine pointer_detect() is poorly named, as it cannot detect anything about the original pointer, which is simply not passed: the dummy argument is not a pointer, so upon the call the compiler will pass the address of the target of iPtr, not iPtr itself. But if iPtr is null then it has no target: the behavior gets undefined.

An undefined behavior is, well, undefined. Or say unpredictable. It could crash, or output unpredictable values, etc... This kind of violation of the standard cannot really be detected at compile-time, and the compiler is not required to do so. gfortran may have compilation options that enable the detection at runtime, though (with performance penalty, as with any other runtime check).

PierU
  • 1,391
  • 3
  • 15