1

Is passing a null pointer to a function and a subroutine valid according to the Fortran 2003 standard? Please suppose that the called function and the subroutine can correctly handle the case in which the dummy argument is a null pointer. I am especially interested in the case where such function and subroutine are a 'member' function and subroutine of a derived type.

I would like to avoid checking the association status of each pointer in the calling side if it is allowed by standard, in much the same way that I don't have to treat the zero-size array separately. At the same time, I do not want to rely on a behavior not specified by the standard.

As far as I tried with the following example, ifort and gfortran showed different behaviors regarding this point.

moo.f90

MODULE moo
  IMPLICIT NONE
  PRIVATE

  PUBLIC LL

  TYPE LL
     INTEGER :: i0
   CONTAINS
     PROCEDURE :: func1
     PROCEDURE :: func2
  END type LL

CONTAINS

  FUNCTION func1(self) RESULT(pLL_dest)
    TYPE(LL), POINTER            :: pLL_dest
    CLASS(LL), TARGET, INTENT(IN) :: self

    write(*,*) 'hello from func1'
    pLL_dest => null()
    !pLL_dest => self

  END FUNCTION func1

  FUNCTION func2(self) RESULT(flg)
    LOGICAL                       :: flg
    CLASS(LL), TARGET, INTENT(IN) :: self

    write(*,*) 'hello from func2'
    flg = .true.

  END FUNCTION func2

END MODULE moo

main.f90

PROGRAM chk_nullpo
  USE moo, ONLY : LL
  IMPLICIT NONE

  !CLASS(LL), POINTER :: pLL_s=>null()
  TYPE(LL),  POINTER :: pLL_s=>null()
  TYPE(LL),  POINTER :: pLL_d=>null()
  LOGICAL           :: flg

  write(*,*) 'associated(pLL_s) =',associated(pLL_s)
  write(*,*) 'associated(pLL_d) =',associated(pLL_d)

  write(*,*) 'func1..'
  pLL_d => pLL_s%func1()
  write(*,*) 'associated(pLL_s) =',associated(pLL_s)
  write(*,*) 'associated(pLL_d) =',associated(pLL_d)

  write(*,*) 'func2..'
  flg =pLL_s%func2()
  write(*,*) 'flg=', flg
  write(*,*) 'associated(pLL_s) =',associated(pLL_s)
  write(*,*) 'associated(pLL_d) =',associated(pLL_d)

  write(*,*) 'normal end'
END PROGRAM chk_nullpo

Executable generated by ifort caused a run-time error when the member subroutine func2 is called with the null pointer.

$ ifort -v
ifort version 14.0.2
$ ifort -c moo.f90 -stand f03 -warn all -check
$ ifort -c main.f90 -stand f03 -warn all -check
$ ifort -o ex_ifort moo.o main.o  -stand f03 -warn all -check
ifort: warning #10182: disabling optimization; runtime debug checks enabled
$ ./ex_ifort 
 associated(pLL_s) = F
 associated(pLL_d) = F
 func1..
 hello from func1
 associated(pLL_s) = F
 associated(pLL_d) = F
 func2..
forrtl: severe (408): fort: (7): Attempt to use pointer PLL_S when it is not associated with a target

Image              PC                Routine            Line        Source             
ex_ifort           0000000000402AE1  Unknown               Unknown  Unknown
ex_ifort           0000000000402336  Unknown               Unknown  Unknown
libc.so.6          00002AC53B23DF45  Unknown               Unknown  Unknown
ex_ifort           0000000000402229  Unknown               Unknown  Unknown
$ 

On the other hand, executable generated by gfortran finished without error.

