Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/ncgvmt.pas
Original file line number Diff line number Diff line change
Expand Up @@ -1354,8 +1354,9 @@ classindex:=classtablelist.IndexOf(tfieldvarsym(sym).var
do_write_vmts(trecorddef(def).symtable,is_global);
objectdef :
begin
{ Skip generics and forward defs }
{ Skip generics, partial specializations and forward defs }
if ([df_generic,df_genconstraint]*def.defoptions<>[]) or
tstoreddef(def).has_generic_paras or
(oo_is_forward in tobjectdef(def).objectoptions) then
continue;
if tobjectdef(def).is_unique_objpasdef then
Expand Down
16 changes: 2 additions & 14 deletions compiler/pgenutil.pas
Original file line number Diff line number Diff line change
Expand Up @@ -1716,18 +1716,6 @@ if (formalobjdef.childof=class_tobject) and
end;
end;

function has_generic_paras(adef: tstoreddef): boolean;
var
i: Integer;
begin
result:=False;
if adef.genericparas<>nil then
for i:=0 to adef.genericparas.Count-1 do
if ((tsym(adef.genericparas[i]).typ=typesym) and (sp_generic_para in tsym(adef.genericparas[i]).symoptions)) or
((tsym(adef.genericparas[i]).typ=constsym) and not (sp_generic_const in tsym(adef.genericparas[i]).symoptions)) then
exit(true);
end;

var
finalspecializename,
ufinalspecializename : tidstring;
Expand Down Expand Up @@ -2263,7 +2251,7 @@ current_scanner.recordtokenbuf:=recordbuf;
tdef(item).ChangeOwner(specializest);
{ for partial specializations we implicitly declare any methods as having their
implementations although we'll not specialize them in reality }
if parse_generic or has_generic_paras(tstoreddef(item)) then
if parse_generic or tstoreddef(item).has_generic_paras then
unset_forwarddef(tdef(item));
end;

Expand All @@ -2279,7 +2267,7 @@ current_scanner.recordtokenbuf:=recordbuf;

{ procdefs are only added once we know which overload we use }
if not parse_generic and (result.typ<>procdef) and
not has_generic_paras(tstoreddef(result)) then
not tstoreddef(result).has_generic_paras then
current_module.pendingspecializations.add(result.typename,result);
end;

Expand Down
20 changes: 20 additions & 0 deletions compiler/symdef.pas
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ tstoreddef = class(tdef)
function is_generic:boolean;
{ same as above for specializations }
function is_specialization:boolean;
{ true if this def still contains generic parameters that require
specialization }
function has_generic_paras:boolean;
{ generic utilities }
function is_generic_param_const(index:integer):boolean;inline;
function get_generic_param_def(index:integer):tdef;inline;
Expand Down Expand Up @@ -2762,6 +2765,23 @@ implementation
end;


function tstoreddef.has_generic_paras: boolean;
var
i: longint;
sym: tsym;
begin
result:=false;
if assigned(genericparas) then
for i:=0 to genericparas.count-1 do
begin
sym:=tsym(genericparas[i]);
if ((sym.typ=symconst.typesym) and (sp_generic_para in sym.symoptions)) or
((sym.typ=symconst.constsym) and not (sp_generic_const in sym.symoptions)) then
exit(true);
end;
end;


procedure tstoreddef.register_def;
var
gst : tgetsymtable;
Expand Down
63 changes: 63 additions & 0 deletions tests/webtbs/tw41788.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
program tw41788;

{$mode delphi}

type
TEnumerator<T> = class
protected
function DoMoveNext: Boolean; virtual;
end;

TEnumerable = class
class function List<T>: TEnumerator<T>; static;
end;

TConsumer<T> = class
procedure Run;
end;

var
MoveCount: LongInt;
ValueDigest: LongInt;

function TEnumerator<T>.DoMoveNext: Boolean;
begin
Inc(MoveCount);
ValueDigest := ValueDigest + 41788;
Result := True;
end;

class function TEnumerable.List<T>: TEnumerator<T>;
begin
Result := TEnumerator<T>.Create;
end;

procedure TConsumer<T>.Run;
var
Enumerator: TEnumerator<T>;
begin
Enumerator := TEnumerable.List<T>;
try
if not Enumerator.DoMoveNext then
Halt(3);
finally
Enumerator.Free;
end;
end;

var
Consumer: TConsumer<LongInt>;
begin
MoveCount := 0;
ValueDigest := 0;
Consumer := TConsumer<LongInt>.Create;
try
Consumer.Run;
finally
Consumer.Free;
end;
if MoveCount <> 1 then
Halt(1);
if ValueDigest <> 41788 then
Halt(2);
end.