Recent

Author Topic: FPC Unleashed (inline vars, statement expr, tuples, match, indexed/lazy labels)  (Read 33613 times)

440bx

  • Hero Member
  • *****
  • Posts: 6463
Another simple feature request:

Enhance the "with" statement to accept using an enumerated type when $SCOPEDENUMS is ON.  Currently the "with" statement does not accept the enumerated type name and, it should when SCOPEDENUMS is ON since in that case the enumerated type identifier also identifies a scope. 

Example:

consider a set made of enumerated identifiers (very common in Pascal)

Code: Pascal  [Select][+][-]
  1. var
  2.   SomeSet : set of scoped_enum_identifier;
  3.  
  4. begin
  5.   ..
  6.   { this  (note: begin/end present for aesthetic purposes only) }
  7.  
  8.   with <scoped_enum_identifier> do
  9.   begin
  10.     SomeSet := [enum_element_a,
  11.                 enum_element_b,
  12.                 enum_element_n];
  13.   end;
  14.  
  15.   { instead of (as currently required) }
  16.  
  17.     SomeSet := [scoped_enum_identifier.enum_element_a,
  18.                 scoped_enum_identifier.enum_element_b,
  19.                 scoped_enum_identifier.enum_element_n];
  20. end;
  21.  

I suggested this feature in the past.  The original suggestion is found at:
https://forum.lazarus.freepascal.org/index.php/topic,70453.msg549591.html#msg549591
and seems to have received a "sympathetic ear" from FPC devs.

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 930
  • Behold, I bring salvation - FPC Unleashed
WITH list improvements

Following up on @440bx's suggestion - the compiler now detects duplicate symbols in WITH lists and rejects them outright:

Code: Pascal  [Select][+][-]
  1. type
  2.   TRec = record
  3.     a, b, c: string;
  4.   end;
  5.  
  6. procedure main;
  7. var
  8.   r1, r2: TRec;
  9. begin
  10.   // Error: Duplicate symbol "r1" in WITH list
  11.   with r1, r2, r1 do
  12.     a := 'foo';
  13. end;
  14.  
  15. begin
  16.   main;
  17. end.

As I mentioned earlier, this only covers plain symbol references. Arbitrary expressions like with p^, GetRec(), a[i] do are left alone - "duplicate" isn't really decidable there. For the simple case though, duplicates are almost always a mistake, so it's an error rather than a warning.



Field shadowing warning

The second piece of this update - bigger in terms of how much code it took to implement - when two records in a WITH list share a field name, the later one silently shadows the earlier one. Classic footgun. Now the compiler warns:

Code: Pascal  [Select][+][-]
  1. type
  2.   TPerson = record
  3.     name: string;
  4.     age: integer;
  5.   end;
  6.   TCompany = record
  7.     name: string;
  8.     founded: integer;
  9.   end;
  10.  
  11. var
  12.   p: TPerson;
  13.   c: TCompany;
  14.  
  15. begin
  16.   p.name := 'Alice';
  17.   p.age := 30;
  18.   c.name := 'Acme';
  19.   c.founded := 1999;
  20.  
  21.   // Warning: Field "name" in WITH entry shadows an earlier field with the same name
  22.   with p, c do
  23.     writeln('name=', name, ' age=', age, ' founded=', founded);
  24.  
  25.   readln;
  26. end.

The name in the writeln resolves to c.name (Acme), not p.name - exactly the kind of thing you want flagged. Both diagnostics are on by default in unleashed mode.



Enum unboxing in WITH

Another one from @440bx's wishlist - in unleashed mode the WITH statement now accepts an enum type name, and exposes its members inside the block.

