Feature announcement: Implicit generic function specializations lazarus / 60-Free Pascal ---------------------------------------------------------------------- PascalDragon · 2022-04-20 17:15 Dear FPC community, The FPC developers are pleased to announce the implementation of a new feature: implicit generic function specializations. This feature was implemented by Ryan Joseph, so thank you very much, Ryan. This feature allows you to use generic routines (functions, procedures, methods) without explicitely specializing them (“<…>” in Delphi modes and “specialize …<…>” in non-Delphi modes) as long as the compiler can determine the correct parameter types for the generic. This feature is enabled with the modeswitch ImplicitFunctionSpecialization and is for now not enabled by default as this has the potential to break existing code. Assume you have the following function: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i generic function Add(aArg1, aArg2: T): T;begin  Result := aArg1 + aArg2;end; Up to now you could only use this function as follows: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i SomeStr := specialize Add('Hello', 'World');SomeInt := specialize Add(2, 5); However with implicit function specializations enabled you can also use it as follows: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i SomeStr := Add('Hello', 'World');SomeInt := Add(2, 5); The compiler will automatically determine the type of the generic parameters based on the parameters you pass in (this is always done left to right). Depending on the passed in parameters (especially if you're using constant values like in the example instead of variables) the compiler might however pick a different type than you expected. You can enforce a specific type by either explicitely specializing the method as before or by inserting a type cast. In the example above the compile will specialize the call with the parameters “2, 5” using an 8-bit signed type (Pascal prefers signed types) instead of a LongInt as in the explicit specialization. If you use “LongInt(2), 5” as parameters then the compiler will pick that instead, however with “2, LongInt(5)” it will still pick an 8-bit type, because the parameter types are determined left to right. If there exists a non-generic overload for which the parameters types match exactly, the compiler will pick that instead of specializing something anew. So assume you also have the following function in scope: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function Add(aArg1, aArg2: LongInt): LongInt;begin  Result := aArg1 + aArg2;end; In the case of “Add(2, 5)” the compiler will not pick the non-generic function, because it determines that an 8-bit type is enough, however if you use “Add(LongInt(2), 5)” the compiler will pick the non-generic function. Aside from simple parameters the compiler also supports arrays and function/method variables: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i generic function ArrayFunc(aArg: specialize TArray): T;var  e: T;begin  Result := Default(T);  for e in aArg do    Result := Result + e;end; type  generic TTest = function(aArg: T): T; generic function Apply(aFunc: specialize TTest; aArg: T): T;begin  Result := aFunc(aArg);end; function StrFunc(aArg: String): String;begin  Result := UpCase(aArg);end; function NegFunc(aArg: LongInt): LongInt;begin  Result := - aArg;end; begin  Writeln(ArrayFunc([1, 2, 3])); // will write 6  Writeln(ArrayFunc(['Hello', 'FPC', 'World'])); // will write HelloFPCWorld   Writeln(Apply(@StrFunc, 'Foobar')); // will write FOOBAR  Writeln(Apply(@NegFunc, 42)); // will write -42end. There are of course a few restrictions for this feature: all generic parameters must be used in the declaration of the routine (implementation only type parameters are not allowed)all parameters that have a generic type must not be default parameters, they need to be used in the call or their type must have been fixed by a parameter further left (as currently default values for parameters of a generic type are not supported this is not much of a restriction, but should that change (e.g. Default(T)) then this restriction will apply)the generic routine must not have constant generic parameters (this might be extended in the future with e.g. static arrays or file types, but for now this restriction stands)the result type is not taken into account, so if only the result type of a routine is generic then an implicit specialization does not work eitherfunction/method pointers to implicit specializations are not yet supported (pointers to explicit specializations are not yet supported either; once this changes the former will change as well)the compiler will silently discard generic functions that it can't specialize the declaration of; however if the declaration can be specialized correctly, but for whatever reason the implementation can not then this will trigger a compilation error This feature is by and in itself Delphi-compatible however there might be differences in what FPC can implicitely specialize and what Delphi can. Especially if Delphi can specialize something that FPC can not, this should be reported. ---------------------------------------------------------------------- mas steindorff · 2022-04-20 18:16 this sounds like it's just the ticket for what I'm working with.  What version /package / ?? do I need to download to access these features? ---------------------------------------------------------------------- Zaher · 2022-04-20 21:38 I love generics ---------------------------------------------------------------------- Okoba · 2022-04-21 07:23 Thank you Sven and specially Ryan. This is a fantastic upgrade. ---------------------------------------------------------------------- Okoba · 2022-04-21 07:30 If I have two overloaded functions in a unit, FPC detects the the correct one but if I split them in two units, it will raise an error: Quoteproject1.lpr(22,8) Error: Wrong number of parameters specified for call to "Test"Before there was not any error, but now, even without the ImplicitFunctionSpecialization mode switch, there is this error. Version I am testing: Lazarus 2.3.0 (rev main-2_3-1668-g34b3b9a49a) FPC 3.3.1 x86_64-win64-win32/win64 This code works: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode Delphi}   function Test(const AString: Rawbytestring): Integer; overload;  begin   end;   function Test(const AList: TArray): Integer; overload;  begin   end; var  S: Rawbytestring;  I: Integer;begin  I := Test(S);end.                 This raise the error: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode Delphi} interface function Test(const AString: Rawbytestring): Integer; overload; implementation function Test(const AString: Rawbytestring): Integer;begin end; end.  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit2; {$mode Delphi} interface function Test(const AList: TArray): Integer; overload; implementation function Test(const AList: TArray): Integer;begin end; end.  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode Delphi} uses unit1, unit2; var  S: Rawbytestring;  I: Integer;begin  I := Test(S);end. ---------------------------------------------------------------------- marcov · 2022-04-21 08:37 Quote from: Okoba on April 21, 2022, 09:30:24 amIf I have two overloaded functions in a unit, FPC detects the the correct one but if I split them in two units, it will raise an error: (possibly related to  https://gitlab.com/freepascal.org/fpc/source/-/issues/39673 that I found last week) ---------------------------------------------------------------------- Warfley · 2022-04-21 08:39 The implicit specialization does not seem to handle inheritance very well: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+}{$ModeSwitch implicitfunctionspecialization} uses  Generics.Collections; generic procedure Foo(lst: specialize TEnumerable);beginend; var  lst: specialize TList; // Inherits from TEnumerablebegin               Foo(lst); // Error  specialize Foo(lst); // worksend. ---------------------------------------------------------------------- PascalDragon · 2022-04-21 12:22 Quote from: mas steindorff on April 20, 2022, 08:16:46 pmthis sounds like it's just the ticket for what I'm working with.  What version /package / ?? do I need to download to access these features? New features are first available only in the development version (aka main) until the next major release (which will be 3.4.0 in some undetermined time in the future). So you'll have to clone the Git repository and compile FPC yourself. Or as is the hype nowadays: use FpcUpDeluxe. Quote from: marcov on April 21, 2022, 10:37:30 amQuote from: Okoba on April 21, 2022, 09:30:24 amIf I have two overloaded functions in a unit, FPC detects the the correct one but if I split them in two units, it will raise an error: (possibly related to  https://gitlab.com/freepascal.org/fpc/source/-/issues/39673 that I found last week) Maybe, yes... @Okoba: please report it nevertheless with a nice, simple example. Quote from: Warfley on April 21, 2022, 10:39:01 amThe implicit specialization does not seem to handle inheritance very well: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+}{$ModeSwitch implicitfunctionspecialization} uses  Generics.Collections; generic procedure Foo(lst: specialize TEnumerable);beginend; var  lst: specialize TList; // Inherits from TEnumerablebegin               Foo(lst); // Error  specialize Foo(lst); // worksend. Please report. ---------------------------------------------------------------------- Okoba · 2022-04-21 13:35 Reported all at #39677, #39678 and #39680. ---------------------------------------------------------------------- PascalDragon · 2022-04-21 16:23 Quote from: Okoba on April 21, 2022, 03:35:35 pmReported all at #39677, #39678 and #39680. Thank you. ---------------------------------------------------------------------- Warfley · 2022-04-22 19:54 Some thing I've found, which I did not report as a bug yet, because I don't know if it is desired (as this might very well introduce a load of unwanted side effects), is that implicit specialization is not recognizing possible implicit casts: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+}{$ModeSwitch advancedrecords}{$ModeSwitch implicitfunctionspecialization} type  generic TTest = record    class operator :=(const rhs: T): specialize TTest;  end; generic procedure Foo(arg: specialize TTest);beginend; begin  specialize foo(42); // Works  foo(42); // Errorend.But it could be useful with for example the TNullable type ---------------------------------------------------------------------- PascalDragon · 2022-04-23 10:31 Quote from: Warfley on April 22, 2022, 09:54:32 pmSome thing I've found, which I did not report as a bug yet, because I don't know if it is desired (as this might very well introduce a load of unwanted side effects), is that implicit specialization is not recognizing possible implicit casts: Currently this is indeed by design as at least Delphi does not allow it either. And I personally prefer that we don't open that pandora's box... (especially es it might become problematic to determine what is a suitable specialization if there are multiple valid implicit assignments to do this in more complex scenarios) ---------------------------------------------------------------------- corpsman · 2024-10-04 09:30 Hello all, the implicit generic specialization is exactly what i was searching for, but. In the usermanual of FPC 3.2.2 ( https://sourceforge.net/projects/freepascal/files/Documentation/3.2.2/ ) it is not listed anymore. Also my FPC compiler Quotecorpsman@corpsman:~$ fpc -h Free Pascal Compiler version 3.2.2+dfsg-9ubuntu1 [2022/04/11] for x86_64 Throws the message Quoteproject1.lpr(4,2) Warning: Illegal compiler switch "IMPLICITFUNCTIONSPECIALIZATION+" when i try to compile this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;{$mode objfpc}{$H+} {$ModeSwitch implicitfunctionspecialization}begin end.  Did i miss something ? ---------------------------------------------------------------------- Warfley · 2024-10-04 10:28 Quote from: corpsman on October 04, 2024, 11:30:02 amDid i miss something ? FPC 3.2.2 is from May 2021, this feature was introduced in 2022 to Trunk/Main so it will be only in the next major version. Also don't expect too much it has quite some limitations, so better be ready to still write "specialize ..." for some time to come ---------------------------------------------------------------------- dbannon · 2024-10-04 11:24 Quote from: corpsman on October 04, 2024, 11:30:02 amDid i miss something ? Yes, several posts further up, PascalDragon said - New features are first available only in the development version (aka main) until the next major release (which will be 3.4.0 in some undetermined time in the future). So you'll have to clone the Git repository and compile FPC yourself. Or as is the hype nowadays: use FpcUpDeluxe. 3.4.0 release date is still undetermined. Davo ---------------------------------------------------------------------- corpsman · 2024-10-04 18:48 Thanks, i missed the part with 3.4 and the original post is 2 years old, so i thought that it is already in .. ---------------------------------------------------------------------- Thaddy · 2025-01-18 08:37 It will also not be in 3.2.4. Note that it indeed has some limitations, mainly the types it chooses, but it is now quite stable and I have been using it since may 2022. If you want the feature, use trunk/main.