-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Handle type[Any] correctly #14876
Handle type[Any] correctly #14876
Conversation
|
ba56f82
to
839f7a4
Compare
839f7a4
to
4ac0df3
Compare
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.
🚀
crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Outdated
Show resolved
Hide resolved
* main: [`airflow`] Add fix to remove deprecated keyword arguments (`AIR302`) (#14887) Improve mdtests style (#14884) Reference `suppress-dummy-regex-options` in documentation of rules supporting it (#14888) [`flake8-bugbear`] `itertools.batched()` without explicit `strict` (`B911`) (#14408) [`ruff`] Mark autofix for `RUF052` as always unsafe (#14824) [red-knot] Improve type inference for except handlers (#14838) More typos found by codespell (#14880) [red-knot] move standalone expression_ty to TypeInferenceBuilder::file_expression_ty (#14879) [`ruff`] Do not simplify `round()` calls (`RUF046`) (#14832) Stop referring to early ruff versions (#14862) Fix a typo in `class.rs` (#14877) [`flake8-pyi`] Also remove `self` and `cls`'s annotation (`PYI034`) (#14801) [`pyupgrade`] Remove unreachable code in `UP015` implementation (#14871) [`flake8-bugbear`] Skip `B028` if `warnings.warn` is called with `*args` or `**kwargs` (#14870)
crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
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.
(Initial responses, still need to make the type ⇒ type[object]
change and add the equivalent_to
tests)
crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
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.
Nice!
(Type::Unknown | Type::Any | Type::Todo(_), _) => false, | ||
(_, Type::Unknown | Type::Any | Type::Todo(_)) => false, |
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.
I removed these since they (and the new Subclass(Any)
form) are handled by the is_fully_static
checks above.
@@ -1027,7 +1084,8 @@ impl<'db> Type<'db> { | |||
| Type::BytesLiteral(_) | |||
| Type::SliceLiteral(_) | |||
| Type::KnownInstance(_) => true, | |||
Type::ClassLiteral(_) | Type::SubclassOf(_) | Type::Instance(_) => { | |||
Type::SubclassOf(SubclassOfType { base }) => matches!(base, ClassBase::Class(_)), |
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.
SubclassOf(Any)
is not fully static
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.
Looking good! In addition to the additional unit tests I mentioned inline, this PR seems a good candidate to run the quickcheck property-based tests for core type algebra invariants. These don't run in CI because they are slow, but it would be good to see whether they catch any issues here. See https://github.com/astral-sh/ruff/blob/main/crates/red_knot_python_semantic/src/types/property_tests.rs
EDIT: just realized that in order to get value from that on this PR, we should also add SubclassOf types to the arbitrary_core_type
function there!
( | ||
Type::Instance(InstanceType { class: self_class }), | ||
Type::Instance(InstanceType { | ||
class: target_class, | ||
}), | ||
) if { | ||
let self_known = self_class.known(db); | ||
matches!( | ||
self_known, | ||
Some(KnownClass::NoneType | KnownClass::NoDefaultType) | ||
) && self_known == target_class.known(db) | ||
} => | ||
{ | ||
true | ||
} |
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.
this gets a little hard to read 😄 how about writing it like this?
( | |
Type::Instance(InstanceType { class: self_class }), | |
Type::Instance(InstanceType { | |
class: target_class, | |
}), | |
) if { | |
let self_known = self_class.known(db); | |
matches!( | |
self_known, | |
Some(KnownClass::NoneType | KnownClass::NoDefaultType) | |
) && self_known == target_class.known(db) | |
} => | |
{ | |
true | |
} | |
(Type::Instance(self_instance), Type::Instance(target_instance)) => { | |
let Some(self_known_class) = self_instance.class.known(db) else { | |
return false; | |
}; | |
if !matches!( | |
self_known_class, | |
KnownClass::NoneType | KnownClass::NoDefaultType | |
) { | |
return false; | |
} | |
Some(self_known_class) == target_instance.class.known(db) | |
} |
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.
We have to be careful here; these return false
in the suggested change are not correct. They would have to be return self == other
, and I'm not sure we want to duplicate that condition in various places.
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.
argh, great spot. thanks.
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.
I had also considered handling each of the special cases with a separate if
statement + early return. Then it would possibly be clearer that self == other
is the fall-back / general case. But we had other large match
blocks elsewhere that made me think that might be preferred house style. I will take a stab at the early return pattern and see if it's more readable.
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.
I will take a stab at the early return pattern and see if it's more readable.
Now using early return. lmkwyt
) if object_class.is_known(db, KnownClass::Object) | ||
&& type_class.is_known(db, KnownClass::Type) => | ||
{ | ||
true | ||
} |
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.
) if object_class.is_known(db, KnownClass::Object) | |
&& type_class.is_known(db, KnownClass::Type) => | |
{ | |
true | |
} | |
) => { | |
object_class.is_known(db, KnownClass::Object) | |
&& type_class.is_known(db, KnownClass::Type) | |
} |
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.
This one should be safe because a Type::Instance
can't be ==
to a Type::SubclassOf
.
I already did this. I didn't mention it here because they are currently broken (surprise!). I'll put up a small PR with a fix. Edit: #14897 |
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.
Thank you for all the updates and the new tests!
FWIW, I did successfully run the property tests on your branch after cherry-picking my fix in #14897 and applying the following patch, where the 4=>3 change is a weird way to suppress the creation of intersection types to prevent running into #14899:
diff --git a/crates/red_knot_python_semantic/src/types/property_tests.rs b/crates/red_knot_python_semantic/src/types/property_tests.rs
index 15f4d5648..e6b223034 100644
--- a/crates/red_knot_python_semantic/src/types/property_tests.rs
+++ b/crates/red_knot_python_semantic/src/types/property_tests.rs
@@ -64,6 +64,10 @@ fn arbitrary_core_type(g: &mut Gen) -> Ty {
Ty::BuiltinClassLiteral("int"),
Ty::BuiltinClassLiteral("bool"),
Ty::BuiltinClassLiteral("object"),
+ Ty::SubclassOfAny,
+ Ty::SubclassOfBuiltinClass("object"),
+ Ty::SubclassOfBuiltinClass("int"),
+ Ty::SubclassOfBuiltinClass("bool"),
])
.unwrap()
.clone()
@@ -78,7 +82,7 @@ fn arbitrary_type(g: &mut Gen, size: u32) -> Ty {
if size == 0 {
arbitrary_core_type(g)
} else {
- match u32::arbitrary(g) % 4 {
+ match u32::arbitrary(g) % 3 {
0 => arbitrary_core_type(g),
1 => Ty::Union(
(0..*g.choose(&[2, 3]).unwrap())
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.
I did successfully run the property tests on your branch
Thanks @sharkdp, was just looking at the property tests myself and running into the assignable
bug
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.
Looks good to me!!
This adds support for
type[Any]
, which represents an unknown type (not an instance of an unknown type), andtype
, which we are choosing to interpret astype[object]
.Closes #14546