Home > Archive > Fortran > February 2007 > overlap of INTENT(IN) and INTENT(OUT) arguments
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
overlap of INTENT(IN) and INTENT(OUT) arguments
|
|
| Beliavsky 2007-02-20, 7:04 pm |
| For the code
module foo_mod
contains
subroutine set_alloc(ii,jj)
! allocate jj and set it to ii
integer, intent(in) :: ii(:)
integer, intent(out), allocatable :: jj(:)
integer :: ni
ni = size(ii)
allocate (jj(ni))
jj = ii
write (*,*) "jj =",jj
end subroutine set_alloc
end module foo_mod
program xset_alloc
use foo_mod, only: set_alloc
implicit none
integer, allocatable :: ii(:)
allocate (ii(3))
ii = (/10,20,30/)
call set_alloc(ii([1,3]),ii)
call set_alloc(ii(1:1),ii)
call set_alloc(ii,ii)
end program xset_alloc
gfortran says at compile time
In file xxset_alloc.f90:23
call set_alloc(ii,ii)
Warning: Same actual argument associated with INTENT(OUT) argument
'jj' and INTENT(IN) argument 'ii' at (1)
and crashes at run time at line 23. G95 and Intel Fortran do not
complain at compile time and run to completion.
Are all three calls to set_alloc nonstandard? If they are, I can see
how warning about the 3rd case would be much easier than warning about
the first two.
| |
| Craig Powers 2007-02-20, 7:04 pm |
| Beliavsky wrote:
> For the code
>
> module foo_mod
> contains
> subroutine set_alloc(ii,jj)
> ! allocate jj and set it to ii
> integer, intent(in) :: ii(:)
> integer, intent(out), allocatable :: jj(:)
> integer :: ni
> ni = size(ii)
> allocate (jj(ni))
> jj = ii
> write (*,*) "jj =",jj
> end subroutine set_alloc
> end module foo_mod
>
> program xset_alloc
> use foo_mod, only: set_alloc
> implicit none
> integer, allocatable :: ii(:)
> allocate (ii(3))
> ii = (/10,20,30/)
> call set_alloc(ii([1,3]),ii)
> call set_alloc(ii(1:1),ii)
> call set_alloc(ii,ii)
> end program xset_alloc
>
> gfortran says at compile time
>
> In file xxset_alloc.f90:23
> call set_alloc(ii,ii)
> Warning: Same actual argument associated with INTENT(OUT) argument
> 'jj' and INTENT(IN) argument 'ii' at (1)
>
> and crashes at run time at line 23. G95 and Intel Fortran do not
> complain at compile time and run to completion.
> Are all three calls to set_alloc nonstandard? If they are, I can see
> how warning about the 3rd case would be much easier than warning about
> the first two.
I think all three cases fall under the proscription against referring to
the same data via two different names.
| |
| glen herrmannsfeldt 2007-02-20, 7:04 pm |
| Beliavsky wrote:
(snip of subroutine that allocates its second argument)
> program xset_alloc
> use foo_mod, only: set_alloc
> implicit none
> integer, allocatable :: ii(:)
> allocate (ii(3))
> ii = (/10,20,30/)
> call set_alloc(ii([1,3]),ii)
> call set_alloc(ii(1:1),ii)
> call set_alloc(ii,ii)
> end program xset_alloc
> gfortran says at compile time
> In file xxset_alloc.f90:23
> call set_alloc(ii,ii)
> Warning: Same actual argument associated with INTENT(OUT) argument
> 'jj' and INTENT(IN) argument 'ii' at (1)
> and crashes at run time at line 23. G95 and Intel Fortran do not
> complain at compile time and run to completion.
> Are all three calls to set_alloc nonstandard? If they are, I can see
> how warning about the 3rd case would be much easier than warning about
> the first two.
Unless a copy is required, and copy back is guaranteed not to
happen, I think they all have to be illegal. If the first one
qualifies as an expression, and so requires a one directional copy,
it could be legal. The others I think have to be illegal.
Separate from the standard, consider what an implementation
is likely (and allowed) to do. It first allocates jj, and then
expects to be able to copy from ii. Allocating jj deallocates
(I believe) a previous instance of it, such that there isn't
anything to copy. (On some systems the memory may not
actually be reused, but you still aren't allowed to access it.)
On the other hand, if all accesses to ii occurred before any
accesses (especially reallocation) to jj then I would say it
is likely that it would work, standard or not.
The usual cause of failure, and one reason for it being
non-standard, is the possible copy back of the arguments.
Even with INTENT(IN) it might be allowed to do that.
-- glen
| |
| Richard Maine 2007-02-20, 7:04 pm |
| Beliavsky <beliavsky@aol.com> wrote:
> Warning: Same actual argument associated with INTENT(OUT) argument
> 'jj' and INTENT(IN) argument 'ii' at (1)
>
> and crashes at run time at line 23. G95 and Intel Fortran do not
> complain at compile time and run to completion.
> Are all three calls to set_alloc nonstandard? If they are, I can see
> how warning about the 3rd case would be much easier than warning about
> the first two.
Yes, they are all nonstandard (but the kind of thing that compilers
aren't required to catch and often don't). Even without the complication
of the allocatable dummy, they violate one of the anti-aliasing rules.
If you have two arguments aliased (as you do), then neither argument may
become defined or undefined in the subroutine. Note, in this regard,
that one of them being intent(out) is enough to violate this, because an
intent(out) dummy becomes undefined on entry. Yes, that counts.
Without the allocatable bit, it would probably happen to work anyway on
at least some compilers, which essentially do nothing with the
intent(out). However, there have been threads here pointing out that
compilers are increasingly actually making constructive use of
intent(out) for optimizations, breaking codes that do this wrong.
With the allocatable bit of this example... I'm slightly puzzled that
you would even expect it to work. You are passing as one actual argument
the body (or part of it) of an allocatable array that becomes
unallocated on entry (that's what intent(out) does to an allocatable
dummy). The only way this would even vaguely make sense is if you make
assumptions about the order in which arguments are processed. The
standard goes to a lot of trouble to avoid such assumptions.
And no, the intent(in) makes no difference. The intent(out) of the
second argument does matter, but the intent(in) doesn't. I'm thinking
that perhaps you are thinking of intent(in) as making a copy (and doing
so before the intent(out) of the secoind argument destroys what you are
trying to copy). It ain't so.
Depending on the exact detail of the implementation, I can see how this
might - just might - happen to appear to work sometimes. But it is
horribly non-standard. It doesn't even count as an extension - more like
an accident. You are probably referencing memory that has been
dallocated, but not yet overwritten by anything else... or something
like that. That doesn't even count as "working"; the most I'll grant is
that it might appear to work sometimes.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
| |
| glen herrmannsfeldt 2007-02-20, 7:04 pm |
| Craig Powers wrote:
(snip)
(snip)
[color=darkred]
> I think all three cases fall under the proscription against referring to
> the same data via two different names.
I think so, too, but consider one he didn't try:
call set_alloc(ii+1,jj)
To do what he wants to do, a copy of the array must be
made somewhere before the reallocation. As written there
is no guarantee of that copy.
subroutine set_alloc(ii,jj)
! allocate jj and set it to ii
integer, intent(in) :: ii(:)
integer, intent(out), allocatable :: jj(:)
integer :: ni
integer, allocatable :: kk(:)
ni = size(ii)
allocate (jj(ni))
kk = ii
allocate (jj(ni))
jj = kk
write (*,*) "jj =",jj
end subroutine set_alloc
This still might not be legal, but it at least has a possibility
of working.
-- glen
| |
| Richard Maine 2007-02-20, 7:04 pm |
| Craig Powers <enigma@hal-pc.org> wrote:
> I think all three cases fall under the proscription against referring to
> the same data via two different names.
Correct.... except for what is almost the "usual" correction (of many
posters - not you in particular). There is *NOT* a prohibition against
referring to the same data via two different names. That's a quite
common and valid thing to do. Please don't overgeneralize to a false
prohibition, or people will do overly complicated things to avoid a
perfectly legit practice.
There are just prohibitions against what you can do in the presense of
such aliasing. In particular, you can't define or undefine the data via
either of the names. That's what the code violates.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
| |
| Tobias Burnus 2007-02-20, 7:04 pm |
| On 20 Feb., 21:08, "Beliavsky" <beliav...@aol.com> wrote:
> G95 and Intel Fortran do not complain at compile time and run to completion.
Note: if you run the created binary through valgrind:
==2048== Invalid read of size 4
==2048== at 0x4019C9: foo_mod_MP_set_alloc (in /dev/shm/a.out)
==2048== by 0x401E3E: MAIN_ (in /dev/shm/a.out)
==2048== by 0x4084AD: main (in /dev/shm/a.out)
==2048== Address 0x4051638 is 40 bytes inside a block of size 48
free'd
for g95, and for ifort:
==2174== Invalid read of size 4
==2174== at 0x4029AF: foo_mod_mp_set_alloc_ (in /dev/shm/a.out)
==2174== by 0x402883: MAIN__ (in /dev/shm/a.out)
==2174== by 0x402669: main (in /dev/shm/a.out)
==2174== Address 0x40460B0 is 0 bytes inside a block of size 8 free'd
Thus: It does also not work with g95/ifort either.
Tobias
| |
| glen herrmannsfeldt 2007-02-20, 7:04 pm |
| Richard Maine wrote:
(snip)
> And no, the intent(in) makes no difference. The intent(out) of the
> second argument does matter, but the intent(in) doesn't. I'm thinking
> that perhaps you are thinking of intent(in) as making a copy (and doing
> so before the intent(out) of the secoind argument destroys what you are
> trying to copy). It ain't so.
Does the INTENT(IN) stop the copy-out if a copy is made before the
call? Not that it matters in this case, but it might in some.
> Depending on the exact detail of the implementation, I can see how this
> might - just might - happen to appear to work sometimes. But it is
> horribly non-standard. It doesn't even count as an extension - more like
> an accident. You are probably referencing memory that has been
> dallocated, but not yet overwritten by anything else... or something
> like that. That doesn't even count as "working"; the most I'll grant is
> that it might appear to work sometimes.
Unfortunately "appear to work sometimes" makes it take longer to
find bugs.
-- glen
| |
| Greg Lindahl 2007-02-20, 7:04 pm |
| In article <1172005514.805433.297910@t69g2000cwt.googlegroups.com>,
Tobias Burnus <burnus@net-b.de> wrote:
>Note: if you run the created binary through valgrind:
.... and PathScale gets:
==29130== Invalid read of size 4
==29130== at 0x400CED: SET_ALLOC.in.FOO_MOD (xset.f90:10)
==29130== Address 0x517B8A0 is 0 bytes inside a block of size 8 free'd
==29130== at 0x4A0548E: free (vg_replace_malloc.c:233)
==29130== by 0x4C88857: _DEALLOC (in /opt/pathscale/lib/3.0/libpathfortran.so.1)
==29130== by 0x400C12: SET_ALLOC.in.FOO_MOD (xset.f90:3)
==29130== by 0x4013C8: main (in /home/lindahl/a.out)
But this isn't a very user-friendly error!
-- greg
| |
| James Giles 2007-02-20, 7:04 pm |
| glen herrmannsfeldt wrote:
....
> Does the INTENT(IN) stop the copy-out if a copy is made before the
> call? Not that it matters in this case, but it might in some.
I suppose that would qualify as a quality of implementation
issue. Without any doubt, INTENT(IN) (if it's properly enforced)
guarantees that no such copy back would be necessary. I suspect,
without any statistics to back it up, that almost no implementations
copy INTENT(IN) arguments back out at the end of a procedure.
--
J. Giles
"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies." -- C. A. R. Hoare
| |
| Craig Powers 2007-02-20, 7:04 pm |
| Richard Maine wrote:
> Craig Powers <enigma@hal-pc.org> wrote:
>
>
> Correct.... except for what is almost the "usual" correction (of many
> posters - not you in particular). There is *NOT* a prohibition against
> referring to the same data via two different names. That's a quite
> common and valid thing to do. Please don't overgeneralize to a false
> prohibition, or people will do overly complicated things to avoid a
> perfectly legit practice.
>
> There are just prohibitions against what you can do in the presense of
> such aliasing. In particular, you can't define or undefine the data via
> either of the names. That's what the code violates.
Right, that was what I meant, but I obviously didn't word it right.
Thanks for the clarification.
| |
| Dick Hendrickson 2007-02-20, 7:04 pm |
| glen herrmannsfeldt wrote:
> Richard Maine wrote:
>
> (snip)
>
>
> Does the INTENT(IN) stop the copy-out if a copy is made before the
> call? Not that it matters in this case, but it might in some.
There seems to be a general belief that INTENT(IN) somehow affects
the code generator (not just in this thread, but in many of them).
It ain't so. INTENT(IN) imposes restrictions on the
user, not the code generator. It puts some small restrictions on the
front end parser. Things like assignment statements to an INTENT(IN)
argument must be diagnosed (at least I think they must, maybe not)
and some combinations of argument attributes aren't compatible with
INTENT(IN). But, the compiler is free to use copy in/out, or pass
by address, or pass by register, or anything else and doesn't have to
do anything different for INTENT(IN) arguments. The Fortran rule is
that your program must not define an INTENT(IN) argument, not that the
compiler must prevent it. (Having said that about 10 times in the
last couple of sentences, maybe I'm overkilling the point. ;( )
If you actually want to legally "evade" the anti-aliasing rules,
use degenerate expressions, rather than INTENT. It's perfectly legal
to do something like
call Oh_my_gosh( (X), X+0, X*1, X)
then the first three arguments are all quite separate and distinct
from X. Since they are expressions, they can't be assigned to. But,
if X is defined or deallocated (or whatever), the first three arguments
will retain their value.
Dick Hendrickson
>
>
> Unfortunately "appear to work sometimes" makes it take longer to
> find bugs.
>
> -- glen
>
| |
| Richard Maine 2007-02-20, 7:04 pm |
| glen herrmannsfeldt <gah@ugcs.caltech.edu> wrote:
> Richard Maine wrote:
>
> (snip)
>
>
> Does the INTENT(IN) stop the copy-out if a copy is made before the
> call? Not that it matters in this case, but it might in some.
You tend to jump down into implementation-level questions far too early,
in my opinion. The code is just plain illegal. The standard says exactly
zero about what happens. As to what particular implementations might or
might not happen to do with this particular piece of illegality... well,
I don't find that particularly interesting here. One certainly can't say
anything about it in general. Heck, from reports in this thread, some
compilers refused to compile it at all, which certainly makes it
pointless to ask about what they do with it at run-time.
And I'll also second Dick's comments about intent(in). You (and others)
seem to make a lot of assumptions that simply are not true about
intent(in). It doesn't necessarily have anything to do with copies - in,
out, or otherwise. A compiler *might* do come copies, and it *might*
make use of intent to optimize things about them, but that is neither
specified by the standard nor particular guaranteed as a matter of
practice either.
I don't think DIck is overkilling it, because the misconceptions don't
yet appear to be dead. :-)
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
| |
| Beliavsky 2007-02-20, 7:04 pm |
| On Feb 20, 5:52 pm, Dick Hendrickson <dick.hendrick...@att.net> wrote:
> If you actually want to legally "evade" the anti-aliasing rules,
> use degenerate expressions, rather than INTENT. It's perfectly legal
> to do something like
> call Oh_my_gosh( (X), X+0, X*1, X)
>
> then the first three arguments are all quite separate and distinct
> from X. Since they are expressions, they can't be assigned to. But,
> if X is defined or deallocated (or whatever), the first three arguments
> will retain their value.
I wanted to write a subroutine copy(x,y) where y(:) is ALLOCATABLE and
is set to x(:). I often want to remove some elements from an array and
want to write (for example)
call copy(x([1,3]),x)
but this is illegal in this case. I knew that call copy(1*x([1,3]),x)
should work, but I wondered how to generalize this to an array of
derived types. Your simple solution of copy((x([1,3])),x) works. You
are a wise man :).
I believe in Fortran 2003 I could just write
integer, allocatable :: ivec(:)
allocate (ivec(3))
ivec = [10,20,30]
ivec = ivec([1,3])
| |
| glen herrmannsfeldt 2007-02-20, 7:04 pm |
| Richard Maine wrote:
(snip)
> You tend to jump down into implementation-level questions far too early,
> in my opinion. The code is just plain illegal. The standard says exactly
> zero about what happens. As to what particular implementations might or
> might not happen to do with this particular piece of illegality... well,
It has to do with how I remember things. If there is no reasonable
implementation then it is reasonable for the standard to disallow it.
The other way around may not be true. In this case, it is not
reasonable to expect a copy, so anything depending on that copy, one
might consider illegal. (Guilty until proven innocent.)
> I don't find that particularly interesting here. One certainly can't say
> anything about it in general. Heck, from reports in this thread, some
> compilers refused to compile it at all, which certainly makes it
> pointless to ask about what they do with it at run-time.
The one case where a copy is pretty much required is if an argument
is an expression. (With the exception of adding 0 and multiplying
by 1.) It might be still illegal as an expression, but it isn't
so obvious.
Your recent note about the possibility of non-uniform stride arrays
disallowing some operations is an implementation issue. In that case,
I had noted the PL/I answer to the implementation problem: copy in
but don't copy out. (There are many cases where PL/I requires a copy
of an argument. In those cases, as in the usual expression case,
it never copies the result back. Again, in implementation issue
determining language features.)
-- glen
| |
| glen herrmannsfeldt 2007-02-20, 7:04 pm |
| Dick Hendrickson wrote:
(snip)
> If you actually want to legally "evade" the anti-aliasing rules,
> use degenerate expressions, rather than INTENT. It's perfectly legal
> to do something like
> call Oh_my_gosh( (X), X+0, X*1, X)
> then the first three arguments are all quite separate and distinct
> from X. Since they are expressions, they can't be assigned to. But,
> if X is defined or deallocated (or whatever), the first three arguments
> will retain their value.
Is this new? I thought this had been previously discussed and found
that it still wasn't allowed. Note that PL/I does require a
copy to be passed for the first three arguments, and the
result not copied back. Previously discusses was that the compiler
might optimize out the +0 and *1.
-- glen
| |
| James Giles 2007-02-20, 7:04 pm |
| glen herrmannsfeldt wrote:
> Dick Hendrickson wrote:
>
> (snip)
>
>
>
> Is this new? I thought this had been previously discussed and found
> that it still wasn't allowed. Note that PL/I does require a
> copy to be passed for the first three arguments, and the
> result not copied back. Previously discusses was that the compiler
> might optimize out the +0 and *1.
They might optimize out the *operations*, but they can't remove
the fact that the arguments are no longer definable. It is that fact
that determines the semantic qualities of the arguments. If an
actual argument is not a variable or subobject thereof (a quality
that in this case you are informally calling "an expression"), then
it's value is determined prior to the call and that value is what the
corresponding dummy argument is associated with. The corresponding
dummy argument may not be changed by the procedure (not defined,
redefined, or undefined). Altering any other dummy argument
should have no effect on the value of the dummies associated with
such expressions.
--
J. Giles
"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies." -- C. A. R. Hoare
| |
| glen herrmannsfeldt 2007-02-20, 7:04 pm |
| James Giles wrote:
(snip)
(snip)[color=darkred]
> They might optimize out the *operations*, but they can't remove
> the fact that the arguments are no longer definable. It is that fact
> that determines the semantic qualities of the arguments. If an
> actual argument is not a variable or subobject thereof (a quality
> that in this case you are informally calling "an expression"), then
> it's value is determined prior to the call and that value is what the
> corresponding dummy argument is associated with. The corresponding
> dummy argument may not be changed by the procedure (not defined,
> redefined, or undefined).
Yes, that was the one I was remembering. Also, that PL/I specifically
does allow one to modify the copy, with the guarantee that it is only
a copy.
> Altering any other dummy argument
> should have no effect on the value of the dummies associated with
> such expressions.
So the system knows which one (and only one) might change, and so can
take appropriate action.
-- glen
| |
| Richard Maine 2007-02-20, 7:04 pm |
| glen herrmannsfeldt <gah@ugcs.caltech.edu> wrote:
> Dick Hendrickson wrote:
>
> Is this new? I thought this had been previously discussed and found
> that it still wasn't allowed. Note that PL/I does require a
> copy to be passed for the first three arguments, and the
> result not copied back. Previously discusses was that the compiler
> might optimize out the +0 and *1.
I'll ignore the PL/I bit. I'll also mostly ignore what the compiler
might or might not do. The standard says that this is valid. That's the
fundamental here. What the compiler does is make it work. If the
compiler makes it so that standard conforming code doesn't work, then
file a bug report. If, however, you try to generalize to something else
that is not standard conforming, but you think you can deduce ought to
work because you have a particular implementation model in mind, then
that's different.
Sometimes it would work for the compiler to optimize out the +0 and *1.
Other times it wouldn't work. If the compiler can tell the difference
that's fine. But the compiler isn't allowed to just say "what the heck"
and do it anyway.
Note, as Dick and James point out, the expressions are not definable.
This has nothing to do with whether a copy is or is not made. It is
illegal regardless of what the implementation does. Speaking of which,
part of the problem with you jumping too quickly to the implementation
level is that a major point of many restrictions in the language is to
allow multiple implementation choices and prohibit code that depends on
which choice is taken. You will not be able to correctly deduce those
restrictions from looking at any particular implementation choice.
Argument passing is very high on the list of areas where this applies. I
also can remember some things in this area (though not the more esoteric
of them) by thinking about implementations, but only by thinking about
multiple implementations. The multiple part is important there. If you
think about any single implementation strategy, you'll get it wrong, no
matter what the strategy is, because the standard is about multiple
strategies.
And no, none of this is new. Not in at least the last several decades.
It certainly goes back to at least f77. Almost surely earlier, but I
don't care to take the time to check that bit for sure. Even if it was
"nw" to f77 (which I doubt), 30 years old would be a bit much to call
"new" in this business.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
| |
| James Giles 2007-02-20, 10:04 pm |
| glen herrmannsfeldt wrote:
> James Giles wrote:
> (snip)
....[color=darkred]
>
> So the system knows which one (and only one) might change, and so can
> take appropriate action.
Well, I don't know what you mean by "the system" or what
you mean by "knows". When processing the above call, the
implementation must accomodate the fact that the "expression"
arguments are not the same as each other nor the same as the
last argument (which is a variable). They all have the same
value (initially), but they aren't the same entity. Now, in terms
of how the implementation does that, I can't think of any approach
other than to pass a copy. So, that's probably what the standard
intends. *(footnote)
But, the standard has traditionally avoided saying anything about
*how* implemenations do things (the pragmatics of the language).
So, it doesn't say anything about making copies. The rules that
do exist probably admit of no other choice, but the standard doesn't
actually say anything about it. This firewall between semantics and
pragmatics is now breaking down. The CONTIGUOUS attribute is
quite blatantly a pragmatic issue not a semantic one. Maybe we'll
eventually see a clarification that actual arguments that are
expressions are passed as copies.
- * In fact, it may send the *same* copy for the first three
arguments - since changing any of those would be a violation
of the language rules. Implementations are free to assume that
such violations don't happen. Or, alternatively stated, that violations
of the standard can produce any results the implementation chooses,
including starting WWIII.
--
J. Giles
"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies." -- C. A. R. Hoare
|
|
|
|
|