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