diff --git a/compiler/ncgvmt.pas b/compiler/ncgvmt.pas index 9762f1f505b..4b4ff1cf745 100644 --- a/compiler/ncgvmt.pas +++ b/compiler/ncgvmt.pas @@ -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 diff --git a/compiler/pgenutil.pas b/compiler/pgenutil.pas index a541953d7c3..89f96e06744 100644 --- a/compiler/pgenutil.pas +++ b/compiler/pgenutil.pas @@ -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; @@ -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; @@ -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; diff --git a/compiler/symdef.pas b/compiler/symdef.pas index 2127d7fbaa3..22383e960b5 100644 --- a/compiler/symdef.pas +++ b/compiler/symdef.pas @@ -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; @@ -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; diff --git a/tests/webtbs/tw41788.pp b/tests/webtbs/tw41788.pp new file mode 100644 index 00000000000..da52ea91c44 --- /dev/null +++ b/tests/webtbs/tw41788.pp @@ -0,0 +1,63 @@ +program tw41788; + +{$mode delphi} + +type + TEnumerator = class + protected + function DoMoveNext: Boolean; virtual; + end; + + TEnumerable = class + class function List: TEnumerator; static; + end; + + TConsumer = class + procedure Run; + end; + +var + MoveCount: LongInt; + ValueDigest: LongInt; + +function TEnumerator.DoMoveNext: Boolean; +begin + Inc(MoveCount); + ValueDigest := ValueDigest + 41788; + Result := True; +end; + +class function TEnumerable.List: TEnumerator; +begin + Result := TEnumerator.Create; +end; + +procedure TConsumer.Run; +var + Enumerator: TEnumerator; +begin + Enumerator := TEnumerable.List; + try + if not Enumerator.DoMoveNext then + Halt(3); + finally + Enumerator.Free; + end; +end; + +var + Consumer: TConsumer; +begin + MoveCount := 0; + ValueDigest := 0; + Consumer := TConsumer.Create; + try + Consumer.Run; + finally + Consumer.Free; + end; + if MoveCount <> 1 then + Halt(1); + if ValueDigest <> 41788 then + Halt(2); +end.