-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[ty] Improve handling of disjointness for NominalInstanceType
s and SubclassOfType
s
#18864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
NominalInstanceType
s and SubclassOfType
s
|
3802c9e
to
28a7054
Compare
Primer analysis:
In general, I think this is all positive. Though the from c_extension import SomethingUnresolved
class Foo: ...
def f(x: Foo | SomethingUnresolved):
if isinstance(x, SomethingUnresolved):
reveal_type(x) # currently `Foo | Unknown`; `(Foo & Unknown) | Unknown` would probably respect the gradual guarantee better? |
Yes, I think we should -- it's clearly a violation of the gradual guarantee not to do so. |
See #18900 |
220fcbf
to
e703f0d
Compare
I never heard the term solid base before and searching for it on google also doesn't give any good results. Is there another term we could use or can we explain the term or link to some reference documentation (or hint that it is explained in the rule's documentation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
crates/ty_python_semantic/resources/mdtest/narrow/conditionals/elif_else.md
Outdated
Show resolved
Hide resolved
crates/ty_python_semantic/resources/mdtest/narrow/type_guards.md
Outdated
Show resolved
Hide resolved
AFAIK the term is only used internally in the CPython code base, not in user-facing docs or error messages. I think we should follow a similar pattern. The term (and concept) are useful in implementing the semantics, and the alternatives are more verbose, so we should go ahead and use the term internally (with a clear definition in a doc comment, which I elaborate on in a review comment above), but avoid the term in user-facing documentation or diagnostics (which I also addressed in review comments above.) |
Yeah, this stuff is really badly documented unfortunately. It's also an important part of Python's semantics (builtin types are common!), though, so I do think it's important that we emulate the semantics... I'll try to improve the docs along the lines of @carljm's suggestions. I think the best explanation I've found of the semantics might be this stack overflow answer: https://stackoverflow.com/a/48136198/13990016 |
e703f0d
to
e54b1a4
Compare
CodSpeed WallTime Performance ReportMerging #18864 will degrade performances by 4.79%Comparing Summary
Benchmarks breakdown
|
Co-authored-by: Carl Meyer <[email protected]>
…into alex/disjointness
4fdd7b6
to
242ccab
Compare
5e902dd
to
2a8e985
Compare
3846c71
to
ad3d451
Compare
Co-authored-by: Carl Meyer <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
There's still an open comment about a rephrasing away from "solid base" in a code comment, but I don't think this is necessary, it's OK if we refer to solid bases in code.
...nstance_layout_conf…_-_Tests_for_ty's_`inst…_-_Built-ins_with_impli…_(f5857d64ce69ca1d).snap
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new diagnostic docs look great!
Great to hear -- thanks for the excellent review!! |
Summary
This PR makes a number of improvements to our disjointness handling for instance types and
type[]
types:The calculation of whether two nominal instance types are disjoint is moved from the somewhat-awkardly-named
NominalInstanceType::is_disjoint_from_nominal_instance_of_class
function to a newClassLiteral::could_coexist_in_mro_with()
method. This reflects the fact that the relation between types (the disjointness relation) is derived from a relation between runtime class objects (MRO compatibility). It also allows us to reuse the method for determining disjointness between twoSubclassOfType
s. Prior to now, that wouldn't have been helpful, but with the other improvements in this PR (see below), it is helpful.Following [red-knot] Report classes inheriting from bases with incompatible
__slots__
#15129, we have understood that classes like this raiseTypeError
at runtime, and have emitted errors if we see such class definitions in first-party code:However, we haven't understood that this also makes the types
A
andB
disjoint: the intersectionA & B
simplifies toNever
, sinceA
andB
can never both exist in any single MRO! This PR generalizes and incorporates our understanding of__slots__
so that we now recognize any class with a non-empty__slots__
definition as being a "solid base". If there are two solid basesA
andB
, andA
is not a subclass ofB
andB
is not a subclass ofA
,A
andB
will be disjoint. Similarly, ifA
has a subclassC
andB
has a subclassD
,C
andD
will be disjoint because they have disjoint solid bases in their respective MROs.Two bugs are also fixed in our handling of
__slots__
. One long-standing one is that we used to think that this class definition raisedTypeError
; this PR teaches ty that it doesn't:Another one was only introduced recently: see [ty] Improve handling of disjointness for
NominalInstanceType
s andSubclassOfType
s #18864 (comment)In addition to understanding that classes with non-empty
__slots__
definitions are solid bases, this PR also adds an understanding to ty that certain builtin classes are also solid bases due to the special way that they are implemented in C. This is done via aKnownClass::is_solid_base
method.Lastly, diagnostics for classes that have conflicting solid bases are improved:
TypeError
Test Plan
QUICKCHECK_TESTS=100000 cargo test --release -p ty_python_semantic -- --ignored types::property_tests::stable