Code: Pascal  [Select][+][-]
  1. {$mode unleashed}
  2.  
  3. type
  4.   TLogLevel = (LogDebug, LogInfo, LogWarn, LogError);
  5.  
  6. var
  7.   enabled: set of TLogLevel;
  8.  
  9. begin
  10.   with TLogLevel do
  11.     enabled := [LogInfo, LogWarn, LogError];
  12.  
  13.   writeln('debug is ', if LogDebug in enabled then 'on' else 'off');
  14.   writeln('info is ',  if LogInfo  in enabled then 'on' else 'off');
  15.   writeln('warn is ',  if LogWarn  in enabled then 'on' else 'off');
  16.   writeln('error is ', if LogError in enabled then 'on' else 'off');
  17. end.

Code: Pascal  [Select][+][-]
  1. {$mode unleashed}
  2. {$scopedenums on}
  3.  
  4. type
  5.   TLogLevel = (LogDebug, LogInfo, LogWarn, LogError);
  6.  
  7. var
  8.   enabled: set of TLogLevel;
  9.  
  10. begin
  11.   with TLogLevel do
  12.     enabled := [LogInfo, LogWarn, LogError];
  13.  
  14.   writeln('debug is ', if TLogLevel.LogDebug in enabled then 'on' else 'off');
  15.   writeln('info  is ', if TLogLevel.LogInfo  in enabled then 'on' else 'off');
  16.   writeln('warn  is ', if TLogLevel.LogWarn  in enabled then 'on' else 'off');
  17.   writeln('error is ', if TLogLevel.LogError in enabled then 'on' else 'off');
  18. end.

Output:

Code: Text  [Select][+][-]
  1. debug is off
  2. info  is on
  3. warn  is on
  4. error is on

Implementation-wise this is tiny - the compiler already had the for x in TEnumType do precedent for accepting a type node where an expression was expected, so WITH just needed the same trick plus a direct push of the enum's own symtable onto the scope stack. No Self/refnode machinery gets involved for this path, because enum members are constants and don't need one.

The inline if cond then 'on' else 'off' in each writeln is a separate unleashed-mode goodie - statement expressions - that happens to fit nicely here.
« Last Edit: April 11, 2026, 06:01:19 am by Fibonacci »
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

440bx

  • Hero Member
  • *****
  • Posts: 6463
Nice improvements :)  Well done.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 930
  • Behold, I bring salvation - FPC Unleashed
FYI - landed "Enum unboxing in WITH", edited the post above with the details. Another one of @440bx's WITH requests off the list.

I called it "enum unboxing" for lack of a better name - if anyone has a nicer term for this thing, shoot.
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

440bx

  • Hero Member
  • *****
  • Posts: 6463
As I said in the previous post, nice improvements, well done! :)  Thank you.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 19125
  • Glad to be alive.
A subversive counter-measure:
Claude and I did write a de-inline tool.
It is not perfect (described in the introduction) but works pretty well.

At the moment it produces some todo's, but it already restores Pascal to its intended form.
I have to do some rtti magic to improve inferred type recognition, though.
[edit] small demo:
From this:
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode objfpc}{$endif}
  2. uses sysutils,classes;
  3.  
  4. procedure UseSomeInlineVars;
  5. begin
  6.   var list:Tstringlist := TStringlist.Create;
  7.   for var i := 0 to 10 do
  8.    begin
  9.      var s:string := 'string'+i.toString;
  10.       for var j := 0 to 9 do List.Add(Random(100).ToString);
  11.    end;  
  12.    writeln(list.text);
  13.    List.free;
  14. end;
  15.  
  16. begin
  17.   UseSomeInlineVars;  
  18.   Readln;
  19. end.
You will get this:
Code: Pascal  [Select][+][-]
  1. program simpleinline;
  2. {$ifdef fpc}{$mode objfpc}{$endif}
  3. uses sysutils,classes;
  4.  
  5. procedure UseSomeInlineVars;
  6. var
  7.   list: Tstringlist;
  8.   i: ; { TODO: add type }
  9.   s: string;
  10.   j: ; { TODO: add type }
  11. begin
  12.   list := TStringlist.Create;
  13.   for i := 0 to 10 do
  14.    begin
  15.      s := 'string'+i.toString;
  16.       for j := 0 to 9 do List.Add(Random(100).ToString);
  17.    end;
  18.    writeln(list.text);
  19.    List.free;
  20. end;
  21.  
  22. begin
  23.   UseSomeInlineVars;
  24.   Readln;
  25. end.

