Conversation
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 85.31%. The percentage of expected errors that received a diagnostic held steady at 78.79%. The number of fully passing files held steady at 65/133. |
Memory usage reportSummary
Significant changesClick to expand detailed breakdownprefect
sphinx
trio
flake8
|
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-await |
40 | 0 | 0 |
invalid-return-type |
1 | 0 | 0 |
| Total | 41 | 0 | 0 |
Changes in flaky projects detected. Raw diff output excludes flaky projects; see the HTML report for details.
sharkdp
approved these changes
Mar 25, 2026
Contributor
sharkdp
left a comment
There was a problem hiding this comment.
The benchmark results are a mixed bag, but overall it seems to be performance neutral.
Comment on lines
+210
to
+213
| #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, get_size2::GetSize, salsa::Update)] | ||
| pub(crate) enum InferableTypeVars<'db> { | ||
| None, | ||
| One(&'a FxHashSet<BoundTypeVarIdentity<'db>>), | ||
| Two( | ||
| &'a InferableTypeVars<'a, 'db>, | ||
| &'a InferableTypeVars<'a, 'db>, | ||
| ), | ||
| Some(InferableTypeVarsInner<'db>), |
Contributor
There was a problem hiding this comment.
Is the main benefit of this layer that we don't need a db when constructing an empty set via ::None? Or are you worried that an interned empty set would be more costly performance-wise?
Details
diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs
index 7cea85b668..7baa46014b 100644
--- a/crates/ty_python_semantic/src/types.rs
+++ b/crates/ty_python_semantic/src/types.rs
@@ -3487,7 +3487,7 @@ impl<'db> Type<'db> {
fn bindings(self, db: &'db dyn Db) -> Bindings<'db> {
match self {
Type::Callable(callable) => {
- CallableBinding::from_overloads(self, callable.signatures(db).iter().cloned())
+ CallableBinding::from_overloads(db, self, callable.signatures(db).iter().cloned())
.into()
}
@@ -3506,23 +3506,24 @@ impl<'db> Type<'db> {
Type::BoundMethod(bound_method) => {
let signature = bound_method.function(db).signature(db);
- CallableBinding::from_overloads(self, signature.overloads.iter().cloned())
+ CallableBinding::from_overloads(db, self, signature.overloads.iter().cloned())
.with_bound_type(bound_method.self_instance(db))
.into()
}
Type::KnownBoundMethod(method) => {
- CallableBinding::from_overloads(self, method.signatures(db)).into()
+ CallableBinding::from_overloads(db, self, method.signatures(db)).into()
}
Type::WrapperDescriptor(wrapper_descriptor) => {
- CallableBinding::from_overloads(self, wrapper_descriptor.signatures(db)).into()
+ CallableBinding::from_overloads(db, self, wrapper_descriptor.signatures(db)).into()
}
// TODO: We should probably also check the original return type of the function
// that was decorated with `@dataclass_transform`, to see if it is consistent with
// with what we configure here.
Type::DataclassTransformer(_) => Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3542,6 +3543,7 @@ impl<'db> Type<'db> {
| KnownFunction::IsSubtypeOf
| KnownFunction::IsDisjointFrom,
) => Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3562,6 +3564,7 @@ impl<'db> Type<'db> {
Some(KnownFunction::IsSingleton | KnownFunction::IsSingleValued) => {
Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3584,6 +3587,7 @@ impl<'db> Type<'db> {
);
Binding::single(
+ db,
self,
Signature::new_generic(
Some(GenericContext::from_typevar_instances(db, [val_ty])),
@@ -3605,6 +3609,7 @@ impl<'db> Type<'db> {
Some(KnownFunction::AssertNever) => {
Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3623,6 +3628,7 @@ impl<'db> Type<'db> {
}
Some(KnownFunction::Cast) => Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3642,6 +3648,7 @@ impl<'db> Type<'db> {
Some(KnownFunction::Dataclass) => {
CallableBinding::from_overloads(
+ db,
self,
[
// def dataclass(cls: None, /) -> Callable[[type[_T]], type[_T]]: ...
@@ -3721,6 +3728,7 @@ impl<'db> Type<'db> {
}
_ => CallableBinding::from_overloads(
+ db,
self,
function_type.signature(db).overloads.iter().cloned(),
)
@@ -3736,7 +3744,8 @@ impl<'db> Type<'db> {
Type::SubclassOf(subclass_of_type) => match subclass_of_type.subclass_of() {
SubclassOfInner::Dynamic(dynamic_type) => {
- Binding::single(self, Signature::dynamic(Type::Dynamic(dynamic_type))).into()
+ Binding::single(db, self, Signature::dynamic(Type::Dynamic(dynamic_type)))
+ .into()
}
SubclassOfInner::Class(class) => self.constructor_bindings(db, class),
SubclassOfInner::TypeVar(tvar) => {
@@ -3761,6 +3770,7 @@ impl<'db> Type<'db> {
Type::SpecialForm(SpecialFormType::TypedDict) => {
Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3818,7 +3828,7 @@ impl<'db> Type<'db> {
// Dynamic types are callable, and the return type is the same dynamic type. Similarly,
// `Never` is always callable and returns `Never`.
Type::Dynamic(_) | Type::Never => {
- Binding::single(self, Signature::dynamic(self)).into()
+ Binding::single(db, self, Signature::dynamic(self)).into()
}
// Note that this correctly returns `None` if none of the union elements are callable.
@@ -3852,7 +3862,7 @@ impl<'db> Type<'db> {
let returns = IntersectionType::from_two_elements(db, typevar_meta, Type::any());
let signature =
Signature::new_generic(Some(context), Parameters::new(db, parameters), returns);
- Binding::single(self, signature).into()
+ Binding::single(db, self, signature).into()
}
// TODO: some `SpecialForm`s are callable (e.g. TypedDicts)
@@ -3866,6 +3876,7 @@ impl<'db> Type<'db> {
},
Type::KnownInstance(KnownInstanceType::NewType(newtype)) => Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3910,6 +3921,7 @@ impl<'db> Type<'db> {
// ```
Some(
Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -3935,6 +3947,7 @@ impl<'db> Type<'db> {
// ```
Some(
CallableBinding::from_overloads(
+ db,
self,
[
Signature::new(
@@ -3987,13 +4000,17 @@ impl<'db> Type<'db> {
// def __new__(cls) -> Self: ...
// ```
Some(
- Binding::single(self, Signature::new(Parameters::empty(), Type::object()))
- .into(),
+ Binding::single(
+ db,
+ self,
+ Signature::new(Parameters::empty(), Type::object()),
+ )
+ .into(),
)
}
KnownClass::Enum => {
- Some(Binding::single(self, Signature::todo("functional `Enum` syntax")).into())
+ Some(Binding::single(db, self, Signature::todo("functional `Enum` syntax")).into())
}
KnownClass::Super => {
@@ -4008,6 +4025,7 @@ impl<'db> Type<'db> {
// ```
Some(
CallableBinding::from_overloads(
+ db,
self,
[
Signature::new(
@@ -4051,6 +4069,7 @@ impl<'db> Type<'db> {
// ```
Some(
Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -4091,6 +4110,7 @@ impl<'db> Type<'db> {
// ```
Some(
Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -4151,6 +4171,7 @@ impl<'db> Type<'db> {
Some(
Binding::single(
+ db,
self,
Signature::new(
Parameters::new(
@@ -4209,6 +4230,7 @@ impl<'db> Type<'db> {
// ```
Some(
CallableBinding::from_overloads(
+ db,
self,
[
Signature::new(Parameters::empty(), Type::empty_tuple(db)),
@@ -4279,6 +4301,7 @@ impl<'db> Type<'db> {
let fallback_bindings = || {
let return_type = self.to_instance(db).unwrap_or(Type::unknown());
Binding::single(
+ db,
self,
Signature::new_generic(
class_generic_context,
@@ -4449,6 +4472,7 @@ impl<'db> Type<'db> {
// to `object`. Keep analysis going and surface the missing-implicit-call
// lint via the builder.
let mut bindings: Bindings<'db> = Binding::single(
+ db,
self_type,
Signature::new(Parameters::gradual_form(), constructor_instance_ty),
)
diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs
index 472a1a7469..6211d5a2d3 100644
--- a/crates/ty_python_semantic/src/types/call/bind.rs
+++ b/crates/ty_python_semantic/src/types/call/bind.rs
@@ -1197,7 +1197,7 @@ impl<'db> Bindings<'db> {
db,
*ty_b,
constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
});
let tracked = InternedConstraintSet::new(db, result);
@@ -1215,7 +1215,7 @@ impl<'db> Bindings<'db> {
db,
*ty_b,
constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
});
let tracked = InternedConstraintSet::new(db, result);
@@ -1233,7 +1233,7 @@ impl<'db> Bindings<'db> {
db,
*ty_b,
constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
});
let tracked = InternedConstraintSet::new(db, result);
@@ -1844,7 +1844,7 @@ impl<'db> Bindings<'db> {
*ty_b,
constraints.load(db, tracked.constraints(db)),
constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
});
let tracked = InternedConstraintSet::new(db, result);
@@ -1882,7 +1882,7 @@ impl<'db> Bindings<'db> {
let extract_inferable = |instance: &NominalInstanceType<'db>| {
if instance.has_known_class(db, KnownClass::NoneType) {
// Caller explicitly passed None, so no typevars are inferable.
- return Some(InferableTypeVars::None);
+ return Some(InferableTypeVars::none(db));
}
let typevars: Option<FxOrderSet<_>> = instance
.tuple_spec(db)?
@@ -1897,7 +1897,7 @@ impl<'db> Bindings<'db> {
let inferable = match overload.parameter_types() {
// Caller did not provide argument, so no typevars are inferable.
- [None] => InferableTypeVars::None,
+ [None] => InferableTypeVars::none(db),
[Some(Type::NominalInstance(instance))] => {
match extract_inferable(instance) {
Some(inferable) => inferable,
@@ -2090,12 +2090,13 @@ pub(crate) struct CallableBinding<'db> {
impl<'db> CallableBinding<'db> {
pub(crate) fn from_overloads(
+ db: &'db dyn Db,
signature_type: Type<'db>,
overloads: impl IntoIterator<Item = Signature<'db>>,
) -> Self {
let overloads = overloads
.into_iter()
- .map(|signature| Binding::single(signature_type, signature))
+ .map(|signature| Binding::single(db, signature_type, signature))
.collect();
Self {
callable_type: signature_type,
@@ -2383,7 +2384,7 @@ impl<'db> CallableBinding<'db> {
// https://github.com/astral-sh/ty/issues/735 for more details.
for overload in &mut self.overloads {
// Clear the state of all overloads before re-evaluating from step 1
- overload.reset();
+ overload.reset(db);
overload.match_parameters(db, expanded_arguments, &mut argument_forms);
}
@@ -3706,7 +3707,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
call_expression_tcx,
return_ty,
errors,
- inferable_typevars: InferableTypeVars::None,
+ inferable_typevars: InferableTypeVars::none(db),
specialization: None,
constraint_set_errors: vec![false; arguments.len()],
}
@@ -4245,8 +4246,11 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
};
// Create Bindings with all overloads and perform full overload resolution
- let callable_binding =
- CallableBinding::from_overloads(self.signature_type, signatures.iter().cloned());
+ let callable_binding = CallableBinding::from_overloads(
+ self.db,
+ self.signature_type,
+ signatures.iter().cloned(),
+ );
let bindings = match Bindings::from(callable_binding)
.match_parameters(self.db, &sub_arguments)
.check_types(
@@ -4522,14 +4526,18 @@ pub(crate) struct Binding<'db> {
}
impl<'db> Binding<'db> {
- pub(crate) fn single(signature_type: Type<'db>, signature: Signature<'db>) -> Binding<'db> {
+ pub(crate) fn single(
+ db: &'db dyn Db,
+ signature_type: Type<'db>,
+ signature: Signature<'db>,
+ ) -> Binding<'db> {
Binding {
signature,
callable_type: signature_type,
signature_type,
constructor_instance_type: None,
return_ty: Type::unknown(),
- inferable_typevars: InferableTypeVars::None,
+ inferable_typevars: InferableTypeVars::none(db),
specialization: None,
argument_matches: Box::from([]),
variadic_argument_matched_to_variadic_parameter: false,
@@ -4764,9 +4772,9 @@ impl<'db> Binding<'db> {
}
/// Resets the state of this binding to its initial state.
- fn reset(&mut self) {
+ fn reset(&mut self, db: &'db dyn Db) {
self.return_ty = Type::unknown();
- self.inferable_typevars = InferableTypeVars::None;
+ self.inferable_typevars = InferableTypeVars::none(db);
self.specialization = None;
self.argument_matches = Box::from([]);
self.parameter_tys = Box::from([]);
diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs
index 785048eb92..669326d700 100644
--- a/crates/ty_python_semantic/src/types/class.rs
+++ b/crates/ty_python_semantic/src/types/class.rs
@@ -1052,7 +1052,7 @@ impl<'db> ClassType<'db> {
let disjointness_visitor = IsDisjointVisitor::default(&constraints);
let checker = TypeRelationChecker::subtyping(
&constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
&relation_visitor,
&disjointness_visitor,
);
@@ -1104,7 +1104,7 @@ impl<'db> ClassType<'db> {
db,
other_alias.specialization(db),
constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
.is_always_satisfied(db)
}
@@ -1175,7 +1175,7 @@ impl<'db> ClassType<'db> {
db,
other_metaclass_instance,
constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
.is_always_satisfied(db)
{
diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs
index 4df1ebcd2e..834a1bc815 100644
--- a/crates/ty_python_semantic/src/types/generics.rs
+++ b/crates/ty_python_semantic/src/types/generics.rs
@@ -3,7 +3,7 @@ use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry;
use std::fmt::Display;
-use itertools::{Either, Itertools};
+use itertools::Itertools;
use ruff_python_ast as ast;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -207,39 +207,31 @@ pub(crate) fn typing_self<'db>(
)
}
-#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, get_size2::GetSize, salsa::Update)]
-pub(crate) enum InferableTypeVars<'db> {
- None,
- Some(InferableTypeVarsInner<'db>),
+#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
+pub(crate) struct InferableTypeVars<'db> {
+ #[returns(ref)]
+ inferable: FxOrderSet<BoundTypeVarIdentity<'db>>,
}
+// The Salsa heap is tracked separately.
+impl get_size2::GetSize for InferableTypeVars<'_> {}
+
impl<'db> InferableTypeVars<'db> {
+ pub(crate) fn none(db: &'db dyn Db) -> Self {
+ Self::new(db, FxOrderSet::default())
+ }
+
pub(crate) fn from_typevars(
db: &'db dyn Db,
typevars: FxOrderSet<BoundTypeVarIdentity<'db>>,
) -> Self {
- if typevars.is_empty() {
- return InferableTypeVars::None;
- }
- Self::Some(InferableTypeVarsInner::new_internal(db, typevars))
+ Self::new(db, typevars)
}
}
-#[salsa::interned(debug, constructor=new_internal, heap_size=ruff_memory_usage::heap_size)]
-pub(crate) struct InferableTypeVarsInner<'db> {
- #[returns(ref)]
- inferable: FxOrderSet<BoundTypeVarIdentity<'db>>,
-}
-
-// The Salsa heap is tracked separately.
-impl get_size2::GetSize for InferableTypeVarsInner<'_> {}
-
impl<'db> BoundTypeVarIdentity<'db> {
pub(crate) fn is_inferable(self, db: &'db dyn Db, inferable: InferableTypeVars<'db>) -> bool {
- match inferable {
- InferableTypeVars::None => false,
- InferableTypeVars::Some(inner) => inner.inferable(db).contains(&self),
- }
+ inferable.inferable(db).contains(&self)
}
}
@@ -253,13 +245,8 @@ impl<'db> BoundTypeVarInstance<'db> {
impl<'db> InferableTypeVars<'db> {
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn merge(self, db: &'db dyn Db, other: Self) -> Self {
- match (self, other) {
- (InferableTypeVars::None, other) | (other, InferableTypeVars::None) => other,
- (InferableTypeVars::Some(self_inner), InferableTypeVars::Some(other_inner)) => {
- let merged = self_inner.inferable(db) | other_inner.inferable(db);
- Self::Some(InferableTypeVarsInner::new_internal(db, merged))
- }
- }
+ let merged = self.inferable(db) | other.inferable(db);
+ Self::new(db, merged)
}
// This is not an IntoIterator implementation because I have no desire to try to name the
@@ -268,10 +255,7 @@ impl<'db> InferableTypeVars<'db> {
self,
db: &'db dyn Db,
) -> impl Iterator<Item = BoundTypeVarIdentity<'db>> + 'db {
- match self {
- InferableTypeVars::None => Either::Left(std::iter::empty()),
- InferableTypeVars::Some(inner) => Either::Right(inner.inferable(db).iter().copied()),
- }
+ self.inferable(db).iter().copied()
}
// Keep this around for debugging purposes
@@ -419,7 +403,7 @@ impl<'db> GenericContext<'db> {
}
#[salsa::tracked(
- cycle_initial=|_, _, _| InferableTypeVars::None,
+ cycle_initial=|db, _, _| InferableTypeVars::none(db),
heap_size=ruff_memory_usage::heap_size,
)]
fn inferable_typevars_inner<'db>(
diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs
index bddc2487ec..679f7dda91 100644
--- a/crates/ty_python_semantic/src/types/infer/builder.rs
+++ b/crates/ty_python_semantic/src/types/infer/builder.rs
@@ -4984,7 +4984,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.signature
.generic_context
.map(|generic_context| generic_context.inferable_typevars(db))
- .unwrap_or(InferableTypeVars::None);
+ .unwrap_or_else(|| InferableTypeVars::none(self.db()));
!overload
.constructor_instance_type
@@ -5664,7 +5664,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.try_to_class_literal(self.db())
.and_then(|class| class.generic_context(self.db()))
.map(|generic_context| generic_context.inferable_typevars(self.db()))
- .unwrap_or(InferableTypeVars::None);
+ .unwrap_or_else(|| InferableTypeVars::none(self.db()));
annotation.filter_disjoint_elements(
self.db(),
Type::homogeneous_tuple(self.db(), Type::unknown()),
diff --git a/crates/ty_python_semantic/src/types/infer/builder/subscript.rs b/crates/ty_python_semantic/src/types/infer/builder/subscript.rs
index d07f4ef240..017152ba28 100644
--- a/crates/ty_python_semantic/src/types/infer/builder/subscript.rs
+++ b/crates/ty_python_semantic/src/types/infer/builder/subscript.rs
@@ -636,7 +636,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
db,
bound,
&constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
.is_never_satisfied(db)
{
@@ -669,7 +669,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
db,
typevar_constraints.as_type(db),
&constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
)
.is_never_satisfied(db)
{
diff --git a/crates/ty_python_semantic/src/types/instance.rs b/crates/ty_python_semantic/src/types/instance.rs
index d0e6881002..9f19e485be 100644
--- a/crates/ty_python_semantic/src/types/instance.rs
+++ b/crates/ty_python_semantic/src/types/instance.rs
@@ -717,7 +717,7 @@ impl<'db> ProtocolInstanceType<'db> {
let disjointness_visitor = IsDisjointVisitor::default(&constraints);
let checker = TypeRelationChecker::subtyping(
&constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
&relation_visitor,
&disjointness_visitor,
);
diff --git a/crates/ty_python_semantic/src/types/relation.rs b/crates/ty_python_semantic/src/types/relation.rs
index 565a592637..7b356302ef 100644
--- a/crates/ty_python_semantic/src/types/relation.rs
+++ b/crates/ty_python_semantic/src/types/relation.rs
@@ -293,7 +293,7 @@ impl<'db> Type<'db> {
/// See [`TypeRelation::Subtyping`] for more details.
pub(crate) fn is_subtype_of(self, db: &'db dyn Db, target: Type<'db>) -> bool {
let constraints = ConstraintSetBuilder::new();
- self.when_subtype_of(db, target, &constraints, InferableTypeVars::None)
+ self.when_subtype_of(db, target, &constraints, InferableTypeVars::none(db))
.is_always_satisfied(db)
}
@@ -335,7 +335,7 @@ impl<'db> Type<'db> {
/// See `TypeRelation::Assignability` for more details.
pub fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool {
let constraints = ConstraintSetBuilder::new();
- self.when_assignable_to(db, target, &constraints, InferableTypeVars::None)
+ self.when_assignable_to(db, target, &constraints, InferableTypeVars::none(db))
.is_always_satisfied(db)
}
@@ -376,7 +376,7 @@ impl<'db> Type<'db> {
db,
target,
constraints,
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
TypeRelation::ConstraintSetAssignability,
)
}
@@ -396,7 +396,7 @@ impl<'db> Type<'db> {
db,
other,
&ConstraintSetBuilder::new(),
- InferableTypeVars::None,
+ InferableTypeVars::none(db),
TypeRelation::Redundancy { pure: false },
)
.is_always_satisfied(db)
@@ -477,7 +477,7 @@ impl<'db> Type<'db> {
/// `false` answers in some cases.
pub(crate) fn is_disjoint_from(self, db: &'db dyn Db, other: Type<'db>) -> bool {
let constraints = ConstraintSetBuilder::new();
- self.when_disjoint_from(db, other, &constraints, InferableTypeVars::None)
+ self.when_disjoint_from(db, other, &constraints, InferableTypeVars::none(db))
.is_always_satisfied(db)
}
@@ -556,13 +556,14 @@ impl<'a, 'c, 'db> TypeRelationChecker<'a, 'c, 'db> {
}
pub(super) fn constraint_set_assignability(
+ db: &'db dyn Db,
constraints: &'c ConstraintSetBuilder<'db>,
relation_visitor: &'a HasRelationToVisitor<'db, 'c>,
disjointness_visitor: &'a IsDisjointVisitor<'db, 'c>,
) -> Self {
Self {
constraints,
- inferable: InferableTypeVars::None,
+ inferable: InferableTypeVars::none(db),
relation: TypeRelation::ConstraintSetAssignability,
given: ConstraintSet::from_bool(constraints, false),
relation_visitor,
@@ -1540,12 +1541,12 @@ pub(super) struct EquivalenceChecker<'a, 'c, 'db> {
}
impl<'c, 'db> EquivalenceChecker<'_, 'c, 'db> {
- fn as_relation_checker(&self) -> TypeRelationChecker<'_, 'c, 'db> {
+ fn as_relation_checker(&self, db: &'db dyn Db) -> TypeRelationChecker<'_, 'c, 'db> {
TypeRelationChecker {
relation: TypeRelation::Redundancy { pure: true },
constraints: self.constraints,
given: self.given,
- inferable: InferableTypeVars::None,
+ inferable: InferableTypeVars::none(db),
relation_visitor: self.relation_visitor,
disjointness_visitor: self.disjointness_visitor,
}
@@ -1565,7 +1566,7 @@ impl<'c, 'db> EquivalenceChecker<'_, 'c, 'db> {
left: Type<'db>,
right: Type<'db>,
) -> ConstraintSet<'db, 'c> {
- let relation_checker = self.as_relation_checker();
+ let relation_checker = self.as_relation_checker(db);
relation_checker
.check_type_pair(db, left, right)
.and(db, self.constraints, || {
diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs
index d5b40ae6a5..4a086eb162 100644
--- a/crates/ty_python_semantic/src/types/signatures.rs
+++ b/crates/ty_python_semantic/src/types/signatures.rs
@@ -323,6 +323,7 @@ impl<'db> CallableSignature<'db> {
let relation_visitor = HasRelationToVisitor::default(constraints);
let disjointness_visitor = IsDisjointVisitor::default(constraints);
let checker = TypeRelationChecker::constraint_set_assignability(
+ db,
constraints,
&relation_visitor,
&disjointness_visitor,
@@ -718,7 +719,7 @@ impl<'db> Signature<'db> {
fn inferable_typevars(&self, db: &'db dyn Db) -> InferableTypeVars<'db> {
match self.generic_context {
Some(generic_context) => generic_context.inferable_typevars(db),
- None => InferableTypeVars::None,
+ None => InferableTypeVars::none(db),
}
}
@@ -781,6 +782,7 @@ impl<'db> Signature<'db> {
let relation_visitor = HasRelationToVisitor::default(constraints);
let disjointness_visitor = IsDisjointVisitor::default(constraints);
let checker = TypeRelationChecker::constraint_set_assignability(
+ db,
constraints,
&relation_visitor,
&disjointness_visitor,
Member
Author
There was a problem hiding this comment.
It was partly to avoid the diff churn changing all of the ::None callsites, but more importantly, there are enough of them that I did want to avoid having to hit the global intern hash table to get the ID of the empty set.
carljm
added a commit
that referenced
this pull request
Mar 25, 2026
* main: [ty] make `test-case` a dev-dependency (#24187) [ty] implement cycle normalization for more types to prevent too-many-cycle panics (#24061) [ty] Silence all diagnostics in unreachable code (#24179) [ty] Intern `InferableTypeVars` (#24161) Implement unnecessary-if (RUF050) (#24114) Recognize `Self` annotation and `self` assignment in SLF001 (#24144) Bump the npm version before publish (#24178) [ty] Disallow Self in metaclass and static methods (#23231) Use trusted publishing for NPM packages (#24171) [ty] Respect non-explicitly defined dataclass params (#24170) Add RUF072: warn when using operator on an f-string (#24162) [ty] Check return type of generator functions (#24026) Implement useless-finally (RUF-072) (#24165) [ty] Add test for a dataclass with a default field converter (#24169) [ty] Dataclass field converters (#23088) [flake8-bandit] Treat sys.executable as trusted input in S603 (#24106) [ty] Add support for `typing.Concatenate` (#23689) `ASYNC115`: autofix to use full qualified `anyio.lowlevel` import (#24166) [ty] Disallow read-only fields in TypedDict updates (#24128) Speed up diagnostic rendering (#24146)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Most importantly, this lets us use
InferableTypeVarsas the parameter to a salsa-tracked function, which, try as I might, I end up needing for theSpecializationBuilderrefactoring.Plus, the type was over-thought to begin with. By far most of these are created by querying a
GenericContext, via a method that was already salsa-tracked. The main thing that we lose is that, when checking assignability of generic callables, we used to be able to "combine" the various inferable sets without having to actually construct a new hash set. Now we do have to do that. But, we can salsa-track themergemethod, which should hopefully claw back some of that. Let's see what CI says!