$ gfortran --version
GNU Fortran (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.

GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING
$ gfortran -c moo.f90 -std=f2003 -Wall -fbounds-check
moo.f90:26.21:

  FUNCTION func2(self) RESULT(flg)
                     1
Warning: Unused dummy argument 'self' at (1)
moo.f90:16.21:

  FUNCTION func1(self) RESULT(pLL_dest)
                     1
Warning: Unused dummy argument 'self' at (1)
$ gfortran -c main.f90 -std=f2003 -Wall -fbounds-check
$ gfortran -o ex_gfortran moo.o main.o  -std=f2003 -Wall -fbounds-check
$ ./ex_gfortran 
 associated(pLL_s) = F
 associated(pLL_d) = F
 func1..
 hello from func1
 associated(pLL_s) = F
 associated(pLL_d) = F
 func2..
 hello from func2
 flg= T
 associated(pLL_s) = F
 associated(pLL_d) = F
 normal end
$ 

Is the behavior of ifort not comforming the standard, or is the behavior of gfortran just graceful? Or, the standard doesn't say anything about this point?

I note that both of these compilers are older versions, and I guess a newer version might show different behavior.

francescalus
  • 30,576
  • 16
  • 61
  • 96
norio
  • 3,652
  • 3
  • 25
  • 33

1 Answers1

2

Both compilers are behaving in a legitimate way here. That is, the code is faulty but not in a way which requires a compiler to provide diagnostics.

Look at the pointer assignment statement

pLL_d => pLL_s%func1()

This is a reference to the binding name func1 of type LL. However, at the point execution reaches here pLL_s is not associated. The procedure reference is therefore not allowed. According to Fortran 2008 12.5.1:

The data-ref in a procedure-designator shall not be ... a pointer that is not associated.

As this is not a numbered constraint it is the responsibility of the programmer to ensure compliance.

Moving on beyond this problem to the general question "is passing a null pointer to a function and a subroutine valid?", the answer is "yes, as long as its use doesn't violate the usual conditions".

In summary, although conceptually the reference in

pLL_d => pLL_s%func1()   ! Using func1 as a binding name

is like that in

pLL_d => func1(pLL_s)    ! For the module's procedure func1

the problem isn't that pLL_s is a not associated pointer actual argument. That latter form is allowed but the former isn't.1


Of interest for this question is the requirement about definition of the function result. For func1 the function result is a pointer, so it is necessary that the association status of the result be defined. If the function is referenced by its binding name then necessarily the association status of self is defined. Indeed, self is associated.


1 That's actually a little of an oversimplification. While it's true that a procedure may be allowed an actual argument that is a not associated pointer, that doesn't hold for those procedures here.

Look at the declaration of the dummy arguments

    CLASS(LL), TARGET, INTENT(IN) :: self

Here self is a non-optional non-pointer dummy. To be argument associated with a pointer actual argument that actual argument must be pointer associated. So, the functions aren't ones which "correctly handle the case in which the dummy argument is a null pointer".

That said, there'd be nothing wrong with a statement like

pLL_d => pLL_s%func1(ptr) ! or
pLL_d => func1(pLL_s, ptr)

with pLL_s pointer associated and ptr a potentially non-associated actual corresponding to a pointer dummy. The passed-object dummy argument is really just a very special case.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Thank you very much for your answer. It's still difficult for me to understand how the code is interpreted by the compilers. In your explanation about `pLL_d => pLL_s%func1()` in the first few paragraphs of your answer, do you mean that `pLL_d` is treated as a procedure pointer, and that this line of code means that `pLL_d` is made to point to the function `func1` (of a particular object of `LL` type)? I would appreciate a bit more of your help. – norio Feb 22 '17 at 01:51
  • `pLL_d` is declared as a data pointer and can never be treated as a procedure pointer. So, on the pointer assignment statement `pLL_d` takes the pointer association status of the _result_ of the function reference `func1(pLL_s)`. For a function `f`, then `f()` means the result of the function, whereas `f` is the function itself, so `ptr=>f` differs from `ptr=>f()`. For type bindings like `pLL_s%func1`, though, these can't be treated as procedures (see [this](https://stackoverflow.com/q/11318816/) for example). – francescalus Feb 22 '17 at 09:17
  • This is quite an interesting and difficult area. Some languages have classmethods where you can call just with `type_name%method()`. What I am not sure now is whether the restriction you cited also holds for type bound procedures without a passed argument, but probably it does (I haven't checked the standard). – Vladimir F Героям слава Feb 22 '17 at 09:21
  • @VladimirF, do you mean whether a reference `pLL_s%func1()` is allowed when `func1` has `nopass` but `pLL_s` isn't associated? – francescalus Feb 22 '17 at 10:08
  • @VladimirF, the restriction on `pLL_s` being associated in a reference like `pLL_s%func1()` is independent of the characteristics of the procedure. So, it isn't a requirement that the corresponding actual argument to the passed-object dummy be associated but explicitly on the reference through the binding name. That is, if there's no passed-object dummy we won't be violating argument association rules, but we will be going against the restrictions relating to R1221. Of course, a not-associated pointer still has dynamic type but general dynamic binding resolution benefits from association. – francescalus Feb 22 '17 at 12:29
  • @francescalus Thank you for your response. I understand your comment about the data pointer and the procedure pointer. So, it's a bit clearer now, but I still need to understand the other parts. I'm just getting that the cases of type-bound procedure and module procedure are treated differently.. – norio Feb 23 '17 at 02:16
  • @francescalus Does your explanation in footnote 1 imply the following: 'A pointer which is not associated can be passed to a procedure as an actual argument only if the corresponding dummy argument has the pointer attribute.'? – norio Feb 23 '17 at 02:33
  • @norio, essentially yes, a pointer which is not associated may be used as an actual argument only when the dummy is a pointer. Except in F2008 where the dummy may be a non-pointer optional argument. In this case, if the actual is not associated the dummy is considered not present. – francescalus Feb 23 '17 at 07:10
  • After a while, it became all clear to me what this answer was telling. It was really the answer to the point I needed to know. Thanks. – norio Mar 22 '17 at 13:22