Recent

Author Topic: Using fields of a record as control variable loop  (Read 709 times)

simone

  • Hero Member
  • *****
  • Posts: 573
Using fields of a record as control variable loop
« on: September 27, 2022, 12:50:52 pm »
Consider this simple program:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. type
  3.   TMyRect=record
  4.     X,Y : integer;
  5.   end;
  6. var
  7.   T : TMyRect;
  8. begin
  9.   for T.X:=1 to 10 do
  10.     writeln(T.X);
  11. end.

The compiler produces the following error: "project1.lpr(9,8) Error: Illegal counter variable".

Is this expected behavior? If so, what is the reason why it is not possible to use the field of a record as control variable in a for loop?

Thanks for explanations.
Microsoft Windows 10 64 bit - Lazarus 3.0 FPC 3.2.2 x86_64-win64-win32/win64

egsuh

  • Hero Member
  • *****
  • Posts: 1273
Re: Using fields of a record as control variable loop
« Reply #1 on: September 27, 2022, 01:03:56 pm »
I think only local variables are allowed as index.

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Using fields of a record as control variable loop
« Reply #2 on: September 27, 2022, 01:37:48 pm »
Perhaps because compiler wants a total control over the placement and lifetime of that variable.

It could end being CPU register, with no memory address at all, and only during the loop span, immediately destroyed after the loop ends.

You can try

Code: Pascal  [Select][+][-]
  1. var
  2.   ...
  3.   i: integer absolute T.X;
  4. ...
  5.   for i := ...
  6.  

Would probably be equally denied.

P.S. frankly, this "loop variable only for loop itself" approach contradicts with Wirth's "clearly designated section for all-the-variables to be declared in"  approach.

Less structured C approach, where every begin-end can have their own variables, is much more fitting this "for loop" design.
It is typical there to declare yet another variable inside loop, like

Code: C  [Select][+][-]
  1. int foo( const int bar )
  2. {  int i = 20, j = 10;
  3.  
  4.    for(int i = j; i < 100; ++i, j += bar )
  5.    {
  6.         // do something
  7.    }
  8.   return i;
  9. }
  10.  

And recent Delphi given up on this. Notice the "loops" secton in https://blog.marcocantu.com/blog/2018-october-inline-variables-delphi.html
« Last Edit: September 27, 2022, 02:27:08 pm by Arioch »

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: Using fields of a record as control variable loop
« Reply #3 on: September 27, 2022, 01:45:40 pm »
Is this expected behavior? If so, what is the reason why it is not possible to use the field of a record as control variable in a for loop?
Yes, it is expected behavior and it is rooted in the fact that a variable used as the index in a "for" loop is not allowed to be modified inside the loop.

If the compiler allowed using global variables as indexes it would have no way to ensure the index is not modified by some other code (particularly in a multi-threaded program.)

Because of this, index variables should be local variables (outside the reach of other program code.)

HTH.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Using fields of a record as control variable loop
« Reply #4 on: September 27, 2022, 02:05:59 pm »
local variables (outside the reach of other program code.)

which T perfectly was...

For the purposes program's global "begin-end" block - the global variables are local, otherwise this would not compile, and it did compile.

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3.   procedure local;
  4.   var local_var: record x: integer end;
  5.       local_var_y: integer absolute local_var.x;
  6.   begin
  7.     local_var.x := 10;
  8.     local_var_y := 20;
  9.  
  10.     // project1.lpr(11,9) Error: Illegal counter variable
  11.     // for local_var_y := 1 to 2 do Writeln(y);
  12.     // for local_var.x := 1 to 2 do Writeln(y);
  13.   end;
  14.  
  15. var global_var: integer; // global vars are perfectly okay for global loops
  16. begin
  17.   for global_var := 0 to 5 do writeln(global_var);
  18.   readln;
  19.   local;
  20.   readln;
  21. end.
« Last Edit: September 27, 2022, 02:07:45 pm by Arioch »

simone

  • Hero Member
  • *****
  • Posts: 573
Re: Using fields of a record as control variable loop
« Reply #5 on: September 27, 2022, 02:12:24 pm »
In this case, I'm not sure that the problem is whether the control variable is local or global.

The following example is identical to the previous one, except that the control variable is a free-standing integer, not an integer field in a record.

In this case, no problem.

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. var
  3.   T : integer;
  4. begin
  5.   for T:=1 to 10 do
  6.     writeln(T);
  7. end.
