Skip to content

output-position widening for invariant builtin containers - #14

Draft
KotlinIsland wants to merge 1 commit into
mainfrom
typeshed-expected-type
Draft

output-position widening for invariant builtin containers#14
KotlinIsland wants to merge 1 commit into
mainfrom
typeshed-expected-type

Conversation

@KotlinIsland

@KotlinIsland KotlinIsland commented Jul 13, 2026

Copy link
Copy Markdown
Owner

fresh-copy methods of invariant containers (list/set/dict) return a brand new object the caller owns, so widening its element type at the call site is sound — but list[int] isn't a list[int | None], so today copy/+/*/ set+dict algebra can't be assigned to a wider specialization.

encode the fix as a Never-defaulted output type parameter unioned into each invariant return position: def copy[Widen = Never](self) -> list[Element | Widen]. with an expected type it solves to the widening; without one it defaults to Never and collapses back, so inference is unchanged.

  • new output-widening typeshed patch (post-pep695 pass, keyed on in out variance so covariant frozenset/tuple are left alone) + regenerated builtins.byi

@KotlinIsland
KotlinIsland force-pushed the typeshed-expected-type branch 3 times, most recently from b9b65a8 to 15868d0 Compare July 14, 2026 06:10
fresh-copy methods of invariant containers (list/set/dict) return a brand new
object the caller owns, so widening its element type at the call site is sound —
but `list[int]` isn't a `list[int | None]`, so today `a.copy()` can't be
assigned to a wider specialization.

encode the fix as a `Never`-defaulted output type parameter unioned into each
invariant return position: `def copy[Widen = Never](self) -> list[Element |
Widen]`. with an expected type it solves to the widening; without one it
defaults to `Never` and collapses back, so inference is unchanged.

new `output-widening` typeshed patch (post-pep695 pass, keyed on `in out`
variance so covariant frozenset/tuple are left alone) + regenerated
builtins.byi. it widens only methods reached by an ordinary call — `copy`,
`set.union`/`difference`/`intersection`/`symmetric_difference` — where the
caller's expected type already flows into inference. operator dunders
(`__add__`, `__getitem__`, ...) are deliberately not widened: making their
returns widen would require bidirectional inference on every binary op and
subscript, which is far too expensive on real code (it times out xarray).
@KotlinIsland
KotlinIsland force-pushed the typeshed-expected-type branch from 15868d0 to 76d8da1 Compare July 14, 2026 07:22
@KotlinIsland
KotlinIsland marked this pull request as draft July 14, 2026 07:42
@KotlinIsland
KotlinIsland force-pushed the main branch 8 times, most recently from 0a30a5b to a2564b5 Compare July 22, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant