Recent

Author Topic: [solved] case with part of string constant  (Read 2280 times)

soerensen3

  • Full Member
  • ***
  • Posts: 213
[solved] case with part of string constant
« on: July 10, 2020, 08:15:21 pm »
I hope this is not a duplicate but I didn't know what to search for in the forum.

I was wondering if this is a bug or if there is some logic behind this behaviour of fpc:
If you try to compile the following code (beside that C is not initialized) you get:
project1.lpr(11,11) Error: Constant Expression expected

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. const
  4.   S = 'abc';
  5.  
  6. var
  7.   C: AnsiChar;
  8. begin
  9.   case C of
  10.     'b': ;
  11.     S[ 1 ]: ;  //<<-- Constant Expression expected
  12.   end;
  13. end.        
  14.  

With delphi mode it is the same. I don't understand it because S is constant as well as the index 1 so this should be a constant expression too.
« Last Edit: July 10, 2020, 11:25:14 pm by soerensen3 »
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: case with part of string constant
« Reply #1 on: July 10, 2020, 08:23:38 pm »
Hi!

You use with S[1] a variable!

In the case construction only constant expressions are allowed!

Winni

soerensen3

  • Full Member
  • ***
  • Posts: 213
Re: case with part of string constant
« Reply #2 on: July 10, 2020, 08:33:59 pm »
No I'm not! Please look at the source again. It's a constant.
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: case with part of string constant
« Reply #3 on: July 10, 2020, 08:42:47 pm »
Hi!

S is a constant .
S[1] is a variable.

Winni

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: case with part of string constant
« Reply #4 on: July 10, 2020, 08:44:16 pm »
No I'm not! Please look at the source again. It's a constant.
Such complex expressions are not calculated by FPC as constants (Delphi, by the way, too). Example simpler:
Code: Pascal  [Select][+][-]
  1. const
  2.   S = 'abc';
  3.   C = S[1];
  4. begin
  5. end.

soerensen3

  • Full Member
  • ***
  • Posts: 213
Re: case with part of string constant
« Reply #5 on: July 10, 2020, 08:51:19 pm »
Ok actually you can even drop the case and the question could be:

Code: Pascal  [Select][+][-]
  1. const
  2.   S = 'abc';
  3.   C = S[ 1 ]; // <<-- Illegal expression
  4.  

Why is S[ 1 ] not considered a constant expression though S is a constant?

Have a look at this:

Code: Pascal  [Select][+][-]
  1. const
  2.   S = 'abc';
  3. begin
  4.   S[ 1 ]:= 'b'; //  <<-- Variable Identifier expected
  5. end.
  6.  
  7.  

But S[ 1 ] is defined at compile time.
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

soerensen3

  • Full Member
  • ***
  • Posts: 213
Re: case with part of string constant
« Reply #6 on: July 10, 2020, 09:06:21 pm »
No I'm not! Please look at the source again. It's a constant.
Such complex expressions are not calculated by FPC as constants (Delphi, by the way, too). Example simpler:
Code: Pascal  [Select][+][-]
  1. const
  2.   S = 'abc';
  3.   C = S[1];
  4. begin
  5. end.


Ok I think I can accept this answer. But now look at this:
Code: Pascal  [Select][+][-]
  1. program program1;
  2.  
  3. const
  4.   S: array of Char = ('a','b','c');
  5.  
  6. begin
  7.   S[1]:= 'b';
  8.   WriteLn( S ); //<-- prints bbc
  9. end.
  10.  
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: case with part of string constant
« Reply #7 on: July 10, 2020, 09:12:11 pm »
It's a constant.
You're right that it is a constant but... when the compiler sees S[1] it wants that expression to be an address.  As a result, it creates a three character string in the data segment which is something it can take the address of. You can see it if you hexdump the exe or if you look at the assembly code any reference to S[1] generates (you'll see an address, not a character.)

