Author Topic: Parameterless method call syntax inside generics  (Read 232 times)

H₂SO₄

  • Sr. Member
  • ****
  • Posts: 493
Parameterless method call syntax inside generics
« on: September 02, 2026, 08:21:42 am »
Here's a little heads-up to those working with generics + advanced records: When invoking a parameterless method on an instance of a parametrized type (without type restrictions), you must omit the parentheses or else the code won't compile.

Take the following example:

Code: Pascal  [Select][+][-]
  1. type
  2.   generic TCurve<T> = record
  3.     Parameters: T;
  4.     function Sample(Progress: Double): TPoint;
  5.   end;
  6.  

Expecting  T  to provide a method  function Evaluate(Progress: Double): TPoint of object  works just fine:

Code: Pascal  [Select][+][-]
  1. function TCurve.Sample(Progress: Double): TPoint;
  2. begin
  3.   Result := Parameters.Evaluate(Progress);
  4. end;

Changing it to  function Evaluate(): TPoint of object  (and passing the argument via a field/property), however, causes the compiler to reject the method call if you add empty parentheses:

Code: Pascal  [Select][+][-]
  1. function TCurve.Sample(Progress: Double): TPoint;
  2. begin
  3.   Parameters.Position := Progress;
  4.   Result := Parameters.Evaluate();  // `Error: Illegal expression` at parentheses
  5. end;

Removing the parentheses fixes this error:

Code: Pascal  [Select][+][-]
  1. function TCurve.Sample(Progress: Double): TPoint;
  2. begin
  3.   Parameters.Position := Progress;
  4.   Result := Parameters.Evaluate;    // Compiles just fine
  5. end;



I'm posting this so others do not fall into the same trap as I did - when I first tried using advanced record methods inside generics, I had the misfortune of choosing a parameterless method as my test subject. Seeing this error I misconcluded that what I sought to do wasn't possible (with the misunderstanding being made worse by comparing it with  T: class  restrictions, which reinforced my belief that the compiler would need to know about available methods in advance since such class-based type restrictions do check for that).

I originally stumbled upon this while writing an ARC smart pointer template and struggling to forward the inner type's finalizer/destructor to the template itself. In the end I wound up with a cursed workaround involving operator overloads that I do not want anyone to repeat - hence why I'm posting this.

LLMs say that this quirk might stem from limitations in FPC's parser, which I haven't been able to verify. But regardless of whether this should be considered a bug (and if so whether fixing it is even worth the effort), it might be a good idea to document this somewhere.

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: Parameterless method call syntax inside generics
« Reply #1 on: September 02, 2026, 09:19:33 am »
The situation changes with {$mode delphi} btw.
Any "programmer" that knows only one programming language is not a programmer

PascalDragon

  • Hero Member
  • *****
  • Posts: 6430
  • Compiler Developer
Re: Parameterless method call syntax inside generics
« Reply #2 on: September 04, 2026, 09:38:09 pm »
Here's a little heads-up to those working with generics + advanced records: When invoking a parameterless method on an instance of a parametrized type (without type restrictions), you must omit the parentheses or else the code won't compile.

Fixed in main.

LLMs say that this quirk might stem from limitations in FPC's parser, which I haven't been able to verify. But regardless of whether this should be considered a bug (and if so whether fixing it is even worth the effort), it might be a good idea to document this somewhere.

Bugs are by definition not to be documented, but fixed.

H₂SO₄

  • Sr. Member
  • ****
  • Posts: 493
Re: Parameterless method call syntax inside generics
« Reply #3 on: September 07, 2026, 08:18:32 am »
Bugs are by definition not to be documented, but fixed.

Yes, you're right. I fell for the power of suggestion (from the AI answer) and didn't even consider that this might just be a genuine bug instead of some deep-rooted limitation. Thanks for the quick fix  :)

 

TinyPortal © 2005-2018