Recent

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

440bx

  • Hero Member
  • *****
  • Posts: 6532
Feature request:

The option of having the compiler automatically initialize all the local variables in a function or procedure to binary zeroes by specifying the keyword "initlocals" in the function/procedure declaration.  For example:

Code: Pascal  [Select][+][-]
  1. procedure MyProc(parameter : parameter_type); initlocals;
  2. var
  3.   SomeVar  : array[0..10] of char;
  4.   OtherVar : TSOMEENUM;
  5.   YnotVar  : TSOME_SET;
  6.  
  7.   IntVar   : integer = 5;
  8. begin
  9. end;
  10.  
presuming the 3 variables consume a total of 256 bytes, the initlocals would cause all 256 bytes to be set to binary zeroes.  After that, the compiler initializes (again), IntVar to 5.

NOTE: the purpose of this feature is NOT to ensure the variables start with valid values, it is to ensure they always start with a predictable and easy to recognize value which can often be very valuable in debugging.

This should be fairly easy to implement, particularly considering that the mechanism used to initialize arrays of ansichar to empty does what this feature does but, for arrays of char only.

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

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
Before I consider anything new, Composable Records needs to land in main. The code is publicly available - all you have to do is install the branch (feat/composable-records). Surely you can handle that ;) In fpcupdeluxe, just put the branch name in Setup+ and you're set.

If even that's too much - I'm publishing the Unleashed Installer today, which simplifies things significantly.
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
Also, the "gitlab" checkbox has to be unchecked to be able to select "unleashed.git".

valdir.marcos

  • Hero Member
  • *****
  • Posts: 1285
I'm old and crabby and think most of the innovations here just pollute an otherwise elegant language that struck (and this is really important in my view) a great compromise between the interests of the program writer and the program reader. 

Who is going to document all of these wonders?  And in a way that will take them accessible to a new generation of programmers (assuming there is one)?   

Marco Cantu's Object Pascal Handbook (10.1) is already 547 pages long.  I'm not sure adding another hundred pages describing relatively esoteric features (that are after all eventually going to be mapped into a very limited assembly language anyway) makes a lot of sense.

We seem to be close to a time when everyone could create his/her own personal programming language and add features at will with the help of a Claude-like AI.   The downside of that future is that nobody but the author will be able to read the programs in  that language (unless they are very fully documented -- and we all know the history of that).

Wisdom even for FPC and Delphi themselves. Both contain several syntaxes that rarely anyone uses, and more appear every year.

440bx

  • Hero Member
  • *****
  • Posts: 6532
Before I consider anything new, Composable Records needs to land in main.
That's unfortunate.

As I stated in a previous post, I consider that "composable records" feature to be a poison pill that turns code into a naming house of cards.  If I saw code like that, one of two things would happen: 1. would not ever consider using it or 2. the very first thing  I'd do, is remove the use of that feature from it.  IOW, no way, ever, would I have code with something like that which means, no way, I'm going to test that "feature".   I wouldn't even allow code like that in throw-away code (I very rarely throw away code, I mostly just forget it.)

All that said, people are welcome to use it.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
I consider that "composable records" feature to be a poison pill

Fair enough - nobody is required to use every feature, and that's how it should be. Composable Records is just first in line for merge to main; what gets used after that is each programmer's call.

That said, I'm a bit confused. A lot of the current shape - union of <type>, bit-fields with size cap on the bitpacked record, explicit container type required on bit-field blocks - came directly from your suggestions in this thread. You proposed the type-bearing container (originally as container, landed as union of T), you proposed moving the size constraint from the union to the bitpacked record, you wanted of <type> required there. I took those, refined them with you, built them in.

Now the whole feature is a "poison pill"? That's a sharp turn. If the concern is specifically the anonymous embed (you've been consistent on that from the start, and fair enough - it's not for everyone), the rest still stands and is largely what we worked out together. Opting out of one part doesn't require rejecting the whole feature.
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: 6532
Now the whole feature is a "poison pill"? That's a sharp turn. If the concern is specifically the anonymous embed (you've been consistent on that from the start, and fair enough - it's not for everyone), the rest still stands and is largely what we worked out together. Opting out of one part doesn't require rejecting the whole feature.
No, it's not the same feature at all.  The composable records thing, allows an already defined record type to be anonymously added to another record, that's what causes the potential for naming conflicts.  None of the things I suggested opens the door to ever having that problem because you can't simply add the layout of a previously defined record without naming it.  The problem is the lack of a named scope in the inclusion of the record layout and, I never suggested "importing" an already defined record layout into another.  I hope you see the difference.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1000
  • Behold, I bring salvation - FPC Unleashed
