Recent

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

440bx

  • Hero Member
  • *****
  • Posts: 6528
That looks really good. Thank you Fibonacci :)

I have a suggestion now ;)

You're using defer like it's used in Go.  There is a possible improvement to it, the improvement is to associate it with the variable it applies to.  for instance, instead of having
Code: Pascal  [Select][+][-]
  1.   var token: THANDLE;
  2.  
have
Code: Pascal  [Select][+][-]
  1.   var token: THANDLE; defer CloseHandle();
  2.  
The advantage is twofold, 1. you cannot forget to specify defer later in the code.  2. if, for some/any reason you decided to remove "token" variable from the code, you don't have to remember to also remove the associated defer statement.  Also, this applies if you decided to rename "token" to something else, you don't have to go through the code and modify  the defer statement.

Since I'm on a roll, how do you feel about my requesting another feature (unrelated to the ones already submitted) ?

« Last Edit: May 04, 2026, 07:13:30 am by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

creaothceann

  • Sr. Member
  • ****
  • Posts: 375
I noticed that "control characters" in source code break syntax highlighting in the IDE when mixed with inline vars, so I had to patch that. Looks like a complete archaism though. Does anyone actually use them? Any real-world code that relies on this? I'd happily rip it out entirely if no one needs it.

Control characters include Tab, so yeah they're used (even though Lazarus seems to make every effort to discourage their use). I usually don't have them displayed in the code editor though.

Fibonacci

  • Hero Member
  • *****
  • Posts: 997
  • Behold, I bring salvation - FPC Unleashed
Also, this applies if you decided to rename "token" to something else, you don't have to go through the code and modify  the defer statement.

If you do a proper refactor (F2 in my IDE), the rename propagates through the entire scope - you don't need to hunt down the defer line manually.



You're using defer like it's used in Go.  There is a possible improvement to it, the improvement is to associate it with the variable it applies to.  for instance, instead of having
Code: Pascal  [Select][+][-]
  1.   var token: THANDLE;
  2.  
have
Code: Pascal  [Select][+][-]
  1.   var token: THANDLE; defer CloseHandle();
  2.  

defer glued to the declaration? But what if the variable never gets initialized further down, and the defer still fires CloseHandle on garbage? I don't quite see how that would work ;)



Since I'm on a roll, how do you feel about my requesting another feature (unrelated to the ones already submitted) ?

The "not in" thing? Not sure... it's pretty much 101% sugar - you can already write if not (x in ...) today; do you really prefer if x not in ... that much?

The bigger picture: FPC currently has 43 modeswitches (not counting m_none and placeholders). Unleashed already adds 11 more, 2 more are about to land (string interpolation + FAM), maybe 3 (for-step - though I think step will go in without a modeswitch). That's why the for-loop counter preservation isn't behind its own modeswitch - it's just "unleashed mode does this". There's getting to be a lot of these. We could give it a switch, but every one is a separate decision: is it worth its own knob, or just "unleashed has this"? ;)



Control characters include Tab, so yeah they're used (even though Lazarus seems to make every effort to discourage their use). I usually don't have them displayed in the code editor though.

I think we're talking about different things. I meant this:
https://www.freepascal.org/docs-html/ref/refsu6.html

"Also, the caret character (^) can be used in combination with a letter to specify a character with ASCII value less than 27. Thus ^G equals #7 - G is the seventh letter in the alphabet. The compiler is rather sloppy about the characters it allows after the caret, but in general one should assume only letters."

So var x: ^TYPE was being parsed as a control character ^T followed by the leftover YPE.

To be precise, this was exactly the case on line 60 of the demo I posted on the previous page: var privs: ^TOKEN_PRIVILEGES; - now fixed in both FPC and the IDE.

Code: Text  [Select][+][-]
  1. ^A = #1   (SOH)
  2. ^I = #9   (TAB)
  3. ^J = #10  (LF)
  4. ^M = #13  (CR)
  5. ^G = #7   (BEL)

This does the same:

