inference: propagate `PartialStruct` for parametric struct construction (#61719)
`abstract_eval_new` previously gated `PartialStruct` formation on
`isconcretedispatch(rt)`. That's correct for fully-instantiated concrete
struct construction, but it discards extended lattice information when
`rt` is a partially-instantiated parametric type, e.g.
`Some{OpaqueClosure{Tuple{Any}, T} where T}` or
`Generator{Vector{Int}, F<:OpaqueClosure{Tuple{Int}, T} where T}`. Such
types still have a well-defined field count and field types, so the
per-field lattice elements (`PartialOpaque`, `Const`, `PartialStruct` of
nested structs, …) carry strictly more precision than the declared field
types — but without `PartialStruct` formation that precision is
discarded at the construction site, and downstream `getfield` reads only
recover the widened type.
Loosen the gate to also accept the parametric case (`fcount !== nothing
&& nargs ≤ fcount`). `nothrow` and the `Const`-folding fast path remain
gated on `isconcretedispatch(rt)` — those genuinely require the concrete
result type — and the new non-concretedispatch arm falls back to
`refine_partial_type(rt)` when no field refinement happens, mirroring
the outer `else` branch.
This complements the eager-body rt refinement
(`abstract_eval_new_opaque_closure`) for an ongoing experiment in JETLS
that runs inference statelessly against top-level chunks, where JETLS
converts closures to opaque closures at re-lowering phase to workaround
the closure name-resolution issues. Common code patterns wrap a closure
in another struct (e.g. `map`'s implementation), and without this change
the wrapping erased the `PartialOpaque` info, so a later call recovered
only the widened `OpaqueClosure{argt, T} where T` and
`abstract_call_unknown`'s OC fallback widened the result to `T<:Any`.
With this change the `PartialStruct` carries the `PartialOpaque` field
through in such cases, so the reading `getfield` hands
`abstract_call_opaque_closure` the precise lattice element it needs.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>