No, it's not the same feature at all.  The composable records thing, allows an already defined record type to be anonymously added to another record, that's what causes the potential for naming conflicts.  None of the things I suggested opens the door to ever having that problem because you can't simply add the layout of a previously defined record without naming it.

When a record is embedded anonymously, its fields enter the outer record's namespace. Duplicate names = compile error, not silent shadowing. The embed is a literal include of the embedded record's content - the compiler walks both field lists, flags any clash, refuses to build.

Named embed is a separate form, also supported. With name: TInner; the access goes through the carrier (outer.name.field), and same field names in both records coexist fine - they're in different scopes.

Two options for two different intents:
  • Anonymous embed (embed TInner;): flat access, shared namespace, compile error on collision
  • Named embed (name: TInner;): scoped access through the carrier, namespaces stay separate by design
On the maintenance scenario - TInner gains a field later that collides with something in TOuter - that's a compile error on the next build, not a silent breakage. The maintainer is forced to deal with it the moment they pull the change. That's the failure mode you want: explicit, immediate, traceable. Not a "naming house of cards" - more like an early warning system.

So I don't see the structural problem you're flagging. If you've got a concrete scenario where it actually bites silently, I'd genuinely like to see it.

Still confused.
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: 6532
Consider this simple example:
Code: Pascal  [Select][+][-]
  1. type
  2.   TREC_1 = record
  3.     FieldName1 : integer;
  4.     FieldName2 : integer;
  5.     FieldName3 : integer;
  6.   end;
  7.  
  8.  
  9. type
  10.   TComposed = record
  11.     SomeName1      : integer;
  12.     embed TREC_1;                { anonymous/unnamed embedding }
  13.     SomeOtherField : integer;
  14.   end;
  15.  

after some time, could be months or years, someone decides to add a field to TREC_1, like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TREC_1 = record
  3.     FieldName1     : integer;
  4.     FieldName2     : integer;
  5.  
  6.     SomeOtherField : integer;
  7.  
  8.     FieldName3     : integer;
  9.   end;
  10.  
now there is a naming conflict with SomeOtherField. Where is the conflict resolved, in the new TREC_1 or in TComposed ?  The potential for this problem grows as the number of unnamed embedded records grows in the composed record(s). 

That problem can occur with any record that embed records without naming them.  I see that as nothing but a can of worms. This is what would make me stay away from that feature like the plague.

Basically, with that feature you can't just look at a record and be certain its field names won't collide with something because any record that is anonymously embedded into another one can potentially cause the problem and that cannot be determined by looking at the record itself.  You only get to know it when you read the layouts of records that use the composition feature which could be hundreds/thousands of lines later or even in a different file. 

Did that clarify the situation ?

« Last Edit: May 16, 2026, 06:57:15 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

flowCRANE

  • Hero Member
  • *****
  • Posts: 986
Feature request:

The option of having the compiler automatically initialize all the local variables in a function or procedure to binary zeroes by specifying the keyword "initlocals" in the function/procedure declaration.

[...]

NOTE: the purpose of this feature is NOT to ensure the variables start with valid values, it is to ensure they always start with a predictable and easy to recognize value which can often be very valuable in debugging.

I really like this suggestion—it eliminates the notorious manual zeroing of variables, and there’s actually quite a lot of that in my typical code. But since this would be a great feature that I'd want to use in absolutely every function, I'd prefer it to be a compiler switch, i.e., {$modeswitch initlocals}, applied to the contents of the entire unit. Or better {$modeswitch zerolocals} because it clearly states that the goal is to zero out the locals. And also because the {$modeswitch initfinal} currently exists and has nothing to do with data initialization.

But that doesn't really matter. Even if this feature were available as a modifier, I'd still use it.
« Last Edit: May 16, 2026, 07:04:12 pm by flowCRANE »
Lazarus 4.6 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

flowCRANE

  • Hero Member
  • *****
  • Posts: 986
Consider this simple example:

[...]

now there is a naming conflict with SomeOtherField. Where is the conflict resolved, in the new TREC_1 or in TComposed ?  The potential for this problem grows as the number of unnamed embedded records grows in the composed record(s).

