Re: [fpc-pascal] Feature announcement: implicit generic function specializations
> If you want to pass a pointer to ^T in a generic function is there anyway > safe to do this currently? Pascal doesn’t allow ^ types in function arguments > (why?) and generics don’t seems to support pointers either (why?). > > generic TValues = array[0..0] of T; > generic PValues = ^specialize TValues; > > I guess the only thing to do is use a untyped pointer and cast it to the > correct type inside the function declaration? > > For example here is a generic QuickSort function which operates on any array > of T. > > type > generic TComparator = function (left: T; right: T; context: pointer): > integer; > > generic procedure QuickSort(_values: pointer; first, last: LongInt; > comparator: specialize TComparator); > type > TValues = array[0..0] of T; > PValues = ^TValues; > var > pivot,j,i: integer; > temp: T; > values: PValues absolute _values; > > Regards, > Ryan Joseph > Not that pretty, but I use something like: == generic TPtr = record public type P = ^T; public ptr: P; end; generic function Ref(out r: specialize TPtr.P):boolean; == Denis Golovan ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
Am Mittwoch, 20. April 2022, 19:15:15 CEST schrieb Sven Barth via fpc-pascal: > 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. How many percent of the users need this feature? Is it a feature that is a must? Can everyone get also a solution without the feature? Does it justify the risk of the whole language (has the potential to break existing code)? ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
> On Apr 22, 2022, at 8:48 PM, Rainer Stratmann via fpc-pascal > wrote: > > How many percent of the users need this feature? > Is it a feature that is a must? > Can everyone get also a solution without the feature? > Does it justify the risk of the whole language (has the potential to break > existing code)? It’s like everything else in programming languages. 1) you can specialize the function manually so implicit specialization is not needed. 2) you can duplicate functions and change types manually so generic functions are not needed. 3) you can program in assembly so high-level languages are needed. 4) etc… :) Joking aside it just makes for less code and more readable code (in my opinion). Regards, Ryan Joseph ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
Am Freitag, 22. April 2022, 17:27:33 CEST schrieb Hairy Pixels via fpc-pascal: > > On Apr 22, 2022, at 8:48 PM, Rainer Stratmann via fpc-pascal > It’s like everything else in programming languages. > > 1) you can specialize the function manually so implicit specialization is > not needed. > 2) you can duplicate functions and change types manually so > generic functions are not needed. > 3) you can program in assembly so high-level languages are needed. > 4) etc… :) > > Joking aside it just makes for less code and more readable code (in my > opinion). From assembly to high-level language there is a huge step. From high-level language to implicit generic function specializations it is a little step regarding the benefits. In my opinion it makes everything more complicated. My mind refuses to read the description of the new feature. But mostly I am worried because of the statement "has the potential to break existing code". ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
On 22/04/2022 17:13, Rainer Stratmann via fpc-pascal wrote: Am Freitag, 22. April 2022, 17:27:33 CEST schrieb Hairy Pixels via fpc-pascal: On Apr 22, 2022, at 8:48 PM, Rainer Stratmann via fpc-pascal From assembly to high-level language there is a huge step. From high-level language to implicit generic function specializations it is a little step regarding the benefits. In my opinion it makes everything more complicated. My mind refuses to read the description of the new feature. But mostly I am worried because of the statement "has the potential to break existing code". Rainer, We assume this is a language feature you will not use in your code, which is fine. However, it is a cause for celebration among those who do welcome such syntax sugar, and will be using the feature to improve the size and readability of their code. kind regards, Howard ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
Am 22.04.2022 um 15:48 schrieb Rainer Stratmann via fpc-pascal: Am Mittwoch, 20. April 2022, 19:15:15 CEST schrieb Sven Barth via fpc-pascal: 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. How many percent of the users need this feature? We don't deal in percentages, however it reduces the amount of typing required to write code with a lot of specializations (in theory an IDE like Lazarus *could* help here as well however). Is it a feature that is a must? It's only syntactic sugar, so *normally* a feature as this wouldn't be really be given much of a thought, however Delphi supports it as well. Can everyone get also a solution without the feature? Yes, by simply doing the specializations as usual (e.g. "specialize Add('Hello', 'World')" instead of "Add('Hello', 'World')"). As said, this is pure syntactic sugar. Please note however that sometimes we also introduce features that you can't workaround that will also require changes to code if that feature in question is used (mainly by enabling the modeswitch). The RTTI attributes come to mind, cause then the directive clause for function directives can no longer be used if you want to use them: === code begin === {$mode objfpc} {$modeswitch prefixedattributes} function Foobar: LongInt; [stdcall, inline]; // this part is invalid //function Foobar: LongInt; stdcall; inline; // and needs to be written like this begin end; begin end. === code end === Does it justify the risk of the whole language (has the potential to break existing code)? That is why it's behind a modeswitch and not enabled by default, so that it's in control of the user whether they want this or not. The problem space is rather small, but it nevertheless exists: you need to have a normal routine overloaded with a generic routine (for example across different units) and the compiler must decide to do an implicit specialization instead of using the non generic routine (as it does without the feature enabled / before the feature was implemented), but the specialization of that function then needs to fail, because the generic routine can't be used with the type the compiler picked Regards, Sven ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
On 20/04/2022 19:15, Sven Barth via fpc-pascal wrote: 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. One more step by Delphi to remove type safety. IMHO a good option would have been to allow specializing multiple overloads in a single statement. But since Embarcadero has decided otherwise I did explore what happens if I throw different types at it, and see how the current implementation deals with this (what I call) lack of type safety. And also asked the question, how should it act? (Because the current behaviour is new, expected to need fixes, and can obviously be fixed). Assume you have the following function: generic function Add(aArg1, aArg2: T): T; ... Up to now you could only use this function as follows: SomeStr := specialize Add('Hello', 'World'); SomeInt := specialize Add(2, 5); However with implicit function specializations enabled you can also use it as follows: SomeStr := Add('Hello', 'World'); SomeInt := Add(2, 5); So what happens if: var b: Byte; c: Cardinal; begin Add(b, c); Well, I tested: It uses the type of the first Param. So it calls a function for both param of type Byte. The cardinal argument is converted. (potentially truncated). If you use numeric constants: writeln(' 0', Add(0, 0) ); // ShortInt writeln('1000', Add(1000, 1000) ); // SmallInt writeln('100K', Add(10, 10) ); // Integer So then, if you try to fix " Add(b, c)" by checking that b and c have the same type => "Add(c, 0)" will fail => because 0 is ShortInt and not cardinal. I created a little test program (see bottom of mail). And it gives a strange warning: Compile Project, Target: C:\Users\martin\AppData\Local\Temp\project1.exe: Success, Warnings: 1, Hints: 3 project1.lpr(67,52) Warning: Range check error while evaluating constants (10 must be between -128 and 127) project1.lpr(41,18) Hint: Local proc "Add$1" is not used 72 lines compiled, 0.2 sec, 105136 bytes code, 5476 bytes data Line 41 is the declaration of the generic generic function Add(aArg1, aArg2: T): T; So why does it generate "Add$1" if it does not use it? (Or rather why does it warn, if this is some internal details?) And if you add to the app (at the top) function Add(aArg1, aArg2: Int64): Int64; overload; begin // write(' Int64 overload '); Result := aArg1 + aArg2; end; Then the line (Shortint, because the first param is ShortInt) writeln(' 1,CK = ',Add(0, 10) ); // ShortInt Will no longer call the specialized function, but instead use the int64 version. If you comment all "Add(...)" calls, except that one => Then you get an additional hint: "project1.lpr(81,41) Hint: Local proc "Add$1$crc9AB0BCED" is not used" So then that very line generates a specialized version for the call, and then the compiler does not use it. Yet if INSTEAD of adding the hardcoded Int64 variant (as given above), I let the compile create a Int64 version by adding writeln(' 1,CK = ',Add(int64(0), 10) ); // ShortInt before the line above... Well the line above will not use that int64 version, but use its own specialization... program Project1; {$mode objfpc} {$ModeSwitch ImplicitFunctionSpecialization } {//$H+} uses SysUtils; Procedure Foo(a: Integer; out r: ShortInt); begin write(' ShortInt '); r := a; end; Procedure Foo(a: Integer; out r: SmallInt); begin write(' SmallInt '); r := a; end; Procedure Foo(a: Integer; out r: Byte); begin write(' Byte '); r := a; end; Procedure Foo(a: Integer; out r: Word); begin write(' Word '); r := a; end; Procedure Foo(a: Integer; out r: Cardinal); begin write(' Cardinal '); r := a; end; Procedure Foo(a: Integer; out r: Integer); begin write(' Integer '); r := a; end; Procedure Foo(a: Integer; out r: Int64); begin write(' Int64 '); r := a; end; Procedure Foo(a: Integer; out r: AnsiString); begin write(' Ansi '); r := ' _A:'+IntToStr(a); end; Procedure Foo(a: Integer; out r: ShortString); begin write(' Short '); r := ' _S:'+IntToStr(a); end; generic function Add(aArg1, aArg2: T): T; var x: T; begin Foo(SizeOf(T), x); {$R-} Result := aArg1 + aArg2 + x; end; var b: byte; c: cardinal; begin b := 0; c := 0; writeln(' B = ',Add(b, b) ); writeln(' C = ',Add(c, c) ); writeln(' 0 = ',Add(0, 0) ); // ShortInt writeln(' 1000 = ',Add(1000, 1000) ); // SmallInt writeln(' 100K = ',Add(10, 10) ); // Integer writeln; writeln(' c,b = ',Add(c, b) ); // Cardinal writeln('
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
On Fri, 22 Apr 2022 20:51:56 +0200 Martin Frb via fpc-pascal wrote: >[...] > I did explore what happens if I throw different types at it, and see > how the current implementation deals with this (what I call) lack of > type safety. > And also asked the question, how should it act? (Because the current > behaviour is new, expected to need fixes, and can obviously be fixed). See Sven's first mail. >[...] > So what happens if: > > var > b: Byte; > c: Cardinal; > begin > Add(b, c); > > Well, I tested: It uses the type of the first Param. So it calls a > function for both param of type Byte. The cardinal argument is > converted. (potentially truncated). Yes, as explained by Sven. > If you use numeric constants: > writeln(' 0', Add(0, 0) ); // ShortInt > writeln('1000', Add(1000, 1000) ); // SmallInt > writeln('100K', Add(10, 10) ); // Integer > > So then, if you try to fix " Add(b, c)" by checking that b and c > have the same type => "Add(c, 0)" will fail => because 0 is ShortInt > and not cardinal. Why is that a fail? > > I created a little test program (see bottom of mail). > > And it gives a strange warning: > > Compile Project, Target: > C:\Users\martin\AppData\Local\Temp\project1.exe: Success, Warnings: > 1, Hints: 3 > project1.lpr(67,52) Warning: Range check error while evaluating > constants (10 must be between -128 and 127) Add(0, 10) Correct range error. > project1.lpr(41,18) Hint: Local proc "Add$1" is not used > 72 lines compiled, 0.2 sec, 105136 bytes code, 5476 bytes data > Line 41 is the declaration of the generic > generic function Add(aArg1, aArg2: T): T; Maybe related to https://gitlab.com/freepascal.org/fpc/source/-/issues/39675 >[...] Mattias ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
On Fri, 22 Apr 2022 20:51:56 +0200 Martin Frb via fpc-pascal wrote: >[...] > Well, I tested: It uses the type of the first Param. So it calls a > function for both param of type Byte. The cardinal argument is > converted. (potentially truncated). I agree that Delphi should have used a better algorithm, like checking the var/out arguments first and otherwise use the smallest integer that fits all related params. Same for strings and boolean. pas2js uses a similar algorithm. In $mode Delphi fpc must use the Delphi algorithm. In other modes it could use a better algorithm. If fpc would use the above algorithm in mode objfpc, the Add would work like FPC's add: generic function Add(a,b: T): T; begin Result:=a+b; end; Add(aByte,aCardinal) -> cardinal And you don't need to put a var/out param leftmost: generic procedure Adding(a,b: T; out c: T); begin c:=a+b; end; Adding(3,4,aWord); Mattias ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
Am Freitag, 22. April 2022, 19:53:34 CEST schrieben Sie: > Am 22.04.2022 um 15:48 schrieb Rainer Stratmann via fpc-pascal: > > Am Mittwoch, 20. April 2022, 19:15:15 CEST schrieb Sven Barth via fpc- pascal: > We don't deal in percentages, however it reduces the amount of typing > required to write code with a lot of specializations (in theory an IDE > like Lazarus *could* help here as well however). Of course 'you' do. When I asked for a simple feature years ago it was refused. And there were several explanations why this feature is not necessary. ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
Am 22.04.2022 um 20:51 schrieb Martin Frb via fpc-pascal: I created a little test program (see bottom of mail). And it gives a strange warning: Compile Project, Target: C:\Users\martin\AppData\Local\Temp\project1.exe: Success, Warnings: 1, Hints: 3 project1.lpr(67,52) Warning: Range check error while evaluating constants (10 must be between -128 and 127) project1.lpr(41,18) Hint: Local proc "Add$1" is not used 72 lines compiled, 0.2 sec, 105136 bytes code, 5476 bytes data Line 41 is the declaration of the generic generic function Add(aArg1, aArg2: T): T; So why does it generate "Add$1" if it does not use it? (Or rather why does it warn, if this is some internal details?) Add$1 is the symbol of the generic itself, *not* the specialization. But please report it, cause that definitely shouldn't be the case anyway, as the generic is after all used by the specializations (and I should compile with -vh more often...) And if you add to the app (at the top) function Add(aArg1, aArg2: Int64): Int64; overload; begin // write(' Int64 overload '); Result := aArg1 + aArg2; end; Then the line (Shortint, because the first param is ShortInt) writeln(' 1,CK = ',Add(0, 10) ); // ShortInt Will no longer call the specialized function, but instead use the int64 version. Correct, because a matching non-generic variant is available and the compiler then picks that. If you comment all "Add(...)" calls, except that one => Then you get an additional hint: "project1.lpr(81,41) Hint: Local proc "Add$1$crc9AB0BCED" is not used" So then that very line generates a specialized version for the call, and then the compiler does not use it. The compiler essentially works like this: it generates a suitable set of overloads. This includes the non-generic routines as well as all generic routines for which suitable type parameters can be found. Out of this set the compiler will then pick the function it will finally use with a preference for non-generic functions (even if parameters might be a worse, though not incompatible match). That said: if the compiler does not pick one of the specializations it should totally discard them, so please report a bug for this as well. Yet if INSTEAD of adding the hardcoded Int64 variant (as given above), I let the compile create a Int64 version by adding writeln(' 1,CK = ',Add(int64(0), 10) ); // ShortInt before the line above... Well the line above will not use that int64 version, but use its own specialization... The compiler determines the generic type parameters for each call. In this case it will be Int64 due to the typecast. However that will not change what the other, previous Add calls use and it also won't affect one any following Add call, cause it will simply determine the set of elligible functions (and other implicit specializations as well as explicit specializaitons are *not* part of this) and then determine which one to call out of this set (the compiler might specialize the same generic function with the same generic type parameters multiple times then, but the part that's dealing with specializations in general will detect that and point to the existing symbol instead). Regards, Sven ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
Am 22.04.2022 um 22:22 schrieb Mattias Gaertner via fpc-pascal: On Fri, 22 Apr 2022 20:51:56 +0200 Martin Frb via fpc-pascal wrote: [...] Well, I tested: It uses the type of the first Param. So it calls a function for both param of type Byte. The cardinal argument is converted. (potentially truncated). I agree that Delphi should have used a better algorithm, like checking the var/out arguments first and otherwise use the smallest integer that fits all related params. Same for strings and boolean. pas2js uses a similar algorithm. Considering that we're talking mainly about differences of width and precision (e.g. the differences between a string and a record are more clear) this can probably be improved without considering Delphi here, because this is nowhere documented... (our non-generic overload selection might lead to different results after all as well) But even if we only improve this for non-Delphi modes I agree that a suggestion like yours would be an improvement. Regards, Sven ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
On 22/04/2022 23:12, Sven Barth via fpc-pascal wrote: Am 22.04.2022 um 20:51 schrieb Martin Frb via fpc-pascal: So why does it generate "Add$1" if it does not use it? (Or rather why does it warn, if this is some internal details?) Add$1 is the symbol of the generic itself, *not* the specialization. But please report it, cause that definitely shouldn't be the case anyway, as the generic is after all used by the specializations (and I should compile with -vh more often...) => Then you get an additional hint: "project1.lpr(81,41) Hint: Local proc "Add$1$crc9AB0BCED" is not used" So then that very line generates a specialized version for the call, and then the compiler does not use it. The compiler essentially works like this: it generates a suitable set of overloads. This includes the non-generic routines as well as all generic routines for which suitable type parameters can be found. Out of this set the compiler will then pick the function it will finally use with a preference for non-generic functions (even if parameters might be a worse, though not incompatible match). That said: if the compiler does not pick one of the specializations it should totally discard them, so please report a bug for this as well. Done https://gitlab.com/freepascal.org/fpc/source/-/issues/39684 https://gitlab.com/freepascal.org/fpc/source/-/issues/39685 ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
Possible one more / Though more of an optimization. If the code has multiple Add(Int64(0), Int64(0)); then they will all use the same procedure (just break in the debugger and check the CRC). But if one specialization is explicit and the other implicit then 2 identical (as far as I can tell with -al ) functions (with diff CRC) are created. There is however a subtle difference in the generate asm. The explicit specialization has comment for the source code # [6] begin ... # [7] Result := aArg1 + aArg2; ... The implicit does not have those. Actually I checked that a bit deeper. The comment occur in (and only in) the first specialization (implicit or explicit). All other specialization are without comments for the source. program Project1; {$mode objfpc} {$ModeSwitch ImplicitFunctionSpecialization } generic function Add(aArg1, aArg2: T): T; begin Result := aArg1 + aArg2; end; begin specialize Add(Int64(0), Int64(0)); // No Hint Add(Int64(0), Int64(0)); end. ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Re: [fpc-pascal] Feature announcement: implicit generic function specializations
> On Apr 23, 2022, at 5:18 AM, Martin Frb via fpc-pascal > wrote: > > Done > https://gitlab.com/freepascal.org/fpc/source/-/issues/39684 > https://gitlab.com/freepascal.org/fpc/source/-/issues/39685 It looks like I need to do some cleanup phase. I can’t remember how it works right now but I think I made the specialization so that it could be considered in overloading but after it was used it needs to be marked so hints aren’t given or deleted entirely (probably at the end of the unit if there is someway to know it was never used). Sven may have some ideas on how this works so feel free to drop them in the Gitlab issue. Regards, Ryan Joseph ___ fpc-pascal maillist - fpc-pascal@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal