Recent

Author Topic: Evaluation of constant statements  (Read 7564 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: Evaluation of constant statements
« Reply #75 on: September 24, 2022, 04:22:20 pm »
And also, topic "absolute" being broken or properly designed has NOTHING in common with "multiple assignment" or "structural ..." or "unpacking ..." nor with "variable initialization" nor with tuples.
This is just a strawman.

It has a great deal to do with it, since it demonstrates that Pascal at one time countenanced modifiers to at least some extent so var...static should be viable. However, the tangent relating to multiple variables in the context of absolute was yours, and tuples came into it purely because I was trying to demonstrate that there was a longstanding problem in this area and that you would gain nothing (and possibly antagonise the developers) by banging on that drum.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

440bx

  • Hero Member
  • *****
  • Posts: 4030
Re: Evaluation of constant statements
« Reply #76 on: September 24, 2022, 05:58:02 pm »
Syntax-wide this would be enough:

Code: Pascal  [Select][+][-]
  1. procedure p (const N: string);
  2. const K: PInteger = @N; // or  ... = Pointer(@N) for {$T+} if not a special case made
  3. begin
  4.    ...
  5. end;
  6.  
Apparently you don't realize the absurdity in the code you presented.  The address of parameter "N" is variable therefore it cannot be assigned to a constant.  IOW, the compiler cannot determine the value of "@N" at compilation time which is a necessary condition to define a constant.

The compiler would have to generate code to create that "constant" at runtime.  The compiler wouldn't accept that expression even if "K" was a variable but, it will happily accept:
Code: Pascal  [Select][+][-]
  1. procedure p (const N: string);
  2. var K : PInteger absolute N; { unlikely to be useful but... it can be done if needed }
  3. var K2 : integer absolute N; { not likely to be useful either }
  4. begin
  5.    ...
  6. end;
  7.  

i do not call to make "absolute" easier to use (i would 20 years ago, though). Being laden with burdens this discouraging users is fine.
You keep claiming that "absolute" is hard to use ... can you explain what it is that makes it hard to use ?   (NOTE: not being able to initialize variables in their declarations with anything other than a constant has nothing to do with "absolute")

(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: Evaluation of constant statements
« Reply #77 on: September 24, 2022, 06:06:27 pm »
Drum memory makes it sound like "The story of Mel".

It is. But i always forget the name, as it is absolutely random constant, unrelated to the expression structure :-)

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Evaluation of constant statements
« Reply #78 on: September 24, 2022, 06:31:38 pm »
Syntax-wide this would be enough:

Code: Pascal  [Select][+][-]
  1. procedure p (const N: string);
  2. const K: PInteger = @N; // or  ... = Pointer(@N) for {$T+} if not a special case made
  3. begin
  4.    ...
  5. end;
  6.  
The address of parameter "N" is variable therefore it cannot be assigned to a constant.

Make it. Compiler is not divine scripture, that mortals dare not touching. Especially as i explicitly talked about some imaginary "ideal Pascal".

You have "writeable constants", you have managed variables that compiler DOES intialize (assign to NULL) in every funciton prologue.
Initializing typed consts would be just maryring those already existing things.

Is it some divine rule that constant must only be calculated in compile time? Constant should not be changed by application code, and that is it.  Using OS memory protection API i can easily change string literals - does it make them not constants? It does not.

Additionally, the compiler equally does NOT know that address of N in compile time when compiling "absolute N" - but this lack of knowledge does not break compilation. Because, sure, the value of @N is not known, but the value of @@N relative to the function's data frame - is known in compile time.

OTOH, you have a point. Since i was talking about "ideal Pascal", then perhaps i should bring in the topic of bound-not-assignable variables. AKA VALues as they are named in Scala. AKA immutable variables as they are called in Rust. And probably many other names.

So, to borrow from Scala - and then mixing two concepts and making exampe much less clear, in "ideal Pascal" it could be like
Code: Pascal  [Select][+][-]
  1. procedure p (const N: string);
  2. val K: PInteger = @N; // or  ... = Pointer(@N) for {$T+} if not a special case made
  3. begin
  4.    ...
  5. end;
  6.  

But perhaps let's separate issues and keep it typed const for a while. I equally dislike that packages/units/classes were not made uniform (synonimous) and with/uses were not, but throwing all the concepts that could've make "ideal Pascal" into discussion would make a perfect salad.

Quote
(NOTE: not being able to initialize variables in their declarations with anything other than a constant has nothing to do with "absolute")

You keep talking about initializing values time and again, and Mark tried to talk about tuples assignment. I never talked about assigning values in "absolute" keyword context. This diversion attempts become obsessive.

Quote
i do not call to make "absolute" easier to use (i would 20 years ago, though). Being laden with burdens this discouraging users is fine.
You keep claiming that "absolute" is hard to use ... can you explain what it is that makes it hard to use ?

1. It volates Pascal norm by de facto precluding declaring several variables in one single declaration. This adds cognitive burden on the programmer. Wirth and Pascal proponents always championed that Pascal (and European languages) are better than C (and American languages) in their strive to simplify, remove "special cases" and lessen cognitive burden.

2. It forces programmer to copy-paste declarations if he need two "absoluted" variables of same type but different offsets. This is not only code bloat, but violating DRY principle it also becomes a maintainance bloat, as copy-pasted pieces tend to eventually diverge, link between them be forgotten, and eventually when program is updated - only some of copy-pasted pieces gets edited, introducing errors.

3. It defies common sense, binding an obviously variable specific attribute (address of this specific variable: automatic or fixed), to an entity shared by many variables (data type). This vialoation of natural  structure needs memorization as yet another special case, increasing cognitive burden.

Forcing separate and exclusive declaration for every absolut'ed variable "because we wrote TP/Delphi/FPC that way" is the same as would've been making TComponent.Name a class property and then requiring every TForm or TLabel that wants to have different name to be subclassed and claiming "it is good design choice because we wrote LCL that way".

Yes, Heilsberg "wrote TP that way". No, it was bad design if his goal was to make it a "native" citizen of Pascal language. But it was a good design if his goal was to make it stick out as explicitly alien thing.
It can not even be explained by "limited hardware" because PDP-11 hardware was much more weak than MS-DOS hardware, yet PDP-11 Pascal had this feature implemented in natural way.
« Last Edit: September 24, 2022, 06:35:51 pm by Arioch »

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Evaluation of constant statements
« Reply #79 on: September 24, 2022, 06:57:30 pm »
However, the tangent relating to multiple variables in the context of absolute was yours

It is, because declaring several variables in one declaration is as Pascalish as can be, and i dislike polluting Pascal's coherence by introducing special cases and exception from rules.

This, however, is not about value assignments. J&W never had assignments in var declarations nor types in const declarations :-)

