Recent

Author Topic: For variable being read after loop... Any way to get an error or warning on it?  (Read 13214 times)

Thaddy

  • Hero Member
  • *****
  • Posts: 15688
  • Censorship about opinions does not belong here.
Mark, it is just an observation. I think that it will stay as undefined.
But a warning may wake people up that the real world is different from expectation.
If I smell bad code it usually is bad code and that includes my own code.

Khrys

  • Jr. Member
  • **
  • Posts: 91
Don't use a For-Loop control-variable after completion of the loop. Period!
EDIT: and not even before entering the loop. Period 2!

EDIT2: The only situation i use a control-variable in multiple places is in reusing it as control-variable in a following For-Loop

ISO 7185:1990 (Pascal Standard)6.8.3.9 For-statements  (page 63 of the .pdf):

Quote from: ISO/IEC 7185:1990
[...] After a for-statement is executed,
other than being left by a goto-statement, the control-variable shall be undefined. Neither a for-
statement nor any procedure-and-function-declaration part of the block that  closest-contains  a for-
statement shall  contain  a statement threatening the variable denoted by the control-variable of the
for-statement.

A statement  S  shall be designated as threatening a variable  V  if one or more of the following
statements is true.

    a)  S  is an assignment-statement and  V  is denoted by the variable-access of  S.
    b)  S  contains an actual variable parameter that denotes  V.
    c)  S  is a procedure-statement that specifies the activation of the required procedure  read  or the
        required procedure  readln, and  V  is denoted by variable-access of a read-parameter-list or
        readln-parameter-list of  S.
    d)  S  is a statement specified using an equivalent program fragment containing a statement
        threatening  V.

This passage has me worried that even just recycling the same variable for two separate loops is not allowed...

Code: Pascal  [Select][+][-]
  1. procedure Test();
  2. var
  3.   I: Integer;
  4. begin
  5.   for I := 0 to 999 do begin
  6.     Foo(I);
  7.   end;
  8.   for I := 0 to 999 do begin // Illegal in ISO Pascal?
  9.     Bar(I);
  10.   end;
  11. end;

I can see how one might argue that the control variable is actually only in scope within the loop, and that because there is only one declaration, the whole thing should be equivalent to this nonsense:

Code: C  [Select][+][-]
  1. void test()
  2. {
  3.     for (int i = 0; i < 1000; i++) {
  4.         foo(i);
  5.     }
  6.     for (i = 0; i < 1000; i++) { // i undefined/out of scope - obviously illegal
  7.         bar(i);
  8.     }
  9. }

...but I wouldn't consider that outcome particularly satisfying. The control variable's declaration looks identical to that of any other local, and I wouldn't want the compiler to silently narrow any variable's scope in such a way.

dbannon

  • Hero Member
  • *****
  • Posts: 3076
    • tomboy-ng, a rewrite of the classic Tomboy
....
Don't use a For-Loop control-variable after completion of the loop. Period!
EDIT: and not even before entering the loop. Period 2!

Kek ?  I think that is a bit extreme.   I believe its fine to use a variable, already used in a for loop, as long as you initialise it, one way or another, before this use and after its use in the for loop. Similarly, use a variable for something else before its going to be used by a for loop as long as you don't expect it to retain its previous value.

If that is not the case, we need a new type, TForLoopVar to ensure we don't break these draconian rules. I don't think so.

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

MarkMLl

  • Hero Member
  • *****
  • Posts: 7674
The control variable's declaration looks identical to that of any other local, and I wouldn't want the compiler to silently narrow any variable's scope in such a way.

OTOH it was fairly explicit in its prohibition of re-use. Thatnks for that, if I have time I'll add the reference to my bug-report.

If working from scratch, I'd still prefer a locally-defined variable. But it's been made clear that it's not going to happen.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 15688
  • Censorship about opinions does not belong here.
I did some more checks and I'd recommend using the range and use ranges and then re-initializing the counter with High(myRange)+1. It takes some more code to take care of premature exits, because in that case the value of the loop variable IS guaranteed. So if the counter is less than high(myRange) the re-used variable does not need to be initalized, but incremented by one if required.
« Last Edit: August 08, 2024, 11:07:42 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

n7800

  • Full Member
  • ***
  • Posts: 138
OFFTOP

