Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Assumed-shape arrays and dummy procedures

63 views
Skip to first unread message

bill

unread,
Apr 4, 2008, 11:49:35 PM4/4/08
to
The code below invokes the subroutine wrapcall with an actual
procedure argument, bar, whose interface differs from the associated
dummy argument, sub, because bar has an explicit-shape argument
declared as x(2) whereas sub has an assumed-shape argument x(:).

module foo
contains
subroutine bar(x)
real, intent(in) :: x(2)

print *, "In bar: x = ", x
end subroutine bar
end module foo

program runfoo
use foo
real :: x(2) = [1, 2]

call wrapcall(x, bar)
contains
subroutine wrapcall(x, sub)
real, intent(in) :: x(:)
interface
subroutine sub(x)
real, intent(in) :: x(:)
end subroutine sub
end interface

print *, "In wrapcall: x = ", x
call sub(x)
end subroutine wrapcall
end program runfoo

Compiling with gfortran we get the output

In wrapcall: x = 1.000000 2.000000
In bar: x = 8.8215270E-39 0.000000

presumably because wrapcall passes a descriptor of x but bar
expects to get the address of x(1). The same happens with g95,
but ifort flags a compilation error:


The characteristics of dummy argument 1 of the associated actual
procedure differ from the characteristics of dummy argument 1 of the
dummy procedure. (12.2) [BAR]
call wrapcall(x, bar)
--------------^
compilation aborted for runfoo.f90 (code 1)


The curious thing is that if we replace the interface block in
wrapcall by

procedure(sarg) :: sub

where sarg has the same (abstract) interface

subroutine sarg(x)
real, intent(in) :: x(:)
end subroutine sarg

then, compiling with g95, everything works fine:

In wrapcall: x = 1. 2.
In bar: x = 1. 2.

It seems perverse that we get a different outcome replacing
the interface block by the procedure statement in this way.
Is this really what the standard says should happen?

Richard Maine

unread,
Apr 5, 2008, 1:03:34 AM4/5/08
to
bill <w.mc...@unsw.edu.au> wrote:

> The code below invokes the subroutine wrapcall with an actual
> procedure argument, bar, whose interface differs from the associated
> dummy argument, sub, because bar has an explicit-shape argument
> declared as x(2) whereas sub has an assumed-shape argument x(:).

Presumably you know this is invalid, as ifort reports.

> The curious thing is that if we replace the interface block in
> wrapcall by
>
> procedure(sarg) :: sub
>
> where sarg has the same (abstract) interface
>
> subroutine sarg(x)
> real, intent(in) :: x(:)
> end subroutine sarg
>
> then, compiling with g95, everything works fine:
>
> In wrapcall: x = 1. 2.
> In bar: x = 1. 2.
>
> It seems perverse that we get a different outcome replacing
> the interface block by the procedure statement in this way.
> Is this really what the standard says should happen?

The standard says the code is invalid. That's pretty much the end of the
story. This is not a case of invalid code that a compiler is required to
be able to diagnose. The compiler is allowed to do anything with it.

I agree that it is a bit curious that the case with a procedure
statement gives a different result from the case with an interface body.
Sounds like there might be a compiler bug buried in there somewhere. But
even if so, this doesn't demonstrate a compiler bug in conformance to
the standard. It just might make me suspicious that, if the two cases
are treated differently, one might be able to come up with valid code
that doesn't work. But you don't have that. You have invalid code that
does "work"; that's not directly a compiler bug in this case.

--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain

James Van Buskirk

unread,
Apr 5, 2008, 9:56:24 PM4/5/08
to
"bill" <w.mc...@unsw.edu.au> wrote in message
news:e450c51d-fbaf-4c18...@s13g2000prd.googlegroups.com...

> The characteristics of dummy argument 1 of the associated actual
> procedure differ from the characteristics of dummy argument 1 of the
> dummy procedure. (12.2) [BAR]
> call wrapcall(x, bar)
> --------------^
> compilation aborted for runfoo.f90 (code 1)

Yeah, ifort has done this since the cvf or even dvf days. It behaves
as though it were building data structures corresponding to dummy
procedures and passing them around via the *.mod files. You get
spoiled by this extra degree of interface checking if dvf/cvf/ifort
is all you ever used, and when you try other compilers generally
you find that they can't distinguish between a function and a
subroutine dummy argument, much less check the whole signature of
the procedure. The reason for this lies in the possibility for a
dummy argument to simply be declared EXTERNAL and impicitly typed
so there is no way for the caller to check its interface. And you
can't require explicit interfaces for everything: what if a
RECURSIVE procedure could be passes itself as an actual argument?
Well, maybe there is no possible useful code where that happens,
but there is no linguistic prohibition either, so you have to take
into account dummies being declared EXTERNAL and implicitly typed,
hence being total wild cards.

There is a good reason for Intel and predecessors to check
procedure arguments possibly to the extent of building data
structures containing their interfaces: mismatched procedure
interfaces are ugly to debug because everything is OK in both the
caller and the callee, it's just the interaction between the two
that's a bug. There is good reason for other vendors not to do
so, however: it's always possible to subvert any procedure
interface checking mechanism, whether intentionally or through
code that is just complicated. Also any feature like this can
be tripped up to the extent of causing errors or ICE. Did
anyone suspect an example was coming?