tuples came into it purely because I was trying to demonstrate that there was a longstanding problem in this area

and that you would gain nothing (and possibly antagonise the developers) by banging on that drum.

tuples came into it purely because I was trying to demonstrate that there was a longstanding problem in this area

I think tuples can be on the table when at very least record literals would be brought into the language, as they are much less ambiguous and records are already part of the language.

I'd say that probably tuples would not be a pressing issue for Pascal until some meta-generics (nested templates) would be.

I mean, when i see in Delphi libraries declarations like

Code: Pascal  [Select][+][-]
  1. TFunc<R> = reference to function: R;
  2. TFunc<R, T1> = reference to function(const v1: T1): R;
  3. TFunc<R, T1, T2> = reference to function(const v1: T1; const v2: T2): R;
  4. TFunc<R, T1, T2, T3> = reference to function(const v1: T1; const v2: T2; const v3: T3): R;
...and so forth for a dozen of lines - it clearly shows tha language lacks expressiveness to declare it only once.

It is a vivid reminder of attempts to create container libraries before generics in Delphi - they had similar nigh infinite copy-paste.

Maybe it would stay forever, or maybe a Pascalish way to replace this scroll with a finite number of declarations eventually be found. I don't know.

But until then there seem to be no point for tuples in the language. They would be indistinguishable from records and records are already here.

And we did not start about pattern matching yet. Because, they have to be designed into the language to make "structural assignment" consis tent, and "structural assignment" indeed is needed for tupples to make sense. It is one big snowball that can not be sliced to salami and solved one thin piece a time.

And hence, i see no relation between very limited and isolated funcitonality of absolute/origin and this blob.

Quote
and that you would gain nothing (and possibly antagonise the developers) by banging on that drum.

Frankly, since i explicitly several time said that i do not call to "extend" or "smoothen" absolute, i hope they can tell calling spade a spade from forcefeeding with spades.

If they can not, if they read their internal voices instead of what i had said, bad luch, but then it is out of my control and the antagonization would happen whatever i do.

