Skip to content

Handle type[Any] correctly#14876

Merged
dcreager merged 24 commits intomainfrom
dcreager/type-of-any
Dec 10, 2024
Merged

Handle type[Any] correctly#14876
dcreager merged 24 commits intomainfrom
dcreager/type-of-any

Conversation

@dcreager
Copy link
Copy Markdown
Member

@dcreager dcreager commented Dec 9, 2024

This adds support for type[Any], which represents an unknown type (not an instance of an unknown type), and type, which we are choosing to interpret as type[object].

Closes #14546

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Dec 9, 2024
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 9, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@dcreager dcreager force-pushed the dcreager/type-of-any branch from ba56f82 to 839f7a4 Compare December 9, 2024 16:57
@dcreager dcreager force-pushed the dcreager/type-of-any branch from 839f7a4 to 4ac0df3 Compare December 9, 2024 19:17
@dcreager dcreager marked this pull request as ready for review December 9, 2024 19:45
Copy link
Copy Markdown
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

🚀

Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md Outdated
Comment thread crates/red_knot_python_semantic/src/types/mro.rs Outdated
* 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)
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md Outdated
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md Outdated
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md Outdated
Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
Copy link
Copy Markdown
Member Author

@dcreager dcreager left a 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)

Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md Outdated
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md Outdated
Comment thread crates/red_knot_python_semantic/resources/mdtest/type_of/any.md
Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
Copy link
Copy Markdown
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Nice!

Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
Comment thread crates/red_knot_python_semantic/src/types/mro.rs Outdated
Comment on lines -608 to -611
(Type::Unknown | Type::Any | Type::Todo(_), _) => false,
(_, Type::Unknown | Type::Any | Type::Todo(_)) => false,
Copy link
Copy Markdown
Member Author

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.

Comment thread crates/red_knot_python_semantic/src/types.rs
| Type::SliceLiteral(_)
| Type::KnownInstance(_) => true,
Type::ClassLiteral(_) | Type::SubclassOf(_) | Type::Instance(_) => {
Type::SubclassOf(SubclassOfType { base }) => matches!(base, ClassBase::Class(_)),
Copy link
Copy Markdown
Member Author

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

Copy link
Copy Markdown
Contributor

@carljm carljm left a 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!

Comment thread crates/red_knot_python_semantic/src/types.rs
Comment on lines +821 to +835
(
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
}
Copy link
Copy Markdown
Member

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?

Suggested change
(
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)
}

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

argh, great spot. thanks.

Copy link
Copy Markdown
Member Author

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.

Copy link
Copy Markdown
Member Author

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

Comment on lines +847 to +851
) if object_class.is_known(db, KnownClass::Object)
&& type_class.is_known(db, KnownClass::Type) =>
{
true
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
) 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)
}

Copy link
Copy Markdown
Contributor

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.

Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
Comment thread crates/red_knot_python_semantic/src/types.rs Outdated
@sharkdp
Copy link
Copy Markdown
Contributor

sharkdp commented Dec 10, 2024

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

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

* main:
  [red-knot] split call-outcome enums to their own submodule (#14898)
  [red-knot] Property tests: account non-fully-static types (#14897)
  ruff: add worktree support to build.rs (#14896)
Copy link
Copy Markdown
Contributor

@sharkdp sharkdp left a 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())

Copy link
Copy Markdown
Member Author

@dcreager dcreager left a 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

Copy link
Copy Markdown
Contributor

@carljm carljm left a 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!!

@dcreager dcreager merged commit d4126f6 into main Dec 10, 2024
@dcreager dcreager deleted the dcreager/type-of-any branch December 10, 2024 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[red-knot] support type[Any]

4 participants