Ending a do loop with continue

I inherited a routine (as part of a larger code) that has an unusual syntax for loops

do 100 i = 1, N
    ! we are inside the loop. perform some repeated task
    continue 100
! we are already out of the loop

At first, I thought I am for sure going to get a lot of “end do missing” as I didn’t see any but to my surprise neither gfortran or ifort complained about it. I assume this is not a standard approach to writing a do loop at least because the the indentation looks horrible and not recognised neither in VS code nor emacs. But I am more curious why this works in the first place. Does it not need to signal to compiler that the loop is finished Coming from C# background, every time I see an old Fortran code I am more surprised.

This is just FORTRAN 77. In fact, the END DO statement was introduced in 1978 by the MIL-STD-1753 extension of the US DoD:

http://www.everyspec.com/MIL-STD/MIL-STD-1700-1799/download.php?spec=MIL-STD-1753.011044.PDF

2 Likes

Indentation plays no role in Fortran, so just ignore that. In fixed-form Fortran, the first 6 columns have special meaning, but otherwise indentation (i.e. whitespace) is ignored completely. (The exception is string literals.)

What version of compiler are you using? I tested the program below with gfortran 5.5 and newer,

       do 100 i = 1, 5
       print *, i
       continue 100
       end

All of them raised the following error

/app/example.f90:3:15:

        continue 100
               1
Error: Syntax error in CONTINUE statement at (1)

Perhaps you meant to write 100 continue instead? Assuming that fixed-form is used, the numeric label must be placed in columns 2-5:

C column numbers
C 34567
      do 100 i = 1, 5
         print *, i
  100    continue
      end
1 Like

The ANSI FORTRAN 66 standard provides an understandable description of the syntax (only a subsection shown below),

3 Likes

Thanks for your replay Ivan. It is indeed the F77 style written in free-form so there is no column constraint as such. I think it didn’t work for you becuase you still had end in your example maybe?
Hopefully, I don’t need to learn all the obselete ways of coding in FORTRAN in future.

Yes, but the column label 100 must still be first on the line, not last, even in free format.

It was common before f77 to end do loops with a labeled executable statement and no loop body indentation. Even multiple nested do loops would typically all terminate on the same statement. Presumably, this was to reduce the number of cards the programmer had to carry around. Then when f77 introduced if-then-else, programmers changed styles a little and started nesting do loops where each loop had its own labeled continue statement, as loops were done in other languages. The mil-std extension introduced enddo, but it still required the statement label. It was only with f90 that do loops no longer required the statement label and could use an unlabeled enddo instead.

5 Likes

As others have noted, it is perfectly fine to use a labeled continue to terminate a do loop, and was the preferred way prior to end do being introduced into the language. (Some Fortran-77 compilers supported the MIL-STD 1753 extensions, and some didn’t. So end do wasn’t totally portable until Fortran-90.)

Where you may need to watch out in legacy codes is when a loop is terminated by a statement other than end do or continue. This was deemed obsolete in Fortran 90, and deleted from the language as of Fortran 2018. Terminating multiple loops on a single statement, and labeled loops in general were also deemed obsolescent in F2018. Gfortran, and perhaps others, are starting to warn about this.

Obviously these days one should always use the unlabeled form of loops with end dos.

There is something to be said for the tidiness of a code with matching labels at the top and bottom of a construct, especially when they are far separated. Happily you can add text labels in modern Fortran:

my_label: do i=1,2
    ! a whole bunch of code
end do my_label

The compiler will enforce matching between the beginning and ending label. It’s especially handy when you have multiple nested constructs such as if-then-else or select case with many branches. The compiler will keep you honest with nesting and matching beginning and ending statements.

If you don’t like the aesthetic of the do appearing midline, you can use this style I like to use.

my_label: &
do i=1,2
    ! a whole bunch of code
end do my_label
2 Likes

The construct label also has the benefit of recovering the functionality you use to get with goto and the labeled continue statement, i.e. exiting a middle loop. E.g.

outer: &
do ...
  middle: &
  do  ...
    inner: &
    do ...
      ...
      exit middle
    end do inner
  end do middle
end do outer
2 Likes

Certainly a construct name (not a numeric statement label) is required when there are nested loops, and you are using CYCLE or EXIT to transfer control from an inner level to an outer level. It can also have nice documentary effect - especially if the loop is very long (e.g., say a screen full or more).

I think this is one of the reasons why programmers continued to use labeled do loops (with numeric statement labels) even after f90 introduced enddo. It allowed them to match the do statement with its continue statement. Some programmers would add inline comments to the do statement or its enddo statement to simulate this, but this was error prone because the compiler did not enforce consistency. Then the construct label was eventually added (in f2003 I think), which does allow the compiler to enforce consistency. The main syntax difference between numeric labels and labeled constructs is that a goto statement from anywhere in the program unit could target the numeric label, whereas only special statements such as cycle and exit from within the construct can reference the construct labels. So the language is better off now than 50 years ago, even though it looks like it took a long detour just to get back to the same place.

