Skip to content

Commit 770ef2a

Browse files
carljmAlexWaygood
andauthored
[red-knot] support deferred evaluation of type expressions (#13131)
Prototype deferred evaluation of type expressions by deferring evaluation of class bases in a stub file. This allows self-referential class definitions, as occur with the definition of `str` in typeshed (which inherits `Sequence[str]`). --------- Co-authored-by: Alex Waygood <[email protected]>
1 parent c6023c0 commit 770ef2a

File tree

3 files changed

+207
-40
lines changed

3 files changed

+207
-40
lines changed

crates/red_knot_python_semantic/src/types.rs

+45-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use ruff_db::files::File;
2-
use ruff_python_ast::name::Name;
2+
use ruff_python_ast as ast;
33

44
use crate::builtins::builtins_scope;
5-
use crate::semantic_index::definition::Definition;
5+
use crate::semantic_index::ast_ids::HasScopedAstId;
6+
use crate::semantic_index::definition::{Definition, DefinitionKind};
67
use crate::semantic_index::symbol::{ScopeId, ScopedSymbolId};
78
use crate::semantic_index::{
89
global_scope, semantic_index, symbol_table, use_def_map, DefinitionWithConstraints,
@@ -14,7 +15,8 @@ use crate::{Db, FxOrderSet};
1415
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
1516
pub(crate) use self::diagnostic::TypeCheckDiagnostics;
1617
pub(crate) use self::infer::{
17-
infer_definition_types, infer_expression_types, infer_scope_types, TypeInference,
18+
infer_deferred_types, infer_definition_types, infer_expression_types, infer_scope_types,
19+
TypeInference,
1820
};
1921

2022
mod builder;
@@ -88,6 +90,24 @@ pub(crate) fn definition_ty<'db>(db: &'db dyn Db, definition: Definition<'db>) -
8890
inference.definition_ty(definition)
8991
}
9092

93+
/// Infer the type of a (possibly deferred) sub-expression of a [`Definition`].
94+
///
95+
/// ## Panics
96+
/// If the given expression is not a sub-expression of the given [`Definition`].
97+
pub(crate) fn definition_expression_ty<'db>(
98+
db: &'db dyn Db,
99+
definition: Definition<'db>,
100+
expression: &ast::Expr,
101+
) -> Type<'db> {
102+
let expr_id = expression.scoped_ast_id(db, definition.scope(db));
103+
let inference = infer_definition_types(db, definition);
104+
if let Some(ty) = inference.try_expression_ty(expr_id) {
105+
ty
106+
} else {
107+
infer_deferred_types(db, definition).expression_ty(expr_id)
108+
}
109+
}
110+
91111
/// Infer the combined type of an array of [`Definition`]s, plus one optional "unbound type".
92112
///
93113
/// Will return a union if there is more than one definition, or at least one plus an unbound
@@ -243,7 +263,7 @@ impl<'db> Type<'db> {
243263
/// us to explicitly consider whether to handle an error or propagate
244264
/// it up the call stack.
245265
#[must_use]
246-
pub fn member(&self, db: &'db dyn Db, name: &Name) -> Type<'db> {
266+
pub fn member(&self, db: &'db dyn Db, name: &ast::name::Name) -> Type<'db> {
247267
match self {
248268
Type::Any => Type::Any,
249269
Type::Never => {
@@ -314,7 +334,7 @@ impl<'db> Type<'db> {
314334
#[salsa::interned]
315335
pub struct FunctionType<'db> {
316336
/// name of the function at definition
317-
pub name: Name,
337+
pub name: ast::name::Name,
318338

319339
/// types of all decorators on this function
320340
decorators: Vec<Type<'db>>,
@@ -329,19 +349,33 @@ impl<'db> FunctionType<'db> {
329349
#[salsa::interned]
330350
pub struct ClassType<'db> {
331351
/// Name of the class at definition
332-
pub name: Name,
352+
pub name: ast::name::Name,
333353

334-
/// Types of all class bases
335-
bases: Vec<Type<'db>>,
354+
definition: Definition<'db>,
336355

337356
body_scope: ScopeId<'db>,
338357
}
339358

340359
impl<'db> ClassType<'db> {
360+
/// Return an iterator over the types of this class's bases.
361+
///
362+
/// # Panics:
363+
/// If `definition` is not a `DefinitionKind::Class`.
364+
pub fn bases(&self, db: &'db dyn Db) -> impl Iterator<Item = Type<'db>> {
365+
let definition = self.definition(db);
366+
let DefinitionKind::Class(class_stmt_node) = definition.node(db) else {
367+
panic!("Class type definition must have DefinitionKind::Class");
368+
};
369+
class_stmt_node
370+
.bases()
371+
.iter()
372+
.map(move |base_expr| definition_expression_ty(db, definition, base_expr))
373+
}
374+
341375
/// Returns the class member of this class named `name`.
342376
///
343377
/// The member resolves to a member of the class itself or any of its bases.
344-
pub fn class_member(self, db: &'db dyn Db, name: &Name) -> Type<'db> {
378+
pub fn class_member(self, db: &'db dyn Db, name: &ast::name::Name) -> Type<'db> {
345379
let member = self.own_class_member(db, name);
346380
if !member.is_unbound() {
347381
return member;
@@ -351,12 +385,12 @@ impl<'db> ClassType<'db> {
351385
}
352386

353387
/// Returns the inferred type of the class member named `name`.
354-
pub fn own_class_member(self, db: &'db dyn Db, name: &Name) -> Type<'db> {
388+
pub fn own_class_member(self, db: &'db dyn Db, name: &ast::name::Name) -> Type<'db> {
355389
let scope = self.body_scope(db);
356390
symbol_ty_by_name(db, scope, name)
357391
}
358392

359-
pub fn inherited_class_member(self, db: &'db dyn Db, name: &Name) -> Type<'db> {
393+
pub fn inherited_class_member(self, db: &'db dyn Db, name: &ast::name::Name) -> Type<'db> {
360394
for base in self.bases(db) {
361395
let member = base.member(db, name);
362396
if !member.is_unbound() {

0 commit comments

Comments
 (0)