Certainly a short example of how a "correct" subroutine (ie. can be
used
by other programs and the original program in other places with no ill
effect) can cause a problem.
So now what I was looking for was a tool that warned me about the
passed values so we'd have a tool to check for this type of error.
I found tests of the little "BAD" code did curious things depending
on which compiler I used, which I expected but found interesting in
a cautionary-tail sort of way. Does anyone consider that the behavior
should be determinant? Every compiler I tried did something different.
Try
to guess if you get an infinite loop with the counter reset to 1 for
each
pass, the right number of passes but the value of I10 always 1 after
the
first call; always 1 after the call but reset at the top of the loop;
seg faults .... what do YOUR favorite compilers do? Note that
several compilers did different things depending on the switches used.
My own habit formed in pre-f90+ days is to always assign the iterator
to another variable before using it in a procedure; but I was
wondering
if it was time to use a a better "best practice" to get a pass-by-
value
behavior. Something like
call changeit(int(i10)) ! kludgy and possible expensive; easy to
remove
or
do i10=2,100;i10_copy=i10 ! have a copy of the counter handy at top
or
icopy=i10;call changeit(icopy) ! Do a copy right before a procedure
call
(Hey! I actually found a place I like a semi-colon in the last two!)
and to double check that something hadn't become standard and slipped
by
me like the DEC % syntax for passing by value or a way to define a
value
PROTECTED while in a do-loop but not outside of it.
I was wondering
a) is it "fair" to expect the compiler to warn when do-iterators
are
used as arguments to non-intrinsic procedures where the argument
is
not explicitly intent(in)? None I tried did.
b) should it be expected that an executable be "smart" enough to
know certain variables are read-only by definition and describe
the
error encountered at run time? segfaulting is better than
getting
a "wrong" answer, but it isn't very informative and might not
have
occurred using different compiler options
c) is the behavior of lines using the i10 iterator variable totally
undefined by the standard once the changeit(3f) procedure has
been
called, meaning the segfault is OK? Most compilers "allowed"
the
change and reset i10 to the counter at the top of each pass,
which
made me concerned this code is no longer illegal.
d) I wish Fortran said the compiler has to enforce an automatic
PROTECTED attribute on a DO-iterator inside the loop. It
doesn't,
right?
f) Does anyone have a compiler that produces an intelligible
warning
at compile or execution time? My experience is that
it's useful to show your vendor a competitor does it better
In my environment I know there will be discussions on whether there
are
ways to never encounter this problem; whether the segfault was the
"good"
behavior and should actually be expected (meaning that any compiler
that
runs this problem is considered to have a bug); whether all runs made
using the previous code are now suspect/unusable or whether we can
confirm
expected behavior for a particular compiler (in which case we can tell
whether bad results were producable or not by looking at the code) ...
so I'm basically guessing what questions I'll have to answer tomorrow
and
realized there might be better answers now-adays than my f77-biased
answers
(habits form after a few million lines of code, after all).
I am hoping to come to the battle well-armed with your responses to
1) Should the compiler be expected to positively prevent a do-
iterator
from changing(in which case any executable that does not fail
has a
compiler bug)?
2) If it can't then shouldn't this code be allowed and have an
expected
behavior defined by the vendor if not the Fortran standard (in
which case the segfault might be considered a bug , being that
two
compilers from the same vendor act differently)?
3) Does any compiler or tool warn about passed do-iterators?
4) What is the "best practice" for preventing this? (ie., if
other languages suggest better ways to ensure a value is read-
only or
pass-by-value-only in certain regions does Fortran now have one
and I missed it?)
Thanks in advance for your replies!
============================================================================
> To make a long story shorter, a much more complicated code than the
> little
> routine below passed an iteration count of a do loop down thru a
> series of
> subroutines and changed it.
BAD!!! BAD!!! A loss of several attaboys or worse. ;-)
In the bad old days the DO counter was in a register and changes
to storage were ignored. So much for history. That is "why" such
changing the DO index is contrary to the Fortran standard.
(Your formmatting makes it hard to read becasuse of the slightly too
long lines.)
Try turning on ALL debugging aids as the error which finally kills
your program may well be a subscript error. Knowing the apparent
technical reason is only a minor curiousity for such an elementary
violation of the standard Fortran semantics.
For more serious debugging you need to try one of the debugging
compilers. That means Salford (or Silverfrost as they now call
themselves) or Lahey's Fujitsu. These are mostly noteworthy for
their undefined variable checking so may not notice the change to
a DO index across a call. When you use a debugging compiler you have
to enable all the options and tolerate a performance hit.
One of my earlier Fortran subroutines, in preparation for writing
a calendar program, printed out four big digits at the top of the page.
To test it out for all digits,
DO I=123,1111,9999
The subroutine has to separate the input into four digits, and
in doing that it set the input variable to zero. That was on
a batch system with a 5000 line limit. At about 20 lines/page that
was 250 pages. It ran the first time, or maybe only the first time
it compiled correctly, printing out 250 pages with 2222 at the top.
(It seems that there was an off by one error in there somewhere.)
> I found tests of the little "BAD" code did curious things depending
> on which compiler I used, which I expected but found interesting in
> a cautionary-tail sort of way. Does anyone consider that the behavior
> should be determinant? Every compiler I tried did something different.
There are many ways to do it, yes. Some will keep the loop variable
in a register, storing into the actual variable each time. (The loop
executes the appropriate number of times), others assume that the
value hasn't changed and use the new value.
> Try to guess if you get an infinite loop with the counter reset
> to 1 for each pass, the right number of passes but the value of
> I10 always 1 after the first call; always 1 after the call but
> reset at the top of the loop; seg faults .... what do YOUR
> favorite compilers do? Note that several compilers did different
> things depending on the switches used.
Maybe even different for different loops in the same program.
Especially on nested loops, the compilers run out of registers
and use different tricks to get around that problem.
(snip)
> I was wondering
> a) is it "fair" to expect the compiler to warn when do-iterators
> are used as arguments to non-intrinsic procedures where the argument
> is not explicitly intent(in)? None I tried did.
Probably not. Especially with separate compilation it might not
even know what the subroutine does. Too much old code (before intent)
would get warnings.
> b) should it be expected that an executable be "smart" enough to
> know certain variables are read-only by definition and describe
> the error encountered at run time? segfaulting is better than
> getting a "wrong" answer, but it isn't very informative and
> might not have occurred using different compiler options
Not all systems have the ability to do that. Many can put constants
into read only storage, but in this case it needs to be writable
by the DO loop, but not by the subroutine. That is hard.
> c) is the behavior of lines using the i10 iterator variable totally
> undefined by the standard once the changeit(3f) procedure has
> been called, meaning the segfault is OK? Most compilers "allowed"
> the change and reset i10 to the counter at the top of each pass,
> which made me concerned this code is no longer illegal.
It is illegal, always has been, and has caused problems for a long time.
Compilers are not required to catch it (especially with separate
compilation).
> d) I wish Fortran said the compiler has to enforce an automatic
> PROTECTED attribute on a DO-iterator inside the loop. It
> doesn't, right?
> f) Does anyone have a compiler that produces an intelligible
> warning at compile or execution time? My experience is that
> it's useful to show your vendor a competitor does it better
Maybe for internal subroutines, where the compiler has a chance
to notice.
> In my environment I know there will be discussions on whether there
> are ways to never encounter this problem; whether the segfault was
> the "good" behavior and should actually be expected (meaning that
> any compiler that runs this problem is considered to have a bug);
> whether all runs made using the previous code are now
> suspect/unusable or whether we can confirm expected behavior for
> a particular compiler (in which case we can tell whether bad results
> were producable or not by looking at the code) ...
> so I'm basically guessing what questions I'll have to answer
> tomorrow and realized there might be better answers now-adays
> than my f77-biased answers (habits form after a few million lines
> of code, after all).
(snip)
-- glen
First attempt:
C:\gfortran\clf\change_do>type change_do.f90
module all_my_funcs
implicit none
contains
subroutine changeit1(i)
integer, intent(inout) :: i
i=1 ! change a do-iterator
end subroutine changeit1
end module all_my_funcs
! CASE I: REPLICATOR
program bad
use all_my_funcs
implicit none
integer i10
do i10=2,10
write(*,*)'before',i10 ! fun guessing the value after the
! first pass
! -- should following call always fail at run time?
! -- should compiler warn about passing i10?
call changeit(i10) ! bad place to change i10
write(*,*)'after',i10
enddo
write(*,*)'out',i10
call changeit(i10) ! OK place to change i10
write(*,*)'end',i10
end program bad
subroutine changeit(i)
i=1 ! change a do-iterator
end subroutine changeit
C:\gfortran\clf\change_do>gfortran change_do.f90 -ochange_do
C:\gfortran\clf\change_do>
Already an error. N1601.pdf, section 5.1.2.7 says:
"The INTENT (INOUT) attribute for a nonpointer dummy argument
specifies that it is intended for use both to receive data from and
to return data to the invoking scoping unit. Such a dummy argument
may be referenced or defined. Any actual argument that becomes
associated with such a dummy argument shall be definable."
Since a DO loop variable is not definable, gfortran should have
busted the above.
Second attempt:
C:\gfortran\clf\change_do>type change_do2.f90
module all_my_funcs
implicit none
contains
subroutine changeit1(i)
integer, value :: i
i=1 ! change a do-iterator
end subroutine changeit1
end module all_my_funcs
! CASE I: REPLICATOR
program bad
use all_my_funcs
implicit none
integer i10
do i10=2,10
write(*,*)'before',i10 ! fun guessing the value after the
! first pass
! -- should following call always fail at run time?
! -- should compiler warn about passing i10?
call changeit(i10) ! bad place to change i10
write(*,*)'after',i10
enddo
write(*,*)'out',i10
call changeit(i10) ! OK place to change i10
write(*,*)'end',i10
end program bad
subroutine changeit(i)
i=1 ! change a do-iterator
end subroutine changeit
C:\gfortran\clf\change_do>gfortran change_do2.f90 -ochange_do2
C:\gfortran\clf\change_do>change_do2
before 2
after 1
before 2
after 1
before 2
after 1
Now, that should have worked. Is this stuff fixed in the
latest gfortran? Mine is:
C:\gfortran\clf\change_do>gfortran -v
Built by Equation Solution <http://www.Equation.com>.
Using built-in specs.
COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=c:/gcc_equation/bin/../libexec/gcc/x86_64-pc-mingw32/4.5.0/l
to-wrapper.exe
Target: x86_64-pc-mingw32
Configured with:
../gcc-4.5-20091008-mingw/configure --host=x86_64-pc-mingw32 --
build=x86_64-unknown-linux-gnu --target=x86_64-pc-mingw32 --prefix=/home/gfortra
n/gcc-home/binary/mingw32/native/x86_64/gcc/4.5-20091008 --with-gmp=/home/gfortr
an/gcc-home/binary/mingw32/native/x86_64/gmp --with-mpfr=/home/gfortran/gcc-home
/binary/mingw32/native/x86_64/mpfr --with-sysroot=/home/gfortran/gcc-home/binary
/mingw32/cross/x86_64/gcc/4.5-20091008 --with-gcc --with-gnu-ld --with-gnu-as
--
disable-shared --disable-nls --disable-tls --enable-libgomp --enable-languages=c
,fortran,c++ --enable-threads=win32 --disable-win32-registry
Thread model: win32
gcc version 4.5.0 20091008 (experimental) (GCC)
--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end
I've skipped over a lot of the questions because some seemed to just
rephrase others, so I didn't answer twice, even to just say something
like "see above". A few I didn't have much useful to say about anyway.
> a) is it "fair" to expect the compiler to warn when do-iterators are
> used as arguments to non-intrinsic procedures where the argument is not
> explicitly intent(in)? None I tried did.
Probably not, as evidenced by the fact that you found none that did. If
some did and some didn't, that might suggest something different, but if
none did, that seems to suggest a consensus that it isn't worth doing.
It is certainly possible to do, and probably not a huge amount of work,
but I suspect the benefits would be questionable.
I have heard of other people running into such bugs, but I don't think
of it as a widespread problem. So compiler vendors probably don't get a
lot of requests for such a feature. Admitedly, it can be a hard one to
debug, as you found.
Such a feature would probably give lots of false positives, which would
lower its practical benefit. For a start, F77 and before didn't have
intent(in), so you'd hit any f77 code that passed index variables as
arguments. That would be lots of f77 codes.
You could try requesting it of vendors. It isn't as though it is an
unreasonable suggestion. I just suspect that the cost/benefit to make it
happen isn't there. All the false positives might even make the benefit
look negative to a vendor who might anticipate lots of support questions
relating to them.
> b) should it be expected that an executable be "smart" enough to
> know certain variables are read-only by definition and describe
> the error encountered at run time?
No. As you noticed, it doesn't happen. I can't see it happening either.
They aren't actually read-only because the values do change at run-time.
Seems like you are asking for them to be read-only during some parts of
execution, but not at others. I don't know of a practical way to do that
with current typical hardware; maybe there is one, but I don't offhand
see it. I'd guess that doing it purely in software, without hardware
assist, would probably add significant overhead.
> c) is the behavior of lines using the i10 iterator variable totally
> undefined by the standard
That one is easy. One can argue about how things ought to be, but
that's a straightforward question about how the standard is. The answer
is "yes, it is totally undefined." That's a typical kind of "the
compiler is allowed to start WWIII" scenario.
> there might be better answers now-adays than my f77-biased answers
> (habits form after a few million lines of code, after all).
Yes, but that's also part of why I think that getting compilers to warn
about it isn't going to happen. All that other f77 code, as well as the
new code written with the same habits, would give false positives.
> 4) What is the "best practice" for preventing this?
You've already alluded to what I think is the single best practice in
this area. Specify INTENT for pretty much every dummy argument. There
are a few cases where you can't specify intent (procedure dummy
arguments, pointer dummy arguments before f2003, alternate returns - but
I don't recommend using alternate returns anyway). All those cases
together still amount to a pretty small fraction. Specify INTENT for all
the rest. Make it part of your coding standards, including one of the
things checked in whatever code review process you have.
Oh, and if you have a procedure that actually needs unspecified intent
because an argument is used differerently in different calls... redesign
to not do that. Maybe it needs to be two different arguments. To me,
that's a big red flag.
F2003 adds the value attribute, which could also be useful in some
cases. But I recommend that you *NOT* go willy nilly and start just
specifying VALUE for everywhere that used to be intent(in). That will
just introduce a different class of errors that are hard to debug, where
you wanted to change a variable and think you did, but the change is
actually only to a local copy. There are times when changing a local
copy is exactly what you want. But there are other times when you
shouldn't be changing it at all and it would be better to get an error
message instead of compiling to code that does the wrong thing. In my
opinion, INTENT(IN) is a better match to the needs in most places than
VALUE. So leave the INTENT(IN) dummies that way unless you have one
where you consciously conclude that VALUE is the preferred behavior.
VALUE is great in some cases, but use it only in those cases - not as a
global "improvement" on INTENT(IN). Used without thought, such an
"improvement" can hurt performance (think needless temporary array
copies) and hide bugs.
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
> On 2009-11-19 11:17:44 -0400, John <urba...@comcast.net> said:
>
>> To make a long story shorter, a much more complicated code than the
>> little
>> routine below passed an iteration count of a do loop down thru a
>> series of
>> subroutines and changed it.
>
> BAD!!! BAD!!! A loss of several attaboys or worse. ;-)
>
> In the bad old days the DO counter was in a register and changes
> to storage were ignored. So much for history. That is "why" such
> changing the DO index is contrary to the Fortran standard.
>
> (Your formmatting makes it hard to read becasuse of the slightly too
> long lines.)
>
> Try turning on ALL debugging aids as the error which finally kills
> your program may well be a subscript error. Knowing the apparent
> technical reason is only a minor curiousity for such an elementary
> violation of the standard Fortran semantics.
>
> For more serious debugging you need to try one of the debugging
> compilers. That means Salford (or Silverfrost as they now call
> themselves) or Lahey's Fujitsu. These are mostly noteworthy for
> their undefined variable checking so may not notice the change to
> a DO index across a call. When you use a debugging compiler you have
> to enable all the options and tolerate a performance hit.
>
As a followup I tried a simple example on my NAG f95. The results are
better than I had expected. The NAG version is 5.1 as they have not
gotten around to releasing 5.2 for MacOsX.
The problem for an implementer is that the subroutine may have
correct uses in other contexts but is not permitted here. So
intent(in) in the source is not a fix. The analysis here seems to
have been a typing of the arguements which was then passed in
hidden arguments that are unique to the debug code. Nice! I would
have thought of caching the DO index and checking on return but
that is just for DO indices. But then I do not write compilers
and had thought about it for a little time (under thirty seconds).
> Gordons-Mac-4:PLTB gordon$ f95 -C=all DO_test.f90
> Gordons-Mac-4:PLTB gordon$ ./a.out
> 1
> Dummy argument N is associated with an expression - cannot assign
> Program terminated by fatal error
> Abort trap
> program dotest
> implicit none
> integer::i
> do i=1,10
> write(6,'(1x,i0)')i
> call incr(i)
> write(6,'(1x,i0)')i
> end do
> stop
> end program dotest
> subroutine incr(n)
> implicit none
> integer::n
> n=n+1
> return
> end subroutine incr
[snip]
With
gcc version 4.5.0 20091118 (experimental) (GCC)
$ gfortran -fcheck=do doloop.f90
$ ./a.out
before 2
after 1
At line 16 of file doloop.f90
Fortran runtime error: Loop variable has been modified
Although for procedures with an explicit interface, it should be
possible to catch this at compile time; at the moment gfortran is not
able to do this.
> Second attempt:
[snip]
$ gfortran doloop.f90
$ ./a.out
before 2
after 2
before 3
after 3
before 4
after 4
before 5
after 5
before 6
after 6
before 7
after 7
before 8
after 8
before 9
after 9
before 10
after 10
out 11
end 11
Also, see
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31593
and links therein for some ongoing work in this area
(wrt. optimization, not diagnostics, though).
--
JB
> C:\gfortran\clf\change_do>gfortran change_do.f90 -ochange_do
You should have used:
$ gfortran -fcheck=all change_do.f90
$ ./a.out
before 2
after 1
At line 15 of file change_do.f90
(snip)
>
> C:\gfortran\clf\change_do>gfortran change_do.f90 -ochange_do
>
> C:\gfortran\clf\change_do>
>
> Already an error. N1601.pdf, section 5.1.2.7 says:
>
> "The INTENT (INOUT) attribute for a nonpointer dummy argument
> specifies that it is intended for use both to receive data from and
> to return data to the invoking scoping unit. Such a dummy argument
> may be referenced or defined. Any actual argument that becomes
> associated with such a dummy argument shall be definable."
>
> Since a DO loop variable is not definable, gfortran should have
> busted the above.
Note, final committee draft of F95, page 128, states:
8.1.4.4.1 (2) The DO variable becomes defined with the value of the
initial
parameter m1.
Whoops, looks like a do-variable is definable. It further states
Except for the incrementation of the DO variable that occurs in
step (3),
the DO variable shall neither be redefined nor become undefined
while
the DO construct is active.
Is your above quoted passage a numbered constraint? If it is not,
then it is a requirement placed on the program(mer). You know
the old saying, "A Fortran processor can do anything it wants
with a nonconforming code, including start WW III."
BTW, what's the point of the module? It's content is never used.
> Second attempt:
(snip is identical to 1first attempt because the module is never
used?)
>
> Now, that should have worked. Is this stuff fixed in the
> latest gfortran?
What is there to fix (besides your nonconforming code)?
--
steve
> As a followup I tried a simple example on my NAG f95. The results are
> better than I had expected.
Indeed. I'm impressed. Kudos to NAG on this one. Guess I was wrong (it
happens) when I said [about dynamically tagging variables as read-only]
>> I'd guess that doing it purely in software, without hardware
>> assist, would probably add significant overhead.
because it looks like that is much what NAG did. Of course, it was in a
debug version, which I might rationalize as defending my comment about
significant overhead, but that would just be rationaliation.
The trick of considering a DO iterator to be an expression for purposes
like this seems neat too.
| I found tests of the little "BAD" code did curious things depending
| on which compiler I used, which I expected but found interesting in
| a cautionary-tail sort of way. Does anyone consider that the behavior
| should be determinant? Every compiler I tried did something different.
Changing the value of a DO variable is not going to
cause some catastrophic failure per se.
However, what's likely to happen is that some subscript reference
becomes out-of-bounds.
Therefore, the actual effects are going to be unpredictable and
depend on the particular compiler and environment,
and of course the particular program.
> With
> gcc version 4.5.0 20091118 (experimental) (GCC)
>> Second attempt:
> [snip]
> $ gfortran doloop.f90
> $ ./a.out
> before 2
> after 2
> before 3
> after 3
OK, so I reloaded:
C:\gfortran\clf\change_do>gfortran -v
Built by Equation Solution <http://www.Equation.com>.
Using built-in specs.
COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=c:/gcc_equation/bin/../libexec/gcc/x86_64-pc-mingw32/4.5.0/l
to-wrapper.exe
Target: x86_64-pc-mingw32
Configured with:
../gcc-4.5-20091112-mingw/configure --host=x86_64-pc-mingw32 --
build=x86_64-unknown-linux-gnu --target=x86_64-pc-mingw32 --prefix=/home/gfortra
n/gcc-home/binary/mingw32/native/x86_64/gcc/4.5-20091112 --with-gmp=/home/gfortr
an/gcc-home/binary/mingw32/native/x86_64/gmp --with-mpfr=/home/gfortran/gcc-home
/binary/mingw32/native/x86_64/mpfr --with-sysroot=/home/gfortran/gcc-home/binary
/mingw32/cross/x86_64/gcc/4.5-20091112 --with-gcc --with-gnu-ld --with-gnu-as
--
disable-shared --disable-nls --disable-tls --enable-libgomp --enable-languages=c
,fortran,c++ --enable-threads=win32 --disable-win32-registry
Thread model: win32
gcc version 4.5.0 20091112 (experimental) (GCC)
So I'm still having problems with this standard conforming code
over here in Win-64 land...
[snip]
Oh, I thought the point of your second example was to show passing
arguments with the VALUE attribute, so I fixed your code to actually
call the module procedure rather than the separate subroutine with an
implicit interface. Perhaps I should have mentioned that.
Of course, when calling the external subroutine, the do loop index is
changed. Then again, the code is not standard conforming so the
compiler can do whatever it pleases (and, due to the limitations of
the usual separate compilation model and the lack of an explicit
interface, it's not possible to detect this at compile time in the
general case). Though I suspect the previously mentioned -fcheck=do
should detect this at runtime.
> So I'm still having problems with this standard conforming code
> over here in Win-64 land...
Sorry, I only use gfortran on Linux, so I can't help you with whatever
win64-specific problems you might have.
--
JB
But I do hate the "WWIII" explanation,even though I'm aware of it's
"merits". My opinion is an ideal standard would not ever have the
statement "result is undefined"; and an ideal language
would not let you write code whose behavior is unknown. For example,
if I replace a DO loop with "equivalent" old seperate lines to
initialize, increment,
and loop (does this bring back memories!):
I10=1
10 CONTINUE
CALL DOSOMETHING(I10)
I10=I10+1
IF(10.LT.22)GOTO 10
Then Fortran defines the behavior; so all the restrictions on do-
iterators and do loops such as not being allowed to change the
iterator
in the loop and not being able to enter into the midst of the loop and
such have evolved to allow optimizations or to force clearer code
structures. I would prefer that being allowed to use the do-iterator
name as an argument in a loop be deprecated, or only allowed if the
iterator has the VALUE attribute; and that compilers warn about it
otherwise (or maybe a new keyword like LOOP..ENDLOOP with no undefined
behaviors and multiple allowed syntaxes).
One of the reasons we prefer Fortran for critical applications is that
the language has less places such mistakes can be made than other
major high-performance languages; being able to eliminate even rarely
encountered ones is useful to us.
Thanks again for all the responses!
Your code is nonconforming. See my other post. The standard forbids
a program that causes a do-variable to become redefined or undefined
while the do-loop is active. Your program is clearly redefining the
do-variable.
--
steve
>> a) is it "fair" to expect the compiler to warn when do-iterators are
>> used as arguments to non-intrinsic procedures where the argument is not
>> explicitly intent(in)? None I tried did.
No, it is not. Having no INTENT is the typical case for older programs.
You do not want to reject them with "sorry, you did not specify an INTENT".
> Probably not, as evidenced by the fact that you found none that did. If
> some did and some didn't, that might suggest something different, but if
> none did, that seems to suggest a consensus that it isn't worth doing.
> It is certainly possible to do, and probably not a huge amount of work,
> but I suspect the benefits would be questionable.
Yes, very questionable for the reason given above. If the procedure has
an explicit interface, I think all compilers will reject the code if the
dummy has an INTENT(OUT) or INTENT(INOUT) attribute.
> I have heard of other people running into such bugs, but I don't think
> of it as a widespread problem. So compiler vendors probably don't get a
> lot of requests for such a feature. Admitedly, it can be a hard one to
> debug, as you found.
Well, for that reason, run-time checks exists. Using -C=all (NAG) or
-fcheck=all (gfortran) definitely diagnoses these problems, but I am
sure also other compilers offer this diagnosis.
My feeling is that most users tend to ignore the options which enable
strict standard checking (e.g. -pedantic -std=f2003) or enable warnings
(e.g. -Wall). One has the option to be very strict by default (e.g. NAG)
or very forgiving (ifort), but either option is inconvenient. (You
usually want to bark at developers and be forgiving to mere users - but
not too forgiving as the program might contain actual errors.) Thus
having the right default settings is difficult. Same goes for warnings;
which one to turn on by default?
Fortunately, bounds checking is nowadays often used and a "check all"
option "silently" enables checks which one would otherwise not choose
(e.g. do-loop checks or recursion checks).
> Such a feature would probably give lots of false positives, which would
> lower its practical benefit. For a start, F77 and before didn't have
> intent(in), so you'd hit any f77 code that passed index variables as
> arguments. That would be lots of f77 codes.
I think the benefit/cost ratio is not very favourable in this case.
Having the policy (for the programmer) to always use explicit interfaces
- via module or host association where possible - and to always use
INTENTs, is more useful. If consistently applied, you cannot run into
this problem.
>> b) should it be expected that an executable be "smart" enough to
>> know certain variables are read-only by definition and describe
>> the error encountered at run time?
Well, you need to have run-time checks for this. It is possible, but
rather slow. Using the checking option, something like that actually
happens, though what check exactly is done, depends on the compiler.
For gfortran, the do-loop check happens just before incrementing the
loop variable; thus it works very reliably but does not give the best
error location. NAG does something trickier and thus has usually a
better error location, but it might miss some do-loop modifications. (I
have somewhere an example where NAG 5.1 misses a change.) One can also
add more checks, but that will make checking really slow and at some
point no one will use the checking anymore because it takes too long.
Tobias
> Your code is nonconforming. See my other post. The standard forbids
> a program that causes a do-variable to become redefined or undefined
> while the do-loop is active. Your program is clearly redefining the
> do-variable.
The problem was that I only had a couple of minutes to make my first
post and I somehow neglected to delete the original subroutine from
my version. Had I done so, the mistake of forgetting to rename the
subroutine in the original invocations would have become evident at
link stage. By the time I got back from hiking I had forgotten all
about renaming the subroutine in the first place. Good thing I
followed accepted practices of posting a complete example rather
than just talking about what my problem was because I was completely
blind to it. The final example is thus:
C:\gfortran\clf\change_do>type change_do2.f90
module all_my_funcs
implicit none
contains
subroutine changeit1(i)
integer, value :: i
i=1 ! change a do-iterator
end subroutine changeit1
end module all_my_funcs
! CASE I: REPLICATOR
program bad
use all_my_funcs
implicit none
integer i10
do i10=2,10
write(*,*)'before',i10 ! fun guessing the value after the
! first pass
! -- should following call always fail at run time?
! -- should compiler warn about passing i10?
call changeit1(i10) ! bad place to change i10
write(*,*)'after',i10
enddo
write(*,*)'out',i10
call changeit1(i10) ! OK place to change i10
write(*,*)'end',i10
end program bad
C:\gfortran\clf\change_do>gfortran change_do2.f90 -ochange_do2
C:\gfortran\clf\change_do>change_do2
before 2
after 2
before 3
after 3
before 4
after 4
before 5
after 5
before 6
after 6
before 7
after 7
before 8
after 8
before 9
after 9
before 10
after 10
out 11
end 11
And so it does work as JB reported.
The for loop in C:
for(i=1;i<=10;i++)
is defined in terms of the assignment, comparison, and increment
operation such that it is legal to modify the variable inside
the loop. It works exactly the same as an assignment, comparison
at the top of the loop, and increment at the bottom.
The Fortran DO loop was, I believe, designed around the index
registers on the 704. Those properties carried through to
Fortran 66, but were changed in Fortran 77. Not allowing the
loop variable to be modified allows for some optimizations
that otherwise can't be done. (Or can be done only after the
compiler verifies that the loop variable isn't modified.)
-- glen
It just occured to me that I am not sure of the requirements in
terms of not changing the value. Is:
I=I+0
allowed or not?
-- glen
No. Nor is I = I.
Regards,
Nick Maclaren.
On 2009-11-20 12:12:27 -0500, glen herrmannsfeldt <g...@ugcs.caltech.edu> said:
> The Fortran DO loop was, I believe, designed around the index
> registers on the 704.
This is a popular story, but Backus explicitly denied it.
See _History of Programming Languages_, edited by Wexelblat,
Academic Press, 1981, 0-12-745040-8, especially at pages 56-57
and later in the Q&A at pages 69-70.
OTOH, when Fortran was later ported to the S/360,
the number of indices allowed increased to 7. Go figure. :-)
But Backus claimed that the number of indices allowed
was the result of a complexity analysis, rather than
a specific hardware consideration. He claimed that
the original compiler was based on an infinite number
of registers, the registers being scheduled later in the compilation.
Backus claims they were concerned with index calculation overhead,
which is entirely consistent with the efficiency concerns
that drove the entire project (Real Programmers ... and all that).
Personally? I wasn't there. :-)
--
Cheers!
Dan Nagle
> My opinion is an ideal standard would not ever have the
> statement "result is undefined"; and an ideal language
> would not let you write code whose behavior is unknown.
Then your "ideal" language and standard would prove far less than ideal
in many other ways. While there are certainly particular cases where the
merits are debatable (and debated), there are often concrete reasons for
the standard to have such specifications (or lack thereof). Some of
those reasons include
1. Performance. I you want to make everything completely determined,
then you will also make everything very slow. Down that path lies
turning off all hardware assist for pretty much anything because
different hardware will do things differently. You'll probably end up
going back to software floatting point. And pretty much forget about any
form of parallelism. Turning off optimizers is also largely a given.
2. Portability. In some sense, this is almost another side of the same
issue as performance. When taken to the extreme the performance of
implementing "every progtam is absolutely deterministic" would be so bad
that no vendors would really do it. Even as the standard is, there are a
few places where vendors decine to follow it quite literally, deciding
that their customers wouldn't like the result.
3. Cost/availaility of compilers. Some of the things in the standard
that cause undefined behavior are that way because it is really hard to
implement otherwise. There goes your selection of compilers. I rather
suspect that the selection would go to zero.
4. And finally, compatablity with pretty much anything already existing.
You'll probably end up throwing out almost every existing substantial
Fortran program out there.
I could probably add some other biggies. Or I could give concrete
examples/support of the above. But it woul dget to be an awfully long
post (even for me) and lots of work.
You might be thinking that "undefined" and its ilk in the Fortran
standard mean that people didn't want to put in the work to specify the
details, or somehing like that. There are probably some cases where that
is so, but I've sat in on a lot of such decisions and the reasons are
quite often some mix of hings like:
1. Machine X can't do the thing in question the same way as machine Y
without penalties that simply will not be accepted. I recall claims that
tightening up some of the IEEE conformance requirements would have cost
a factor of 100 on the machines of one major vendor. I couldn't testify
as to how accurate those claims were, but no way a standard with an
effect like that was going to pass. hat particular issue was the cause
of a lot of stuff that might seem strange in the F2003 material relating
to IEEE.
2. Established existing practice implements this feature in different
ways on different machines.
3. Specifying the result would preclude optimization, parallelisation,
use of some system architectures, etc.
4. Implementation is impactical for any of several reasons. Some
undefined things relate to error conditions. File position after an I/O
error is one example. Sometimes these are even hardware errors.
Yes, someimes these reasons relate to even detecting the situation and
giving a warning message.
One of the purposes of the Fortran standard is as a guide to what you
can coung on to work portably. Part of that purpose is achieved by
telling you what things are not portable. When the standard says that
something is undefined, nonstandard, or something like that, it doesn't
mean you can't ever do those things or that they might not be perfectly
consistent on a particular compiler. It just means that you are put on
warning that there is no guarantee that the sam ething will happen on
other machines.
Realize that the Fortran standard is not in a position to dictate how
machines and operating systems wil be built. That limits what it can
realistically specify. It is most definitely not a case of the Fortran
standard being defined in some ivory tower ideal and then imposed on the
cendors and programmers. The standard is very heavily influenced by
things like vendor feedback on what is practical to implement with
acceptable costs.
Well. I ramble. Not that this is particularly unusual for me. I could
ramble on quite a lot longer, but I think this is (well more than)
enough.
I disagree. I think that it could be done, but not starting from
here. Yes, there would still be unspecified aspects, but it would
always be clear what was unspecified, or the compiler would be
required to insert a run-time trap.
But I really DO mean "not starting from here". Such a language
would be conceptually different from almost all (perhaps all)
current ones, at all levels from specification to implementation.
>You might be thinking that "undefined" and its ilk in the Fortran
>standard mean that people didn't want to put in the work to specify the
>details, or somehing like that. There are probably some cases where that
>is so, but I've sat in on a lot of such decisions and the reasons are
>quite often some mix of hings like: ...
The thing that I think is disgraceful is that most specifications
include both the following as "undefined" without making any
distinction:
The action is essential and fully-defined except for some
minor differences of implementation. An example is the exact
result of adding two reasonable floating-point numbers.
All bets are off if the action occurs. An example is
writing outside the bounds of an array (in an unchecked
implementation).
Fortran merely follows standard practice, and my remark is that
I regard the standard practice as disgraceful. I have heard that
modern Ada is better, but have not had time to check.
Regards,
Nick Maclaren.
>>> My opinion is an ideal standard would not ever have the
>>> statement "result is undefined"; and an ideal language
>>> would not let you write code whose behavior is unknown.
(snip)
> I disagree. I think that it could be done, but not starting from
> here. Yes, there would still be unspecified aspects, but it would
> always be clear what was unspecified, or the compiler would be
> required to insert a run-time trap.
Actually, it sounds at least a little like Java. Java has
many more restrictions on what the compiler and processor can
do than pretty much any other language/system. Among others,
IEEE floating point is required, the bit widths of integers
are specified, and the allowable optimizations are restricted.
> But I really DO mean "not starting from here". Such a language
> would be conceptually different from almost all (perhaps all)
> current ones, at all levels from specification to implementation.
(snip)
> The thing that I think is disgraceful is that most specifications
> include both the following as "undefined" without making any
> distinction:
> The action is essential and fully-defined except for some
> minor differences of implementation. An example is the exact
> result of adding two reasonable floating-point numbers.
Well, in addition both Fortran and C allow for ones complement
and sign magnitude integers. This comes up often on comp.lang.c
when someone makes a suggestion that only applies to twos complement.
Not that I am against allowing the different representations, but
such machines really are rare.
> All bets are off if the action occurs. An example is
> writing outside the bounds of an array (in an unchecked
> implementation).
The 'all bets are off' includes modifying the executable code,
though I don't believe that I have ever seen that happen.
Overwriting the return address on the stack maybe not so
hard, though.
> Fortran merely follows standard practice, and my remark is that
> I regard the standard practice as disgraceful. I have heard that
> modern Ada is better, but have not had time to check.
-- glen
> The 'all bets are off' includes modifying the executable code,
> though I don't believe that I have ever seen that happen.
I have. Heck, the normal implementation of adjustable dimensions on some
CDC compilers involved a procedure modifying its own executable code
using a table of the places where the adjustable dimension value
affected the code.
I've also seen programs that unintentionally tried to execute data as
though it were code. There are multiple scenarios where that can happen.
Passing a variable as an actual argument for a procedure dummy is an
easy one; there are others.
So if the call is wrong it will modify the wrong code? Otherwise
intentionally modified doesn't count. Most now try to keep the code
far from the data, so it isn't so likely as it used to be.
> I've also seen programs that unintentionally tried to execute data as
> though it were code. There are multiple scenarios where that can happen.
> Passing a variable as an actual argument for a procedure dummy is an
> easy one; there are others.
That one I have seen many times. I remember it being especially
popular with beginning Fortran programmers who don't realize
you need to dimension arrays in the subroutine, in which case the
compiler assumes it is a function name as actual argument.
-- glen
What do you think?
See "redefined" above.
Not at all! Java is a disaster. Inter alia, its approach has the
effect of turning (say) numeric bugs into logic ones, which is no
improvement (except to certain computer scientists). And, as you
say, it is incompatible with optimisation.
>> The thing that I think is disgraceful is that most specifications
>> include both the following as "undefined" without making any
>> distinction:
>
>> The action is essential and fully-defined except for some
>> minor differences of implementation. An example is the exact
>> result of adding two reasonable floating-point numbers.
>
>Well, in addition both Fortran and C allow for ones complement
>and sign magnitude integers. This comes up often on comp.lang.c
>when someone makes a suggestion that only applies to twos complement.
People would realise how much of a red herring that was, if more
people still had a clue about optimisation, numerics and exception
handling. The restrictions are needed even for twos' complement,
as soon as you consider any two of those.
>> All bets are off if the action occurs. An example is
>> writing outside the bounds of an array (in an unchecked
>> implementation).
>
>The 'all bets are off' includes modifying the executable code,
>though I don't believe that I have ever seen that happen.
>Overwriting the return address on the stack maybe not so
>hard, though.
I have, often. It doesn't happen in Unix-like systems all that often,
because executable code is read-only, but even now it happens to
interpreted codes, JIT codes, application-loaded executable code,
and so on.
Regards,
Nick Maclaren.