subtype: harden deferred-narrowing covariant intersection
Rebase of the deferred-narrowing covariant intersection (#61787) onto
current master, incorporating the `Intersect` meet nodes of #61940 and the
covariant-occurrence scope tracking of the Type{} refactor, plus fixes for
the correctness issues found in review:
- Never mutate typevar objects in place. The witness unification now
records on the binding when one exists and otherwise substitutes the
variable away everywhere (bounds, result, and the innervars arrays via
the new `swap_innervar_entries`), re-creating the canonical witness when
its bound needs tightening. The previous in-place writes lacked write
barriers (matching the GC-corruption crash in this PR's earlier CI run)
and corrupted types shared with earlier intersection results.
- Prevent and detect bound-reference cycles. Witness lower-bound joins and
the constraintkind-1 fast path refuse to record self-referential bounds,
and `finish_unionall`'s innervar re-sort now skips moves that would
close a reference cycle (checked transitively through the flattened
env), restoring the termination bound. Two intersections that previously
hung forever in the re-sort loop now terminate.
- `diagonal_bound_meet` uses `simple_meet`'s over-approximation mode
instead of the exact mode, which now allocates internal `Intersect`
nodes that must not escape into result typevars (this broke the Base
bootstrap), and unwraps trivial `S where S<:U` bounds so the witness
meet can see through the wrapping `finish_unionall` produces. The meet
of Vararg tuple bounds falls back to the whole-tuple over-approximation
instead of under-approximating to `Union{}`, which wrongly emptied
nonempty intersections such as
`Tuple{T,T} where T` ∩ `Tuple{Tuple{Vararg{Int}}, Tuple{Int}}`.
- A covariant var∩var witness is recorded on both variables' lower bounds
(gated on the second variable being in diagonal-collapse mode), so a
diagonal pop sees witnesses from every occurrence regardless of which
side the dispatch picked.
- The `iub == vb->var` case in `finish_unionall` falls back to the old
bound-widening behavior when the single-covariant-occurrence guard does
not apply, instead of routing through the UnionAll-wrap machinery, which
degraded unrelated bounds and split variables.
- Re-intersection rounds that switch to constraintkind 1 start from the
variable's original bounds and an empty innervars list so residue from
the discarded round cannot masquerade as diagonal witnesses.
- `diagonal_leaf_witness` resolves through the env binding rather than the
stale typevar field, and `intersect_var` no longer resolves typevar
upper-bound chains before the meet, which dropped `b<:S` constraints
(e.g. `Tuple{T4,S3,S3} where {S3,T4<:S3}` ∩ `Tuple{Int,String,String}`
returned a non-subtype result instead of `Union{}`).
Five tightness assertions that the pre-rebase branch enabled are restored
to `@test_broken`: the covariant-occurrence scoping changes on master mean
the machinery no longer tightens `T<:Tuple{S}`-style bounds to equality in
those cases; the results are sound but not subtypes of one input.
Fixes #61773.
This commit was written with the assistance of generative AI (Claude).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>