Compilers disagree on whether a program END statement can be a target when a contained section is present. Although unusual in my experience I see nothing in the standard so far that says it should not work. I was going to submit this as a bug stating it should run, even though it is trivial to work around. Does anyone think it is standard to not work? A number of programs have “goto 9999” i n them with “9999 end” that were undergoing some modernization where statement functions were being converted to contained functions when this came up. Ultimately the GOTOs will probably be removed, and adding “9999 CONTINUE” just before the contains instead of labeling the END is a simple work-around but the way I read it the standard says that should work, and it does with Intel and flang compilers but fails with nvfortran and gfortran so far.
I know the code is valid, but I’m curious as to why would you put a label on the end statement as a goto target, instead of just issuing a stop statement?
I did not do it originally. Old code. There was no CONTINUE statement in the standard, which was a lot clearer than a GOTO to an executable statement that acted on data. So a GOTO to an executable line that changed
no data probably was a good practice for the time. I do not remember ever numbering an END in a subroutine, function or program myself; but that may be because it did not occur to me that it was legal. I vaguely remember being surprised by it the first time I saw it – sort of like a zero in column 6 of the first of a continued statement in fixed form. I used Fortran for quite a while before I tried to use a zero to continue a line on the second line and got very confused. I see numbered END and RETURN on and off in others’ code and when all there was was a few different GOTO forms for flow control it seems a good idea. I almost always see it numbered 999 or 9999. If that convention was followed then seeing a GOTO 9999 meant you knew that was exiting the procedure or program – something clear and simple when it was very easy to create spaghetti code.
McCracken’s “A guide to FORTRAN programming” (Wiley, 1961) gives details of ten FORTRAN dialects. They all had CONTINUE statements. So does every Fortran standard from f66 to f2023.
The CONTAINS statement separates the body of a main program, module, submodule, or subprogram from any internal or module subprograms it might contain, or it introduces the type-bound procedure part of a derived-type definition (7.5.5). The CONTAINS statement is not executable.
This infers that the label on the END PROGRAM is outside the scope of the main program code, and is basically useless text. Gfortran seems to be doing the right thing.
CONTINUE has been a part of Fortran since the earliest IBM compilers. Far fewer uses now than in the early days though.
Section 5.3.3 says, “The end-program-stmt , end-function-stmt , end-subroutine-stmt , and end-mp-subprogram-stmt statements are executable, and may be branch target statements (11.2).”
Since it is not allowed to branch from within a contained procedure to the end-program-stmt, that sentence only has meaning to branch from before the contains statement, from somewhere within the body of the main program, to the end-program-stmt.
It only has meaning if there is no CONTAINS section. Otherwise you could write:
program hello
implicit none
call message ()
contains
subroutine message ()
print *, ‘hello world!’
go to 99999
end subroutine
99999 end program
which is also illegal.
There has long been a school of thought that there should only be a single exit point in a procedure. No early RETURNs sprinkled throughout the procedure. Thought being that any cleanup and/or error handling be done at a single common location. It is one of the few use cases where GOTO makes sense. (I don’t necessarily advocate it, but I’ve seen a lot of code written that way.)
If no cleanup needs to be done, simply placing a statement label on the END statement, and GOTOing to it throughout the procedure might make sense. However the presence of CONTAINS breaks this. So one would need to use a labeled CONTINUE just before the CONTAINS statement to get the same effect.
A more modern approach when a common cleanup code is required, could be to surround the bulk of the code with a BLOCK construct. Then EXIT as needed to skip to the cleanup code. Though some might argue it is “six of one, half dozen of the other”…
I find I use BLOCK quite a bit to refactor old code that has several GOTOs that exit out of nested loops etc to WRITE or PRINT statements that print out error conditions. ie. I will replace something like the following
Subroutine sub1
If (some_init_error) GO TO 1000
Do 300 k=1,nk
...
If (error1) GO TO 2000
Do 200 j=1,nk
If (error2) GO TO 3000
...
If (error4) GO TO 4000
Do 100, i=1,nk
If (error5) GO TO 5000
100 Continue
200 Continue
300 Continue
1000 Write(stderr,'(" Error 1000")')
Go To 6000
2000 Write(stderr,'(" Error 2000")')
Go To 6000
3000 Write(stderr,'(" Error 3000")')
Go To 6000
4000 Write(stderr,'("Error 4000")')
Go to 6000
5000 Write(stderr,'(Error 5000")')
6000 Continue
Return
End Subroutine sub1
with the something like the following.
Subroutine sub1
Integer :: error_flag
BLOCK1 :: BLOCK
If (some_init_error) Then
error_flag = 1000
EXIT BLOCK1
End If
Do k=1,nk
...
If (error1) Then
error_flag = 2000
EXIT BLOCK1
End If
Do j=1,nk
If (error2) Then
error_flag = 3000
EXIT BLOCK1
End If
...
If (error4) Then
error_flag = 4000
EXIT BLOCK1
End If
Do i=1,nk
If (error5) Then
error_flag = 5000
Exit BLOCK1
End If
End Do
End Do
End Do
End BLOCK BLOCK1
Select Case (error_flag)
Case(1000)
Write(stderr,'(" Error 1000")')
Case(2000)
Write(stderr,'(" Error 2000")')
Case(3000)
Write(stderr,'(" Error 3000")')
Case(4000)
Write(stderr,'("Error 4000")')
Case(5000)
Write(stderr,'(Error 5000")')
End Select
Return
End Subroutine sub1
I like to use the old statement labels as the error flags for the SELECT CASE because it gives me a reference back to the original code. I agree that using GO TOs for this case might be a little easier and it probably comes down to a matter of taste. My position on GO TOs is they would be a lot more palatable if we could have alphanumeric labels instead of just numeric ones. Given we now have something similar with named constructs (DO, BLOCK, IF etc) I would think that would be something that would be fairly straightforward to do.
The first code I used BLOCK to replace GO TOs was the PCHIP monotone interpolators in the netlib SLATEC library here. I would also like to see what spag or fpt would generate with the PCHIP source. The pattern I illustrated above occurs in most of the routines in PCHIP. Using BLOCK was the only way I could see that would allow me to replace the GO TOs in a clean way
program hello
implicit none
call message ()
goto 9999
contains
subroutine message ()
print *, ‘hello world!’
end subroutine
99999 end program
I agree on that, the question to me is it the end program allowed to be a target of the goto in the main program? You cannot leave the end-prgm off; so it is not completely ignored after the contains statement. At least the intel compilers allow to goto; when I saw the GNU bug report I thought the original question was answered and the intel interpretation was standard and not an extension, but after some of these discussions I am not so sure. Do any other compilers allow the original mini-reproducer?
Again, it is perfectly fine to place a label on an END statement. However you can only branch to it IFF there is no CONTAINS section. Once the CONTAINS statement appears, the label is out of anyone’s scope and can not be branched to. A curious little corner case, and fortunately harmless.
The comment in the gfortran bug report is wrong. The compiler is doing the right thing.
There were a few compilers prior to F90 that supported some form of internal procedures. The 36-bit Univac, now Unisys, compilers are examples. I have no idea if, in the presence of internal procedures, there was a way to branch to the program units’ END statement or not.
That section does not state any restrictions regarding statement label targets. Neither does the section that I quoted, Section 5.3.3. I interpreted that lack of restriction to imply that branching to that target is allowed, while you interpreted that lack of restriction to imply that branching is not allowed.
It is not the lack of an explicit restriction. It is the “separation” from the main body of code. The END statement resides after the separation point.
Think of whatever comes from the contains to right before the end-<unit>-stmt as not really being there, but still having access to the parent scope —pretty much like interfaces being inside a module but still requiring an import statement.
(GCC and Intel implement contained procedures in terms of an executable stack, so it makes sense to think of contained procedure invocations as jumps elsewhere.)
The standard says that the contains statement is not executable, which means any implementation doesn’t have to rely on it to mark the end of the program unit —only the end-<unit>-stmt counts and probably there’s no special there-was-a-contains flag at runtime.
The contained subprograms are still separated from the main body of the code even with the label on the END PROGRAM statement, and that statement is 1) not part of any of the contained subprograms, and 2) is still the last line of the program unit.
Is this something that the standard committee needs to clarify?