Just out of curiosity....

I understand when the generic is parsed, the compiler makes checks that it compiles. So certain things must be known.
Some things the compiler happily assumes to be ok.

Given an non-constrained param, then (in code) any member is assumed to be ok. But in declarations, members of the param can only be used if the compiler knows them.

  generic TGenA<T1,T2> = class
    X: T1;
    Y: T2.unknown;
  end;

What reason may there be to reject the line for Y?

"X: T1" shows that the compiler can continue with a field of unknown type (because T1 is not known, until a specialize happens).
"Y" would be exactly as unknown as "X", or wouldn't it?

Also, in code you can do
     n := T2.foo.bar;
and this can later be a field or a method, or even a constant....


The  reason why I got curious is that, I can imagine (and that may be to limited) 2 reasons: - technical => the compiler can't do it (but I don't see how, since "T2.unknown" is as indeterminable as "T1"   (the compiler just needs to parse the members, as it already does when it compiles code in the begin...end)

- checking early for errors. Ensuring that the generic can be specialized
  I.e. ensuring that "T2" has that field.


The 2nd option (if it is) does however IMHO not ensure anything. (Ignoring that error can still happen in the begin..end), they can also still happen in the declaration when being specialized.
E.g. when having

  generic TGenA<T2: TMyClass> = class // TMyClass has  "public type unknown = byte;"
    Y: T2.unknown;
  end;

Then that does guarantee (almost) nothing.
Since TGen can be specialized with any sublclass of TMyClass, any such subclass may redefine or hide what "unknown" is. So when specialising, it can still fail. (it only can't be completely absent, as then the base class will provide).

So what did I miss, what is the gain of that check?


Example
"TX2" will fail, because a field can not be used as type. (the presence in the base class does not prevent this)


program Project1; {$mode objfpc}
uses Unit1;
type
  TClassA1 = class(TClassA)
  private type    T1 = byte;
  end;

  TClassA2 = class(TClassA)
    T1: integer;
  end;

  TX1 = specialize TGenA<TClassA1>;
//  TX2 = specialize TGenA<TClassA2>;

begin {} end.


unit Unit1; {$mode ObjFPC}
interface
type
  TClassA = class
  private type     T1 = record a: byte; end;
  end;

  generic TGenA<T_A: TClassA> = class
    F1: T_A.T1;
  end;

implementation
end.

_______________________________________________
fpc-devel maillist  -  [email protected]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to