C:\gfortran\clf\ambinterf>type ambinterf.f90
! File: ambinterf.f90
! Public domain 2008 James Van Buskirk
module interfs
implicit none
contains
subroutine callthru(qsub, option)
implicit logical(q)
integer, intent(in) :: option
external qsub

if(option == 1) then
write(*,'(a)') 'Calling subroutine A'
call A(qsub, 2)
else if(option == 2) then
write(*,'(a)') 'Calling subroutine B'
call B(qsub, 2)
else if(option == 3) then
write(*,'(a)') 'Calling subroutine C'
call C(qsub, 2)
end if
end subroutine callthru

subroutine A(sub, n)
integer, intent(in) :: n
interface
subroutine sub(x)
implicit none


real, intent(in) :: x(:)
end subroutine sub
end interface

real x(n)
integer i

x = [(i,i=1,n)]
call sub(x)
end subroutine A

subroutine subA(x)
real, intent(in) :: x(:)

write(*,*) 'In subA, x = ', x
end subroutine subA

subroutine B(sub, n)
integer, intent(in) :: n
interface
function sub(x)
implicit none
integer, intent(in) :: x
logical sub(x)
end function sub
end interface

write(*,*) 'Result of subB = ', sub(n)
end subroutine B

function subB(x)
integer, intent(in) :: x
logical subB(x)
integer i

subB = [(modulo(i,2) == 0,i=1,x)]
end function subB

subroutine C(sub, n)
integer, intent(in) :: n
interface
function sub(n)
implicit none
integer, intent(in) :: n
integer sub
end function sub
end interface

if(n /= 2) then
write(*,*) 'In subroutine C, result of subC = ', sub(n)
else
write(*,*) 'Nothing to do in subroutine C'
end if
end subroutine C

subroutine subC(n)
integer, intent(in) :: n

write(*,*) 'Shouldn''t have reached this statement'
end subroutine subC
end module interfs

program ambiguous
use interfs
implicit none
integer n

n = 1
call callthru(subA, n)
n = 2
call callthru(subB, n)
n = 3
call callthru(subC, n)
end program ambiguous
! End of file: ambinterf.f90

C:\gfortran\clf\ambinterf>c:\gcc_equation\bin\x86_64-pc-mingw32-gfortran
ambinte
rf.f90 -oambinterf
ambinterf.f90:16.19:

call B(qsub, 2)
1
Error: Type/rank mismatch in argument 'sub' at (1)
ambinterf.f90:19.19:

call C(qsub, 2)
1
Error: Type/rank mismatch in argument 'sub' at (1)
ambinterf.f90:90.14:

use interfs
1
Fatal Error: Can't open module file 'interfs.mod' for reading at (1): No
such fi
le or directory
x86_64-pc-mingw32-gfortran: Internal error: Aborted (program f951)
Please submit a full bug report.
See <http://gcc.gnu.org/bugs.html> for instructions.

C:\gfortran\clf\ambinterf>ifort ambinterf.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.

ambinterf.f90(97) : Error: The shape matching rules of actual arguments and
dumm
y arguments have been violated. [SUBB]
call callthru(subB, n)
-----------------^
compilation aborted for ambinterf.f90 (code 1)

Jeez, I thought gfortran would pass this and ifort reject it but
they are both abusing me here! Is there something really wrong
with the above code?

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


Tobias Burnus

unread,
Apr 6, 2008, 6:02:06 PM4/6/08
to
On Apr 6, 3:56 am, "James Van Buskirk" <not_va...@comcast.net> wrote:
>       subroutine callthru(qsub, option)
>          implicit logical(q)
>          integer, intent(in) :: option
>          external qsub
>
>          if(option == 1) then
>             write(*,'(a)') 'Calling subroutine A'
>             call A(qsub, 2)

"A" takes a subroutine as argument.

>          else if(option == 2) then
>             write(*,'(a)') 'Calling subroutine B'
>             call B(qsub, 2)

While "B" takes a function as argument.

I believe the program is invalid. Quoting the Fortran 2003 standard:

"12.4.1.3 Actual arguments associated with dummy procedure entities"
[...]
"If the interface of the dummy argument is implicit and either the
name of the
dummy argument is explicitly typed or it is referenced as a function,
the dummy
argument shall not be referenced as a subroutine"

As you once reference "qsub" as function and once as subroutine (by
using them as actual argument), the program is invalid and the
compiler is free to reject it. Interestingly, most compiler accept a
reduced version which contains such calls. For the most reduced
version which uses implicit typing, of my compilers, only gfortran
rejects it. For the full program, most of my compilers reject it.

In conclusion, I think gfortran is right and your program is invalid.


>       subroutine  A(sub, n)
>          integer, intent(in) :: n
>          interface
>             subroutine sub(x)
>                implicit none
>                real, intent(in) :: x(:)
>             end subroutine sub
>          end interface

>       subroutine B(sub, n)


