Skip to content

[ty] Only unsoundly upcast type[] types to their constructor Callable type during assignability checks, not during redundancy/subtyping checks#23834

Merged
AlexWaygood merged 2 commits intomainfrom
unsound-upcast
Mar 9, 2026
Merged

[ty] Only unsoundly upcast type[] types to their constructor Callable type during assignability checks, not during redundancy/subtyping checks#23834
AlexWaygood merged 2 commits intomainfrom
unsound-upcast

Conversation

@AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood commented Mar 9, 2026

Fixes astral-sh/ty#2981

Test plan

  • Added mdtests
  • Ran QUICKCHECK_TESTS=1000000 cargo test --profile=profiling -p ty_python_semantic -- --ignored types::property_tests::stable locally, observed no failures

@AlexWaygood AlexWaygood added bug Something isn't working ty Multi-file analysis & type inference labels Mar 9, 2026
@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 9, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 87.10%. The percentage of expected errors that received a diagnostic held steady at 77.81%. The number of fully passing files held steady at 63/131.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 9, 2026

Memory usage report

Summary

Project Old New Diff Outcome
flake8 47.90MB 47.90MB -
trio 117.86MB 117.86MB -
prefect 693.71MB 693.70MB -0.00% (10.67kB) ⬇️
sphinx 265.43MB 265.36MB -0.03% (72.60kB) ⬇️

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
Type<'db>::try_call_dunder_get_ 10.26MB 10.26MB -0.02% (2.32kB) ⬇️
Type<'db>::member_lookup_with_policy_ 15.17MB 15.16MB -0.01% (1.85kB) ⬇️
Type<'db>::class_member_with_policy_ 16.95MB 16.95MB -0.01% (1.24kB) ⬇️
CallableType 1.87MB 1.87MB +0.04% (864.00B) ⬇️
lookup_dunder_new_inner 274.41kB 273.70kB -0.26% (728.00B) ⬇️
is_redundant_with_impl::interned_arguments 5.30MB 5.30MB -0.01% (704.00B) ⬇️
Type<'db>::try_call_dunder_get_::interned_arguments 2.95MB 2.95MB -0.02% (624.00B) ⬇️
Type<'db>::member_lookup_with_policy_::interned_arguments 5.44MB 5.44MB -0.01% (624.00B) ⬇️
Type<'db>::class_member_with_policy_::interned_arguments 9.15MB 9.15MB -0.01% (624.00B) ⬇️
is_redundant_with_impl 5.50MB 5.50MB -0.01% (612.00B) ⬇️
UnionType<'db>::from_two_elements_ 5.10MB 5.10MB -0.01% (472.00B) ⬇️
ClassType<'db>::into_callable_ 10.38kB 9.96kB -4.06% (432.00B) ⬇️
infer_definition_types 87.36MB 87.36MB -0.00% (348.00B) ⬇️
Type<'db>::apply_specialization_ 3.54MB 3.54MB +0.01% (340.00B) ⬇️
IntersectionType 2.28MB 2.28MB -0.01% (336.00B) ⬇️
... 7 more

sphinx

Name Old New Diff Outcome
infer_definition_types 24.06MB 24.05MB -0.03% (7.00kB) ⬇️
infer_deferred_types 5.60MB 5.60MB -0.10% (5.82kB) ⬇️
FunctionType<'db>::signature_ 2.28MB 2.27MB -0.25% (5.73kB) ⬇️
FunctionType 3.12MB 3.12MB -0.18% (5.61kB) ⬇️
Type<'db>::class_member_with_policy_ 7.55MB 7.55MB -0.06% (4.98kB) ⬇️
Type<'db>::member_lookup_with_policy_ 6.10MB 6.10MB -0.05% (3.04kB) ⬇️
Type<'db>::class_member_with_policy_::interned_arguments 3.99MB 3.99MB -0.07% (2.95kB) ⬇️
StaticClassLiteral<'db>::implicit_attribute_inner_ 2.38MB 2.37MB -0.12% (2.84kB) ⬇️
Type<'db>::try_call_dunder_get_ 4.94MB 4.94MB -0.05% (2.75kB) ⬇️
lookup_dunder_new_inner 121.68kB 119.16kB -2.07% (2.52kB) ⬇️
StaticClassLiteral<'db>::implicit_attribute_inner_::interned_arguments 1.91MB 1.90MB -0.12% (2.44kB) ⬇️
GenericContext 139.89kB 137.48kB -1.72% (2.41kB) ⬇️
StaticClassLiteral<'db>::try_mro_ 2.11MB 2.11MB -0.10% (2.13kB) ⬇️
FunctionType<'db>::last_definition_signature_ 215.75kB 213.65kB -0.97% (2.10kB) ⬇️
ClassType<'db>::into_callable_ 6.23kB 4.46kB -28.42% (1.77kB) ⬇️
... 31 more

