Skip to content

Commit 006b9af

Browse files
committed
Use a thread local
1 parent 459ae8f commit 006b9af

19 files changed

Lines changed: 520 additions & 770 deletions

crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,24 @@ def assign_recursive_alias_attribute(x: RecursiveAttributeAlias):
501501
def delete_recursive_alias_attribute(x: RecursiveAttributeAlias):
502502
del x.attr
503503

504+
class RecursiveMissingAttribute: ...
505+
506+
type RecursiveMissingAttributeAlias = RecursiveMissingAttribute | RecursiveMissingAttributeAlias
507+
508+
def load_missing_recursive_alias_attribute(x: RecursiveMissingAttributeAlias):
509+
# error: [unresolved-attribute] "Attribute `missing` is not defined on `RecursiveMissingAttribute` in union `RecursiveMissingAttributeAlias`"
510+
reveal_type(x.missing) # revealed: Divergent
511+
512+
def delete_missing_recursive_alias_attribute(x: RecursiveMissingAttributeAlias):
513+
# error: [unresolved-attribute] "Attribute `missing` is not defined on `RecursiveMissingAttribute` in union `RecursiveMissingAttributeAlias`"
514+
del x.missing
515+
516+
type RecursiveIntAttributeAlias = int | RecursiveIntAttributeAlias
517+
518+
def load_missing_recursive_int_alias_attribute(x: RecursiveIntAttributeAlias):
519+
# error: [unresolved-attribute] "Attribute `missing` is not defined on `int` in union `RecursiveIntAttributeAlias`"
520+
reveal_type(x.missing) # revealed: Divergent
521+
504522
class RecursiveKwargs(TypedDict):
505523
kind: Literal["a"]
506524
a: int

crates/ty_python_semantic/src/types.rs

Lines changed: 88 additions & 138 deletions
Large diffs are not rendered by default.

