C:\gfortran\clf\NULLtest>type NULLtest.f90
program NULLtest
implicit none
integer, parameter :: ik8 = int_ptr_kind()
interface
subroutine sub(x)
import ik8
implicit none
!DEC$ ATTRIBUTES REFERENCE, ALLOW_NULL :: x
integer(ik8) x
end subroutine sub
end interface
integer(ik8), pointer :: p
integer(ik8) x
integer(ik8), parameter :: y = 0
nullify(p)
call sub(p)
call sub(NULL())
allocate(p)
p = -163
call sub(p)
x = -43
call sub(x)
call sub(0)
call sub(0_ik8)
x = 0
call sub(x)
call sub(y)
call sub(transfer(.FALSE._ik8,y))
call sub((y))
call sub((0_ik8))
end program NULLtest
subroutine sub(x)
integer, parameter :: ik8 = int_ptr_kind()
integer(ik8), value :: x
integer(ik8) y
pointer(p, y)
if(x == 0) then
write(*,'(a)') 'In sub, x = NULL'
else
p = x
write(*,'(a,i0)') 'In sub, x = ', y
end if
end subroutine sub
C:\gfortran\clf\NULLtest>ifort NULLtest.f90
Intel(R) Fortran Compiler for Intel(R) EM64T-based applications, Version 9.1
Build 20061104
Copyright (C) 1985-2006 Intel Corporation. All rights reserved.
NULLtest.f90(29) : Warning: The data type of the actual argument does not
match
the definition. [TRANSFER]
call sub(transfer(.FALSE._ik8,y))
------------^
Microsoft (R) Incremental Linker Version 8.00.40310.39
Copyright (C) Microsoft Corporation. All rights reserved.
-out:NULLtest.exe
-subsystem:console
NULLtest.obj
C:\gfortran\clf\NULLtest>NULLtest
In sub, x = NULL
In sub, x = NULL
In sub, x = -163
In sub, x = -43
In sub, x = NULL
In sub, x = NULL
In sub, x = 0
In sub, x = NULL
In sub, x = 0
In sub, x = 0
In sub, x = 0
Now, I don't get that warning about line 29, but it appears that in
this context "a zero" means a literal or named constant zero,
independent of kind, but not a variable with value zero or even an
initialization expression evaluating to zero. Have I got that
right?
I seemed to recall some issue with gfortran and passing nullified
pointers in this context but a direct test didn't yield any of the
problems I remembered:
C:\gfortran\clf\NULLtest>type NULLtest2.f90
program NULLtest2
use ISO_C_BINDING
implicit none
interface
subroutine sub(x) bind(C)
import C_CHAR
implicit none
character(C_CHAR) x(*)
end subroutine sub
end interface
character(8), target :: x
character(kind=C_CHAR), pointer :: ptr(:)
type(C_PTR) cp
x = 'Hello!'//achar(0)
call sub(x)
allocate(ptr(8))
ptr = transfer('Sample'//achar(0),['x'])
! ptr = transfer('Sample'//achar(0),ptr) ! Causes ICE
call sub(ptr)
deallocate(ptr)
call sub(ptr)
cp = C_NULL_PTR
call C_F_POINTER(cp,ptr,[0])
call sub(ptr)
cp = C_LOC(x(1:1))
call C_F_POINTER(cp,ptr,[0])
call sub(ptr)
end program NULLtest2
subroutine sub(x) bind(C)
use ISO_C_BINDING
implicit none
type(C_PTR), value :: x
character(C_CHAR) c
pointer(p, c)
if(C_ASSOCIATED(x)) then
p = transfer(x,p)
write(*,'(a)',advance='no') 'In sub, x = '
do while(c /= achar(0))
write(*,'(a)',advance='no') c
p = p+1
end do
write(*,'()')
else
write(*,'(a)') 'In sub, x = NULL'
end if
end subroutine sub
C:\gfortran\clf\NULLtest>c:\gfortran\win64\bin\x86_64-pc-mingw32-gfortran
NULLte
st2.f90 -fcray-pointer -oNULLtest2
C:\gfortran\clf\NULLtest>NULLtest2
In sub, x = Hello!
In sub, x = Sample
In sub, x = NULL
In sub, x = NULL
In sub, x = Hello!
As can be seen, if the ICE is worked around as noted, everything is
going down smooth. Maybe I'll have to review my earlier code and
see if it has a mistake.
--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end
> Now, I don't get that warning about line 29, but it appears that in
> this context "a zero" means a literal or named constant zero,
> independent of kind, but not a variable with value zero or even an
> initialization expression evaluating to zero. Have I got that
> right?
Yup. The sole intent of this attribute is to allow you to pass the
named constant NULL (or similar) to ease the use of the Win32 API
where that is used to specify an omitted argument (passed by
reference) that could be of any type. It's picky for a reason.
In the early days of DVF, we had to create silly-looking values such
as NULL_SECURITY_ATTRIBUTES if you wanted to omit that argument in a
Win32 call - this was declared as a nullified pointer of the proper
type. By creating the ALLOW_NULL attribute, coding to the Win32 API
was made easier. In general, we use this internally in the provided
Win32 API modules and don't expect customers to use it in their own
code (though it's documented in case you want to.)
Steve
> On Dec 19, 11:59 pm, "James Van Buskirk" <not_va...@comcast.net>
> wrote:
>> Now, I don't get that warning about line 29, but it appears that in
>> this context "a zero" means a literal or named constant zero,
>> independent of kind, but not a variable with value zero or even an
>> initialization expression evaluating to zero. Have I got that
>> right?
> Yup. The sole intent of this attribute is to allow you to pass the
> named constant NULL (or similar) to ease the use of the Win32 API
> where that is used to specify an omitted argument (passed by
> reference) that could be of any type. It's picky for a reason.
My point was that it didn't seem picky enough to me. The tricky
part happens when the dummy argument is documented as a pointer to
integer and the intent is to provide an integer, not NULL, but the
actual argument is defined through all sorts of !DEC$ IF, INCLUDE,
ans USE so that it's not obvious without looking through several
layers of indirection that the named constant had the value zero.
Then when the infrequently encountered path that invokes the function
actually happens, it won't be at all obvious where things went wrong.
I realize that you're stuck with this feature (of treating literal or
named constant with the special value of zero differently in this
case) but when the invocation would have been standard-conforming
without ALLOW_NULL and the meaning is changed by ALLOW_NULL it
would be less scary to me if a warning were printed out.
> In the early days of DVF, we had to create silly-looking values such
> as NULL_SECURITY_ATTRIBUTES if you wanted to omit that argument in a
> Win32 call - this was declared as a nullified pointer of the proper
> type. By creating the ALLOW_NULL attribute, coding to the Win32 API
> was made easier. In general, we use this internally in the provided
> Win32 API modules and don't expect customers to use it in their own
> code (though it's documented in case you want to.)
Definitely a reasonable extension, with the one worry above.
Now I was able to reconstruct what I didn't like that gfortran did
in a similar situation but with bind(C):
C:\gfortran\clf\NULLtest>type NULLtest3.f90
program NULLtest3
use ISO_C_BINDING
implicit none
interface
subroutine sub(x) bind(C)
use ISO_C_BINDING
implicit none
type(C_PTR) x
end subroutine sub
end interface
type(C_PTR) cp
character(kind=C_CHAR), pointer :: string(:)
type(C_PTR), pointer :: fp
character(*,C_CHAR), parameter :: message = 'Test message'//achar(0)
nullify(fp)
write(*,'(/a)') 'Result of passing nullified pointer to sub'
call sub(fp)
allocate(fp)
cp = C_NULL_PTR
fp = cp
write(*,'(/a)') 'Result of passing pointer to C_NULL_PTR to sub'
call sub(fp)
allocate(string(1))
string = achar(0)
cp = C_LOC(string(1))
fp = cp
write(*,'(/a)') 'Result of passing pointer to empty string to sub'
call sub(fp)
deallocate(string)
allocate(string(len(message)))
! string = transfer(message,string) ! As before
string = transfer(message,[message(1:1)])
cp = C_LOC(string(1))
fp = cp
write(*,'(/a)') 'Result of passing message to sub'
call sub(fp)
write(*,'(/a)') 'Result of passing C_NULL_PTR to sub'
call sub(C_NULL_PTR)
write(*,'(/a)') 'Result of passing NULL() to sub'
call sub(NULL())
end program NULLtest3
subroutine sub(x) bind (C)
use ISO_C_BINDING
implicit none
type(C_PTR), value :: x
type(C_PTR), pointer :: ptr
character(kind=C_CHAR), pointer :: string(:)
integer n
if(C_ASSOCIATED(x)) then
call C_F_POINTER(x, ptr)
if(C_ASSOCIATED(ptr)) then
n = 1
do
call C_F_POINTER(ptr, string, [n])
if(string(n) == achar(0)) exit
n = n+1
end do
if(n == 1) then
write(*,*) 'Sub received pointer to empty string'
else
write(*,*) 'Sub received pointer to: ',string(1:n-1)
end if
else
write(*,*) 'Sub received pointer to NULL'
end if
else
write(*,*) 'Sub received NULL input'
end if
end subroutine sub
C:\gfortran\clf\NULLtest>c:\gfortran\win64\bin\x86_64-pc-mingw32-gfortran
NULLte
st3.f90 -oNULLtest3
C:\gfortran\clf\NULLtest>NULLtest3
Result of passing nullified pointer to sub
Sub received NULL input
Result of passing pointer to C_NULL_PTR to sub
Sub received pointer to NULL
Result of passing pointer to empty string to sub
Sub received pointer to empty string
Result of passing message to sub
Sub received pointer to: Test message
Result of passing C_NULL_PTR to sub
Sub received pointer to NULL
Result of passing NULL() to sub
Sub received pointer to NULL
It's the result of the last test that I don't like: the NULL()
intrinsic should yield a nullified pointer so the result should
be the same as the first test. I'm not sure why it isn't here.
module stuff
implicit none
contains
subroutine nonsense(p)
real, pointer :: p
write(*,*) associated(p)
end subroutine nonsense
end module stuff
program NULLtest4
use stuff
implicit none
real, pointer :: p
call nonsense(NULL())
call nonsense(NULL(NULL(p)))
end program NULLtest4
And since structure construction is supposed to work 'as if' it were
a bunch of componentwise assignment statements, will this work when
gfortran's handling of mixed integer/logical operations is updated?
Will it be documented?
program mixtest
implicit none
type tl
logical x
end type tl
type ti
integer x
end type ti
logical l
integer i
l = 1
i = .TRUE.
write(*,*) l, i
write(*,*) tl(1)
write(*,*) ti(.TRUE.)
end program mixtest