Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2025-11

generics defy scoping rules -- Intended?

9 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin Frb

Normally, if I refer to a type then the first type found by the name will be taken => and if that type is not compatible, then I get an error. => See Example 2

But, if I try to specialize a generic, and some other type by the same name exists, and comes first in the search order, then it will be ignored.

The specialize below, ignores the "TFoo = class"  which can not be used, and finds the TFoo from unit1.

Is that intended?

unit Unit1;{$mode ObjFPC}
interface
type
   generic TFoo<a> = class F: A; end;
implementation
end.
program Project1;{$Mode objfpc}
uses Unit1;
type
   TFoo = class end;
   //TFoo = word;
   TBar = specialize TFoo<byte>;  // Works, ignores the class (or word) TFoo
begin
end.

############# Example 2  ##############

unit Unit1;{$mode ObjFPC}
interface
type
TFoo = class end;   // TFoo that could be base class
implementation
end.
program Project1;{$Mode objfpc}
uses Unit1;
type
   TFoo = word;
   TFoo2 = class(TFoo) end;  // <<<<<< Fails / does not see the class TFOO
begin
end.

Reply by email

Michael Van Canneyt
to Martin Frb
Martin Frb wrote 11 lines
Normally, if I refer to a type then the first type found by the name will be taken => and if that type is not compatible, then I get an error. => See Example 2 But, if I try to specialize a generic, and some other type by the same name exists, and comes first in the search order, then it will be ignored. The specialize below, ignores the "TFoo = class"  which can not be used, and finds the TFoo from unit1. Is that intended?

Delphi allows to overload a generic with a non-generic class.

TFoo = class end;
TFoo <a> = class F: a end;

FPC has experimental support for this as far as I know.

So the answer would be:
Yes, the specialize will look for a generic definition, ignoring classes with the same name that are not generic.

Michael.

Reply by email

Sven Barth
to Martin Frb: Normally, if I refer to a type then the first type found by the name will be taken => and…

Martin Frb via fpc-devel schrieb am Mo.,
24. Nov. 2025, 21:30:

Show quoted text 12 lines
Normally, if I refer to a type then the first type found by the name will be taken => and if that type is not compatible, then I get an error. => See Example 2 But, if I try to specialize a generic, and some other type by the same name exists, and comes first in the search order, then it will be ignored. The specialize below, ignores the "TFoo = class" which can not be used, and finds the TFoo from unit1. Is that intended?

Yes, because in fact they don't have the same name. The name of a generic with a single parameter is essentially "TFoo" while that of a generic with two parameters is "TFoo<,>".

The amount of generic parameters is part of the type or routine name.

Note: non-Delphi modes currently don't allow type overloading in the same unit, but it's on my ToDo-list to make it possible there as well, but it requires some adjustments to allow compatibility to existing code which is why it's not possible yet.

Regards,
Sven

Reply by email

Hairy Pixels
to Michael Van Canneyt: Delphi allows to overload a generic with a non-generic class. TFoo = class end; TFoo <a>…

On Nov 25, 2025 at 4:48:52 PM, Michael Van Canneyt via fpc-devel <

Show quoted text 13 lines
wrote: Delphi allows to overload a generic with a non-generic class. TFoo = class end; TFoo <a> = class F: a end; FPC has experimental support for this as far as I know. So the answer would be: Yes, the specialize will look for a generic definition, ignoring classes with the same name that are not generic.

specialize TFoo<byte> actually searches for the symbol "TFoo$1" so it's a different symbol name than just TFoo.

I think though if tried to declare TFoo like "a: TFoo" it would give an error that generics can't be used without specialization thus make using TFoo unusable, but I may be wrong about that.

Reply by email

Martin Frb
to Sven Barth: Martin Frb via fpc-devel schrieb am Mo., 24. Nov. 2025, 21:30: Yes, because in fact they…
Sven Barth wrote 8 lines
Yes, because in fact they don't have the same name. The name of a generic with a single parameter is essentially "TFoo" while that of a generic with two parameters is "TFoo<,>". The amount of generic parameters is part of the type or routine name.

