Search code examples
pointersfortranstandards

fortran: Detecting null-pointer passed as dummy argument


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!


Solution

  • 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.