Regards,

Thaddy (and Claude)
« Last Edit: April 11, 2026, 01:41:11 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

440bx

  • Hero Member
  • *****
  • Posts: 6463
At the moment it produces some todo's, but it already restores Pascal to its intended form.
Inline variables do _not_ violate Pascal's intended form.  Proof of that is visible everywhere in the language, e.g, nested functions/procedure allow the programmer to create another group of variables and, there is no limit established by the language in the number of them.

The real problem is not the inline variables, the real problem is misusing the begin/end (compound statement) construction as a scoping structure which it was _obviously_ never meant to be.   The correct implementation would have been to create _explicit_ scope start/end keywords where the standard Pascal pattern used in functions and procedures would be replicated inline.  Unfortunately, what passes for language "designers" these days are little more than programming raccoons scavenging C's syntactic trash can.  That's the reason inline variables are so poorly implemented but, they most definitely belong in Pascal because they increase locality and modularity which are hallmarks of the language.

« Last Edit: April 11, 2026, 01:39:22 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 19125
  • Glad to be alive.
Well, I disagree: just for where you should declare variables alone.
But I wrote it was subversive. (At least for this particular thread  ;D )

To me it is the rudiments of an essential tool I need.
It is also already useful for translating Delphi code, or code from the forked compiler of this thread, with inline var pollution to the main Freepascal syntax (3.2.2 and trunk). It remains Delphi compatible in that case.

Note I am certainly interested in this branch and actively running it.
But I disagree with some "features" like this one, so here's an undo feature.
I will make it into an announcement later.
« Last Edit: April 11, 2026, 02:22:20 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Fibonacci

  • Hero Member
  • *****
  • Posts: 930
  • Behold, I bring salvation - FPC Unleashed
I will make it into an announcement later.

Looking forward to it! Can't wait for the Unleashed ecosystem to grow - announcements for de-inliners, de-statement-expressionizers, de-tuplelizers, de-multistring-ifiers... it's going to be beautiful :D
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

440bx

  • Hero Member
  • *****
  • Posts: 6463
here is another improvement I'd like to see: an alternative to declaring static/persistent variables that does not use the absurd "writable constants" thing.

Using "static", scavenged from C or, "persistent" which IMO is clearer would be an improvement.  Also, the use of either "static" or "persistent" should then allow using "absolute" to retype them as desired (which the current implementation does not allow because it sees them as constants instead of the variables they really are.)

Implementation should not be difficult since it consists of scanning and parsing a new keyword that is associated with functionality that is already part of the compiler.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

paweld

  • Hero Member
  • *****
  • Posts: 1607
here is another improvement I'd like to see: an alternative to declaring static/persistent variables that does not use the absurd "writable constants" thing.
Just use the {$J-} switch
Best regards / Pozdrawiam
paweld

440bx

  • Hero Member
  • *****
  • Posts: 6463
@paweld

here is another improvement I'd like to see: an alternative to declaring static/persistent variables that does not use the absurd "writable constants" thing.
Just use the {$J-} switch
It doesn't do the trick because the variable is still declared in a "const" section.  For obvious reasons, variables should NOT be declared in "const" sections.



@Thaddy,

you can offer a CreateWritableConstants utility to save writable constants from extinction. ;)

What would be really nice, not to mention somewhat impressive, would be for Claude to read the entire FPC compiler and _separate_ the scanner and parser from the backend.  IOW, remove the entire code generation back end leaving only the syntax checker and the resulting symbol tables that can be customized for various purposes, such as doing static analysis, formatting and custom refactoring among other things that come to mind.

Just having a version of FPC that just did syntax checking would be useful since most of the compilation time is spent in linking (producing the binary), that feature could be rather useful when working with a large code base.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 19125
  • Glad to be alive.
