Generic specialization of local type results in invalid PPU
## Summary Bug when specializing generics using local types, for example TList from system.generics.collections. ## System Information <!-- The more information are provided the easier it is to replicate the bug --> - **Operating system:** Any - tested on wasm32 - **Processor architecture:** Should be any, but bug manifests fastest on wasm. - **Compiler version:** main - **Device:** Any ## Steps to reproduce Attached example with script reproduces the problem. ## Example Project <!-- If possible, please create an example project that exhibits the problematic behavior, and link to it here in the bug report. --> ## What is the current bug behavior? Compiler crashes when recompiling ## What is the expected (correct) behavior? Compiler compiles correctly when recompiling. ## Possible patch Bug apparent in case of a transitive specialization chain: ``` TList<TLocal> -> TEnumerable<TLocal> -> TEnumerator<TLocal> ``` See attached test case with 4 units: unita,unitb,unitc, unitd, p (program) This is a reduced example created from original code in FMX: The corrupted specialization is `TEnumerator<TEnumerable<TChangedLink>.PT>` where `TChangedLink` is a type declared inside the `procedure TimerProc` (a method-local nested type). The `T` is `TEnumerable<TChangedLink>.PT` — i.e. `PT` is a nested public type inside the generic `TEnumerable<T>`, accessed through specialization. Crash signature in original code and in simplified example: ``` EAccessViolation in wasm32/wasmdef.pas:49 get_para_push_size ``` on recompile after touch The abstract function ``` DoGetCurrent: T; virtual; abstract; ``` inherited from `TEnumerator<T>` ends up in a specialized class whose `returndef` references a symbol in the procedure-local symtable. When `unitb.ppu` is written, that `returndef` serializes via a `defref` whose target lives in a transient (procedure-local) symtable. On reload during a recompile, the reference can't be resolved -> `returndef = nil.` The wasm32 codegen path `(init_paraloc_info(callerside)` from `ncgrtti.pas:295` during RTTI emission, then `get_funcretloc -> get_para_push_size(nil)` hits the AV; on other targets it may manifest differently or silently produce broken output. Possible fix: The existing code in `generate_specialization_phase2` decides the target symtable for a specialization by looking at each type parameter's immediate sym owner: ``` if psym.owner.symtabletype in [localsymtable,parasymtable] then ... ``` That catches the direct case (`TList<TLocal>` where `TLocal` is a proc-local typesym), but not the indirect case that arises with transitively-created specializations: - `Generics.Collections.TEnumerable<T> declares `PT = ^T` and `function GetPtrEnumerator: TEnumerator<PT>`. - When `TList<TLocal>` is specialized (with `TLocal` a proc-local record), `TEnumerator<PT>` must also be specialized. - At that point `PT` is a typesym in `TEnumerable<TLocal>'s objectsymtable` — its immediate owner is not a local symtable, even though its underlying `typedef (^TLocal)` points to a type that is proc-local. - Old code falls through to unit scope. The PPU then records a defref to `TLocal` from a unit-scoped class, which can't be resolved on reload -> `returndef = nil` -> AV in wasm32 `get_para_push_size` (and silently broken RTTI on other targets). The fix walks down into the type parameter's typedef chain: ``` pointerdef ->pointeddef, arraydef ->elementdef, setdef ->elementdef, filedef ->typedfiledef, classrefdef -> pointeddef ``` and checks each underlying type's owner for a local/para symtable. If found, the specialization is placed there; otherwise it falls back to the original immediate-owner check, then to unit scope. To reproduce bug: [gen-bug.zip](/uploads/bc23348d8ea06f8945fc8bbd5eb2fba9/gen-bug.zip) Fix: [pgenutil.diff](/uploads/cebe932d8f5f3835238bd3c76e72edc2/pgenutil.diff)
issue