From d73c504efaaa724b30e97a11e3b5dacd4579fc30 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 6 Sep 2026 14:23:43 +0300 Subject: [PATCH] rtti: skip generic methods in the legacy VMT method table (fixes upstream#41410) A published open generic method has no callable body, but the legacy method table writer still counted it and emitted a reference to the generic template symbol, so a valid class failed at link time with an undefined symbol. Make the counting and writing predicates symmetric and exclude tprocdef.is_generic, as the extended RTTI writer already does. Ordinary published methods and real specializations are unaffected. FPC issue: https://gitlab.com/freepascal.org/fpc/source/-/issues/41410 Co-Authored-By: Claude Fable 5.1 --- compiler/ncgvmt.pas | 6 ++++-- tests/webtbs/tw41410.pp | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/webtbs/tw41410.pp diff --git a/compiler/ncgvmt.pas b/compiler/ncgvmt.pas index 9762f1f505b..d5cdb1ab951 100644 --- a/compiler/ncgvmt.pas +++ b/compiler/ncgvmt.pas @@ -462,7 +462,8 @@ TMsgInt = record begin pd:=tprocdef(Tprocsym(p).ProcdefList[i]); if (pd.procsym=tsym(p)) and - (pd.visibility=vis_published) then + (pd.visibility=vis_published) and + not pd.is_generic then inc(plongint(arg)^); end; end; @@ -490,7 +491,8 @@ tvmtasmoutput = record begin pd:=tprocdef(Tprocsym(p).ProcdefList[i]); if (pd.procsym=tsym(p)) and - (pd.visibility=vis_published) then + (pd.visibility=vis_published) and + not pd.is_generic then begin { l: name_of_method } lists^.pubmethodstcb.start_internal_data_builder(current_asmdata.AsmLists[al_const],sec_rodata,_class.vmt_mangledname,datatcb,l); diff --git a/tests/webtbs/tw41410.pp b/tests/webtbs/tw41410.pp new file mode 100644 index 00000000000..1a5596f0c79 --- /dev/null +++ b/tests/webtbs/tw41410.pp @@ -0,0 +1,44 @@ +program tw41410; + +{$mode delphi} + +type + {$M+} + TContainer = class + private + FValue: Integer; + published + procedure Ping; + procedure Test(const AValue: T); + end; + {$M-} + +procedure TContainer.Ping; +begin + FValue := 7; +end; + +procedure TContainer.Test(const AValue: T); +begin + FValue := SizeOf(AValue); +end; + +var + Container: TContainer; +begin + Container := TContainer.Create; + try + if Container.MethodAddress('Ping') = nil then + Halt(1); + if Container.MethodAddress('Test') <> nil then + Halt(2); + Container.Ping; + if Container.FValue <> 7 then + Halt(3); + Container.Test(42); + if Container.FValue <> SizeOf(Int64) then + Halt(4); + finally + Container.Free; + end; +end.