Recent

Author Topic: Does FPC fully support preserving the FOR variable when a GOTO or BREAK is used?  (Read 3004 times)

440bx

  • Hero Member
  • *****
  • Posts: 5809
A lot of opinion (to which I agree btw, I see no good reason for it to be any other way), but there is the detail point of actually finding documentation for it.
The documentation is right there in the FPC Guide, Chapter 13. STATEMENTS page 195:
Quote
The value of the loop variable is undefined after a loop has completed or if a loop is not executed at all. However, if the loop was terminated prematurely with an exception or a break or goto statement, the loop variable retains the value it had when the loop was exited.
The part "if the loop was terminated prematurely" is crystal clear: that't the only case when the value of the loop variable is predictable.  It's the only case because if not "terminated prematurely" then the first part of the sentence applies "The value of the loop variable is undefined after a loop has completed." 

It couldn't be any clearer. If the loop didn't terminate prematurely then the loop variable is undefined.  IOW, the presence of a goto, a break or any other flow control that could break the loop is insufficient, it must be present _and_ must be executed for the loop to terminate prematurely.

The documentation is _complete_ and crystal clear.



« Last Edit: October 01, 2025, 11:29:57 am by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

jamie

  • Hero Member
  • *****
  • Posts: 7306
Ive only seen delphi optimize the loop var in code when there was no reference being made to it and no early exit control inplace and that was a down counter register looking for a zero results or overflow.

  I never thought about jumping into a for loop, that wouldnt make much sense to me ?

  The initial question was if fpc honors the use of the loop variable when any early form of loop exit exists in the statement no matter how the loop ended.

Jamie

 
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 18321
  • Here stood a man who saw the Elbe and jumped it.
And that is in the MANUAL.... >:D
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

440bx

  • Hero Member
  • *****
  • Posts: 5809
  The initial question was if fpc honors the use of the loop variable when any early form of loop exit exists in the statement no matter how the loop ended.
And the answer is documented to be NO.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Seenkao

  • Hero Member
  • *****
  • Posts: 717
    • New ZenGL.
Ответ был конечно уже дан, но всё же я хотел бы добавить из своих наблюдений и соображений.

1. В своём большинстве переменная используемая в цикле определена если: цикл был запущен, по окончанию тела цикла (если цикл был запущен) и при прерывании цикла.
- тут надо учитывать, что под определением "определена" - это то значение переменной которое следуют сразу за циклом. А не то значение, которое вы решили взять по прошествию ещё 1 и более строчек кода (эти строчки могли менять значение переменной).
2. Переменная используемая в цикле не определена, если цикл не выполнялся (есть вариант, что переменная будет определена, но лучше на это не рассчитывать).

При всём при этом надо помнить, что циклы бывают разными и по окончанию цикла, переменные могут выдавать разное значение. Это важно!
----------------------------------------------------

Google translate:
The answer has of course already been given, but I would still like to add from my observations and considerations.

