fix typeguard overriding logic

This commit is contained in:
Eric Mark Martin 2025-10-22 17:09:23 -04:00
parent 69e84b055c
commit 01b4b96047
2 changed files with 42 additions and 5 deletions

View file

@ -359,7 +359,33 @@ def narrowed_type_must_be_exact(a: object, b: Baz):
reveal_type(a) # revealed: Foo reveal_type(a) # revealed: Foo
``` ```
## Complex boolean logic with TypeGuard and TypeIs ## TypeGuard overrides normal constraints
TypeGuard constraints override any previous narrowing, but additional "regular" constraints can be
added on to TypeGuard constraints.
```py
from typing_extensions import TypeGuard, TypeIs
class A: ...
class B: ...
class C: ...
def f(x: object) -> TypeGuard[A]:
return True
def g(x: object) -> TypeGuard[B]:
return True
def h(x: object) -> TypeIs[C]:
return True
def _(x: object):
if f(x) and g(x) and h(x):
reveal_type(x) # revealed: B & C
```
## Boolean logic with TypeGuard and TypeIs
TypeGuard constraints need to properly distribute through boolean operations. TypeGuard constraints need to properly distribute through boolean operations.

View file

@ -313,8 +313,16 @@ impl<'db> Conjunction<'db> {
/// Evaluate this conjunction to a single type. /// Evaluate this conjunction to a single type.
/// If there's a `TypeGuard` constraint, it replaces the regular constraint. /// If there's a `TypeGuard` constraint, it replaces the regular constraint.
/// Otherwise, returns the regular constraint. /// Otherwise, returns the regular constraint.
fn evaluate_type_constraint(self) -> Type<'db> { fn evaluate_type_constraint(self, db: &'db dyn Db) -> Type<'db> {
self.typeguard.unwrap_or(self.constraint) self.typeguard.map_or_else(
|| self.constraint,
|typeguard_constraint| {
IntersectionBuilder::new(db)
.add_positive(typeguard_constraint)
.add_positive(self.constraint)
.build()
},
)
} }
} }
@ -363,7 +371,7 @@ impl<'db> NarrowingConstraint<'db> {
db, db,
self.disjuncts self.disjuncts
.into_iter() .into_iter()
.map(Conjunction::evaluate_type_constraint), .map(|disjunct| Conjunction::evaluate_type_constraint(disjunct, db)),
) )
} }
} }
@ -1214,7 +1222,10 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
_ => return None, _ => return None,
}; };
Some(InternalConstraints::from_iter([(place, NarrowingConstraint::regular(narrowed_type))])) Some(InternalConstraints::from_iter([(
place,
NarrowingConstraint::regular(narrowed_type),
)]))
} }
fn evaluate_match_pattern_value( fn evaluate_match_pattern_value(