Sign in Join

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

Mailing lists / fpc-devel / 2025-07

generic forward declaration?

8 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

Are there? Or is there something else?

The below compiles. No duplicate TBar. Looks like a forward declaration...

program Project1;{$Mode objfpc}
type
   generic TBar<A,B> = class; // forward
   generic TBar<A, B> = class
     F:A;
     X:B;
   end;
begin
end.

But the next does not compile "Identifier not found TBar" (inside TFoo). Yet the identifier is clearly there.

https://gitlab.com/freepascal.org/fpc/source/-/issues/24097 seems to say it is supported since 2022?

program Project1;
{$Mode objfpc}
type
   generic TBar<A,B> = class; // forward
   generic TFoo<A, B> = class
     F:specialize TBar<A>;
     X:B;
   end;
   generic TBar<A, B> = class
     F:specialize TFoo<A>;
     X:B;
   end;
begin
end.

Reply by email

Michael Van Canneyt
to Martin Frb
Martin Frb wrote 32 lines
Are there? Or is there something else? The below compiles. No duplicate TBar. Looks like a forward declaration... program Project1;{$Mode objfpc} type   generic TBar<A,B> = class; // forward   generic TBar<A, B> = class     F:A;     X:B;   end; begin end. But the next does not compile "Identifier not found TBar" (inside TFoo). Yet the identifier is clearly there. https://gitlab.com/freepascal.org/fpc/source/-/issues/24097 seems to say it is supported since 2022? program Project1; {$Mode objfpc} type   generic TBar<A,B> = class; // forward   generic TFoo<A, B> = class     F:specialize TBar<A>;

You're missing a type parameter here.
According to the definition, TBar needs 2 type parameters.

Show quoted text 5 lines
    X:B;   end;   generic TBar<A, B> = class     F:specialize TFoo<A>;

Same here, you're missing a type parameter. TFoo needs 2 type parameters.

if you fix that (I added B in both cases, just to test) it compiles.

Michael.

Reply by email

Martin Frb
to Michael Van Canneyt
Michael Van Canneyt wrote
Same here, you're missing a type parameter. TFoo needs 2 type parameters.

Ups, thanks.

Any obvious  (or non obvious) mistake of mine in the below?

Because either of the 2 commented specialize cause errors in the generics... (but only if TFoo.F  SWAPS the order of param).

It compiles fine
- with both the specialize commented
- or with TBar replacing "F" with the commented "F"

program Project1;
{$Mode objfpc}
type
   generic TBar<A,B> = class; // forward   // LINE 5
   generic TFoo<A, B> = class
     F:specialize TBar<A,B>;
     X:B;
   end;      // LINE 10
   generic TBar<A, B> = class
     F:specialize TFoo<B,A>;
     //F:specialize TFoo<A,B>;
     X:B;
   end;
   //x1 = specialize TBar<Byte, Boolean>;  // project1.lpr(5,28) Fatal:
Syntax error, "identifier" expected but ";" found
   //x2 = specialize TFoo<Byte, Boolean>; // project1.lpr(10,6) Fatal:
Syntax error, "identifier" expected but ";" found
begin
end.

Reply by email

Sven Barth
to Martin Frb

Martin Frb via fpc-devel schrieb am Di.,
29. Juli 2025, 20:57:

Show quoted text 42 lines
On 29/07/2025 09:36, Michael Van Canneyt wrote: > Same here, you're missing a type parameter. TFoo needs 2 type parameters. Ups, thanks. Any obvious (or non obvious) mistake of mine in the below? Because either of the 2 commented specialize cause errors in the generics... (but only if TFoo.F SWAPS the order of param). It compiles fine - with both the specialize commented - or with TBar replacing "F" with the commented "F" program Project1; {$Mode objfpc} type generic TBar<A,B> = class; // forward // LINE 5 generic TFoo<A, B> = class F:specialize TBar<A,B>; X:B; end; // LINE 10 generic TBar<A, B> = class F:specialize TFoo<B,A>; //F:specialize TFoo<A,B>; X:B; end; //x1 = specialize TBar<Byte, Boolean>; // project1.lpr(5,28) Fatal: Syntax error, "identifier" expected but ";" found //x2 = specialize TFoo<Byte, Boolean>; // project1.lpr(10,6) Fatal: Syntax error, "identifier" expected but ";" found begin end.