1. In most cases, a variable used in a loop is defined if: the loop was started, at the end of the loop body (if the loop was started), and when the loop was interrupted.
- It's important to note that "defined" refers to the variable's value immediately following the loop, not the value you decide to take after one or more lines of code (these lines could change the variable's value).
2. A variable used in a loop is undefined if the loop wasn't executed (there's a chance the variable will be defined, but it's best not to count on it).

It's important to remember that loops are different, and variables may return different values ​​at the end of the loop. This is important!
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

TBMan

  • Sr. Member
  • ****
  • Posts: 277
I'll use a boolean flag to indicate success.

Code: Pascal  [Select][+][-]
  1. var
  2.   GotIt:boolean;
  3.  
  4. GotIt := false;
  5. For index := 1 to maxitems do
  6.   begin
  7.      if something(index) then
  8.      begin
  9.        moresomething;
  10.        GotIt := true;
  11.        break;
  12.      end;
  13.   end;
  14.  
  15.  If GotIt then
  16.    begin
  17.       actions here
  18.    end;  
  19.  
  20.  

If you need to know the counter variable then just use another counter to track the loop's progress...

Code: Pascal  [Select][+][-]
  1. var
  2.   index, IndexIs:integer;
  3.  
  4. IndexIs := 1;
  5. for index := 1 to maxitems do
  6.    begin
  7.        inc(indexIs);
  8.       if something then break else keepGoing;
  9.    end;
  10.  
  11. If IndexIs = MaxItems then doSomethingElse;
  12.  

But the "something" could have happened on the last loop, so the first example I gave is better.
I love programming.

Some things I've done using PTCgraph:

NFL Retro Football (almost finished):
https://www.youtube.com/watch?v=78mTtsd7ppk


Solitaire games:
https://www.youtube.com/watch?v=zmtxI7FdWuQ&list=PLa4BPpFl34iVhFwX1JZwVm3vE5ay_i3R2

jamie

  • Hero Member
  • *****
  • Posts: 7306
There is a time and place for everything, and this presented code isn't it!

The reason I use that code is because it generates tighter and less code.

I never said I had any problems with it, been using it for years in Delphi because I know the limits of where it may decide
to not play well.

 Reading the manual usually helps, I only needed to know if FPC properly honors the loop counter if the loop completes with those control points in the code. It allows it before the loop completes so I can assume that its same in that respect.

 Have a good day, I got what I needed now onto finding issues unrelated to this.

jamie
 
The only true wisdom is knowing you know nothing

440bx

  • Hero Member
  • *****
  • Posts: 5809
The reason I use that code is because it generates tighter and less code.
if the code does not need to be correct, it can always be made tighter and smaller until there is no code (if it doesn't have to work correctly, there is no need for code.)

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 18321
  • Here stood a man who saw the Elbe and jumped it.
BTW the main cause that the loop variable in for to is undefined is simply that when the loop has finished in the regular way, the compiler is free to re-use the register where the loop variable is held. And that makes sense.
This can even happen when the loop itself is in its last iteration. Exiting the loop prematurely keeps the loop register intact.

I used to rely on the way Delphi worked in the past (emperically valid) and noticed fpc differed. Then I noticed both Delphi and Freepascal documented that the loop variable is undefined. Those were biting bugs...
Nowadays the empirics prove you wrong all the time and in both manifestations.
« Last Edit: October 02, 2025, 09:28:33 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Ten_Mile_Hike

  • Full Member
  • ***
  • Posts: 121
Just to add fuel to the fire, it is my distinct impression that

1. Standard ISO Pascal leaves the loop counter variable "undefined" outside of the loop
2. FPC documentation aside... my tests show that in FPC the counter variable retains its
    correct value outside the loop even if BREAK was used inside the loop (I don't know about GOTO)
3. If you want to make certain that the value of the loop counter is correct outside the loop even in ISO Pascal
    simply declare the loop counter variable as a global 

My 3 postulates above are likely correct because I have been wrong only once in my life. It was in 1969
and involved my assumed understanding of what female logic and behavior would be.
When any government, or any church for that matter, undertakes to say to its subjects, This you may not read, this you
must not see, this you are forbidden to know, the end result is tyranny and oppression no matter how holy the motives.

Robert A. Heinlein

PascalDragon

  • Hero Member
  • *****
  • Posts: 6189
  • Compiler Developer
2. FPC documentation aside... my tests show that in FPC the counter variable retains its
    correct value outside the loop even if BREAK was used inside the loop (I don't know about GOTO)

And of course your tests were covering each and every situation that the compiler might optimize code with each and every version? With your statement a single counter example is enough:

Code: Pascal  [Select][+][-]
  1. program tfor;
  2.  
  3. {$mode objfpc}
  4.  
  5. procedure Test(aArg: LongInt);
  6. var
  7.   i: Integer;
  8. begin
  9.   for i := 0 to aArg do begin
  10.     Writeln('Foo');
  11.   end;
  12.  
  13.   Writeln(i);
  14. end;
  15.  
  16. begin
  17.   Test(2);
  18.   Test(6);
  19. end.

Compiled with a relatvely current FPC 3.3.1 on x86_64-win64 with -O3 the output is this:

Code: [Select]
Foo
Foo
Foo
0
Foo
Foo
Foo
Foo
Foo
Foo
Foo
0

(the output with 3.2.2 on the same platform and with the same optimization is 2 and 6 for the Writeln(i) respectively)

Thaddy

  • Hero Member
  • *****
  • Posts: 18321
  • Here stood a man who saw the Elbe and jumped it.
That's what I like about Karl Popper. Thx Sven/Sarah.
https://en.wikipedia.org/wiki/Falsifiability
Back in 1979 this was a real eye-opener.
« Last Edit: October 07, 2025, 07:22:12 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

old_DOS_err

  • New Member
  • *
  • Posts: 44
Sorry, but that text from the manual is a bit worrying.

I've done a fair bit of assembler and still like to use Asm code blocks as sometimes it is easier to do certain bit work that way.

For that value to be undefined at the conclusion of the For loop, if it is allowed to run fully, it implies the value of iteration is being placed into the ECX register (CX in 8086, unsure about RCX, I still live in 16 bits with assembler, let’s just say ECX to keep it simple) and a standard assembler LOOP is being run.

Which makes sense. What it does also explain is why upon completion the variable has the maximum value, not one over. Usually if one is using a Repeat or While and a single step iteration, the variable, if the loop runs fully, is 1 over. Which is another reason the counter variable in a For cannot be used to determine certain actions (like testing if an item is in a list).

Thus If I am reading it correctly, it means that the iteration variable is loaded with the maximum (unsure about DownTo) value, which is then copied to the ECX, then the loop is run. So when a Break, Goto or Exit is encountered, it is actually using the value obtained from the ECX, not the variable.

That means some of my code may be technically unsound, oops. Good job I do a lot of testing.

I'm still uncertain though why it says the variable is undefined, experience tells me that it always has the maximum value if allowed to fully run. I assume that the original variable is basically treated as a scratch location just for value for the ECX, but I have no data on that. Anyway, getting caught up in trivia again.

A little demo, more to prove to myself, that Exit also works.

Code: Pascal  [Select][+][-]
  1. Function test_for( Const n : Byte ) : Byte;
  2.   Var i : Byte;
  3.   Begin
  4.     Result := 100;  // default
  5.  
  6.     For i := 1 To 20 Do
  7.       If i = n Then Exit( i );
  8.   End;
  9.  
  10. Procedure for_demo;
  11.   Begin
  12.     WriteLn( '  test with 3 = ', test_for( 3 ) );
  13.     WriteLn( '  test with 20 = ', test_for( 20 ) );
  14.     WriteLn( '  test with 21 = ', test_for( 21 ) );
  15.   End;
  16.  

These produce the expected results, confirming Exit is seen as valid.
« Last Edit: October 07, 2025, 03:42:47 pm by old_DOS_err »

Thaddy

  • Hero Member
  • *****
  • Posts: 18321
  • Here stood a man who saw the Elbe and jumped it.
AND documented, so why the fuss?
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

old_DOS_err

  • New Member
  • *
  • Posts: 44
AND documented, so why the fuss?

Ok found the relevant section in Reference guide for Free Pascal, version 3.2.2. It's an old copy, but still useful. I thought it was the elusive Programmers Reference Manual, which sadly I don't have a copy of (have had a few searches online, but no luck).

My days of being able to read a computer manual cover to cover are long behind me. But even if I had read that line, it’s too vague for me, without a clear example of when that might be relevant or under what conditions one could make that value change, become unpredictable, it doesn’t really imply much, personally speaking.

Also I’ve seen wrong values come up, especially with nested procedures, which I use a lot. Sometimes the team changes the code to fix bugs. I’m sure many of us could name a change we have seen.

I suppose at the end of the day it is a question of how much we assume. I came from Turbo Pascal, I have made an assumption that I can treat free pascal as an extension of that. At times that assumption bites me in the backside.

I think that is all I was trying to convey, that when we use any compiler or interpreter we are making assumptions that the underlying code is going to perform a certain way. Then through testing we find whether our code works, then have the job of tracing the problem when it doesn’t.

 

TinyPortal © 2005-2018