But yes, your notice made me repeat this disclaimer in every post :-/
« Last Edit: September 24, 2022, 07:01:35 pm by Arioch »

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Evaluation of constant statements
« Reply #80 on: September 24, 2022, 07:14:59 pm »
[(though (* and *) only work mixed with { and } in ISO mode)

if by "mixed" you meant" nested", then sounds like a bug, because Delphi handles them.
Not sure about TP, i believe it did but might remember wrong.

Or, if you meant that *) closes the { - then "deisgn by committee" at it's finest

440bx

  • Hero Member
  • *****
  • Posts: 4030
Re: Evaluation of constant statements
« Reply #81 on: September 24, 2022, 08:25:23 pm »
Syntax-wide this would be enough:

Code: Pascal  [Select][+][-]
  1. procedure p (const N: string);
  2. const K: PInteger = @N; // or  ... = Pointer(@N) for {$T+} if not a special case made
  3. begin
  4.    ...
  5. end;
  6.  

Make it. Compiler is not divine scripture, that mortals dare not touching. Especially as i explicitly talked about some imaginary "ideal Pascal".

You are not looking at the big picture and, a capable compiler is a very big picture that shouldn't have any blurred areas.  There are a lot of problems with what you're suggesting.  First, the value of "K" changes every time the function is called.  That hardly meets the definition of "constant". In addition to that, neither O/Ss, nor any CPUs I know of provide read/write protection at the single byte/word/dword/qword level, the best they can do these days is page level (often 4096) bytes.  That means, there is no mechanism to ensure your "constant" remains constant.  Additionally, your constant "K" isn't a constant, it is a variable which means another variable could take its address and change its value by de-referencing it.  Of course, to prevent that from happening, you could have your "ideal Pascal" forbid taking the address of such "constants" (which are constants like the Pope is an astronaut) but, what if the program actually needs the address of that "constant" for something other than changing its value ?... what then ?

You have "writeable constants", you have managed variables that compiler DOES intialize (assign to NULL) in every funciton prologue.
Initializing typed consts would be just maryring those already existing things.
the "writeable constants" thing is a disgrace, making it even worse than it already is, is not a good idea (some of the reasons were mentioned above.)  At least, in the case of writeable constants, it is fairly simple to inform a programmer that those things aren't constants, they are _variables_ (or more accurately, an identifier that is a memory address - which may or may not be writeable.)

Is it some divine rule that constant must only be calculated in compile time? Constant should not be changed by application code, and that is it.  Using OS memory protection API i can easily change string literals - does it make them not constants? It does not.
Any value that is calculated at runtime, is by definition not constant because its value will change from whatever initial value the compiler assigned to it to whatever value is calculated at runtime.  Therefore it is not a constant.  That's not a "divine rule", it's the definition of constant, if it changes, it is _not_ a constant.  What you want is a "one-time" variable.

Additionally, the compiler equally does NOT know that address of N in compile time when compiling "absolute N" - but this lack of knowledge does not break compilation. Because, sure, the value of @N is not known, but the value of @@N relative to the function's data frame - is known in compile time.
strictly speaking, you are correct when you say that the compiler does NOT know the address of N at compile time BUT, it _knows_ the offset from whatever point it is relative to and that's all it needs to know.  When compiling the compiler doesn't know any runtime addresses (except in very unusual cases) but, it knows the offsets of every global variable, parameter and local variable because it is the compiler that determines them.  In the case of absolute, it just means put the absolute-d variable at the same offset as some other variable.  That's it.  That's all the magic and the compiler has all the information it needs _at compile time_ to make it happen.

You keep talking about initializing values time and again, and Mark tried to talk about tuples assignment. I never talked about assigning values in "absolute" keyword context. This diversion attempts become obsessive.
You're the one who mixed Apples and Oranges by mixing "absolute" with multiple variable initialization in one of your examples.  Don't complain it is being brought up because you're the one who brought it up.

1. It volates Pascal norm by de facto precluding declaring several variables in one single declaration.
It would be a contradiction, not to mention meaningless and redundant, to allow absolute in a multiple variable declaration. There is no use in declarating multiple names, all of the same type for another variable.  That's totally useless.

This adds cognitive burden on the programmer. Wirth and Pascal proponents always championed that Pascal (and European languages) are better than C (and American languages) in their strive to simplify, remove "special cases" and lessen cognitive burden.
I see more "cognitive burden" in putting the programmer in a situation where he/she wonders why some variable "A" is being absolute-d multiple times all using the same data type.  That doesn't make any sense.

2. It forces programmer to copy-paste declarations if he need two "absoluted" variables of same type but different offsets.
if the offsets differ then it means that _different_ variables are being absolute-d.  Donald Knuth didn't write the "Art of Copy/Pasting", he wrote "The Art of Computer Programming".   

This is not only code bloat, but violating DRY principle it also becomes a maintainance bloat, as copy-pasted pieces tend to eventually diverge, link between them be forgotten, and eventually when program is updated - only some of copy-pasted pieces gets edited, introducing errors.
if anything, "absolute" makes code much clearer and, it doesn't generate any bloat (no code is generated to implement "absolute" at runtime) and, should substantially reduce the need for copy/pasting.

3. It defies common sense, binding an obviously variable specific attribute (address of this specific variable: automatic or fixed), to an entity shared by many variables (data type). This vialoation of natural  structure needs memorization as yet another special case, increasing cognitive burden.
I'm really starting to wonder if you understand what "absolute" is used for.  The purpose of "absolute" is to treat memory identified by some variable as having a _different_ data type than the one of the "absolute"-d variable.  An extremely convenient features that saves a great deal of typecasting when used correctly.

Forcing separate and exclusive declaration for every absolut'ed variable "because we wrote TP/Delphi/FPC that way" is the same as would've been making TComponent.Name a class property and then requiring every TForm or TLabel that wants to have different name to be subclassed and claiming "it is good design choice because we wrote LCL that way".
As previously mentioned, it makes no sense to "absolute" multiple variables in a single statement _unless_ your "ideal Pascal" would allow a list of variables after the "absolute" clause which would make it prone to transposition errors.  A very poor idea.

It can not even be explained by "limited hardware" because PDP-11 hardware was much more weak than MS-DOS hardware, yet PDP-11 Pascal had this feature implemented in natural way.
I cannot comment on that because I never used Pascal on a PDP-11 but, I only see problems and downsides in what you've proposed so far.
(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: Evaluation of constant statements
« Reply #82 on: September 24, 2022, 09:20:34 pm »
Syntax-wide this would be enough:

Code: Pascal  [Select][+][-]
  1. procedure p (const N: string);
  2. const K: PInteger = @N; // or  ... = Pointer(@N) for {$T+} if not a special case made
  3. begin
  4.    ...
  5. end;
  6.  

Make it. Compiler is not divine scripture, that mortals dare not touching. Especially as i explicitly talked about some imaginary "ideal Pascal".

You are not looking at the big picture

I think that is exactly what i do here, looking at the big picture of imaginary ideal, while ignoring small details of backward compatibility and legacy implementations :-)

Quote
First, the value of "K" changes every time the function is called.

So what? outside of the function execution span it does not exist, and inside the funcito neexecution span it does not change. Fine by me.

Quote
That hardly meets the definition of "constant".

To me it does.
Code: Pascal  [Select][+][-]
  1.  procedure N(const K: integer);
  2. begin
  3. ...
  4. end;

Would you argue K is not constant enough, if it does not have one and the same, fixed in compile-time value no matter how many time the funciton is called ?

So, i see no more a problem with local constant getting value in the prologue, than i do with constant parameter.

Quote
In addition to that, neither O/Ss, nor any CPUs I know of provide read/write protection at the single byte/word/dword/qword level

Irrelevant, as long as we talk about language domain.

Should i repeat it again? If i look outside pure Pascal - i can call VirtualProtect or WriteProcessMemory and change eveyrthing, including string literals.
If you look at CPU level or OS level - then there is no such thing as constant at all.

So my proposal is no worse and is no less a constant than string literals, which are perfectly mutable then.

Quote
the best they can do these days is page level (often 4096) bytes.
Did you hear about DR0 .. DR3 (or was it DR7 ?) on 80386 CPU ? About TD386 debugger?

But okay, since the went THAT level of abstraction, you can dispatch a separate page for every constant. The challenge (strange one, if you ask me) complete.

But don't ask me what alleged defiiciences of mass market hardware has with hypothetical "ideal" language syntax. I did not bring it.

Quote
That means, there is no mechanism to ensure your "constant" remains constant.

False. There is. It is called language-level security/safety.
If you really need to be absolutely watertight, then Pascal should just not provide for procedure calls outside RTL and should provide no means in the language to write the code violating those constants.

For example, every pointer operation in runtime is checked by compiler against white and black lists of allowed addresses. So does Move, FillChar and everyone. So do indexed array accesses.
OS calls are barred, so is asm. No way to upset a constant.

...otherwise there is no "true constant" at all as even string literals are easily assignable using OS API.

Quote
Additionally, your constant "K" isn't a constant, it is a variable which means another variable could take its address and change its value by de-referencing it.

It is no different from all the other typed constants of today, so is not making a languageworse than it already is.

Code: Pascal  [Select][+][-]
  1. procedure N(var P: string);
  2. var
  3.    A: string absolute P;
  4.    B: PPointer;
  5.    C: Pointer absolute B;
  6. begin
  7.    C := @P;
  8.    B^ := nil;
  9.    A := 'absoutely safe and immutable pointer - come to me now!';
  10. end;
  11.  

Quote
actually needs the address of that "constant" for something other than changing its value ?... what then ?
Same as with de facto assignable string literals of today Pascal.

It would be enough if "normal code" would not do it by accident, protecting me from shooting my own foot.
As long as i can call OS API you can not prevent me from assigning to ANY constant. Even the constant onlined into the code can be changed together with code.

BTW, i think i saw warnings in some Pascal flavours exactly about taking pointers to specific letters in the string. And taking char pointer from a string is expensive operation (in Delphi at least), as it enforces call to UniqueString to keep preserve COW semantics in the presence of pointers. So, yeah, in practical languages abstractions do leak.


You have "writeable constants", you have managed variables that compiler DOES intialize (assign to NULL) in every funciton prologue.
Initializing typed consts would be just maryring those already existing things.
the "writeable constants" thing is a disgrace, making it even worse than it already is, is not a good idea (some of the reasons were mentioned above.)  At least, in the case of writeable constants, it is fairly simple to inform a programmer that those things aren't constants, they are _variables_ (or more accurately, an identifier that is a memory address - which may or may not be writeable.)

Is it some divine rule that constant must only be calculated in compile time? Constant should not be changed by application code, and that is it.  Using OS memory protection API i can easily change string literals - does it make them not constants? It does not.
Any value that is calculated at runtime, is by definition not constant because its value will change from whatever initial value the compiler assigned to it to whatever value is calculated at runtime.  Therefore it is not a constant.  That's not a "divine rule", it's the definition of constant, if it changes, it is _not_ a constant.  What you want is a "one-time" variable.
[/quote]

Call it an ex-parenthesis constant parameter then. Whatever. A rose is a rose is a rose.
Also let's push for FPC devs to finally recognize cons parameters are not really constant butt are "externally assigned varables" and force them to change it in documentaiton first, and in syntax later.

Code: [Select]
procedure N(const Z: double); begin ... end; If Z value can not be calcuated in compile-time it is not true constant and compilation should be aborted! Yeee-hooo!

P.S. Frankly, compared to all the low-level sideeffects use of "absolute" can bring - can anyone even make a comprehensive list of them? - the need to explain that function parameters might have different addresses on different calls seems to me a manageable problem.

Quote
Additionally, the compiler equally does NOT know that address of N in compile time when compiling "absolute N" - but this lack of knowledge does not break compilation. Because, sure, the value of @N is not known, but the value of @@N relative to the function's data frame - is known in compile time.

strictly speaking, you are correct when you say that the compiler does NOT know the address of N at compile time BUT, it _knows_ the offset from whatever point it is relative to and that's all it needs to know.

You just said the same thing i said starting with "@@N" in a more verbose form. Now, this would be EXACTLY the same with my "const-ptr-based absolute". Because it is one and the same thing, just dressed in different syntax sgar.

Quote
That's all the magic and the compiler has all the information it needs _at compile time_ to make it happen.

Bingo!!!  The compiler has all the information it needs to compile the const-ptr-initialization into the function prologue!!!


Quote
You're the one who mixed Apples and Oranges by mixing "absolute" with multiple variable initialization in one of your examples.


False.
And not only false - it is impossible to mix, as absolute variables can NOT be initialized.
Surprise, right?

I always told about multiple variable defined in a single declaration, but never about being initialized.

This strawman is rotting for so long - it is getting slimey now.

Quote
1. It volates Pascal norm by de facto precluding declaring several variables in one single declaration.
It would be a contradiction, not to mention meaningless and redundant, to allow absolute in a multiple variable declaration.

No it won't. Addresses should be different which i repeated over and over again. Yet another strawman you invented to put your words into my mouths. Don't.

The rest is skipped as having zero relation to what i ever said.

Quote
This adds cognitive burden on the programmer. Wirth and Pascal proponents always championed that Pascal (and European languages) are better than C (and American languages) in their strive to simplify, remove "special cases" and lessen cognitive burden.
I see more "cognitive burden" in putting the programmer in a situation where he/she wonders why some variable "A" is being absolute-d multiple times all using the same data type.  That doesn't make any sense.

So, you just said that because use of "absolute" makes program harder to READ it makes sense to make "absolute" syntax harder to WRITE.
For the sake of programmers who would later read it makes sense in this case to put more burden on the programmer who writes.

I agree.
"Absolute" is made in an unpascalish way explicitly discouraging its use and that is a good thing.


2. It forces programmer to copy-paste declarations if he need two "absoluted" variables of same type but different offsets.
if the offsets differ then it means that _different_ variables are being absolute-d.[/quote]

You mean the opposite thing that i meant. "Absolute-d" variable, when i wrote it, is one which declaration was changed by "absolute" keyword.


Quote
This is not only code bloat, but violating DRY principle it also becomes a maintainance bloat, as copy-pasted pieces tend to eventually diverge, link between them be forgotten, and eventually when program is updated - only some of copy-pasted pieces gets edited, introducing errors.
if anything, "absolute" makes code much clearer

Than what?
How exactly
Code: [Select]
var X: integer absolute Z; Y: integer; more clear than 
Code: [Select]
var X origin Z: integer; Y: integer; and
Code: [Select]
var X origin Z, Y: integer;
Quote
it doesn't generate any bloat (no code is generated

It is the bloat itself, even if results in no executable opcodes.
Type declarations do not generate code just by existing, still they can be bloat and still they are source code.

Quote
should substantially reduce the need for copy/pasting.

If we think that "absolute should be promoted and make easy to use" then PDP-11 syntax is the ideal to strive for.

Then about "should" but in fact they increase.

Code: Pascal  [Select][+][-]
  1. var X origin A, Y origin B, Z: array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end;

and we compare it with equivalent

Code: Pascal  [Select][+][-]
  1. var
  2.   X: array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end absolute A;
  3.   Y: array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end absolute B;
  4.   Z: array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end;
  5.  

It would take a special mindset to say the latter "should reduce the copy/paste" of the former.

And please, do not suggest me workarounds that reduce copy-paste by introducing boilerplate. I know them.

However, if we consider that reducing of language vocabulary (number of keywords and special cases in syntax patterns) is a virtue and "absolute" is to be discourages because it is dangerous, then we have another pattern:

Code: Pascal  [Select][+][-]
  1. type
  2.   TMyAbsolutedArray = array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end absolute A;
  3.   PMyAbsolutedArray = ^TMyAbsolutedArray;
  4. const
  5.   X: PMyAbsolutedArray = @A;
  6.   Y: PMyAbsolutedArray = @B;
  7. var
  8.   Z: TMyAbsolutedArray;
  9.  

and it has to be compared with the same

Code: Pascal  [Select][+][-]
  1. var
  2.   X: array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end absolute A;
  3.   Y: array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end absolute B;
  4.   Z: array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end;
  5.  

or with intermediate

Code: Pascal  [Select][+][-]
  1. type
  2.   TMyAbsolutedArray = array[Low(TMyRange) .. High(TMyRange)] of array [TMyAnotherRange] of record U: double; V: real; end absolute A;
  3. var
  4.   X: TMyAbsolutedArray absolute A;
  5.   Y: TMyAbsolutedArray absolute B;
  6.   Z: TMyAbsolutedArray;
  7.  

And it is no less clear at least.
It also clearly separates normal and safe vars from absoluted and dangerous vars into different sections.
It would also clealy visually separate their consequent use later;

Code: Pascal  [Select][+][-]
  1.   Z[100][20].U := 0; // safe and trivial
  2.   X^[100][20].U := 0; // non-trivial and unsafe
  3.  

The visual cue that both "absolute" and "origin" lack totally.

-----------

TL;DR:

One can come from two presumptions.
Actually three, but "My habits are good, because i don't have to make effort and re-learn" is not an interesting one, so - skipped.

Either the unsafe trick with absolute is good to use as often as possible and should be made as easier to use as possible.

Or this unsafe trick is dangerous and should be discouraged, and programmers should be goaded to only use it when safer approaches have worse penalties.

If the former is taken, then address should be bound to the variable name, as it was done in examples with "origin".

If the latter is taken - typed const pointer approach is better than absolute, as it achives the same without a one-use keyword and without a special case in syntax, and also provides visual cues to tell safe uses from unsafe ones.

the "absolute" keyword as it was conceived in TP combines worst aspects of those approaches.
« Last Edit: September 24, 2022, 09:36:33 pm by Arioch »

440bx

  • Hero Member
  • *****
  • Posts: 4030
Re: Evaluation of constant statements
« Reply #83 on: September 24, 2022, 09:30:27 pm »
Hopeless!
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: Evaluation of constant statements
« Reply #84 on: September 24, 2022, 09:44:40 pm »
Is it some divine rule that constant must only be calculated in compile time? Constant should not be changed by application code, and that is it.

Yes, it is. However you have quite correctly pointed out a complicating factor: Object Pascal uses "const" both to represent a true constant, and to indicate that a function parameter must not be changed by the function's code.

Focussing on the first of those, which is where we came in with the original "inconstant constants" issue, there are again two cases but (fortunately) they are compatible.

A constant address, such as

Code: Pascal  [Select][+][-]
  1. program veeblefetzer;
  2.  
  3. var
  4.   a: integer;
  5.  
  6. const
  7.   b= @a;
  8.  

is something which is allocated by the compiler, and which will only be modified by addition or subtraction by linker/loader hence may be tracked by a fixup.

The second, a numeric constant such as

Code: Pascal  [Select][+][-]
  1. const
  2.   c= 5;
  3.   d= 1 shl 5;
  4.  
  5. var
  6.   e: array[0..d-1] of byte;
  7.  

is something which may be reused by the compiler for arbitrary calculations other than addition or subtraction hence cannot be tracked by a fixup.

A constant parameter does not fit into either of those categories, since neither its value nor its address can be known until runtime. For example:

Code: Pascal  [Select][+][-]
  1. function factorial(const n: integer): integer;
  2.  
  3. const
  4.   K: PInteger = @N; // or  ... = Pointer(@N) for {$T+} if not a special case made
  5.  
  6. begin
  7.   if n <= 1 then
  8.     result := 1
  9.   else
  10.     result := factorial(n - 1)
  11. end;
  12.  

It is quite obviously absurd to consider K to be a constant in this case, since its value cannot be known at compilation time. In addition, the fixup list cannot accommodate it since in the general case the depth of the recursion is not computable at compilation time.

MarkMLl
« Last Edit: September 24, 2022, 09:52:27 pm by MarkMLl »
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Evaluation of constant statements
« Reply #85 on: September 24, 2022, 10:08:28 pm »
Code: Pascal  [Select][+][-]
  1. function factorial(const n: integer): integer;
  2.  
  3. const
  4.   K: PInteger = @N; // or  ... = Pointer(@N) for {$T+} if not a special case made
  5.  
  6. begin
  7.   if n <= 1 then
  8.     result := 1
  9.   else
  10.     result := factorial(n - 1)
  11. end;
  12.  

It is quite obviously absurd to consider K to be a constant in this case, since its value cannot be known at compilation time.

Then it is equally absurd to consider N a constant.

But if we accept the definition than constant is what compiler prevents you from accidentally changing in normal unassuming (for readers) application code, unless you make a special and conscious effort to break free and use unsafe, obviously (for readers) "hackerish" code, then there is no more contradiction and both are constants.

Even if you run on Harvard arch (and much more so on von Neumann arcxh) then there is, strictly spaking, no such thing as constant.
Even firmware today is written into PROM not ROM. So - no absolute constants there.

So, what remains is separation of "dull normal safe" code from "unsafe hackerish and requiring special attention" one.

And then both are constants (assuming {$J-}) as long as FPC would give me safety net (against accidental blunders) and meet both k := nil and n:=0 met with compilation error.

Now, if there are people who see merit in separating "compile-time constants" from "runtime-constant" or "true constants" from "once-bound variables" or by any other name - i am personally all for it. In my mental image, in Pascal, both const and val and var would have their, distinct an important, use cases.

But until that is done, the only practical interpretation of constant, as i see, is one that can be not changed accidentally, without obviously tricky unsafe code.
Then we can start naming "true consts" as constant values and "untrue typed consts" as "constant variables" (or by any other names).

And if you know C++...

Code: C  [Select][+][-]
  1. const int * A;
  2. int const * B;
  3. int * const C;
  4.  

which of those is constant, and which is not, and why? :-D
« Last Edit: September 24, 2022, 10:12:28 pm by Arioch »

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: Evaluation of constant statements
« Reply #86 on: September 24, 2022, 10:10:59 pm »
In addition to that, neither O/Ss, nor any CPUs I know of provide read/write protection at the single byte/word/dword/qword level, the best they can do these days is page level (often 4096) bytes.

Wandering off-topic even by our standards, 16 bytes in x86 protected mode with the original 64K segments. However there was an effective limit of 8K segments per process (plus a constant 8K for the OS), which was a killer.

Noting your "these days" qualification, in principle an x86_64 hypervisor (with granularity no smaller than a 4K page) could run a large number of apps as x86 VMs (with 16 byte granularity).

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Evaluation of constant statements
« Reply #87 on: September 24, 2022, 10:18:27 pm »
16 bytes in x86 protected mode with the original 64K segments. However there was an effective limit of 8K segments per process (plus a constant 8K for the OS), which was a killer.

You are tempting me...

Assuming we run on bare metal and can use all the supervisor mode features (DOS extender), then i can put LOADALL into every funciton prolouge and have 8K not per process but per function, which would be enough for any practical purposes :-D

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: Evaluation of constant statements
« Reply #88 on: September 24, 2022, 10:39:54 pm »
Assuming we run on bare metal and can use all the supervisor mode features (DOS extender), then i can put LOADALL into every funciton prolouge and have 8K not per process but per function, which would be enough for any practical purposes :-D

Much what DR tried to get unmodified real-mode code running on a '286, but it turned out there were massive speed penalties.

The 8+8K segment limitation is pretty deeply wired, and (in my opinion at least since I've done the bare metal stint) was more of a killer than the segment size (fingered as the problem by most armchair commentards): as soon as you start to allow one code segment per unit plus another to allow it to be read as data plus a privileged one for debugging plus padding for alignment the allowance vanishes pretty damn quickly.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: Evaluation of constant statements
« Reply #89 on: September 25, 2022, 12:21:25 am »
on 16-bits 286 - for sure. Any significantly large data structure would exceed 64KB and you have to change segments/selectors pretty often.

Anyway, with 386 you had "unreal mode", so you could give away all segments and all page descriptors but very few to debugger trap purposes.

additionally all the interrupts would cancel your LOADALL cache/shadow abuses, which should demand either functions being run in CLI mode (still not enough to fence off NMI) or have their LOADALL getup somehow published for interrupt handlers to restore them. Not sure iof that can be done, as IRET (or was it RTI? but i think RTI was on DEC not x86, would at very least reset CS in real mode, and in protected mode would suck in the whole TSS. So i suspect, for simplicity of wiring, IRET de facto recalculated all selector registers even in real mode. IOW you would have to do IRET into a trampoline and...

so, yeah, a practical OS with "unmodified apps" won't fly far, but debugger which can afford speed penalties? I wonder...

also, a specially instrumented protected-mode compiler can do, i think. Even in 286 confines. Let's see

1. We have to set global var-constants into a memory segment with hardware read-only mode.
The only thing that precludes this is the idea that adjacently declared global variables should have adjacent addresses.
AFAIR it is no so in C++ and they can live with it.
Even in Pascal it is not rigorously so due to alignment/padding.

So, if we liberate compiler to reorder global variables - we get rid of alignment problems (the compiler would just combine same-sized variables together into stripes, with no slack spaces inside - we reduce memory usage for free).
We won't be able to assume about addresses and do pointer math with magic constants. Not a big loss if you ask me. If i truly would need it i can always force it via use of packed array and absolute).
One more thing lost would be lost cache locality - but just the same, in those select places i would need it i can always enforce it by using ad hoc packed record var and absolute (or just var of packed record type alone, namespaces are good).

Another problem emerges with stack-allocated variables and const-vars: because of recursion we just can not avoid interleaving. Dead-end? Not quite.
We can just have ES the same memory as SS but with read-only segment descriptor.
The compiler then would only access const-vars via ES, while normal vars would be accessed by default SS addressing.

operations like string copy/search would require "push DS pop ES" but those are compiler intrinsics and they would restore ES as r/o SS on exit.

asm blocks, if allowed, would have requirements to restore ES on exit. Compiler can even go as far as adding SEG ES prefix to opcodes explicitly addressing const-vars. Though not sure about reliability. But BASM bloacks are not about safety by default, so okay.

objects/classes/records - they just do not have const-var members. Would they - we would have to give up on 286 and go 386 for the sole sake of having FS or GS, to serve as r/o clone of DS.

But it seems to me that a compiler, targeting 286 PM, and enforcing hardware protection of Pascal-level explicit access to constants is feasible, if re-arrange of global vars is allowed.
Implicit access via dangling pointers or runaway array indexes would still be possible though.

Another avenue would be having 3 stacks.
We are used to one single stacks carrying both flow control (return addresses) and data (sack frames, parameters and local vars).
But it does not have to be this way. There were CPUs with split stack for data and control flow. Those where you just can NOT overwrite exit address by exceeding local buffer var.

For this experiment, though, we would have to have three stacks: control, vars and consts.
Okay, down with perfecitonism, two stacks. Code+vars and const-vars.
Can we have it in 286? in PDP-11?

In PDP-11 the only special case for R6 to be SP was exactly about interrupt handling. For all other purposes you were explicitly selecting your stack pointer register. You could even took R7/PC for SP and let the Lord have mercy on your program. Oh, got carried away. There was no R/O virtual memory as we know it. There was XM - something akin to EMS - but that would be hardly usable.

286 ISA though is not so orthogonal...

Still, let us dedicate ES:[DI] for a separate stack of const-parameters and local const-vars.
What is left uncovered? Well, we still need to write there in funciton prologues and when calling functions with const-params.

AFAIR 286 selectors had 3 LSbs  occupied by LDT/GDT choser and IOPL requests.
We can asusme GDT to be R/O while LDT to be R/W but for generality of purpose let's not.
Let's make all segments allocated in pairs, even R/W one and the next odd one being R/O

The function prologue then, for the functions having const-params and/or const-vars, could include

Code: ASM  [Select][+][-]
  1. PUSH DI
  2. PUSH AX
  3. ADD DI, n ; size of local const-vars
  4. MOV ES:[DI-x], nnn ; initializing const-vars
  5. MOV ES:[DI-x], nnn ;
  6. MOV AX, ES ; writeable const-stack selector
  7. ADD AX, 8
  8. MOV ES, AX ; r/o const-stack selector
  9. POP AX
  10.  

the epilogue then would have the complimentary added
Code: ASM  [Select][+][-]
  1. POP DI
  2.  

ES is fine to be in read-only mode on exit

Const-stack (carrying const-params and local const-vars) is not accessible via CS, DS or SS.

How a function call with const-parameters then look like?
Well, the following would be added:

Code: ASM  [Select][+][-]
  1. ; conventional stack preparation
  2. MOV AX, ES; R/O const-stack
  3. SUB AX, 8  ; R/W const-stack
  4. MOV ES, AX
  5. MOV AX, param-value
  6. STOSW ; CLD assumed, but isn't it always so?
  7. MOV AX, another-param-value
  8. STOSW
  9. CALL function
  10.  

and after the call

Code: ASM  [Select][+][-]
  1. SUB EDI, 4
  2.  

Hmmm.... seems workable :-)

Frankly, it means 4 ES selector reloads per call of a function with const-params or const-vars. On 80386 with some internal cache for selectors it hopefully would be not much of a problem, as only two selectors would be hammerred time and again, making them hot for cache. On 80286...

I don't know where to look for exact amount of cycles for selector change, but AFAIR the 80286 did not have any cache other than instructions pre-fetch buffer (8 or 16 bytes). No RAM/DATA cache on-chip, and neither AFAIR were on mainboards.


Quote from: Intel
When a selector value is loaded into. the visible part of a segment register, the 80286 automatically loads 6
bytes of the associated descriptor into the hidden part of the register. These 6 bytes, therefore, contain
the size, base, and access type of the selected segment.

3 RAM reads. Would be a hit.

Quote from: Intel
Unlike the probabilistic caches of other architectures, however, the 80286 cache is
completely deterministic: the caching of descriptors is explicitly controlled by the program.

IOW there is no cache in the processor, but programs can try to never change slector registers.

Quote from: Intel
The internal cache registers
(virtual address translation hardware) are therefore dynamically shared among the 16K different
segments potentially addressable within the user's virtual address space. No software overhead (either
system or application) is required to perform this operation.

One damn careful wording, mad skills...

So, a price to pay for hardware-enforced safety of const-params and const-vars would be added 3x4 = 12 ISA-bus memory I/O cycles per procedure call. Eeek!



Ideally we would need three stacks not two - but in the 286 tight set of registers it would be really problematic. AMD64 then, while increasing number and orthogonalizing use for general registers, lacks FS/GS.

386 would lend us FS or GS for data-stack, since we would have to use some register for pointer. BP? is SS-stack would only have control flow information (and saved registers), then no one would, in normal code, use SS:[BP+const]. So we could re-assign BP to be FS:[BP+const] base for local variables abd variable parameters. The binary code would be really bloated then with SEGFS prefixes, yet for the sake of security...
« Last Edit: September 25, 2022, 12:56:26 am by Arioch »

 

TinyPortal © 2005-2018