Skip to content

Commit 49557a9

Browse files
authored
[red-knot] Simplify object out of intersections (#15511)
1 parent c9b99e4 commit 49557a9

File tree

4 files changed

+26
-7
lines changed

4 files changed

+26
-7
lines changed

crates/red_knot_python_semantic/resources/mdtest/comparison/intersections.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ def _(o: object):
9292
n = None
9393

9494
if o is not None:
95-
reveal_type(o) # revealed: object & ~None
96-
95+
reveal_type(o) # revealed: ~None
9796
reveal_type(o is n) # revealed: Literal[False]
9897
reveal_type(o is not n) # revealed: Literal[True]
9998
```

crates/red_knot_python_semantic/resources/mdtest/intersection_types.md

+15
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,21 @@ def _(
283283
reveal_type(not_object) # revealed: Never
284284
```
285285

286+
### `object & ~T` is equivalent to `~T`
287+
288+
A second consequence of the fact that `object` is the top type is that `object` is always redundant
289+
in intersections, and can be eagerly simplified out. `object & P` is equivalent to `P`;
290+
`object & ~P` is equivalent to `~P` for any type `P`.
291+
292+
```py
293+
from knot_extensions import Intersection, Not, is_equivalent_to, static_assert
294+
295+
class P: ...
296+
297+
static_assert(is_equivalent_to(Intersection[object, P], P))
298+
static_assert(is_equivalent_to(Intersection[object, Not[P]], Not[P]))
299+
```
300+
286301
### Intersection of a type and its negation
287302

288303
Continuing with more [complement laws], if we see both `P` and `~P` in an intersection, we can

crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ if isinstance(x, (A, B)):
9191
elif isinstance(x, (A, C)):
9292
reveal_type(x) # revealed: C & ~A & ~B
9393
else:
94-
# TODO: Should be simplified to ~A & ~B & ~C
95-
reveal_type(x) # revealed: object & ~A & ~B & ~C
94+
reveal_type(x) # revealed: ~A & ~B & ~C
9695
```
9796

9897
## No narrowing for instances of `builtins.type`

crates/red_knot_python_semantic/src/types/builder.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,16 @@ impl<'db> InnerIntersectionBuilder<'db> {
284284
}
285285
}
286286
_ => {
287-
let addition_is_bool_instance = new_positive
287+
let known_instance = new_positive
288288
.into_instance()
289-
.and_then(|instance| instance.class.known(db))
290-
.is_some_and(KnownClass::is_bool);
289+
.and_then(|instance| instance.class.known(db));
290+
291+
if known_instance == Some(KnownClass::Object) {
292+
// `object & T` -> `T`; it is always redundant to add `object` to an intersection
293+
return;
294+
}
295+
296+
let addition_is_bool_instance = known_instance == Some(KnownClass::Bool);
291297

292298
for (index, existing_positive) in self.positive.iter().enumerate() {
293299
match existing_positive {

0 commit comments

Comments
 (0)