>          integer, intent(in) :: n
>          interface
>             function sub(x)
>                implicit none
>                integer, intent(in) :: x
>                logical sub(x)
>             end function sub
>          end interface

Tobias

glen herrmannsfeldt

unread,
Apr 6, 2008, 7:53:18 PM4/6/08
to
Tobias Burnus wrote:
(snip)

> I believe the program is invalid. Quoting the Fortran 2003 standard:
>
> "12.4.1.3 Actual arguments associated with dummy procedure entities"
> [...]
> "If the interface of the dummy argument is implicit and either the
> name of the dummy argument is explicitly typed or it is referenced
> as a function, the dummy argument shall not be referenced
> as a subroutine"

I once did something like:

external sub,fun
call test(sub,.true.,x)
call test(fun,.false.,y)
write(*,*) x,y
end

subroutine test(f,select,xy)
logical select
if(select) call f(xy)
if(.not.select) xy=f(1.23)
return
end

subroutine sub(x)
x=3.4
return
end

function fun(x)
fun=4.5
return
end


Note that sub is always called as a subroutine and fun always
as a function, yet some (most) compiler would refuse it.
It seems that the part of the standard quoted supports the
refusal to compile it.

Error: Unclassifiable statement following IF-clause at (1)

That is, in: if(.not.select) xy=f(1.23)

Commenting out the previous statement allows it to compile.
I do believe a better message could be expected in this case.

-- glen

James Van Buskirk

unread,
Apr 6, 2008, 8:15:35 PM4/6/08
to
"Tobias Burnus" <bur...@net-b.de> wrote in message
news:448ebf53-39c6-4737...@s50g2000hsb.googlegroups.com...

> I believe the program is invalid. Quoting the Fortran 2003 standard:

> "12.4.1.3 Actual arguments associated with dummy procedure entities"
> [...]
> "If the interface of the dummy argument is implicit and either the
> name of the
> dummy argument is explicitly typed or it is referenced as a function,
> the dummy
> argument shall not be referenced as a subroutine"

> As you once reference "qsub" as function and once as subroutine (by
> using them as actual argument), the program is invalid and the
> compiler is free to reject it. Interestingly, most compiler accept a
> reduced version which contains such calls. For the most reduced
> version which uses implicit typing, of my compilers, only gfortran
> rejects it. For the full program, most of my compilers reject it.

> In conclusion, I think gfortran is right and your program is invalid.

OK, I like that passage. However, if we go back to the start of
section 12.4 we see:

"The forms of procedure reference are:"

"R1217 function-reference is procedure-designator([actual-arg-spec-list])"

"R1218 call-stmt is CALL procedure-designator[([actual-arg-spec-lest])]"

So using a procedure as an actual argument does not seem to count as
a reference of the actual argument procedure. The really tough case
lies in subroutine C, and here the program really is nonconforming,
but the compilers were rejecting the program for other reasons.

subroutine C(sub, n)
integer, intent(in) :: n
interface


function sub(n)
implicit none
integer, intent(in) :: n
integer sub

end function sub
end interface

if(n /= 2) then


write(*,*) 'In subroutine C, result of subC = ', sub(n)
else
write(*,*) 'Nothing to do in subroutine C'
end if
end subroutine C

Subroutine C, as seen above, has a function dummy argument but is
ultimately passed a subroutine actual argument. Now if we changed
it around a bit so that the specification part were:

implicit integer s
integer, intent(in) :: n
external sub

the question gets more interesting: does linguistic reference
count as reference or does the thread of execution actually have
to include the procedure reference for it to count as a reference?
Although it may seem obvious that linguistic reference is sufficient,
there are other instances, for example where a pointer with undefined
association status is passed as an actual argument but is still OK
provided the association status of the pointer is never checked nor
relied on in the callee.

Even if subroutine C cannot be passed a subroutine as an actual
argument, we could comment out one of the lines that leads to
invoking it in this fashion and both gfortran and ifort would
still reject it. I think that the standard might permit passing
anonymous procedures around like this. Certainly it could be
done via C_FUNPTRs. Let me know what you think.

P.S. earlier I remarked that f95 doesn't have a way to make all
possible interfaces explicit if a recursive procedure is passed
itself as an actual argument. I recall that I had been informed
a long time ago that it is possible to do this in f03, so another
example:

C:\gfortran\clf\ambinterf>type recurse.f90
module recursive_mod
implicit none
contains
recursive subroutine sub(x, depth)
procedure(sub) x
integer, intent(in) :: depth

if(depth > 0) then
write(*,*) 'In sub, depth = ', depth
call x(sub, depth-1)
end if
end subroutine sub
end module recursive_mod

program test
use recursive_mod
implicit none

call sub(sub, 10)
end program test

C:\gfortran\clf\ambinterf>c:\gcc_equation\bin\x86_64-pc-mingw32-gfortran

recurse
.f90 -orecurse

C:\gfortran\clf\ambinterf>recurse
In sub, depth = 10
In sub, depth = 9
In sub, depth = 8
In sub, depth = 7
In sub, depth = 6
In sub, depth = 5
In sub, depth = 4
In sub, depth = 3
In sub, depth = 2
In sub, depth = 1

Sorry, couldn't resist!

0 new messages