Home > front end >  fortran: Detecting null-pointer passed as dummy argument
fortran: Detecting null-pointer passed as dummy argument

Time:12-01

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!

CodePudding user response:

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.

CodePudding user response:

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

  • Related