Code: Pascal  [Select][+][-]
  1. writeln(^I'hello');
  2. writeln(#9'hello');

So... which one will you use - #9 or ^I? ;)
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

creaothceann

  • Sr. Member
  • ****
  • Posts: 375
I see. Wikipedia calls it caret notation... Personally I use #, never used ^ for that.

Thaddy

  • Hero Member
  • *****
  • Posts: 19249
  • Glad to be alive.
I see. Wikipedia calls it caret notation... Personally I use #, never used ^ for that.
It is really old, but is supported in FPC - in ALL modes! - , so you can't really use caret notation without screwing up the support for it.
objects are fine constructs. You can even initialize them with constructors.

Fibonacci

  • Hero Member
  • *****
  • Posts: 997
  • Behold, I bring salvation - FPC Unleashed
I see. Wikipedia calls it caret notation... Personally I use #, never used ^ for that.
It is really old, but is supported in FPC - in ALL modes! - , so you can't really use caret notation without screwing up the support for it.

Anything written in the past decade that actually uses it? What would stop working if I ripped it out completely?

With your experience around the older corners of FPC (no offence, lol) - any chance you could tell me whether tearing it out by the roots would break anything?

Anyway - I managed to tame it. Caret notation works, all Unleashed features work, and I re-attached the fork to official FPC again, so you can now see "how out of date" Unleashed is: "This branch is 120 commits ahead of and 9 commits behind fpc/FPCSource:main" :)
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

Fibonacci

  • Hero Member
  • *****
  • Posts: 997
  • Behold, I bring salvation - FPC Unleashed
Since I'm on a roll, how do you feel about my requesting another feature (unrelated to the ones already submitted) ?

The "not in" thing? Not sure... it's pretty much 101% sugar - you can already write if not (x in ...) today; do you really prefer if x not in ... that much?

The bigger picture: FPC currently has 43 modeswitches (not counting m_none and placeholders). Unleashed already adds 11 more, 2 more are about to land (string interpolation + FAM), maybe 3 (for-step - though I think step will go in without a modeswitch). That's why the for-loop counter preservation isn't behind its own modeswitch - it's just "unleashed mode does this". There's getting to be a lot of these. We could give it a switch, but every one is a separate decision: is it worth its own knob, or just "unleashed has this"? ;)

Totally forgot Delphi already has it - in that case, definitely adding is not and not in to Unleashed too. No dedicated modeswitch.
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: 6528
You're using defer like it's used in Go.  There is a possible improvement to it, the improvement is to associate it with the variable it applies to.  for instance, instead of having
Code: Pascal  [Select][+][-]
  1.   var token: THANDLE;
  2.  
have
Code: Pascal  [Select][+][-]
  1.   var token: THANDLE = INVALID_HANDLE_VALUE; defer CloseHandle();
  2.  

defer glued to the declaration? But what if the variable never gets initialized further down, and the defer still fires CloseHandle on garbage? I don't quite see how that would work ;)
You're absolutely right.  I forgot to show that with that syntax the variable must be assigned an initial value at declaration time.  Mea culpa.  The initial value is what later allows the code to determine if there is a resource to act upon (use CloseHandle on the value in the given example.)



Since I'm on a roll, how do you feel about my requesting another feature (unrelated to the ones already submitted) ?

The "not in" thing?
No, it's not the "not in" thing.  It's the "variant location" thing.  Specifically:  in Pascal, a record variant is forced to be last in the record's definition, the net effect of that is that there can only be one variant.  That's quite an annoyance when porting C unions to FPC.  The simple solution is to require the "case" statement in the record to have its own "end" instead of sharing the record's "end".   if you chose to do it that way then the current parsing behavior is affected since doing it that way will require it to force the presence of an additional "end" keyword to end the case.  I believe this solution to be the most "economical" one in terms of code and effort.

