Recent

Author Topic: strange behaviour with nested WITH statements.  (Read 3230 times)

Bitbeisser

  • New Member
  • *
  • Posts: 33
strange behaviour with nested WITH statements.
« on: May 21, 2024, 04:51:38 am »
In another programming forum, there was recently a discussion about scope and possible ambiguity of references to equally named (and typed) elements.
I created the following test program and was surprised that the following even compiled without an error (actually, kind of proving the ambiguity, though I would have expected that the assignment to BufferB [Counter] would actually throw at least one compiler error
Code: Pascal  [Select][+][-]
  1. Program Withscopetest;
  2.  
  3. Var A : Record
  4.           Counter : Integer;
  5.           BufferA : Array [1..100] of Byte;
  6.         end; // A
  7.     B : Record
  8.           Counter : Integer;
  9.           BufferB : Array [101..200] of Char;
  10.         end; // B
  11. begin
  12.   A.Counter := -1;
  13.   B.Counter := -1;
  14.   FillChar (A.BufferA, 100, 0);
  15.   FillChar (B.BufferB, 100, '?');
  16.   with A do
  17.     with B do
  18.       begin
  19.         Counter := 1;
  20.         BufferA [Counter] := Counter;
  21.         BufferB [Counter] := Char (Counter+48);
  22.       end;
  23.   WriteLn (A.BufferA [1]);
  24.   WriteLn (B.BufferB [101]);
  25.   ReadLn;
  26. end.
  27.  

What am I missing here?  :-\

PS: Sorry, but what I missed in my original post, this is with FPC 3.2.2 (the one that comes with Lazarus 3.2), 64 bit on Windows 10/64 bit
« Last Edit: May 21, 2024, 07:15:14 am by Bitbeisser »

Josh

  • Hero Member
  • *****
  • Posts: 1344
Re: strange behaviour with nested WITH statements.
« Reply #1 on: May 21, 2024, 07:42:14 am »
When I set the for debugmode then I Get Range Error as expected
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

440bx

  • Hero Member
  • *****
  • Posts: 4761
Re: strange behaviour with nested WITH statements.
« Reply #2 on: May 21, 2024, 09:41:53 am »
That behavior has nothing to do with the usage of "with" (no pun intended.)  FPC is a bit "peculiar" when it comes to enforcing array bounds.

Not only are array bounds not really enforced by the compiler but, they are managed in a very "unique" way.  Have a look at the sample program in the message:
https://forum.lazarus.freepascal.org/index.php/topic,44655.msg314233.html#msg314233
and make it a point to read the comments that mention the differences between FPC and Delphi in the treatment of array indexes.
(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: 16201
  • Censorship about opinions does not belong here.
Re: strange behaviour with nested WITH statements.
« Reply #3 on: May 21, 2024, 09:45:12 am »
Apart from the scoping ambiguity indeed, when compiled with {$rangechecks on} you will get an error.
That this compiles without error if range checks are off is correct, because code can be written on purpose to make use of overflow of a range. E.g. the index of an array[0..255]  becomes zero after index 255 is reached in $R- state, but will throw an error in $R+ state.( code is a simple ringbuffer and legal. This works in both FPC and D7.)
Code: Pascal  [Select][+][-]
  1. program ringbuffers;
  2. {$R-}{$ifdef fpc}{$mode delphi}{$endif}
  3. {$apptype console}
  4. var
  5.   RB: array[0..255] of double;
  6.    b:byte;
  7. begin
  8.   b:=0;
  9.   repeat
  10.     writeln(b);
  11.     inc(b);
  12.   until true = false;
  13.   // ctr-c to quit
  14. end.
A practical application that I use myself to write continuously running oscillators for sound processing.
The array contains the wave shape.
« Last Edit: May 21, 2024, 10:27:10 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Warfley

  • Hero Member
  • *****
  • Posts: 1771
Re: strange behaviour with nested WITH statements.
« Reply #4 on: May 21, 2024, 09:55:09 am »
Yeah with introduces scoping issues with shadowing, not just in this example but general (e.g. with over a control in an lack application hides the form properties like caption, color, etc.)

With is actually a terrible construct, so bad that even JavaScript, a language notorious for always keeping backwards compatibility even for the most awful features, deprecated it.
If even JavaScript devs say that this is to bad to even keep for backwards compatibility, it's a really bad feature

440bx

  • Hero Member
  • *****
  • Posts: 4761
Re: strange behaviour with nested WITH statements.
« Reply #5 on: May 21, 2024, 10:36:12 am »
That this compiles without error if range checks are off is correct
No, it is _not_ correct and the reason is very simple, in an array the bounds are part of the _data type_ definition.  Simply ignoring the bounds as FPC does is like suddenly treating a string as a floating point because the compiler found it convenient and less work than to treat it as what it really is.

Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2.  
  3. {-----------------------------------------------------------------------------}
  4. { NOTE: this code tested with FPC v3.0.4 and Delphi 10 (Seattle)              }
  5. {-----------------------------------------------------------------------------}
  6.  
  7. program RangeChecks;
  8.  
  9. const
  10.   SPACE = ' ';
  11.  
  12. var
  13.   AnArray : array[0..0] of ansichar;
  14.  
  15.   b       : integer;
  16.   AnInt   : integer;         // 32bit integer
  17.  
  18.   AnInt64 : int64;           // 64bit integer
  19.  
  20. begin
  21.   AnArray[0]  := SPACE;      // no problem here
  22.  
  23.     // -------------------------------------------------------------------------
  24.     // case 1.
  25.     // Delphi emits an error for this expression (as it should)
  26.     // FPC emits a warning but, at least, generates CORRECT code.
  27.  
  28.     AnArray[5]  := SPACE;    // Delphi won't compile this (which is correct)
  29.  
  30.  
  31.     // -------------------------------------------------------------------------
  32.     // case 2.
  33.     // same as above for this case
  34.  
  35.     AnArray[-5] := SPACE;    // nor this but, FPC does. At least, in this case
  36.                              // it generates CORRECT code for it.
  37.  
  38.   writeln('press enter/return to end this program');
  39.   readln;
  40. end.
  41.  
Delphi does NOT compile the program above and, that is the correct behavior, one that FPC does not replicate.
(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: 16201
  • Censorship about opinions does not belong here.
Re: strange behaviour with nested WITH statements.
« Reply #6 on: May 21, 2024, 12:14:13 pm »
In the case of my example, that compiles in both Delphi and FPC and for the reasons I mentioned: my code IS legal.
(And not a imo corner case like yours)

You can compile your corner case with -Sew or add {$warn 4110 error} to your code to obtain the same behavior as Delphi.
Code: Bash  [Select][+][-]
  1. fpc -Sew x440stuff.pas
  2. Free Pascal Compiler version 3.3.1-15711-geff10ee9b7 [2024/05/18] for x86_64
  3. Copyright (c) 1993-2024 by Florian Klaempfl and others
  4. Target OS: Win64 for x64
  5. Compiling x440stuff.pas
  6. x440stuff.pas(25,13) Warning: (treated as error) Range check error while evaluating constants (5 must be between 0 and 0)
  7. x440stuff.pas(32,13) Warning: (treated as error) Range check error while evaluating constants (-5 must be between 0 and 0)
  8. x440stuff.pas(38) Fatal: There were 2 errors compiling module, stopping
  9. Fatal: Compilation aborted
  10. Error: d:\fpcupdeluxe\fpc\bin\x86_64-win64\ppcx64.exe returned an error exitcode
« Last Edit: May 21, 2024, 12:54:51 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 859
Re: strange behaviour with nested WITH statements.
« Reply #7 on: May 21, 2024, 12:29:50 pm »
No ambiguity here. You should understand namespace concept. Namespaces are nested. When variable name is resolved - it's searched in all namespaces one after another starting from inside one (last) to outside one (first). First match is used. With simply adds new namespace to namespace stack. I.e. in your case it would be:

Code: [Select]
<global namespace>
  <var namespace>
    <A namespace>
      <B namespace>
        YOU ARE HERE!
      </B namespace>
    </A namespace>
  </var namespace>
</global namespace>
Therefore Counter would be used from B. Why range check error isn't generated - is completely different question. May be just because it's runtime error,  that is generated in Debug mode only?
« Last Edit: May 21, 2024, 12:33:21 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Thaddy

  • Hero Member
  • *****
  • Posts: 16201
  • Censorship about opinions does not belong here.
Re: strange behaviour with nested WITH statements.
« Reply #8 on: May 21, 2024, 12:34:16 pm »
OP explicitly excluded the with ambiguity from his question, because he is well aware of it.. It is about the range....
If I smell bad code it usually is bad code and that includes my own code.

440bx

  • Hero Member
  • *****
  • Posts: 4761
Re: strange behaviour with nested WITH statements.
« Reply #9 on: May 21, 2024, 12:47:16 pm »
(And not a imo corner case like yours)
Hardly a corner case.  Any correctly implemented Pascal compiler would NOT compile the example code I provided.  No compiler switches of any kind needed because it is NOT a matter of range checking, it is a matter of data type enforcement (which in this case happens to include the bounds of an array.)

BTW, I didn't say your example wasn't legal.  My point is and remains, that FPC's treatment of array boundaries is not compliant with the Pascal language definition.  FPC should not compile the sample code I presented with or without switch because that code is _not_ valid Pascal code.

Lastly, the compiler can only enforce bounds when the index is a constant (as in the code I presented) and cannot enforce the bounds at compile time when the index is a variable (since the variable's value cannot be determined at compile time.)


(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: 16201
  • Censorship about opinions does not belong here.
Re: strange behaviour with nested WITH statements.
« Reply #10 on: May 21, 2024, 12:53:26 pm »
It should be
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode delphi}}{$warn 4109 error}{$warn 4110 error}{$endif}
At runtime handle ERangeError or error 201
« Last Edit: May 21, 2024, 01:53:16 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Bitbeisser

  • New Member
  • *
  • Posts: 33
Re: strange behaviour with nested WITH statements.
« Reply #11 on: May 21, 2024, 05:59:38 pm »
OP explicitly excluded the with ambiguity from his question, because he is well aware of it.. It is about the range....
Exactly!
I made this test as an example for someone in a different forum to show that it is very easy to introduce ambiguity and hence (not so) subtle sources for hard-to-track-down bugs in non-trivial programs. And this has been this way for the 48 years that I am programming in Pascal now...

What indeed got me worried is that there is no range checking when the assignment to BufferB [Counter] is done. I would have expected that there would be a runtime error (out of bounds), regardless which "Counter" is being set to 1 at the beginning on the nested WITH block.
If it is B.Counter, as the documentation at 13.2.8 states
Quote
This also is a clear example of the fact that the variables are tried last to first, i. e., when the compiler encounters a variable reference, it will first check if it is a field or method of the last variable. If not, then it will check the last-but-one, and so on
. So setting Counter to 1, it should generate a runtime error on the BufferB assignment, as it is defined as an array of 101..200, trying to access element [1].

There also should be a range check error if, contrary to the documentation, A.Counter would be assigned the 1.

But what gets really troubling is the output of the program. BufferA [1] has clearly been changed from the initialized 0 to 1 (Counter), BufferB [101] shows the initialized value of "?" instead of the character "1". So is the assignment to BufferB [Counter] in this case ignored, as nothing is written to the first element of the array, "just in case" the compiler is "assuming" to write to the first element, regardless what the logical array boundaries are   :-\ or does it write that byte just "somewhere"?

As I would expect that without specifically changing the range checking to be off, it is always on, this does not work as expected. Btw, I compiled and ran this from within Lazarus 3.2, and I am definitely NOT intentionally turning off any of the range or IO checking, as that goes against the basic principles of Pascal.

Thaddy

  • Hero Member
  • *****
  • Posts: 16201
  • Censorship about opinions does not belong here.
Re: strange behaviour with nested WITH statements.
« Reply #12 on: May 21, 2024, 08:20:21 pm »
Actually range checking is normally always off, since it affects code size.
You can adapt your code to use my defines: then it mimics delphi and handles the rangecheck not as a warning, but as an error.
If I smell bad code it usually is bad code and that includes my own code.

Bitbeisser

  • New Member
  • *
  • Posts: 33
Re: strange behaviour with nested WITH statements.
« Reply #13 on: May 22, 2024, 12:07:23 am »
Actually range checking is normally always off, since it affects code size.
You can adapt your code to use my defines: then it mimics delphi and handles the rangecheck not as a warning, but as an error.
When did that change? One of the reasons to use Pascal is that by default ALL checks, range check, IO check should be ENABLED, unless someone intentionally disables this...

The PC where I made the test is currently doing some Windows update, but I will test this later today... :(

Josh

  • Hero Member
  • *****
  • Posts: 1344
Re: strange behaviour with nested WITH statements.
« Reply #14 on: May 22, 2024, 12:55:51 am »
Screenshots of Default (Debug & Release)
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

 

TinyPortal © 2005-2018