View Issue Details

IDProjectCategoryView StatusLast Update
0037911FPCDocumentationpublic2020-12-11 00:44
ReporterPedro Gimeno Assigned ToMichael Van Canneyt  
PrioritynormalSeverityminorReproducibilityhave not tried
Status closedResolutionfixed 
Fixed in Version3.3.1 
Summary0037911: Expand the FOR documentation to cover some essential aspects
DescriptionI went to the documentation to check if FOR loops evaluate the second argument every iteration or not, but this aspect is not mentioned. The closest thing that it says is that "Free Pascal always calculates the upper bound before initializing the counter variable with the initial value", but that alone doesn't leave it clear that this upper bound is cached and not re-evaluated during iteration (which I've checked to be the case).

Furthermore, jumping from outside the loop to inside the loop via a GOTO statement is not explicitly forbidden. Doing this skips the caching phase of the upper bound, with unpredictable results, therefore it should not be allowed. Adding a check in the compiler may be difficult, but documenting that it's plainly not supported and the result is undefined is simpler.
TagsNo tags attached.
Fixed in Revision1766
FPCOldBugId
FPCTarget3.2.2
Attached Files

Activities

Pedro Gimeno

2020-10-14 16:41

reporter   ~0126297

Please find attached a suggested patch.
patch-37911.diff (2,268 bytes)   
Index: ref.tex
===================================================================
--- ref.tex	(revision 1758)
+++ ref.tex	(working copy)
@@ -9513,23 +9513,28 @@
 \end{enumerate}
 After this check, the statement after \var{Do} is executed. After the
 execution of the statement, the control variable is increased or decreased
-with 1, depending on whether \var{To} or \var{Downto} is used.
+by 1, depending on whether \var{To} or \var{Downto} is used.
 The control variable must be an ordinal type, no other
 types can be used as counters in a loop.
 
 \begin{remark}
 \begin{itemize}
-\item \fpc always calculates the upper bound before initializing
-the counter variable with the initial value.
+\item \fpc always evalulates the upper bound before initializing
+the counter variable with the initial value, and does not evalulate it again
+during iteration.
 \item It is not allowed to change (i.\,e. assign a value to) the value of a
 loop variable inside the loop.
 \item The value of the loop variable is undefined after a loop has
-completed or if a loop is not executed at all. If the loop was terminated prematurely with an exception or a
-\var{break} statement, the loop variable retains the value it had when the
-loop was exited.
+completed or if a loop is not executed at all. If the loop was terminated
+prematurely with an exception, a \var{break} statement or a \var{goto}
+statement, the loop variable retains the value it had when the loop was
+exited.
 \item For nested procedures, a loop variable must be a local variable.
-If you declare a loop variable outside the nested procedure where the loop is, the compiler will
-complain. It is however allowed to use a global variable in a procedure.
+If you declare a loop variable outside the nested procedure where the loop
+is, the compiler will complain. It is however allowed to use a global
+variable in a procedure.
+\item Using \var{goto} to jump inside the body of the loop will produce
+undefined results.
 \end{itemize}
 \end{remark}
 
@@ -9574,7 +9579,8 @@
 begin
 end.
 \end{verbatim}
-because the variable \var{I} is not defined in \var{Nested}.
+because the variable \var{i} is not defined in \var{Nested}, and it's not
+global either.
 
 But the following will compile:
 \begin{verbatim}
patch-37911.diff (2,268 bytes)   

Michael Van Canneyt

2020-11-08 14:22

administrator   ~0126792

Fixed, only saw your patch after I fixed it, so the wording will differ. Thanks for reporting !

Pedro Gimeno

2020-12-03 13:53

reporter   ~0127335

@michael Thanks. I see two problems with the applied patch. One is that it mentions goto without specifying how it's used, and from the current wording, it's easy to infer that using goto to jump from inside to outside the loop, or from a location inside the loop to another location within the same loop, causes undefined behaviour, but that's not the case. Only jumping from outside the loop to inside the loop causes undefined behaviour, due to the fact that it's skipping the initialization of the limit value, therefore the iteration count can be anything.

The other is that it doesn't mention that after using goto to jump outside the loop, the loop variable is valid at the destination of the goto. In fact, a break statement is pretty much syntax sugar for a goto statement.

Michael Van Canneyt

2020-12-03 14:25

administrator   ~0127336

It is not correct that jumping outside the loop is defined.
It depends on where you jump to.
Probably if you jump just outside the loop (after the end) then it will be so that the variable is defined.
But this need not be so for other jump destinations; you simply do not know in general.

So I prefer not to document it as such and will leave it as 'undefined'.

Sven Barth

2020-12-04 09:25

manager   ~0127339

Without the modeswitch NonLocalGoto the compiler won't let you jump somewhere where this would be problematic (e.g. it doesn't allow you to jump out of or into an exception related block). Only with the NonLocalGoto switch you'd be able to jump directly from one function to another.
That the loop variable is defined after a Break or Goto (as long as the target label is local) is in fact a very well defined behavior and the compiler ensures that it is that way.

Michael Van Canneyt

2020-12-04 09:42

administrator   ~0127340

I find this behaviour highly illogical.
However, I did again some digging, and the goto seems indeed to be documented in ISO 7185. I must have missed that the first time I checked, apologies.
Adapted documentation accordingly in rev 1787.

Pedro Gimeno

2020-12-11 00:43

reporter   ~0127527

@michael It's very logical to me. A break statement skips the iteration code (the code used to advance the value of the variable); if the value was defined inside the loop, it must be defined after a broken loop. Similarly, a goto outside the loop also skips the iteration code, and the variable keeps its value for the same reason; that's what variables do. Only the normal termination of the loop leaves the variable undefined, because the iteration code may or may not be executed once more after the loop variable hits the final value, depending on how the loop is implemented.

Thanks for the change; I believe this issue can be closed now.

Issue History

Date Modified Username Field Change
2020-10-11 23:48 Pedro Gimeno New Issue
2020-10-11 23:48 Pedro Gimeno Status new => assigned
2020-10-11 23:48 Pedro Gimeno Assigned To => Michael Van Canneyt
2020-10-14 16:41 Pedro Gimeno Note Added: 0126297
2020-10-14 16:41 Pedro Gimeno File Added: patch-37911.diff
2020-11-08 14:22 Michael Van Canneyt Status assigned => resolved
2020-11-08 14:22 Michael Van Canneyt Resolution open => fixed
2020-11-08 14:22 Michael Van Canneyt Fixed in Version => 3.3.1
2020-11-08 14:22 Michael Van Canneyt Fixed in Revision => 1766
2020-11-08 14:22 Michael Van Canneyt FPCTarget => 3.2.2
2020-11-08 14:22 Michael Van Canneyt Note Added: 0126792
2020-12-03 13:53 Pedro Gimeno Note Added: 0127335
2020-12-03 14:25 Michael Van Canneyt Note Added: 0127336
2020-12-04 09:25 Sven Barth Note Added: 0127339
2020-12-04 09:42 Michael Van Canneyt Note Added: 0127340
2020-12-11 00:43 Pedro Gimeno Note Added: 0127527
2020-12-11 00:44 Pedro Gimeno Status resolved => closed