Please report a bug. Though this might end up not compiling nevertheless due to the specialization chain going ad infinitum...

Regards,
Sven

Reply by email

Martin Frb
to Sven Barth
Sven Barth wrote
Please report a bug. Though this might end up not compiling nevertheless due to the specialization chain going ad infinitum...

https://gitlab.com/freepascal.org/fpc/source/-/issues/41349

It does not need to go ad infinitum.

In the interface, when it encounters the circle, it only needs to know that this is a class (as it does for normal circular class references.)

However, I can see that it is trickier. Because it does need to do the specialisation at some point, so it's available during compiling the implementation.
And because the circle can be any amount of steps (greater than 2), it can't defer it until implementation, because if there are more than one step left, then the issue would likely happen then.

I can (with my non existing knowledge of the internals) **imagine** 2 scenarios.

It goes into recursion immediately, but
- detects when it needs to specialize something that is "in progress"
- it does not go into recursion, but adds it to a "todo list", which is processed after the currents specialize.

Of course, it still needs an abort for endless recursion, if it keeps creating new types (e.g. each loop inherits and extends the passed in type)

Reply by email

Mattias Gaertner
to Martin Frb
Martin Frb wrote 8 lines
On 29/07/2025 23:39, Sven Barth via fpc-devel wrote: > > Please report a bug. Though this might end up not compiling > nevertheless due to the specialization chain going ad infinitum... > https://gitlab.com/freepascal.org/fpc/source/-/issues/41349 It does not need to go ad infinitum.

It depends on the compiler implementation.

pas2js compiles it.
OTOH pas2js currently creates 3 internal specializations for each TFoo and TBar.

Mattias

Reply by email

Martin Frb
to Mattias Gaertner
Mattias Gaertner wrote 17 lines
On 7/30/25 11:44, Martin Frb via fpc-devel wrote: > On 29/07/2025 23:39, Sven Barth via fpc-devel wrote: >> >> Please report a bug. Though this might end up not compiling >> nevertheless due to the specialization chain going ad infinitum... >> > https://gitlab.com/freepascal.org/fpc/source/-/issues/41349 > > It does not need to go ad infinitum. It depends on the compiler implementation. pas2js compiles it. OTOH pas2js currently creates 3 internal specializations for each TFoo and TBar.

There should only be

TFoo<Byte, Boolean>
TFoo<Boolean, Byte>

If you got 3 then you got one of them twice. And if they are different types, then at some point you get issues with assigning them to the fields?

Reply by email

Mattias Gaertner
to Martin Frb
Martin Frb wrote 10 lines
On 30/07/2025 12:23, Mattias Gaertner via fpc-devel wrote: >[...] > pas2js compiles it. > OTOH pas2js currently creates 3 internal specializations for each TFoo > and TBar. There should only be TFoo<Byte, Boolean> TFoo<Boolean, Byte>

Yes.
Well, actually, in your example you don't use them, so pas2js omits a lot.

But internally while resolving the code it already creates a specialization for F

   generic TFoo<A, B> = class
     F:specialize TBar<A,B>;

with the generic template parameter A and B, so it can resolve and check the generic code. This specialization is never transpiled to code. It merely exists for type checking. It's a todo to create less of these internal specializations.

If you got 3 then you got one of them twice. And if they are different types, then at some point you get issues with assigning them to the fields?

Each generic type in pas2js has a list of specializations with each combination of parameters unique.
This is enough for simple programs, but for packages Delphi supports duplicate specializations.

Mattias

Reply by email