also Lazarus has a convenient "highlight" feature, just select the loop variable name and it will highlight it in the remaining code; note that the latter won't work too well if the loop variable name is short like "i" as highlighting doesn't have "words only" checkbox (or I couldn't find it) and hence will highlight all "i"s in all words on the screen, making it harder to read).

Go to IDE Options > Editor > Display > Markup and Matches and pay attention to the option "Match whole words, if length is less or equal to:". You can simply enter a large value like 100, then when you place the cursor in any word, only other whole words will be highlighted. But when you select text, only the selected part will still be highlighted.

Alternatively, you can use custom syntax highlighting in IDE Options > Editor > Display > User defined markup. You can check the help to learn how to use it - there you can also configure part of a word or whole words. I often use it during refactoring, when I need to highlight certain variables in the code. I also highlight the variables i and j in different colors, so as not to confuse them (helps with errors in nested loops).

Quote
for i := 0 to 10 do ;
I didn’t notice the semicolon after the “do”  ::)

I have previously requested that the code tools not put semicolon after the “do” But to no avail.

As MarkMLl hinted, this is configurable. The option is in IDE Options > CodeTools > Identifier Completion, checkbox "Add semicolon".

In some cases, this option really does more harm than good. See related issue #37818.

Joanna

  • Hero Member
  • *****
  • Posts: 1090
I don’t want to give up having it adding a semicolon most of the time where it’s intended.
@ Khrys your pascal code didn’t need all the extra begin ends . Here is cleaner code.
Code: Pascal  [Select][+][-]
  1.  procedure Test();
  2. var
  3.   I: Integer;
  4. begin
  5. for I := 0 to 999 do
  6.     Foo(I);
  7. for I := 0 to 999 do // Illegal in ISO Pascal?
  8.     Bar(I);
  9. end;
Also it makes no sense To me to not be able to use a loop variable more than once.
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

ASBzone

  • Hero Member
  • *****
  • Posts: 713
  • Automation leads to relaxation...
    • Free Console Utilities for Windows (and a few for Linux) from BrainWaveCC
This passage has me worried that even just recycling the same variable for two separate loops is not allowed...

Code: Pascal  [Select][+][-]
  1. procedure Test();
  2. var
  3.   I: Integer;
  4. begin
  5.   for I := 0 to 999 do begin
  6.     Foo(I);
  7.   end;
  8.   for I := 0 to 999 do begin // Illegal in ISO Pascal?
  9.     Bar(I);
  10.   end;
  11. end;


There's nothing illegal about that.

I think what folks are talking about is trying to leverage the value of the variable when you've exited the loop, as though that value is reliable (without reinitialization).

For instance, this is not good:

Code: Pascal  [Select][+][-]
  1. procedure Test();var
  2.   I: Integer;
  3. begin
  4.   for I := 0 to 999 do begin
  5.     Foo(I);
  6.   end;
  7.   if (I = 999) then begin // This should not be done, as I is undefined at this time
  8.     Bar(I);
  9.     Bar(I+1);
  10.   end;
  11. end;
  12.  
-ASB: https://www.BrainWaveCC.com/

Lazarus v3.5.0.0 (2216170cde) / FPC v3.2.3-1387-g3795cadbc8
(Windows 64-bit install w/Win32 and Linux/Arm cross-compiles via FpcUpDeluxe on both instances)

My Systems: Windows 10/11 Pro x64 (Current)

jamie

  • Hero Member
  • *****
  • Posts: 6578
no, you misunderstood it, as how many here have, too.

There is nothing wrong with re-using the variable for something else or even another loop, I don't understand why this is getting out of hand?

 The issue is, you should not be using the VALUE of the last loop counter. It has nothing to do with not re-using the variable for something else or another loop outside the circle.

 For example..

Code: Pascal  [Select][+][-]
  1. For I := 0 to Something do
  2.   begin
  3.    whatever;
  4.   end;
  5.  

At this point, I may or maynot contain the last index value of the last loop counter.

So its not wise to do this.
Code: Pascal  [Select][+][-]
  1.   if I < Something Then do whatever;
  2.  

Do you understand now?
The only true wisdom is knowing you know nothing

dbannon

  • Hero Member
  • *****
  • Posts: 3076
    • tomboy-ng, a rewrite of the classic Tomboy
The issue is, you should not be using the VALUE of the last loop counter. It has nothing to do with not re-using the variable for something else or another loop outside the circle.

While I agree Jamie, there seems some doubt expressed. One experienced poster indicated that a variable (not the value of the variable) that might  be used in a for loop should not be used in any other way (except in another for loop).  And the ISO document says -