Nah, for that I have a macro that makes it static.... The macro is called static.
Code: Pascal  [Select][+][-]
  1. // full program
  2. {$j+}{$macro on}{$ifopt J+}{$define static := const {$endif}
  3. static v:integer = 100;
  4. begin
  5.   writeln(v);
  6. end.
Throws something at you when the mode means a real const...
(I have a fancy version for that macro too....)

What I will do, though, is a {$coperators+} removal tool along the same lines.

BTW: Claude clearly separates parser and scanner: hence so many things in the compiler code - which is not very easy - of which it has surprisingly good knowledge. (and then you run out of free tokens and have to wait a couple of hours, just keep it open in the browser and press continue the next morning.)
Not a commercial, but Claude and Deepseek have a very deep understanding of how the compiler and its architecture looks like. It would be a waste of time not to use it.
« Last Edit: April 11, 2026, 10:04:48 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Thaddy

  • Hero Member
  • *****
  • Posts: 19125
  • Glad to be alive.
Looking forward to it! Can't wait for the Unleashed ecosystem to grow - announcements for de-inliners, de-statement-expressionizers, de-tuplelizers, de-multistring-ifiers... it's going to be beautiful :D
Well, I do not disagree with all of it, and I am using the code.... :D :)
But yes, nowadays we seem to we can write disinfections for whatever someone tries you infect you with.  O:-)
« Last Edit: April 11, 2026, 10:01:24 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Fibonacci

  • Hero Member
  • *****
  • Posts: 930
  • Behold, I bring salvation - FPC Unleashed
here is another improvement I'd like to see: an alternative to declaring static/persistent variables that does not use the absurd "writable constants" thing.

I do use this feature, it's quite handy - the variable preserves its value between calls to the same function, which is useful for counters, caching, one-time init flags etc.

So if I understand correctly, what you want is: typed constants should be actual constants (not writable), and a new keyword like static would take over the role of what "writable typed constants" do today? Something like:

Code: Pascal  [Select][+][-]
  1. // before:
  2. procedure Foo;
  3. const
  4.   counter: Integer = 0;
  5. begin
  6.   Inc(counter);
  7. end;
  8.  
  9. // proposed behavior with static:
  10. procedure Foo;
  11. static
  12.   counter: Integer = 0;
  13. begin
  14.   Inc(counter);
  15. end;
  16.  
  17. // proposed behavior with const:
  18. procedure Foo;
  19. const
  20.   counter: Integer = 0;
  21. begin
  22.   Inc(counter); // throw an error
  23. end;
  24.  



Also, the use of either "static" or "persistent" should then allow using "absolute" to retype them as desired (which the current implementation does not allow because it sees them as constants instead of the variables they really are.)

Actually, this already works. Typed constants with absolute compile and behave as expected:

Code: Pascal  [Select][+][-]
  1. {$mode unleashed}
  2.  
  3. procedure test;
  4. const
  5.   x: integer = 1;
  6. var
  7.   a: integer absolute x;
  8. begin
  9.   writeln('a (absolute of x) = ', a);
  10.   inc(x);
  11. end;
  12.  
  13. // output:
  14. // a (absolute of x) = 1
  15. // a (absolute of x) = 2
  16. // a (absolute of x) = 3
  17. // a (absolute of x) = 4
  18. // a (absolute of x) = 5
  19.  
  20. begin
  21.   for var i := 1 to 5 do test;
  22.   readln;
  23. end.

So the absolute part of the request is already covered :) (unless you meant something else by it?) As for the static keyword itself - this would break compat with FPC and Delphi, since neither has it. But tuples are coming soon, and those already break compat. The fork has its own direction now.



On a related note - I'm thinking about creating a syntax reference document that could be fed to AI coding assistants, so they can generate valid Unleashed code. Could be useful for anyone doing AI-assisted Pascal development with the fork.



Well, I do not disagree with all of it, and I am using the code.... :D :)

Glad you're using it! You know what's easier than writing a de-inliner? Clicking the star button on GitHub ;)
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

 

TinyPortal © 2005-2018