1 Like

I learned Fortran between 1991 and 1997 (i.e., between the 90 and 95 standards), and I think the [label:] do ... enddo [label] was already there.

I guess what changed in f2003/f2008 was extending the label: to any construct.

In Fortran 90 [label:] could be at the beginning of these constructs and the label would then be at their end statements: do, if, select.
In addition these constructs could have their names at their end statements: function, module, program, subroutine, type.

Certainly brings back memories about the internal rules we had about any statements having or using numeric labels, and how the rules were used to avoid differences between programming environments. Some always executed a DO loop once; the confusion caused by nested loops with goto statement going to a single executable statement at the bottom; some allowing goto statements to go to the same statement from outside the loop, …things
I rarely remember unless unraveling some old code. There were a lot of different vintages of rules. The last one I remember (most?) of was that no line with a numeric label was allowed to be anything other than a CONTINUE statement and that numeric labels that were powers of ten were only used for DO loops id( once CONTINUE was available). The counters all had to be “Innn”, as in

      do i10=1,20
         do i20=11,33,2 
20       continue
10    continue

so you could identify the index value and which loop it belonged to; I sometimes forget all of that unless working on code that has not been updated; but it all still works. All those rules worked; we really had no spaghetti code accept for code brought in; and those conventions made it easy to update the code so the majority of codes were gradually updated to the point I rarely see any of that obscure code anymore; but I do remember keeping odd stats about the percentage of GOTO statements in a code, the super-low amount of comments (if you had to type them on cards, and then carry them and keep them in order comments were not a priority) long before longer variable names made more code “self-describing”, … rather than learn all that you might want to look into some of the automated updating tools available if the code warrants the purchase.

The downside of the otherwise great backward compatibility Fortran has provided is it is compatible with old bad practices and now-obsolete methods. But, you do not have to update your code for decades at a time either!

I do recall updating one code in particular with > 7600 GOTO statements and no procedures and one comment at the top that was the program name, so I can sympathize
but I tend to block those memories out.

As thoughts tend to meander when reminiscing it got me wondering about some of the old
bugs and if they would still be possible. The story is long about the trouble a typo caused
one day and how human error is great at complementing computer errors but it boils down to this being a legal FORTRAN code:

1234   goto 1234
end

I was impressed to see the first two compilers I tried produce a warning message about that.

Some long time ago someone psted a bit of code on usenet

do 10 i=whatever
   do 20 j=whatever
      if (something) goto 10
 20 i = i + 1
10 i = i + 1

If I recall this code gave at least 3 different results on different compilers.
Yes, you can have any legal statement on the label, not just continue.
At least you could. I hope this has been ruled deprecated.

(Has Lionel retired? He might remember this from his Sun or DEC days.)

I think the “trick” here is that this goes to the beginning of the labeled statement, not the end of the loop as might be expected, so the statement should be executed in this case. When the loop end is a continue or an enddo, then it is clear what does and doesn’t get executed.

so the statement should be executed

I’m pretty sure that compilers were rather ambiguous about that at the time.

I remember there were many cases that were defined by the standard that looked odd and that gave unexpected results. One example in the F77 standard itself was

      N = 0
      DO 200 I = 1, 10
      J = I
      DO 200 K = 5, 1
      L = I
  200 N = N + 1
  201 CONTINUE

After execution, I=11, J=10, K=5, N=0, and L is not defined by these statements. This is because the K loop should skip over the terminal statement instead of to its beginning, as would a GOTO 200 within that loop. I think as long as the GOTO statements were within the loop ranges, all of the various behaviors were well defined by the standard, although sometimes surprising as in this example. (BTW, before F77, transfer out of and back into a loop range was allowed, leading to even more surprising results. Also, the K loop range would have been illegal before F77.) It would be interesting if you can find a link to the online discussion that resulted in the various outputs in the code above to see if those were compiler errors or language ambiguities.

The above has always been illegal Fortran coding, as you are not allowed to modify the index variable within the body of the loop. Whether compilers detect it, and how they compile it if they accept it, is another matter.

Shared DO terminations and terminations with other than END DO or CONTINUE have been declared obsolescent since Fortran 90. And Fortran 2018 deleted them. Of course most compilers will continue to support them “forever”. Hopefully by requiring some sort of compatibility option, or at least by issuing a warning message.

Fortran 2018 really wants everyone to use the block form of DO. That is, by using END DO and no numeric statement labels.

Ah, of course. I overlooked that detail in my previous reply.