In this case, a name conflict should result in a compilation error (standard duplicate identifier error), because there are two variables with the same name within the same scope. This is exactly the same situation as with objects and classes, where the derived object/class has a field/method/property with the same name as the base object/class (e.g., in the protected section). So this is nothing out of the ordinary in the world of Free Pascal.
« Last Edit: May 16, 2026, 07:11:18 pm by flowCRANE »
Lazarus 4.6 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

440bx

  • Hero Member
  • *****
  • Posts: 6532
In this case, a name conflict should result in a compilation error (standard duplicate identifier error), because there are two variables with the same name within the same scope. This is exactly the same situation as with objects and classes, where the derived object/class has a field/method/property with the same name as the base object/class (e.g., in the protected section). So this is nothing out of the ordinary in the world of Free Pascal.
When I add a field to a record, I expect the field to be in the record's scope and nowhere else.  That's what records are for, to define a container for fields.  Your comment about the problem also occurring due to inheritance in objects and classes is one of many, many reasons I don't use OOP, locality went out the window.  I don't need OOP's problems to be imported into regular records.

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

flowCRANE

  • Hero Member
  • *****
  • Posts: 986
Your comment about the problem also occurring due to inheritance in objects and classes is one of many, many reasons I don't use OOP, locality went out the window.  I don't need OOP's problems to be imported into regular records.

I feel the same way, which is why I didn't suggest this kind of data embedding. The only thing I objected to was the idea that an unnamed structure should be declared solely by specifying its data type. 8)

But I know that people might need something like this. For example, in C game development, I’ve seen people insert a base structure in a similar way as the first element in a child structure, thereby achieving polymorphism for ordinary structures. The problem is that they used the name Header for such a structure, which created an additional namespace—something that was undesirable.

But this problem doesn't affect me, because I use fat structures instead of something resembling inheritance.
« Last Edit: May 16, 2026, 07:58:54 pm by flowCRANE »
Lazarus 4.6 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

creaothceann

  • Sr. Member
  • ****
  • Posts: 375
Consider this simple example:
Code: Pascal  [Select][+][-]
  1. type
  2.   TREC_1 = record
  3.     FieldName1 : integer;
  4.     FieldName2 : integer;
  5.     FieldName3 : integer;
  6.   end;
  7.  
  8.  
  9. type
  10.   TComposed = record
  11.     SomeName1      : integer;
  12.     embed TREC_1;                { anonymous/unnamed embedding }
  13.     SomeOtherField : integer;
  14.   end;
  15.  

after some time, could be months or years, someone decides to add a field to TREC_1, like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TREC_1 = record
  3.     FieldName1     : integer;
  4.     FieldName2     : integer;
  5.  
  6.     SomeOtherField : integer;
  7.  
  8.     FieldName3     : integer;
  9.   end;
  10.  
now there is a naming conflict with SomeOtherField. Where is the conflict resolved, in the new TREC_1 or in TComposed ?

In TComposed, since TREC_1 could be used elsewhere.


That problem can occur with any record that embed records without naming them.

As mentioned by flowCRANE it can also occur with inheritance. If this occurs then the field in the outer structure can perhaps be removed - a shared name implies shared functionality. Or the outer structure can use its own (renamed) field. The most laborious solution is to convert the anonymous embedded structure to a named structure, and uses either properties or the fully qualified name to refer to the inner structure's fields.

But I'd expect this to occur extremely infrequently in practice - and you get a compile time error.


When I add a field to a record, I expect the field to be in the record's scope and nowhere else.  That's what records are for, to define a container for fields.  Your comment about the problem also occurring due to inheritance in objects and classes is one of many, many reasons I don't use OOP, locality went out the window.  I don't need OOP's problems to be imported into regular records.

Records can just describe a memory layout, they don't have to be containers themselves. For example a 16-color palette and a 256-color palette can share the same layout, consisting of the header and the first 16 colors; it wouldn't make sense to divide a 256-color palette into two parts.

This can of course also solved via macros or include files, but it's less elegant.

440bx

  • Hero Member
  • *****
  • Posts: 6532
I really see no value in that feature but only problems.

if the fields of another record are needed, just copy and paste them, that makes the fields visible in the record and, if there is a name conflict, it is strictly local to the record that received the new fields therefore immediately visible.

That "feature" just obscures the fields (their names are not immediately visible) and opens the door to naming problems.  I don't see a single thing that is good about it except that it saved the programmer from doing a copy/paste which isn't worth all the potential problems it creates. 
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018