Skip to content

Commit 305174f

Browse files
committed
address review
1 parent eb01dfa commit 305174f

File tree

4 files changed

+55
-43
lines changed

4 files changed

+55
-43
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,11 @@ def _(flag: bool):
174174
if isinstance(x, int, foo="bar"):
175175
reveal_type(x) # revealed: Literal[1] | Literal["a"]
176176
```
177+
178+
## `type[]` types are narrowed as well as class-literal types
179+
180+
```py
181+
def _(x: object, y: type[int]):
182+
if isinstance(x, y):
183+
reveal_type(x) # revealed: int
184+
```

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,11 @@ t = int if flag() else str
239239
if issubclass(t, int, foo="bar"):
240240
reveal_type(t) # revealed: Literal[int, str]
241241
```
242+
243+
### `type[]` types are narrowed as well as class-literal types
244+
245+
```py
246+
def _(x: type, y: type[int]):
247+
if issubclass(x, y):
248+
reveal_type(x) # revealed: type[int]
249+
```

crates/red_knot_python_semantic/src/types.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub(crate) use self::display::TypeArrayDisplay;
1515
pub(crate) use self::infer::{
1616
infer_deferred_types, infer_definition_types, infer_expression_types, infer_scope_types,
1717
};
18+
pub use self::narrow::KnownConstraintFunction;
1819
pub(crate) use self::signatures::Signature;
1920
use crate::module_name::ModuleName;
2021
use crate::module_resolver::{file_to_module, resolve_module, KnownModule};
@@ -3075,23 +3076,6 @@ impl<'db> FunctionType<'db> {
30753076
}
30763077
}
30773078

3078-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3079-
pub enum KnownConstraintFunction {
3080-
/// `builtins.isinstance`
3081-
IsInstance,
3082-
/// `builtins.issubclass`
3083-
IsSubclass,
3084-
}
3085-
3086-
impl KnownConstraintFunction {
3087-
fn apply_constraint(self, class: Class) -> Type {
3088-
match self {
3089-
Self::IsInstance => Type::instance(class),
3090-
Self::IsSubclass => Type::subclass_of(class),
3091-
}
3092-
}
3093-
}
3094-
30953079
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
30963080
/// have special behavior.
30973081
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]

crates/red_knot_python_semantic/src/types/narrow.rs

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::semantic_index::expression::Expression;
77
use crate::semantic_index::symbol::{ScopeId, ScopedSymbolId, SymbolTable};
88
use crate::semantic_index::symbol_table;
99
use crate::types::{
10-
infer_expression_types, ClassLiteralType, IntersectionBuilder, KnownClass,
11-
KnownConstraintFunction, KnownFunction, Truthiness, Type, UnionBuilder,
10+
infer_expression_types, ClassBase, ClassLiteralType, IntersectionBuilder, KnownClass,
11+
KnownFunction, SubclassOfType, Truthiness, Type, UnionBuilder,
1212
};
1313
use crate::Db;
1414
use itertools::Itertools;
@@ -83,27 +83,37 @@ fn all_negative_narrowing_constraints_for_expression<'db>(
8383
NarrowingConstraintsBuilder::new(db, ConstraintNode::Expression(expression), false).finish()
8484
}
8585

86-
/// Generate a constraint from the type of a `classinfo` argument to `isinstance` or `issubclass`.
87-
///
88-
/// The `classinfo` argument can be a class literal, a tuple of (tuples of) class literals. PEP 604
89-
/// union types are not yet supported. Returns `None` if the `classinfo` argument has a wrong type.
90-
fn generate_classinfo_constraint<'db>(
91-
db: &'db dyn Db,
92-
classinfo: &Type<'db>,
93-
constraint_fn: KnownConstraintFunction,
94-
) -> Option<Type<'db>> {
95-
match classinfo {
96-
Type::Tuple(tuple) => {
97-
let mut builder = UnionBuilder::new(db);
98-
for element in tuple.elements(db) {
99-
builder = builder.add(generate_classinfo_constraint(db, element, constraint_fn)?);
86+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
87+
pub enum KnownConstraintFunction {
88+
/// `builtins.isinstance`
89+
IsInstance,
90+
/// `builtins.issubclass`
91+
IsSubclass,
92+
}
93+
94+
impl KnownConstraintFunction {
95+
/// Generate a constraint from the type of a `classinfo` argument to `isinstance` or `issubclass`.
96+
///
97+
/// The `classinfo` argument can be a class literal, a tuple of (tuples of) class literals. PEP 604
98+
/// union types are not yet supported. Returns `None` if the `classinfo` argument has a wrong type.
99+
fn generate_constraint<'db>(self, db: &'db dyn Db, classinfo: Type<'db>) -> Option<Type<'db>> {
100+
match classinfo {
101+
Type::Tuple(tuple) => {
102+
let mut builder = UnionBuilder::new(db);
103+
for element in tuple.elements(db) {
104+
builder = builder.add(self.generate_constraint(db, *element)?);
105+
}
106+
Some(builder.build())
100107
}
101-
Some(builder.build())
102-
}
103-
Type::ClassLiteral(ClassLiteralType { class }) => {
104-
Some(constraint_fn.apply_constraint(*class))
108+
Type::ClassLiteral(ClassLiteralType { class })
109+
| Type::SubclassOf(SubclassOfType {
110+
base: ClassBase::Class(class),
111+
}) => Some(match self {
112+
KnownConstraintFunction::IsInstance => Type::instance(class),
113+
KnownConstraintFunction::IsSubclass => Type::subclass_of(class),
114+
}),
115+
_ => None,
105116
}
106-
_ => None,
107117
}
108118
}
109119

@@ -428,11 +438,13 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
428438
let class_info_ty =
429439
inference.expression_ty(class_info.scoped_expression_id(self.db, scope));
430440

431-
generate_classinfo_constraint(self.db, &class_info_ty, function).map(|constraint| {
432-
let mut constraints = NarrowingConstraints::default();
433-
constraints.insert(symbol, constraint.negate_if(self.db, !is_positive));
434-
constraints
435-
})
441+
function
442+
.generate_constraint(self.db, class_info_ty)
443+
.map(|constraint| {
444+
let mut constraints = NarrowingConstraints::default();
445+
constraints.insert(symbol, constraint.negate_if(self.db, !is_positive));
446+
constraints
447+
})
436448
}
437449
// for the expression `bool(E)`, we further narrow the type based on `E`
438450
Type::ClassLiteral(class_type)

0 commit comments

Comments
 (0)