[red-knot] Distribute intersections on negation#13962
Conversation
|
Sorry, in my rush to send this in with the test I added passing I had intro'd a regression on another test. Please ignore this PR until I get this green (unless you know what's actually wrong here of course) |
|
@rtpg no worries. I'll convert the PR to a draft and you can mark it as ready when it's ready :) |
|
|
Alright this one is ready for review, all green etc. |
| IntersectionBuilder::new(&db) | ||
| .add_positive(it) | ||
| .add_negative(st) | ||
| .build(), |
There was a problem hiding this comment.
a part of me wants something like Type::intersection(elts: Vec<Type<'db>>) for testing but I got pretty used to writing out the intersection builder at this point 😆
There was a problem hiding this comment.
Ha, you aren't the first to say this after working on IntersectionBuilder, cc @sharkdp 😆 It's not a problem at all to add test helpers to make tests less verbose, someone just needs to feel the pain sufficiently to actually do it!
| | Self::Set | ||
| | Self::Dict => module.name() == "builtins", | ||
| Self::GenericAlias | Self::ModuleType | Self::FunctionType => module.name() == "types", | ||
| Self::Sized | Self::Hashable => { |
There was a problem hiding this comment.
basically all of the "interesting" protocols in typing are deprecated aliases at this point to members of collections.abc.
carljm
left a comment
There was a problem hiding this comment.
Looks great, thank you! Just a few suggested changes, main one being to remove Sized and Hashable from KnownClass
|
|
||
| /// Lookup the type of `symbol` in the `typing` module namespace. | ||
| /// | ||
| /// Returns `Unbound` if the `types` module isn't available for some reason. |
There was a problem hiding this comment.
| /// Returns `Unbound` if the `types` module isn't available for some reason. | |
| /// Returns `Unbound` if the `typing` module isn't available for some reason. |
| // Typing | ||
| Sized, | ||
| Hashable, |
There was a problem hiding this comment.
Sorry to catch this only after you'd done the work, but I don't see any reason for these to be known classes? We should only use KnownClass when either a) the type has special-cased behavior that is not reflected in its typeshed definition, or b) we will commonly need to conjure this type "from thin air" within the type inference code itself, usually because the type is builtin and sometimes created "from thin air" by the runtime (e.g. bool) or because it is closely related to other special-cased types (e.g. KnownClass::Int and Type::IntLiteral, etc.)
I don't think Sized or Hashable meet either of those criteria.
If you want to use these types (or any other typeshed type) in tests, you don't need it to be a KnownClass; that's just a minor convenience. You can just use typing_symbol_ty (added in this PR, and something we'll definitely want) directly in the test.
There was a problem hiding this comment.
I hadn't quite internalized that I only actually really need typing_symbol_ty, thanks for pointing that out. Will remove theKnownClass entries
| .build(); | ||
| // intersection = int & ~(Any & ~Literal[1]) | ||
| // -> int & (~Any | Literal[1]) | ||
| // (~Any is treated as Any for adding purposes) |
There was a problem hiding this comment.
| // (~Any is treated as Any for adding purposes) | |
| // (~Any is equivalent to Any) |
| // (~Any is treated as Any for adding purposes) | ||
| // -> (int & Any) | (int & Literal[1]) | ||
| // -> (int & Any) | Literal[1] | ||
| let intersection = IntersectionBuilder::new(&db) |
There was a problem hiding this comment.
Confusing to name this intersection when it is actually a union :)
| let intersection = IntersectionBuilder::new(&db) | |
| let union = IntersectionBuilder::new(&db) |
There was a problem hiding this comment.
FWIW another option here is to just assert on the displayed form of the type, build_negative_union_de_morgan does that
There was a problem hiding this comment.
At first wasn't a fan because of the display issues, but given it's DNF (I belive) it's unambiguous... will do that
| .add_positive(ta) | ||
| .build(); | ||
|
|
||
| assert_eq!(intersection.elements(&db), &[i_and_a, t1,]); |
There was a problem hiding this comment.
| assert_eq!(intersection.elements(&db), &[i_and_a, t1,]); | |
| assert_eq!(union.elements(&db), &[i_and_a, t1,]); |
| let it = KnownClass::Int.to_instance(&db); | ||
| let st = KnownClass::Sized.to_instance(&db); | ||
| let ht = KnownClass::Hashable.to_instance(&db); | ||
| // sh_t: Sized & ~Hashable |
There was a problem hiding this comment.
| // sh_t: Sized & ~Hashable | |
| // s_not_h_t: Sized & ~Hashable |
also clean up some other details
Summary
This does two things:
A & ~(B & C)to(A & ~B) | (A & ~C)) (fixing [red-knot] fix adding intersection to negative side of an intersection #13931)typingtoKnownClassThe second point was to enable writing a test for the first case using protocols defined in
typing(here,SizedandHashable). Was a bit worried about another test that could be susceptible to simplifications, and it was straightforward to pull inTest Plan
cargo test