-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[red-knot] Do not assume that x != 0 if x inhabits ~Literal[0]
#17370
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
Changes from all commits
5711a33
0d06017
19c2d1d
0b96c20
eee6d34
ee1b632
221eb2e
2d390df
5575f30
f3d4eeb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -23,12 +23,21 @@ def negate(n1: Not[int], n2: Not[Not[int]], n3: Not[Not[Not[int]]]) -> None: | |||||||||||||||||||||
| reveal_type(n2) # revealed: int | ||||||||||||||||||||||
| reveal_type(n3) # revealed: ~int | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def static_truthiness(not_one: Not[Literal[1]]) -> None: | ||||||||||||||||||||||
| static_assert(not_one != 1) | ||||||||||||||||||||||
| static_assert(not (not_one == 1)) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # error: "Special form `knot_extensions.Not` expected exactly one type parameter" | ||||||||||||||||||||||
| n: Not[int, str] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def static_truthiness(not_one: Not[Literal[1]]) -> None: | ||||||||||||||||||||||
| # these are both boolean-literal types, | ||||||||||||||||||||||
| # since all possible runtime objects that are created by the literal syntax `1` | ||||||||||||||||||||||
| # are members of the type `Literal[1]` | ||||||||||||||||||||||
| reveal_type(not_one is not 1) # revealed: bool | ||||||||||||||||||||||
| reveal_type(not_one is 1) # revealed: bool | ||||||||||||||||||||||
|
Comment on lines
+30
to
+34
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this comment doesn't seem to reflect the code immediately below it anymore. The comment says "these are both boolean-literal types", but the assertion reveals
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # But these are both `bool`, rather than `Literal[True]` or `Literal[False]` | ||||||||||||||||||||||
| # as there are many runtime objects that inhabit the type `~Literal[1]` | ||||||||||||||||||||||
| # but still compare equal to `1`. Two examples are `1.0` and `True`. | ||||||||||||||||||||||
| reveal_type(not_one != 1) # revealed: bool | ||||||||||||||||||||||
| reveal_type(not_one == 1) # revealed: bool | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Intersection | ||||||||||||||||||||||
|
|
@@ -170,13 +179,11 @@ Static assertions can be used to enforce narrowing constraints: | |||||||||||||||||||||
| ```py | ||||||||||||||||||||||
| from knot_extensions import static_assert | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def f(x: int) -> None: | ||||||||||||||||||||||
| if x != 0: | ||||||||||||||||||||||
| static_assert(x != 0) | ||||||||||||||||||||||
| def f(x: int | None) -> None: | ||||||||||||||||||||||
| if x is not None: | ||||||||||||||||||||||
| static_assert(x is not None) | ||||||||||||||||||||||
| else: | ||||||||||||||||||||||
| # `int` can be subclassed, so we cannot assert that `x == 0` here: | ||||||||||||||||||||||
| # error: "Static assertion error: argument of type `bool` has an ambiguous static truthiness" | ||||||||||||||||||||||
| static_assert(x == 0) | ||||||||||||||||||||||
| static_assert(x is None) | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Truthy expressions | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5264,12 +5264,6 @@ impl<'db> TypeInferenceBuilder<'db> { | |
| }; | ||
|
|
||
| match (op, result) { | ||
| (ast::CmpOp::Eq, Some(Type::BooleanLiteral(true))) => { | ||
| return Ok(Type::BooleanLiteral(false)); | ||
| } | ||
| (ast::CmpOp::NotEq, Some(Type::BooleanLiteral(false))) => { | ||
| return Ok(Type::BooleanLiteral(true)); | ||
| } | ||
|
Comment on lines
-5267
to
-5272
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think this removal is correct. I guess to address the TODO comments added above (where we intersect with But I think maybe
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No, I don't think so. |
||
| (ast::CmpOp::Is, Some(Type::BooleanLiteral(true))) => { | ||
| return Ok(Type::BooleanLiteral(false)); | ||
| } | ||
|
|
@@ -5319,6 +5313,9 @@ impl<'db> TypeInferenceBuilder<'db> { | |
| // we would get a result type `Literal[True]` which is too narrow. | ||
| // | ||
| let mut builder = IntersectionBuilder::new(self.db()); | ||
|
|
||
| builder = builder.add_positive(KnownClass::Bool.to_instance(self.db())); | ||
|
|
||
| for pos in intersection.positive(self.db()) { | ||
| let result = match intersection_on { | ||
| IntersectionOn::Left => { | ||
|
|
@@ -5393,6 +5390,8 @@ impl<'db> TypeInferenceBuilder<'db> { | |
| ast::CmpOp::LtE => Ok(Type::BooleanLiteral(n <= m)), | ||
| ast::CmpOp::Gt => Ok(Type::BooleanLiteral(n > m)), | ||
| ast::CmpOp::GtE => Ok(Type::BooleanLiteral(n >= m)), | ||
| // We cannot say that two equal int Literals will return True from an `is` or `is not` comparison. | ||
| // Even if they are the same value, they may not be the same object. | ||
| ast::CmpOp::Is => { | ||
| if n == m { | ||
| Ok(KnownClass::Bool.to_instance(self.db())) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.