Thanks, which just for confirmation brings up a follow up question. (I haven't used generic function much yet, so maybe I miss something)

           function  Foo   (aParam: Integer): integer;
   generic function  Foo<T>(aParam: t)      : integer;

Those are 2 different function too? (not an overload? because "T" could be integer in same cases)

Do you then always have to call the generic using
  specialize Foo<integer>(1)
?

I seem to remember a discussion about adding automatic specialization? But then
  foo('abc') // would be clear, to specialize with string but
  foo(1)  // could be either

Reply by email

Hairy Pixels
to Martin Frb

On Nov 25, 2025 at 9:55:46 PM, Martin Frb via fpc-devel <

Show quoted text 23 lines
wrote: Thanks, which just for confirmation brings up a follow up question. (I haven't used generic function much yet, so maybe I miss something) function Foo (aParam: Integer): integer; generic function Foo<T>(aParam: t) : integer; Those are 2 different function too? (not an overload? because "T" could be integer in same cases) Do you then always have to call the generic using specialize Foo<integer>(1) ? I seem to remember a discussion about adding automatic specialization? But then foo('abc') // would be clear, to specialize with string but foo(1) // could be either _______________________________________________

yes those are two different functions. The generic function is just a template which includes the parsed tokens and is named with $X where X is the number of generic parameters. Only at the time of specialization is the code generated, until the it's just an array of tokens.

there is a {$modeswitch implicitfunctionspecialization} mode switch ( my work so I apologize for the bugs 😂) which infers the parameter type so that foo('abc') becomes foo<String>('String').

Reply by email

Martin Frb
to Hairy Pixels
Hairy Pixels wrote 21 lines
On Nov 25, 2025 at 9:55:46 PM, Martin Frb via fpc-devel wrote: > Thanks, which just for confirmation brings up a follow up question. > (I haven't used generic function much yet, so maybe I miss something) > >           function  Foo   (aParam: Integer): integer; >   generic function  Foo<T>(aParam: t)      : integer; > > yes those are two different functions. The generic function is just a template which includes the parsed tokens and is named with $X where X is the number of generic parameters. Only at the time of specialization is the code generated, until the it's just an array of tokens. there is a {$modeswitch implicitfunctionspecialization} mode switch ( my work so I apologize for the bugs 😂) which infers the parameter type so that foo('abc') becomes foo<String>('String').

So then with that switch, what will
  foo(1)

resolve to?

- Always the non generic
- Always the generic
- Which every is first in scope
?

Reply by email

Sven Barth
to Martin Frb

Martin Frb via fpc-devel schrieb am Di.,
25. Nov. 2025, 17:29:

Show quoted text 34 lines
On 25/11/2025 16:14, Hairy Pixels via fpc-devel wrote: On Nov 25, 2025 at 9:55:46 PM, Martin Frb via fpc-devel < > wrote: > Thanks, which just for confirmation brings up a follow up question. (I > haven't used generic function much yet, so maybe I miss something) > > function Foo (aParam: Integer): integer; > generic function Foo<T>(aParam: t) : integer; > > > yes those are two different functions. The generic function is just a template which includes the parsed tokens and is named with $X where X is the number of generic parameters. Only at the time of specialization is the code generated, until the it's just an array of tokens. there is a {$modeswitch implicitfunctionspecialization} mode switch ( my work so I apologize for the bugs 😂) which infers the parameter type so that foo('abc') becomes foo<String>('String'). So then with that switch, what will foo(1) resolve to? - Always the non generic - Always the generic - Which every is first in scope ?

The non-generic is preferred as it might provide a better concrete implementation.

Regards,
Sven

Reply by email

Sven Barth
to Martin Frb: Thanks, which just for confirmation brings up a follow up question. (I haven't used…

Martin Frb via fpc-devel schrieb am Di.,
25. Nov. 2025, 15:55:

Show quoted text 25 lines
On 25/11/2025 14:19, Sven Barth via fpc-devel wrote: Yes, because in fact they don't have the same name. The name of a generic with a single parameter is essentially "TFoo" while that of a generic with two parameters is "TFoo<,>". The amount of generic parameters is part of the type or routine name. Thanks, which just for confirmation brings up a follow up question. (I haven't used generic function much yet, so maybe I miss something) function Foo (aParam: Integer): integer; generic function Foo<T>(aParam: t) : integer; Those are 2 different function too? (not an overload? because "T" could be integer in same cases) Do you then always have to call the generic using specialize Foo<integer>(1) ?

Correct. It doesn't matter what the generic might be specialized to, the generic is definitely a separate routine from the non-generic one and not an overload.

Regards,
Sven

Reply by email