To see that in action in the source, declare a variable "p : pointer;" then add a statement "p := @S[1]".  if the compiler saw S strictly as a compile time constant it would not allow taking the address of any of its elements but, it does, which proves that even though you declared it as a constant, the compiler allocated memory for it, which means it's no longer a compile time constant, it's a memory constant (somewhat like a typed constant when writeableconst is off.)

Code: Pascal  [Select][+][-]
  1. const
  2.   S = 'abc';
  3.   C = S[ 1 ]; // <<-- Illegal expression
  4.  

Why is S[ 1 ] not considered a constant expression though S is a constant?
because the expression S[1] could also be S[somevariable] which can only be calculated at runtime.  As a result, the compiler takes the "safe route", it puts the constant into memory, that way it can be accessed when the value of "somevariable" is determined.   Once it places into memory, it is no longer a compiler constant, it's a memory constant and those need to be accessed using an address, therefore the "S[1]" cannot be used in a constant expression since the address of S[1] is not known at compile time. (edit: actually, the address of S[1] is known at compile time but, if it were the address of S[v] it wouldn't be.  To account for that case, the compiler plays it "safe" and sees S[1] as if it were S[v].)

Strictly speaking, if the compiler was really smart, it would see that S[aconstantindex] is also a constant and would simply choose the right character at compile time but, it's not that smart.  It plays it safe instead.

Your last example is a typed constant, which isn't really a constant, it's a static variable which is why you can change the value of "a" to "b".

« Last Edit: July 10, 2020, 09:15:52 pm 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.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: case with part of string constant
« Reply #8 on: July 10, 2020, 09:22:23 pm »
Hi!

Nice that 440bx did the job with the Borland const ... which is (nearly) never const.

@soerensen3:

Pascal is just a language like English, Danish, German .....
Perhaps as child you started some discussions why the hammer is not female or ...
Now you just use it .

And in the most cases Pascal is much more logic than a spoken language. And in those strange cases like this: ask the compiler builders.

But most time it helps to grab in the Borland history. Like reading a Turbo Pascal 7.0 manual. There you will  find about the const with the wrong name. Unlogic and awful.
History - they call it.

Winni

« Last Edit: July 10, 2020, 09:26:36 pm by winni »

soerensen3

  • Full Member
  • ***
  • Posts: 213
Re: case with part of string constant
« Reply #9 on: July 10, 2020, 09:32:49 pm »
Thanks for your detailed answers! So it turned out you were all right! It would be nice I the compiler would handle constant indexes of constants or throw an error in the latter (by default) case but I both can probably be considered a corner case.
I now found this which also explains it: https://stackoverflow.com/questions/3240689/why-pascal-const-arrays-arent-actually-constants
« Last Edit: July 10, 2020, 09:34:43 pm by soerensen3 »
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: case with part of string constant
« Reply #10 on: July 10, 2020, 10:07:28 pm »
@sorensen3

If you are satisfied with the answers you got, you may want to add "[SOLVED]" in the thread title.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: case with part of string constant
« Reply #11 on: July 11, 2020, 11:24:31 am »
Ok I think I can accept this answer. But now look at this:
Code: Pascal  [Select][+][-]
  1. program program1;
  2.  
  3. const
  4.   S: array of Char = ('a','b','c');
  5.  
  6. begin
  7.   S[1]:= 'b';
  8.   WriteLn( S ); //<-- prints bbc
  9. end.
  10.  

Typed constants are writable by default due to legacy reasons. You need to use $J- to disable that:

Code: Pascal  [Select][+][-]
  1. program program1;
  2.  
  3. {$J-}
  4.  
  5. const
  6.   S: array[0..2] of Char = ('a','b','c');
  7.  
  8. begin
  9.   S[1]:= 'b';
  10.   WriteLn( S ); //<-- prints bbc
  11. end.
  12.  

Code: [Select]
PS C:\fpc\git> .\compiler\ppcx64.exe -n -Furtl\units\x86_64-win64 -viwn -FEtestoutput .\fpctests\tarrconst.pp
Target OS: Win64 for x64
Compiling .\fpctests\tarrconst.pp
tarrconst.pp(9,3) Error: Can't assign values to const variable
tarrconst.pp(12) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted

By the way: I take it you mean array[0..2] of Char instead of array of Char, because otherwise the compiler will complain about not being able to write variables of that type ;)

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: [solved] case with part of string constant
« Reply #12 on: July 13, 2020, 02:54:31 pm »
I'm not sure I should say anything but... in the little program:
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2.  
  3. program TestConstants;
  4.  
  5. const
  6.   s            = 'abc';
  7.  
  8.   charconstant = s[1];   // <- "illegal expression" ... but no reason why!
  9.  
  10. begin
  11. end.
  12.  
It would be nice if the compiler could at least clue the programmer as to why the expression is illegal. 

Quote
Free Pascal Compiler version 3.0.4 [2017/10/06] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling TestConstantsF.lpr
TestConstantsF.lpr(8,22) Error: Illegal expression
TestConstantsF.lpr(11,4) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted

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

Leledumbo

  • Hero Member
  • *****
  • Posts: 8746
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: [solved] case with part of string constant
« Reply #13 on: July 16, 2020, 11:38:32 am »
It would be nice if the compiler could at least clue the programmer as to why the expression is illegal. 
It would be nicer if the compiler can recognize a constant indexing expression and treat it as a constant, too. But yea, something like "Illegal expression, constant expression expected but S[1] is not a constant expression" should be better than only the former, but I'm not sure if capturing the context is easy enough.

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: [solved] case with part of string constant
« Reply #14 on: July 16, 2020, 01:12:45 pm »
It would be nicer if the compiler can recognize a constant indexing expression and treat it as a constant, too.
Absolutely.  That's the ideal and sensible course of action.

But yea, something like "Illegal expression, constant expression expected but S[1] is not a constant expression" should be better than only the former, but I'm not sure if capturing the context is easy enough.
I believe the problem is related to typed constants.  Whether readable or writable, it seems that whenever a constant is typed it ends up being in memory thus, no longer a compile time constant. 

In that message you suggest, which is a definite improvement over simply "illegal expression", it would be quite reasonable for a programmer to wonder why S[1] is not a constant (which is what the OP was asking.)  if S is a constant then all elements of S should be constants.

FPC compiles the test program below but, Delphi does not:
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2. {$MODE DELPHI}
  3.  
  4. program TestConstants;
  5.  
  6. const
  7.   s = 'abc';
  8.  
  9.   //charconstant = s[1];   // <- "illegal expression" ... but no reason why!
  10.  
  11. var
  12.   c : char;
  13.   p : pchar;
  14.  
  15. begin
  16.   c := s[1];     // the compiler didn't determine at compile time that
  17.                  // s[1] is "a"
  18.  
  19.                  // instead it converts the constant into a non-writeable
  20.                  // string.
  21.  
  22.   writeln(c);
  23.  
  24.   // cannot take the address of "s"
  25.  
  26.   //p := @s;     // <- this is NOT allowed
  27.  
  28.  
  29.   // but can take the address of elements of "s"
  30.  
  31.   p := @s[1];    // <- but this is allowed !!
  32.   p := @s[0];    // <- and so is this      !!
  33. end.          
That piece of code should _not_ compile.

It's interesting that, like FPC, Delphi does save the string "abc" in memory, yet, in that particular case, it still allows referencing S[1] as a constant (as it should) but, FPC does not.  For FPC, once in memory, it's no longer a "real constant" (compile time constant.)

Also Interesting, Delphi didn't figure out that S[1] is "a" (presuming first element is at 1 - which is what it is for a string), instead, like FPC it used memory references to get the value "a".




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

 

TinyPortal © 2005-2018