« Last Edit: September 27, 2022, 02:14:41 pm by simone »
Microsoft Windows 10 64 bit - Lazarus 3.0 FPC 3.2.2 x86_64-win64-win32/win64

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Using fields of a record as control variable loop
« Reply #6 on: September 27, 2022, 02:25:51 pm »
It is much simpler: a record is not an ordinal. A loop variable *must* be an ordinal.
Also note that a hardcast would work, but not if you count from 1 instead of zero.
(The former belonging to the ministry of silly programmers - except strings, but that ain't no ordinal....)
« Last Edit: September 27, 2022, 02:30:33 pm by Thaddy »
Specialize a type, not a var.

simone

  • Hero Member
  • *****
  • Posts: 573
Re: Using fields of a record as control variable loop
« Reply #7 on: September 27, 2022, 02:35:13 pm »
In my first example, the control variable is not a record, but an integer field of a record, thus an ordinal variable.
Microsoft Windows 10 64 bit - Lazarus 3.0 FPC 3.2.2 x86_64-win64-win32/win64

Josh

  • Hero Member
  • *****
  • Posts: 1271
Re: Using fields of a record as control variable loop
« Reply #8 on: September 27, 2022, 02:58:10 pm »
@Nicole
I understand your thinking, but the variable T is a record structure, your trying to use a field of a record which is not allowed.
It is highly recommended to use local defined ordinal types for loop indexing; so they are in scope only in the procedure/function thet are used.
Global Ordinal Variables can be used but you will need to make sure that the global variables is not altered elsewhere in code, or you could end up with very hard to find bugs.
Also remember that the end value of a for index variable is not guaranteed to be what you think, it could be any value once the for loop has finished.
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: Using fields of a record as control variable loop
« Reply #9 on: September 27, 2022, 03:32:27 pm »
In this case, I'm not sure that the problem is whether the control variable is local or global.
The problem is global vs local but, the compiler can only go so far to enforce the rule: "for" index variables cannot be modified by user code. Even though that's the rule, the compiler has many, hard to impossible to avoid, limitations when it comes to enforcing it.

The following example is identical to the previous one, except that the control variable is a free-standing integer, not an integer field in a record.

In this case, no problem.

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. var
  3.   T : integer;
  4. begin
  5.   for T:=1 to 10 do
  6.     writeln(T);
  7. end.
A perfectly reasonable example but, when it comes to enforcing the rule "the index should not be modified by user code" there are problems with it.  In a multi-threaded program, since the index is a global variable, there is nothing that prevents the other thread from modifying it.  The solution is: "don't allow the index to be a global variable" but, fully enforcing it would cause the example you presented above to no longer compile, forcing the programmer to create a function with a local "T" variable in order to implement a trivial program.  Not desirable since it is cumbersome.

Consider the following program:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. type
  3.   TMyRect=record
  4.     X,Y : integer;
  5.   end;
  6.  
  7.  
  8. procedure A1();
  9. var
  10.   r : TMyRect;
  11.  
  12.   i : integer absolute r;  { dirty trick       }
  13.  
  14.   p : pinteger;            { more dirty tricks }
  15. begin
  16.   for i := 1 to 10 do
  17.   begin
  18.     writeln(i);
  19.     writeln(r.X);
  20.   end;
  21.  
  22.   readln;
  23.  
  24.   p := @i;                 { aliasing          }
  25.   for i := 1 to 10 do
  26.   begin
  27.     { cause an infinite loop                   }
  28.  
  29.     writeln(i);
  30.     writeln(r.X);
  31.  
  32.     //if i = 5 then i := 1;  { this won't compile }
  33.  
  34.     if i = 5 then p^ := 1;   { but this will      }
  35.   end;
  36.  
  37.   readln; { will never get here }
  38. end;
  39.  
  40. begin
  41.   A1();
  42.  
  43.   readln;
  44. end.
CAUTION: the above code causes an infinite loop (which is something that is not supposed to be possible when using a "for" loop.)

The compiler wants to enforce the rule "no changes to the index variable by user-code" but, its ability to enforce it is rather limited, it tries but, that's really all it can do. 

As far as why it doesn't accept r.X as an index is because that would make it more difficult for the compiler to figure out if "x" is being modified in the loop (think about what would happen in a variant record, it would easy to overlay some other variable on top of "x" and change that one instead - similar to what "p" does in the above example.)

bottom line: the compiler tries to enforce the rule "no user changes to the index variable" but, its ability to enforce it is limited, as a result, there are things it won't allow (such as r.X as the index) to give itself a better chance but, that's all it is, a better _chance_. 

Technically, there is no difference between using "i" or "r.X", both are offsets to an ordinal type (integer in this case.)

HTH.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Using fields of a record as control variable loop
« Reply #10 on: September 27, 2022, 04:00:56 pm »
In my first example, the control variable is not a record, but an integer field of a record, thus an ordinal variable.
Nope, a field of a record is a mistake, unless you hardcast, which is also bad programming.
Specialize a type, not a var.

 

TinyPortal © 2005-2018