"After a for-statement is executed ... the control-variable shall be undefined."

I initially assumed that means the value of the variable shall be undefined. But it is a standards document and one should not assume sloppy wording or try and read between the lines of a standards document.

From a risk assessment perspective, this is an unlikely risk but one with catastrophic consequences if it occurs !

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

Khrys

  • Jr. Member
  • **
  • Posts: 91
I think what folks are talking about is trying to leverage the value of the variable when you've exited the loop, as though that value is reliable (without reinitialization).

The issue is, you should not be using the VALUE of the last loop counter.

Yes, the loop counter is undefined after the loop has exited; I wasn't trying to restart that (resolved) argument.
I'm afraid that I went even further off-topic in my last reply here trying to discuss this specific passage. I hadn't made it clear enough that this is what I wanted to focus on:

Quote from: ISO/IEC 7185:1990
[...] Neither a for-statement nor any procedure-and-function-declaration part of the block that  closest-contains  a for-statement shall  contain  a statement threatening the variable denoted by the control-variable of the for-statement. [...]

I will admit that I simply don't understand this passage well enough to definitively answer the question on whether reuse of the control variable is allowed at all (per the specification).

Thaddy

  • Hero Member
  • *****
  • Posts: 15688
  • Censorship about opinions does not belong here.
Yes, the loop counter is undefined after the loop has exited;
That is only true if the loop has fully completed.
If the loop is aborted by break et all, the counter is defined.
That is an important distinction. This is also documented.
https://www.freepascal.org/docs-html/ref/refsu58.html
It is a bit repeating myself, but emphasis should be noted.
For many it is not immediately obvious what its purpose is, but consider:
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode objfpc}{$endif}{$R-}
  2. type
  3.   myRange = 0..9;
  4. var
  5.   i:myRange; // it does not need to be a range
  6.   j:integer;  
  7. begin
  8.   writeln('defined range');
  9.   for i := 0 to high(myRange) do
  10.   begin
  11.     write(i:3);
  12.     // A condition that will cause a break
  13.     if (i>0) and (i  mod 7 = 0) then break;
  14.   end;
  15.   writeln;
  16.   writeln(i:3);
  17.   // now process the condition that caused the break
  18.   // and subsequently resume the loop.
  19.   inc(i); // previous has been processed
  20.   for i := i to high (myRange) do write(i:3);
  21.   writeln;
  22.   writeln(i:3);  
  23. end.
It is no accident that the loop value is defined on break, it is by design, although few people use the above construct.
(you could also use a goto to process the condition and subsequently return to the same loop)
 
« Last Edit: August 09, 2024, 07:40:57 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

440bx

  • Hero Member
  • *****
  • Posts: 4550
In Pascal the "for" control variable is a variable just like any other.  Therefore it can be reused just like any Pascal variable.

It would be a different story if the variable was defined _within_ the "for" statement, e.g, "for integer : a := 1 to ... ", then the control variable only exists as long as the loop exists but, that's not standard Pascal.  The scope of the control variable is _not_ determined by the "for" loop.

All that said, re-using variables (not just a "for" loop control variable) makes code optimization more difficult because the optimizer has to figure out that some tasks have nothing to do with each other in spite of the fact that they are sharing a variable.

IOW, it is the control variable's _value_ that may be undefined after the loop, not the variable.  The variable is as defined after the loop as it was before it.  It's value, that's a different story.
« Last Edit: August 09, 2024, 07:35:42 am by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Khrys

  • Jr. Member
  • **
  • Posts: 91
Yes, the loop counter is undefined after the loop has exited;
That is only true if the loop has fully completed.
If the loop is aborted by break et all, the counter is defined.

You're right of course, I forgot to mention that again  %)  It should've said  Yes, the loop counter is undefined in most cases after the loop has exited.

MarkMLl

  • Hero Member
  • *****
  • Posts: 7674
IOW, it is the control variable's _value_ that may be undefined after the loop, not the variable.  The variable is as defined after the loop as it was before it.  It's value, that's a different story.

I've tried to be careful with my terminology throughout this thread, i.e referred to "no defined value" to try to emphasise that a variable remains declared.

Allowing the control variable to be declared local to the for statement would be the best solution, but it's been made clear that that's not going to happen... which I feel is unfortunate, since the language has already been mauled by the inclusion of anonymous methods which depart much further from Wirth's intent.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018