Another possibility would be to add a "union" facility to the language.  The pros of that option is that the union construct could behave exactly as it does in C (which variants don't.)  That would make the porting even more straightforward.



if you decided to implement this feature using the first option (ending the "case" with its own "end"), I recommend you also add the ability to define an anonymous enumeration in the case itself, for instance:
Code: Pascal  [Select][+][-]
  1. case (firstcase, secondcase, etccase) of
  2.   firstcase: ( field definitions inside here);
  3.   secondcase : { definitions }
  4.   thirdcase : { definitions }
  5. end;
  6.  
that would allow naming each case, which is nice and potentially very useful when values need to be assigned to the fields that are part of a specific case.  Going that route also enables the compiler to scope the anonymous enumeration, by that I mean, every branch is uniquely identified by RecordName.anonymousenumerationelement.<field name applicable to selected branch>.  Since the anonymous enumeration only exists in the record's scope, there is no room for its element names to collide with other identifiers outside the record definition.

That's the core idea.  if you have any questions, by all means, feel free to ask. :)
« Last Edit: May 04, 2026, 02:06:35 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 997
  • Behold, I bring salvation - FPC Unleashed
No, it's not the "not in" thing.  It's the "variant location" thing.  Specifically:  in Pascal, a record variant is forced to be last in the record's definition, the net effect of that is that there can only be one variant.  That's quite an annoyance when porting C unions to FPC.  The simple solution is to require the "case" statement in the record to have its own "end" instead of sharing the record's "end".   if you chose to do it that way then the current parsing behavior is affected since doing it that way will require it to force the presence of an additional "end" keyword to end the case.  I believe this solution to be the most "economical" one in terms of code and effort.

Another possibility would be to add a "union" facility to the language.  The pros of that option is that the union construct could behave exactly as it does in C (which variants don't.)  That would make the porting even more straightforward.



if you decided to implement this feature using the first option (ending the "case" with its own "end"), I recommend you also add the ability to define an anonymous enumeration in the case itself, for instance:
Code: Pascal  [Select][+][-]
  1. case (firstcase, secondcase, etccase) of
  2.   firstcase: ( field definitions inside here);
  3.   secondcase : { definitions }
  4.   thirdcase : { definitions }
  5. end;
  6.  
that would allow naming each case, which is nice and potentially very useful when values need to be assigned to the fields that are part of a specific case.  Going that route also enables the compiler to scope the anonymous enumeration, by that I mean, every branch is uniquely identified by RecordName.anonymousenumerationelement.<field name applicable to selected branch>.  Since the anonymous enumeration only exists in the record's scope, there is no room for its element names to collide with other identifiers outside the record definition.

That's the core idea.  if you have any questions, by all means, feel free to ask. :)

Could this MR (open in upstream FPC) be what you're looking for?

[Feature] Record Composition - MR 498 by Warfley

It introduces {$ModeSwitch RecordComposition} with a uses keyword inside records that lets you embed another record (named or anonymous inline). The MR description explicitly lists "allowing for non-trailing and multiple variant parts" and C11 anonymous-struct/union compatibility ({$PackRecords C}) as goals.

Sketch:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = record
  3.     a: integer;
  4.     uses record           // first variant - anonymous nested
  5.       case k1: byte of
  6.         0: (b: integer);
  7.         1: (c: pchar);
  8.     end;
  9.     d: longword;
  10.     uses record           // second variant - also fine
  11.       case k2: byte of
  12.         0: (e: int64);
  13.         1: (f: extended);
  14.     end;
  15.   end;

This MR is already on my list to be merged into Unleashed - though there's still some work needed on the IDE autocompletion side before it can ship cleanly. Does it cover your use case?
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: 6528
Does it cover your use case?
At first sight, I believe it does.  The only concern I have with what's shown in that example is that there is no example of an untagged case, because of that, I'm not sure how that's handled.

The other thing that is a bit of a concern is that, there should be no need for a "uses record" just to define another case (variant), the "uses record" in that case (no pun intended) looks like unnecessary noise to me. 

Personally, I'd do record composition in a more economical way, something along the lines of "uses <record type>" to bring the entire external definition into the current definition and consider the multiple variant thing a completely separate issue to have its own solution.  I think the resulting syntax would be cleaner and more intuitive that way.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 997
  • Behold, I bring salvation - FPC Unleashed
New features: `is not` / `not in`, property compound assignment, for-step loops

Three additions landed in main. Each is gated on its own modeswitch (or is unleashed-mode only); details below per feature.



`is not` and `not in` operators

Available in {$mode unleashed}, no dedicated modeswitch.

Standard Pascal forces an extra pair of parentheses for negated runtime type checks and set membership tests:

Code: Pascal  [Select][+][-]
  1. if not (Obj is TFoo) then ...
  2. if not (x in [Apple, Orange]) then ...

Unleashed lets you write it the way you say it out loud:

Code: Pascal  [Select][+][-]
  1. if Obj is not TFoo then ...
  2. if x not in [Apple, Orange] then ...

Both forms are Delphi-compatible - Delphi has supported them for years.

Both forms compile to the same node tree as the parenthesised version, so semantics, error messages, and runtime cost are unchanged.

Outside Unleashed the parser rejects both: `obj is not T` reads as `obj is (not T)` and triggers an operator error, `x not in S` is a syntax error at `not`.

Demo

Code: Pascal  [Select][+][-]
  1. program is_not_not_in_demo;
  2.  
  3. {$mode unleashed}
  4.  
  5. type
  6.   tanimal = class
  7.     name: string;
  8.     constructor create(const aname: string);
  9.   end;
  10.   tdog = class(tanimal) end;
  11.   tcat = class(tanimal) end;
  12.  
  13.   tcolor = (red, green, blue, yellow, purple);
  14.   tpalette = set of tcolor;
  15.  
  16. constructor tanimal.create(const aname: string);
  17. begin
  18.   name := aname;
  19. end;
  20.  
  21. procedure greet(a: tanimal);
  22. begin
  23.   if a is not tdog then
  24.     writeln(a.name, ' is not a dog')
  25.   else
  26.     writeln(a.name, ' is a dog - woof!');
  27. end;
  28.  
  29. var
  30.   pets: array of tanimal;
  31.   warm: tpalette;
  32.   c   : tcolor;
  33.   i   : longint;
  34. begin
  35.   pets := [tdog.create('Rex'), tcat.create('Whiskers'), tdog.create('Buddy')];
  36.   for i := 0 to high(pets) do
  37.     greet(pets[i]);
  38.   for i := 0 to high(pets) do
  39.     pets[i].free;
  40.  
  41.   writeln;
  42.  
  43.   warm := [red, yellow];
  44.   for c := low(tcolor) to high(tcolor) do
  45.     if c not in warm then
  46.       writeln(c, ' is cold')
  47.     else
  48.       writeln(c, ' is warm');
  49.  
  50.   readln;
  51. end.



Compound assignment on properties

Available in {$mode unleashed}, no dedicated modeswitch.

Compound operators +=, -=, *=, /=, div=, mod=, and=, or=, xor=, shl=, shr= work directly on a class or record property that has both read and write accessors.

Stock FPC rejects `prop += x` with `Error: Variable identifier expected`, on the grounds that the read accessor and the write accessor can target different storage. So you have to spell it out by hand:

Code: Pascal  [Select][+][-]
  1. f.Name := f.Name + 'bar';
  2. f.Count := f.Count * 2;

With Unleashed:

Code: Pascal  [Select][+][-]
  1. f.Name  += 'bar';    // -> f.Name  := f.Name + 'bar'
  2. f.Count *= 2;        // -> f.Count := f.Count * 2

The expansion is the same node tree the user would build manually: one getter call on the read side, the binary operator, one setter call on the write side. Side effects in the accessors fire exactly as in the manual rewrite, no more and no fewer.

The C-style operators (+=, -=, *=, /=) still need {$coperators on} or -Sc as in any FPC mode. The word-based operators (and=, or=, xor=, mod=, div=, shl=, shr=) work without it.

Limitations

- Property must have both read and write accessors.
- Indexed properties (property X index N: ...) and parametrized properties (property Items[idx: integer]: ...) are not supported. Use the explicit rewrite for those.

Demo

Code: Pascal  [Select][+][-]
  1. program prop_compound_demo;
  2.  
  3. {$mode unleashed}
  4. {$coperators on}
  5.  
  6. type
  7.   tbox = class
  8.   private
  9.     fn: integer;
  10.     fs: string;
  11.     function  getn: integer;
  12.     procedure setn(v: integer);
  13.     function  gets: string;
  14.     procedure sets(const v: string);
  15.   public
  16.     property n: integer read getn write setn;
  17.     property s: string  read gets write sets;
  18.   end;
  19.  
  20. function  tbox.getn: integer;          begin result := fn; end;
  21. procedure tbox.setn(v: integer);       begin fn := v; end;
  22. function  tbox.gets: string;           begin result := fs; end;
  23. procedure tbox.sets(const v: string);  begin fs := v; end;
  24.  
  25. var
  26.   b : tbox;
  27. begin
  28.   b := tbox.create;
  29.   b.n := 10;
  30.   b.s := 'foo';
  31.  
  32.   writeln('-- arithmetic --');
  33.   b.n += 5;       writeln('n += 5    -> ', b.n);
  34.   b.n -= 3;       writeln('n -= 3    -> ', b.n);
  35.   b.n *= 2;       writeln('n *= 2    -> ', b.n);
  36.   b.n div= 4;     writeln('n div= 4  -> ', b.n);
  37.   b.n mod= 4;     writeln('n mod= 4  -> ', b.n);
  38.  
  39.   writeln('-- bitwise --');
  40.   b.n := $FF;
  41.   b.n and= $0F;   writeln('n and= $0F -> $', hexstr(b.n, 2));
  42.   b.n or=  $30;   writeln('n or=  $30 -> $', hexstr(b.n, 2));
  43.   b.n xor= $05;   writeln('n xor= $05 -> $', hexstr(b.n, 2));
  44.   b.n shl= 4;     writeln('n shl= 4   -> $', hexstr(b.n, 4));
  45.  
  46.   writeln('-- string --');
  47.   b.s += 'bar';   writeln('s += ''bar'' -> ', b.s);
  48.  
  49.   b.free;
  50.   readln;
  51. end.



For-step loops

Modeswitch forstep, on by default in {$mode unleashed}.

`step N` clause in for-loops to advance the counter by an arbitrary positive amount on each iteration. Works with both `to` and `downto`, and with inline `var`.

Forward and backward

Code: Pascal  [Select][+][-]
  1. for i := 1 to 10 step 2 do
  2.   write(i, ' ');                   // 1 3 5 7 9
  3.  
  4. for i := 20 downto 1 step 3 do
  5.   write(i, ' ');                   // 20 17 14 11 8 5 2
  6.  
  7. for var k := 5 to 50 step 5 do
  8.   write(k, ' ');                   // 5 10 15 ... 50

The step expression must be ordinal and positive. Use `downto` for descending loops; the step itself is always positive. A constant zero or negative step is a parse error.

Single evaluation

The step expression is evaluated once before the loop starts, so calls with side effects fire only once:

Code: Pascal  [Select][+][-]
  1. for i := 0 to 12 step ComputeStep() do
  2.   Use(i);                          // ComputeStep called exactly once

Same for the upper bound, the same way classic for-loops already do.

Preserved counter

The loop-end check restores the last in-range value before leaving the loop, so the counter holds the last value the body actually saw, not the overshoot:

Code: Pascal  [Select][+][-]
  1. for i := 1 to 10 step 4 do ;       // body runs at 1, 5, 9
  2. { i = 9 }
  3.  
  4. for i := 20 downto 1 step 4 do ;   // body runs at 20, 16, 12, 8, 4
  5. { i = 4 }

Same guarantee on classic for-loops in Unleashed mode.

`step` is a context-sensitive keyword

Not a reserved token. The parser only checks for it in one position - right after the to / downto expression and before do. Anywhere else `step` stays an ordinary identifier:

Code: Pascal  [Select][+][-]
  1. var step: integer = 5;             // OK - variable named step
  2.  
  3. function step: integer;            // OK - function named step
  4. begin Result := 7; end;
  5.  
  6. type TFoo = record step: integer end;   // OK - field named step
  7.  
  8. for i := 1 to step do Use(i);      // OK - upper bound is the variable step
  9.  
  10. for i := 0 to step step 1 do       // OK - first `step` is the upper bound,
  11.   Use(i);                          //      second `step` is the keyword

Lazarus' SynEdit highlighter mirrors this: only the keyword position lights up, not identifiers.

Demo

Code: Pascal  [Select][+][-]
  1. program forstep_demo;
  2.  
  3. {$mode unleashed}
  4.  
  5. function compute_step : integer;
  6. begin
  7.   writeln('  [compute_step called]');
  8.   result := 5;
  9. end;
  10.  
  11. var
  12.   i, step: integer;
  13. begin
  14.   writeln('-- forward, step 2 --');
  15.   for i := 1 to 9 step 2 do
  16.     write(i, ' ');
  17.   writeln;
  18.  
  19.   writeln('-- downto, step 3 --');
  20.   for i := 20 downto 1 step 3 do
  21.     write(i, ' ');
  22.   writeln;
  23.  
  24.   writeln('-- inline var, step 5 --');
  25.   for var k := 5 to 50 step 5 do
  26.     write(k, ' ');
  27.   writeln;
  28.  
  29.   writeln('-- step expression evaluated once --');
  30.   for i := 0 to 12 step compute_step do
  31.     write(i, ' ');
  32.   writeln;
  33.  
  34.   writeln('-- preserved counter (last value the body saw) --');
  35.   for i := 1 to 10 step 4 do ;
  36.   writeln('after for 1..10 step 4: i = ', i);          // 9
  37.   for i := 20 downto 1 step 4 do ;
  38.   writeln('after for 20 downto 1 step 4: i = ', i);    // 4
  39.  
  40.   writeln('-- step is a context-sensitive keyword --');
  41.   step := 3;
  42.   for i := 0 to step step 1 do                         // upper = var step (=3), keyword = 1
  43.     write(i, ' ');
  44.   writeln;
  45.  
  46.   readln;
  47. end.
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12398
  • Debugger - SynEdit - and more
    • wiki
What then happens to (which works in normal fpc)

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$Mode objfpc}
  3. type
  4.   TClass = class of TObject;
  5.  
  6. operator not(a:TClass): TClass;
  7. begin end;
  8.  
  9. var a: TObject;
  10. begin
  11.   writeln(a is not TObject);
  12. end.
  13.  

Fibonacci

  • Hero Member
  • *****
  • Posts: 997
  • Behold, I bring salvation - FPC Unleashed
What then happens to (which works in normal fpc)

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. var a: TObject;
  3. begin
  4.   writeln(a is not TObject);
  5. end.

How about this one? Doesn't compile at all.

And why is your operator body empty? Write proper code and we'll talk - maybe there's something to fix.
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12398
  • Debugger - SynEdit - and more
    • wiki
And why is your operator body empty? Write proper code and we'll talk - maybe there's something to fix.

Because it was a sample => just show the outlines of what exists.... (Could have put some .... into that body).

I also know that it doesn't always work. But the Syntax was (technically) already taken....

I know Delphi didn't care either. But Delphi merged (more or less) with C builder. So their selling point is, if you are sitting on old Pascal code, and you don't know what to do, just get some C programmers and continue as if it was C code. So Delphi's goal is to "Make Pascal C now". So pointing to Delphi as example is really not helpful....

---------
Mind, the comment is not on the "feature" => just the conflict created by the specific choice.

Fibonacci

  • Hero Member
  • *****
  • Posts: 997
  • Behold, I bring salvation - FPC Unleashed
Fair point - the syntax was technically taken by operator not(TClass), and Unleashed reclaims it for the more universally useful reading. Code that needed the old parsing migrates with one pair of parens: a is (not TObject). As for Delphi - I'm not buying the whole package, just noting that is not is well-defined there and reads the way users actually pronounce it.
FPC Unleashed - inline vars, tuples, statement expressions, array equality, compound assignments, indexed/lazy labels, no-RTTI & more. ⭐ Star it on GitHub!

 

TinyPortal © 2005-2018