…ble` type during assignability checks, not during redundancy/subtyping checks
@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 9, 2026

mypy_primer results

Changes were detected when running on open source projects
scikit-build-core (https://github.com/scikit-build/scikit-build-core)
- src/scikit_build_core/build/wheel.py:99:20: error[no-matching-overload] No overload of bound method `__init__` matches arguments
- Found 62 diagnostics
+ Found 61 diagnostics

sympy (https://github.com/sympy/sympy)
- sympy/polys/domains/algebraicfield.py:335:21: error[unresolved-attribute] Attribute `zero` is not defined on `(...) -> Unknown` in union `type[ANP[MPQ]] | ((...) -> Unknown)`
+ sympy/polys/domains/algebraicfield.py:335:21: error[unresolved-attribute] Attribute `zero` is not defined on `(...) -> Unknown` in union `type[ANP[MPQ] | Unknown] | ((...) -> Unknown)`
- sympy/polys/domains/algebraicfield.py:336:20: error[unresolved-attribute] Attribute `one` is not defined on `(...) -> Unknown` in union `type[ANP[MPQ]] | ((...) -> Unknown)`
+ sympy/polys/domains/algebraicfield.py:336:20: error[unresolved-attribute] Attribute `one` is not defined on `(...) -> Unknown` in union `type[ANP[MPQ] | Unknown] | ((...) -> Unknown)`
- sympy/polys/domains/old_fractionfield.py:29:21: error[unresolved-attribute] Attribute `zero` is not defined on `(...) -> Unknown` in union `Unknown | <class 'DMF'> | ((...) -> Unknown)`
+ sympy/polys/domains/old_fractionfield.py:29:21: error[unresolved-attribute] Attribute `zero` is not defined on `(...) -> Unknown` in union `Unknown | <class 'DMF'> | type[Unknown] | ((...) -> Unknown)`
- sympy/polys/domains/old_fractionfield.py:30:20: error[unresolved-attribute] Attribute `one` is not defined on `(...) -> Unknown` in union `Unknown | <class 'DMF'> | ((...) -> Unknown)`
+ sympy/polys/domains/old_fractionfield.py:30:20: error[unresolved-attribute] Attribute `one` is not defined on `(...) -> Unknown` in union `Unknown | <class 'DMF'> | type[Unknown] | ((...) -> Unknown)`
- sympy/polys/domains/old_polynomialring.py:41:21: error[unresolved-attribute] Object of type `(...) -> Unknown` has no attribute `zero`
- sympy/polys/domains/old_polynomialring.py:42:20: error[unresolved-attribute] Object of type `(...) -> Unknown` has no attribute `one`
+ sympy/polys/domains/old_polynomialring.py:41:21: error[unresolved-attribute] Attribute `zero` is not defined on `(...) -> Unknown` in union `Unknown | ((...) -> Unknown) | type[Unknown]`
+ sympy/polys/domains/old_polynomialring.py:42:20: error[unresolved-attribute] Attribute `one` is not defined on `(...) -> Unknown` in union `Unknown | ((...) -> Unknown) | type[Unknown]`
+ sympy/polys/domains/polynomialring.py:223:16: error[missing-argument] No argument provided for required parameter `init` of bound method `__init__`
- sympy/testing/tests/test_module_imports.py:27:43: error[invalid-argument-type] Argument to function `getfile` is incorrect: Expected `ModuleType | ((...) -> Any) | FrameType | CodeType | TracebackType`, found `FrameType | None`
+ sympy/testing/tests/test_module_imports.py:27:43: error[invalid-argument-type] Argument to function `getfile` is incorrect: Expected `ModuleType | type[Any] | ((...) -> Any) | ... omitted 3 union elements`, found `FrameType | None`
- Found 16493 diagnostics
+ Found 16494 diagnostics

@AlexWaygood AlexWaygood marked this pull request as ready for review March 9, 2026 12:39
@AlexWaygood AlexWaygood marked this pull request as draft March 9, 2026 12:45
@AlexWaygood AlexWaygood marked this pull request as ready for review March 9, 2026 12:48
Comment on lines -2010 to -2014
static_assert(is_subtype_of(type[A], Callable[[int], A]))
static_assert(not is_subtype_of(type[A], Callable[[str], A]))

static_assert(is_subtype_of(type[B], Callable[[str], B]))
static_assert(not is_subtype_of(type[B], Callable[[int], B]))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the assignability relation still needs to hold here, but it still has good coverage in type_properties/is_assignable_to.md. I considered putting the new tests in this file, since that's where the old tests being removed were, but my new tests intermingle is_subtype_of, is_assignable_to and union-simplification assertions, so I don't think they're a great fit for this file.

Comment on lines +1190 to +1194
let upcasted = if let Type::SubclassOf(inner) = self {
match relation {
TypeRelation::Subtyping
| TypeRelation::SubtypingAssuming
| TypeRelation::Redundancy { .. } => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional nit: The two fallback cases below are the same, so you could merge this into

Suggested change
let upcasted = if let Type::SubclassOf(inner) = self {
match relation {
TypeRelation::Subtyping
| TypeRelation::SubtypingAssuming
| TypeRelation::Redundancy { .. } => {
let upcasted = if let Type::SubclassOf(inner) = self
&& matches!(relation,
TypeRelation::Subtyping
| TypeRelation::SubtypingAssuming
| TypeRelation::Redundancy { .. }) {

(cargo fmt will want to rearrange that whitespace a bit)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, but I wanted an exhaustive match here so we don't forget to update it if/when we add more type relations in the future. I can refactor to avoid the repeated fallback logic while keeping the exhaustive match, though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, actually getting rid of the duplication is a bit annoying and the repetition is pretty minimal, so I'm inclined to leave it as is? Happy to fix it up in a followup if you disagree, though :-)

@AlexWaygood AlexWaygood merged commit 7e31572 into main Mar 9, 2026
51 checks passed
@AlexWaygood AlexWaygood deleted the unsound-upcast branch March 9, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[failing property test] subclass-of types cannot be considered subtypes of the Callable type of their constructors

2 participants