Skip to content

[ty] Fix inference polarity for explicitly annotated initializer receivers - #30278

Draft
sharkdp wants to merge 4 commits into
mainfrom
codex/ty-receiver-polarity
Draft

sharkdp wants to merge 4 commits into
mainfrom
codex/ty-receiver-polarity

Conversation

@sharkdp

@sharkdp sharkdp commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

A constructor with self: Out[bool | None] produces an instance that can return None. Previously, assigning Out() to a variable of type Out[bool] silently narrowed the result. ty now reports invalid-assignment for both the covariant and mixed-variance examples in astral-sh/ty#4977.

When inferring a specialization from actual ≤ formal, a type variable on the actual side receives an upper bound. For a bare constructor, an explicit initializer receiver also determines the direction of inference. With covariant Out, inferring from Out[bool | None] to Out[T] supplies the lower bound bool | None ≤ T. The assignment requires T ≤ bool, so no specialization satisfies both. Explicit specializations still undergo the usual receiver check.

The second commit removes the fallback that recovered constructor type arguments from the specialized first parameter. The inferred return specialization now carries those arguments directly. One existing variadic constructor test changes from WithParamSpec[Any] to WithParamSpec[str | None | Any].

Alternative to #30179 and #30270. Fixes astral-sh/ty#4977.

@sharkdp sharkdp added bug An issue describing something that isn't working, or a PR that fixes a bug ty The ty type checker labels Nov 10, 2026
@sharkdp
sharkdp deployed to automations November 10, 2026 20:01 — with GitHub Actions Active
@astral-automations-bot

astral-automations-bot Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 98.60%. The percentage of expected errors that received a diagnostic held steady at 98.86%. The number of fully passing files held steady at 137/146.

Results for 39b8b44 · Workflow run

@astral-automations-bot

astral-automations-bot Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Memory usage report

Summary

Project Old New Diff Outcome
trio 90.82MB 91.71MB +0.99% (919.91kB) ⏫
flake8 35.98MB 35.98MB - ✅
prefect 446.32MB 446.32MB - ✅
sphinx 176.07MB 176.07MB - ✅

Significant changes

Click to expand detailed breakdown

trio

Name Old New Diff Outcome
for_constraint_pair_inner 605.88kB 1.10MB +85.72% (519.37kB) ⏫
for_constraint_pair_inner::interned_arguments 547.89kB 1001.64kB +82.82% (453.75kB) ⏫
infer_expression_types_impl 5.66MB 5.75MB +1.69% (97.59kB) ⏫
when_constraint_set_assignable_to_owned_impl 681.07kB 746.77kB +9.65% (65.70kB) ⏫
TypePair 1.28MB 1.23MB -4.16% (54.56kB) ⬇️
IntersectionType 285.28kB 240.96kB -15.54% (44.32kB) ⬇️
merged_specialization_inner 118.84kB 86.65kB -27.09% (32.20kB) ⬇️
is_redundant_with_impl 359.32kB 332.79kB -7.38% (26.53kB) ⬇️
when_constraint_set_equivalent_to_impl 60.78kB 36.14kB -40.53% (24.64kB) ⬇️
simplify_intersection_pair_impl 94.46kB 70.23kB -25.65% (24.23kB) ⬇️
InternedSequentConstraint 159.41kB 180.98kB +13.53% (21.57kB) ⏫
inferred_parameter_context 116.40kB 95.77kB -17.73% (20.63kB) ⬇️
UnionType 338.52kB 318.86kB -5.81% (19.66kB) ⬇️
simplify_intersection_pair_impl::interned_arguments 80.75kB 67.88kB -15.94% (12.88kB) ⬇️
Type<'db>::apply_specialization_inner_::interned_arguments 688.59kB 677.50kB -1.61% (11.09kB) ⬇️
... 42 more

Results for 39b8b44 · Workflow run

@astral-automations-bot

astral-automations-bot Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

ecosystem-analyzer results

Lint rule Added Removed Changed
type-assertion-failure 0 18 2
Total 0 18 2

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Raw diff (20 changes)
scipy-stubs (https://github.com/scipy/scipy-stubs)
- tests/stats/test_kde.pyi:26:1 error[type-assertion-failure] Type `gaussian_kde[float64 | (Any & Top[number[Any, complex]]) | (Any & numpy.bool[builtins.bool])]` does not match asserted type `gaussian_kde[float64]`
+ tests/stats/test_kde.pyi:26:1 error[type-assertion-failure] Type `gaussian_kde[float64 | Any]` does not match asserted type `gaussian_kde[float64]`
- tests/stats/test_kde.pyi:35:1 error[type-assertion-failure] Type `gaussian_kde[float64 | (Any & Top[number[Any, complex]]) | (Any & numpy.bool[builtins.bool])]` does not match asserted type `gaussian_kde[float64]`
+ tests/stats/test_kde.pyi:35:1 error[type-assertion-failure] Type `gaussian_kde[float64 | Any]` does not match asserted type `gaussian_kde[float64]`
- tests/stats/test_new_distributions.pyi:70:1 error[type-assertion-failure] Type `Uniform[tuple[()], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[()], float64 | Any]`
- tests/stats/test_new_distributions.pyi:71:1 error[type-assertion-failure] Type `Uniform[tuple[int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:72:1 error[type-assertion-failure] Type `Uniform[tuple[int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:73:1 error[type-assertion-failure] Type `Uniform[tuple[int, int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:74:1 error[type-assertion-failure] Type `Uniform[tuple[int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:75:1 error[type-assertion-failure] Type `Uniform[tuple[int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:76:1 error[type-assertion-failure] Type `Uniform[tuple[int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:77:1 error[type-assertion-failure] Type `Uniform[tuple[int, int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:78:1 error[type-assertion-failure] Type `Uniform[tuple[int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:79:1 error[type-assertion-failure] Type `Uniform[tuple[int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:80:1 error[type-assertion-failure] Type `Uniform[tuple[int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:81:1 error[type-assertion-failure] Type `Uniform[tuple[int, int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:82:1 error[type-assertion-failure] Type `Uniform[tuple[int, int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:83:1 error[type-assertion-failure] Type `Uniform[tuple[int, int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:84:1 error[type-assertion-failure] Type `Uniform[tuple[int, int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:85:1 error[type-assertion-failure] Type `Uniform[tuple[int, int, int], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[tuple[int, int, int], float64 | Any]`
- tests/stats/test_new_distributions.pyi:86:1 error[type-assertion-failure] Type `Uniform[tuple[int, *tuple[Any, ...]], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[AtLeast1D[Any], float64 | Any]`
- tests/stats/test_new_distributions.pyi:87:1 error[type-assertion-failure] Type `Uniform[tuple[int, *tuple[Any, ...]], float64 | (Any & Top[floating[Any]])]` does not match asserted type `Uniform[AtLeast1D[Any], float64 | Any]`

Full report with detailed diff · Timing results


Results for 39b8b44 · Workflow run

@sharkdp
sharkdp deployed to automations November 10, 2026 20:15 — with GitHub Actions Active
@sharkdp
sharkdp deployed to automations November 10, 2026 20:41 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
automations — 39b8b445 Deployed Nov 10, 2026 by sharkdp via security-review / security review #98073
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug An issue describing something that isn't working, or a PR that fixes a bug ty The ty type checker

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explicit self annotation on __init__ is overridden by the assignment's declared type (regression in 0.0.99)

1 participant