crates/ty_python_semantic/src/types/bound_super.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use crate::{
1313
SubclassOfType, Type, TypeVarBoundOrConstraints, UnionBuilder,
1414
constraints::ConstraintSet,
1515
context::InferContext,
16-
cyclic::ActiveRecursionDetector,
1716
diagnostic::{INVALID_SUPER_ARGUMENT, UNAVAILABLE_IMPLICIT_SUPER_ARGUMENTS},
1817
relation::EquivalenceChecker,
1918
signatures::{Parameter, Parameters, Signature},
@@ -488,24 +487,8 @@ impl<'db> BoundSuperType<'db> {
488487
pivot_class_type: Type<'db>,
489488
owner_type: Type<'db>,
490489
) -> Result<Type<'db>, BoundSuperError<'db>> {
491-
let recursion_detector = ActiveRecursionDetector::default();
492-
Self::build_impl(db, pivot_class_type, owner_type, &recursion_detector)
493-
}
494-
495-
fn build_impl(
496-
db: &'db dyn Db,
497-
pivot_class_type: Type<'db>,
498-
owner_type: Type<'db>,
499-
recursion_detector: &ActiveRecursionDetector<Type<'db>>,
500-
) -> Result<Type<'db>, BoundSuperError<'db>> {
501-
let delegate_to = |type_to_delegate_to| {
502-
BoundSuperType::build_impl(
503-
db,
504-
pivot_class_type,
505-
type_to_delegate_to,
506-
recursion_detector,
507-
)
508-
};
490+
let delegate_to =
491+
|type_to_delegate_to| BoundSuperType::build(db, pivot_class_type, type_to_delegate_to);
509492

510493
// Delegate but rewrite errors to preserve TypeVar context.
511494
let delegate_with_error_mapped =
@@ -756,12 +739,7 @@ impl<'db> BoundSuperType<'db> {
756739
return Ok(builder.build());
757740
}
758741
Type::TypeAlias(_) => {
759-
return owner_type.visit_type_alias_value(
760-
db,
761-
recursion_detector,
762-
|| Ok(Type::unknown()),
763-
delegate_to,
764-
);
742+
return owner_type.visit_type_alias_value(db, || Ok(Type::unknown()), delegate_to);
765743
}
766744
Type::TypeVar(bound_typevar) => {
767745
let typevar = bound_typevar.typevar(db);

crates/ty_python_semantic/src/types/call/arguments.rs

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use ruff_python_ast as ast;
66
use rustc_hash::FxHashMap;
77

88
use crate::Db;
9-
use crate::types::cyclic::ActiveRecursionDetector;
109
use crate::types::enums::{enum_member_literals, enum_metadata};
1110
use crate::types::tuple::Tuple;
1211
use crate::types::typed_dict::extract_unpacked_typed_dict_keys_from_value_type;
@@ -496,32 +495,21 @@ impl<'a, 'db> FromIterator<(Argument<'a>, Option<Type<'db>>)> for CallArguments<
496495
///
497496
/// In other words, it returns `true` if [`expand_type`] returns [`Some`] for the given type.
498497
pub(crate) fn is_expandable_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> bool {
499-
let recursion_detector = ActiveRecursionDetector::default();
500-
is_expandable_type_impl(db, ty, &recursion_detector)
501-
}
502-
503-
fn is_expandable_type_impl<'db>(
504-
db: &'db dyn Db,
505-
ty: Type<'db>,
506-
recursion_detector: &ActiveRecursionDetector<Type<'db>>,
507-
) -> bool {
508498
match ty {
509499
Type::NominalInstance(instance) => {
510500
let class = instance.class(db);
511501
class.is_known(db, KnownClass::Bool)
512502
|| instance.tuple_spec(db).is_some_and(|spec| match &*spec {
513503
Tuple::Fixed(fixed_length_tuple) => fixed_length_tuple
514504
.iter_all_elements()
515-
.any(|element| is_expandable_type_impl(db, element, recursion_detector)),
505+
.any(|element| is_expandable_type(db, element)),
516506
Tuple::Variable(_) => false,
517507
})
518508
|| enum_metadata(db, class.class_literal(db)).is_some()
519509
}
520510
Type::Union(_) => true,
521511
Type::TypeAlias(_) => {
522-
ty.visit_type_alias_value_or_default(db, recursion_detector, |value_ty| {
523-
is_expandable_type_impl(db, value_ty, recursion_detector)
524-
})
512+
ty.visit_type_alias_value_or_default(db, |value_ty| is_expandable_type(db, value_ty))
525513
}
526514
_ => false,
527515
}
@@ -531,15 +519,6 @@ fn is_expandable_type_impl<'db>(
531519
///
532520
/// Returns [`None`] if the type cannot be expanded.
533521
fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Vec<Type<'db>>> {
534-
let recursion_detector = ActiveRecursionDetector::default();
535-
expand_type_impl(db, ty, &recursion_detector)
536-
}
537-
538-
fn expand_type_impl<'db>(
539-
db: &'db dyn Db,
540-
ty: Type<'db>,
541-
recursion_detector: &ActiveRecursionDetector<Type<'db>>,
542-
) -> Option<Vec<Type<'db>>> {
543522
// NOTE: Update `is_expandable_type` if this logic changes accordingly.
544523
match ty {
545524
Type::NominalInstance(instance) => {
@@ -560,8 +539,7 @@ fn expand_type_impl<'db>(
560539
let per_element: Vec<_> = fixed_length_tuple
561540
.iter_all_elements()
562541
.map(|element| {
563-
expand_type_impl(db, element, recursion_detector)
564-
.unwrap_or_else(|| vec![element])
542+
expand_type(db, element).unwrap_or_else(|| vec![element])
565543
})
566544
.collect();
567545

@@ -594,9 +572,7 @@ fn expand_type_impl<'db>(
594572
Type::Union(union) => Some(union.elements(db).to_vec()),
595573
// For type aliases, expand the underlying value type.
596574
Type::TypeAlias(_) => {
597-
ty.visit_type_alias_value_or_default(db, recursion_detector, |value_ty| {
598-
expand_type_impl(db, value_ty, recursion_detector)
599-
})
575+
ty.visit_type_alias_value_or_default(db, |value_ty| expand_type(db, value_ty))
600576
}
601577
// We don't handle `type[A | B]` here because it's already stored in the expanded form
602578
// i.e., `type[A] | type[B]` which is handled by the `Type::Union` case.

crates/ty_python_semantic/src/types/call/bind/constructor.rs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use super::{ArgumentForms, Binding, Bindings, CallableBinding, CallableItem};
22
use crate::db::Db;
33
use crate::types::call::arguments::CallArguments;
44
use crate::types::constraints::ConstraintSetBuilder;
5-
use crate::types::cyclic::ActiveRecursionDetector;
65
use crate::types::generics::Specialization;
76
use crate::types::signatures::Parameter;
87
use crate::types::{BoundTypeVarInstance, ClassLiteral, DynamicType, Type, TypeContext};
@@ -566,29 +565,18 @@ fn constructor_returns_instance<'db>(
566565
db: &'db dyn Db,
567566
class_literal: ClassLiteral<'db>,
568567
return_ty: Type<'db>,
569-
) -> bool {
570-
let recursion_detector = ActiveRecursionDetector::default();
571-
constructor_returns_instance_impl(db, class_literal, return_ty, &recursion_detector)
572-
}
573-
574-
fn constructor_returns_instance_impl<'db>(
575-
db: &'db dyn Db,
576-
class_literal: ClassLiteral<'db>,
577-
return_ty: Type<'db>,
578-
recursion_detector: &ActiveRecursionDetector<Type<'db>>,
579568
) -> bool {
580569
match return_ty {
581-
Type::TypeAlias(_) => {
582-
return_ty.visit_type_alias_value_or_assume_valid(db, recursion_detector, |value_ty| {
583-
constructor_returns_instance_impl(db, class_literal, value_ty, recursion_detector)
584-
})
585-
}
586-
Type::Union(union) => union.elements(db).iter().all(|element| {
587-
constructor_returns_instance_impl(db, class_literal, *element, recursion_detector)
588-
}),
589-
Type::Intersection(intersection) => intersection.iter_positive(db).any(|element| {
590-
constructor_returns_instance_impl(db, class_literal, element, recursion_detector)
570+
Type::TypeAlias(_) => return_ty.visit_type_alias_value_or_assume_valid(db, |value_ty| {
571+
constructor_returns_instance(db, class_literal, value_ty)
591572
}),
573+
Type::Union(union) => union
574+
.elements(db)
575+
.iter()
576+
.all(|element| constructor_returns_instance(db, class_literal, *element)),
577+
Type::Intersection(intersection) => intersection
578+
.iter_positive(db)
579+
.any(|element| constructor_returns_instance(db, class_literal, element)),
592580
// Spec says an explicit `Any` return type should be considered non-instance.
593581
Type::Dynamic(DynamicType::Any) => false,
594582
// But a missing return annotation should be considered instance.

crates/ty_python_semantic/src/types/callable.rs

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use crate::{
1111
Parameter, Parameters, Signature, SubclassOfInner, Type, TypeContext, TypeMapping,
1212
TypeVarBoundOrConstraints, UnionType,
1313
constraints::{ConstraintSet, IteratorConstraintsExtension},
14-
cyclic::ActiveRecursionDetector,
1514
known_instance::FunctoolsPartialInstance,
1615
relation::{TypeRelation, TypeRelationChecker},
1716
signatures::{CallableSignature, PartialSignatureApplication},
@@ -50,23 +49,9 @@ impl<'db> Type<'db> {
5049
self,
5150
db: &'db dyn Db,
5251
policy: UpcastPolicy,
53-
) -> Option<CallableTypes<'db>> {
54-
let recursion_detector = ActiveRecursionDetector::default();
55-
self.try_upcast_to_callable_with_policy_impl(db, policy, &recursion_detector)
56-
}
57-
58-
fn try_upcast_to_callable_with_policy_impl(
59-
self,
60-
db: &'db dyn Db,
61-
policy: UpcastPolicy,
62-
recursion_detector: &ActiveRecursionDetector<Type<'db>>,
6352
) -> Option<CallableTypes<'db>> {
6453
if let Some(fallback) = self.materialized_divergent_fallback() {
65-
return fallback.try_upcast_to_callable_with_policy_impl(
66-
db,
67-
policy,
68-
recursion_detector,
69-
);
54+
return fallback.try_upcast_to_callable_with_policy(db, policy);
7055
}
7156

7257
match self {
@@ -100,9 +85,7 @@ impl<'db> Type<'db> {
10085
if let Place::Defined(place) = call_symbol
10186
&& place.is_definitely_defined()
10287
{
103-
place
104-
.ty
105-
.try_upcast_to_callable_with_policy_impl(db, policy, recursion_detector)
88+
place.ty.try_upcast_to_callable_with_policy(db, policy)
10689
} else {
10790
None
10891
}
@@ -115,7 +98,7 @@ impl<'db> Type<'db> {
11598

11699
Type::NewTypeInstance(newtype) => newtype
117100
.concrete_base_type(db)
118-
.try_upcast_to_callable_with_policy_impl(db, policy, recursion_detector),
101+
.try_upcast_to_callable_with_policy(db, policy),
119102

120103
Type::SubclassOf(subclass_of_ty) if policy == UpcastPolicy::Sound => {
121104
Some(CallableTypes::one(CallableType::function_like(
@@ -131,11 +114,7 @@ impl<'db> Type<'db> {
131114
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
132115
let upcast_callables = bound
133116
.to_meta_type(db)
134-
.try_upcast_to_callable_with_policy_impl(
135-
db,
136-
policy,
137-
recursion_detector,
138-
)?;
117+
.try_upcast_to_callable_with_policy(db, policy)?;
139118
Some(upcast_callables.map(|callable| {
140119
let signatures = callable
141120
.signatures(db)
@@ -153,11 +132,7 @@ impl<'db> Type<'db> {
153132
for constraint in constraints.elements(db) {
154133
let element_upcast = constraint
155134
.to_meta_type(db)
156-
.try_upcast_to_callable_with_policy_impl(
157-
db,
158-
policy,
159-
recursion_detector,
160-
)?;
135+
.try_upcast_to_callable_with_policy(db, policy)?;
161136
for callable in element_upcast.into_inner() {
162137
let signatures = callable
163138
.signatures(db)
@@ -186,11 +161,8 @@ impl<'db> Type<'db> {
186161
Type::Union(union) => {
187162
let mut callables = SmallVec::new();
188163
for element in union.elements(db) {
189-
let element_callable = element.try_upcast_to_callable_with_policy_impl(
190-
db,
191-
policy,
192-
recursion_detector,
193-
)?;
164+
let element_callable =
165+
element.try_upcast_to_callable_with_policy(db, policy)?;
194166
callables.extend(element_callable.into_inner());
195167
}
196168
Some(CallableTypes::new(callables))
@@ -199,17 +171,14 @@ impl<'db> Type<'db> {
199171
Type::LiteralValue(literal) => match literal.kind() {
200172
LiteralValueTypeKind::Enum(enum_literal) => enum_literal
201173
.enum_class_instance(db)
202-
.try_upcast_to_callable_with_policy_impl(db, policy, recursion_detector),
174+
.try_upcast_to_callable_with_policy(db, policy),
203175
_ => None,
204176
},
205177

206178
Type::TypeAlias(_) => self.visit_type_alias_value(
207179
db,
208-
recursion_detector,
209180
|| Some(CallableTypes::one(CallableType::unknown(db))),
210-
|value_ty| {
211-
value_ty.try_upcast_to_callable_with_policy_impl(db, policy, recursion_detector)
212-
},
181+
|value_ty| value_ty.try_upcast_to_callable_with_policy(db, policy),
213182
),
214183

215184
Type::KnownBoundMethod(method) => Some(CallableTypes::one(CallableType::new(

crates/ty_python_semantic/src/types/class.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ use crate::types::generics::{
3030
};
3131
use crate::types::known_instance::DeprecatedInstance;
3232
use crate::types::member::Member;
33-
use crate::types::relation::{RelationContext, TypeRelation, TypeRelationChecker};
34-
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
33+
use crate::types::relation::{
34+
HasRelationToVisitor, InvariantRelationVisitor, IsDisjointVisitor, TypeRelation,
35+
TypeRelationChecker,
36+
};
37+
use crate::types::signatures::{
38+
CallableSignature, Parameter, Parameters, Signature, SignatureRelationVisitor,
39+
};
3540
use crate::types::tuple::TupleSpec;
3641
use crate::types::{
3742
ApplyTypeMappingVisitor, CallableType, CallableTypes, DataclassParams,
@@ -1190,9 +1195,20 @@ impl<'db> ClassType<'db> {
11901195
/// Return `true` if `other` is present in this class's MRO.
11911196
pub(super) fn is_subclass_of(self, db: &'db dyn Db, target: ClassType<'db>) -> bool {
11921197
let constraints = ConstraintSetBuilder::new();
1193-
let context = RelationContext::default(&constraints);
1194-
let checker =
1195-
TypeRelationChecker::subtyping(&constraints, InferableTypeVars::None, &context);
1198+
let relation_visitor = HasRelationToVisitor::default(&constraints);
1199+
let disjointness_visitor = IsDisjointVisitor::default(&constraints);
1200+
let signature_relation_visitor = SignatureRelationVisitor::default();
1201+
let invariant_relation_visitor = InvariantRelationVisitor::default();
1202+
let materialization_visitor = ApplyTypeMappingVisitor::default();
1203+
let checker = TypeRelationChecker::subtyping(
1204+
&constraints,
1205+
InferableTypeVars::None,
1206+
&relation_visitor,
1207+
&disjointness_visitor,
1208+
&signature_relation_visitor,
1209+
&invariant_relation_visitor,
1210+
&materialization_visitor,
1211+
);
11961212
checker
11971213
.check_class_pair(db, self, target)
11981214
.is_always_satisfied(db)

0 commit comments

Comments
 (0)