[fpc-devel] Additional generic type constraints

Kostas Michalopoulos badsectoracula at gmail.com
Thu Feb 23 17:59:14 CET 2023


On 2/22/23 15:11, Sven Barth wrote:
> Kostas Michalopoulos via fpc-devel <fpc-devel at lists.freepascal.org 
> <mailto:fpc-devel at lists.freepascal.org>> schrieb am Mi., 22. Feb. 2023, 
> 10:37:
> 
>      > Because Delphi doesn't have them and when constraints were
>     implemented
>      > they were implemented for Delphi compatibility.
> 
>     Can they be added? The original announcement ~13 years ago mentioned
>     that those could be added at some point. "object" and "operator X"
>     would
>     be quite useful for me.
> 
> 
> Some additional constraints might be added. However I have none of them 
> planned currently and enough other things to do.
> 
> 
>     I tried to use constraints at some point in my code but they proven too
>     limited in functionality - the biggest issue i faced was that i
>     couldn't
>     use a specialization with a forward declared class (which is the entire
>     point of the forward declaration). For example i have a generic
>     collection that only works on TSerializable subclasses so i gave it
>     that
>     constraint, but in another unit i need to define a specialization
>     for it
>     before it was defined (so the class was available with a forward class
>     declaration) since that specialization also needs to be used by the
>     class itself. In general it seems like having constrained generic work
>     with a tree structure of derived types that use the generic itself is
>     impossible.
> 
>     Wouldn't storing a list of "specializations to confirm later in this
>     unit" work (with later being when the class is actually defined in the
>     unit)? The language already has forward declarations for a bunch of
>     other things.
> 
> 
> Forward declarations can not be used for constraints, because a 
> specialization of the generic might be used before the forward declared 
> type is fully defined and the compiler *must* be able to check whether 
> the parameter in question is compatible at the time the specialization 
> is declared.
> 
> Regards,
> Sven
> 

That is what the compiler does now, but can't it be changed to treat
constraints that use forward declarations as unconstrained (at least as
far as these forward declarations are concerned) until the moment they
are declared and validate those constraints at that point?

After all the code below does work if there is no constraint despite the
specialization being used before the forward declaration:

----
program Test;
{$MODE OBJFPC}{$H+}
type
    TProvidesFoo = class
      function Foo: Integer; virtual; abstract;
    end;

    // The following with a constraint could be instead:
    // generic TGetFoo<T: TProvidesFoo> = class
    generic TGetFoo<T> = class
      Wrap: T;
      function GetFoo: Integer;
    end;

type
    TForwardClass = class;
    // Instead of making the test here, add it to a list to check later...
    TFCHasFoo = specialize TGetFoo<TForwardClass>;

procedure DumpFoo(FCHasFoo: TFCHasFoo);
begin
    Writeln(FCHasFoo.GetFoo);
end;

function TGetFoo.GetFoo: Integer;
begin
    Result:=Wrap.Foo;
end;

type
    // ...and when this is declared check the if any of the remaining
    // checks in the list will be valid or not and remove it from the list
    TForwardClass = class(TProvidesFoo)
      function Foo: Integer; override;
    end;
    function TForwardClass.Foo: Integer;
    begin
      Result:=42;
    end;

var
    FC: TForwardClass;
    HF: TFCHasFoo;
begin
    HF:=TFCHasFoo.Create;
    HF.Wrap:=TForwardClass.Create;
    DumpFoo(HF);
    HF.Wrap.Free;
    HF.Free;
end.
----

At the end of the day this isn't a major flaw, after all if the expected
functionality wasn't there (e.g. the generic code tried to access
something the type used in the specialization didn't provide) you'd
still get a compile error anyway, though it does feel a bit like there a
hole in Free Pascal's type safety here (e.g. imagine passing a type to a
specialization that happens to have the same function call so the code
compiles but the call does something different than what you'd expect).

Kostas



More information about the fpc-devel mailing list