[ty] Add (unused) inferable parameter to type property methods (#20865)

A large part of the diff on #20677 just involves threading a new
`inferable` parameter through all of the type property methods. In the
interests of making that PR easier to review, I've pulled that bit out
into here, so that it can be reviewed in isolation. This should be a
pure refactoring, with no logic changes or behavioral changes.
This commit is contained in:
Douglas Creager 2025-10-15 09:05:15 -04:00 committed by GitHub
parent 85ff4f3eef
commit 8817ea5c84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 580 additions and 185 deletions

File diff suppressed because it is too large Load diff

View file

@ -26,7 +26,9 @@ use crate::types::enums::is_enum_class;
use crate::types::function::{ use crate::types::function::{
DataclassTransformerParams, FunctionDecorators, FunctionType, KnownFunction, OverloadLiteral, DataclassTransformerParams, FunctionDecorators, FunctionType, KnownFunction, OverloadLiteral,
}; };
use crate::types::generics::{Specialization, SpecializationBuilder, SpecializationError}; use crate::types::generics::{
InferableTypeVars, Specialization, SpecializationBuilder, SpecializationError,
};
use crate::types::signatures::{Parameter, ParameterForm, ParameterKind, Parameters}; use crate::types::signatures::{Parameter, ParameterForm, ParameterKind, Parameters};
use crate::types::tuple::{TupleLength, TupleType}; use crate::types::tuple::{TupleLength, TupleType};
use crate::types::{ use crate::types::{
@ -597,7 +599,8 @@ impl<'db> Bindings<'db> {
Type::FunctionLiteral(function_type) => match function_type.known(db) { Type::FunctionLiteral(function_type) => match function_type.known(db) {
Some(KnownFunction::IsEquivalentTo) => { Some(KnownFunction::IsEquivalentTo) => {
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
let constraints = ty_a.when_equivalent_to(db, *ty_b); let constraints =
ty_a.when_equivalent_to(db, *ty_b, InferableTypeVars::None);
let tracked = TrackedConstraintSet::new(db, constraints); let tracked = TrackedConstraintSet::new(db, constraints);
overload.set_return_type(Type::KnownInstance( overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked), KnownInstanceType::ConstraintSet(tracked),
@ -607,7 +610,8 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::IsSubtypeOf) => { Some(KnownFunction::IsSubtypeOf) => {
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
let constraints = ty_a.when_subtype_of(db, *ty_b); let constraints =
ty_a.when_subtype_of(db, *ty_b, InferableTypeVars::None);
let tracked = TrackedConstraintSet::new(db, constraints); let tracked = TrackedConstraintSet::new(db, constraints);
overload.set_return_type(Type::KnownInstance( overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked), KnownInstanceType::ConstraintSet(tracked),
@ -617,7 +621,8 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::IsAssignableTo) => { Some(KnownFunction::IsAssignableTo) => {
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
let constraints = ty_a.when_assignable_to(db, *ty_b); let constraints =
ty_a.when_assignable_to(db, *ty_b, InferableTypeVars::None);
let tracked = TrackedConstraintSet::new(db, constraints); let tracked = TrackedConstraintSet::new(db, constraints);
overload.set_return_type(Type::KnownInstance( overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked), KnownInstanceType::ConstraintSet(tracked),
@ -627,7 +632,8 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::IsDisjointFrom) => { Some(KnownFunction::IsDisjointFrom) => {
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
let constraints = ty_a.when_disjoint_from(db, *ty_b); let constraints =
ty_a.when_disjoint_from(db, *ty_b, InferableTypeVars::None);
let tracked = TrackedConstraintSet::new(db, constraints); let tracked = TrackedConstraintSet::new(db, constraints);
overload.set_return_type(Type::KnownInstance( overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked), KnownInstanceType::ConstraintSet(tracked),
@ -1407,7 +1413,10 @@ impl<'db> CallableBinding<'db> {
let parameter_type = overload.signature.parameters()[*parameter_index] let parameter_type = overload.signature.parameters()[*parameter_index]
.annotated_type() .annotated_type()
.unwrap_or(Type::unknown()); .unwrap_or(Type::unknown());
if argument_type.is_assignable_to(db, parameter_type) { if argument_type
.when_assignable_to(db, parameter_type, overload.inferable_typevars)
.is_always_satisfied()
{
is_argument_assignable_to_any_overload = true; is_argument_assignable_to_any_overload = true;
break 'overload; break 'overload;
} }
@ -1633,7 +1642,14 @@ impl<'db> CallableBinding<'db> {
.unwrap_or(Type::unknown()); .unwrap_or(Type::unknown());
let first_parameter_type = &mut first_parameter_types[parameter_index]; let first_parameter_type = &mut first_parameter_types[parameter_index];
if let Some(first_parameter_type) = first_parameter_type { if let Some(first_parameter_type) = first_parameter_type {
if !first_parameter_type.is_equivalent_to(db, current_parameter_type) { if !first_parameter_type
.when_equivalent_to(
db,
current_parameter_type,
overload.inferable_typevars,
)
.is_always_satisfied()
{
participating_parameter_indexes.insert(parameter_index); participating_parameter_indexes.insert(parameter_index);
} }
} else { } else {
@ -1750,7 +1766,12 @@ impl<'db> CallableBinding<'db> {
matching_overloads.all(|(_, overload)| { matching_overloads.all(|(_, overload)| {
overload overload
.return_type() .return_type()
.is_equivalent_to(db, first_overload_return_type) .when_equivalent_to(
db,
first_overload_return_type,
overload.inferable_typevars,
)
.is_always_satisfied()
}) })
} else { } else {
// No matching overload // No matching overload
@ -2461,6 +2482,7 @@ struct ArgumentTypeChecker<'a, 'db> {
call_expression_tcx: &'a TypeContext<'db>, call_expression_tcx: &'a TypeContext<'db>,
errors: &'a mut Vec<BindingError<'db>>, errors: &'a mut Vec<BindingError<'db>>,
inferable_typevars: InferableTypeVars<'db, 'db>,
specialization: Option<Specialization<'db>>, specialization: Option<Specialization<'db>>,
} }
@ -2482,6 +2504,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
parameter_tys, parameter_tys,
call_expression_tcx, call_expression_tcx,
errors, errors,
inferable_typevars: InferableTypeVars::None,
specialization: None, specialization: None,
} }
} }
@ -2514,11 +2537,12 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
} }
fn infer_specialization(&mut self) { fn infer_specialization(&mut self) {
if self.signature.generic_context.is_none() { let Some(generic_context) = self.signature.generic_context else {
return; return;
} };
let mut builder = SpecializationBuilder::new(self.db); // TODO: Use the list of inferable typevars from the generic context of the callable.
let mut builder = SpecializationBuilder::new(self.db, self.inferable_typevars);
// Note that we infer the annotated type _before_ the arguments if this call is part of // Note that we infer the annotated type _before_ the arguments if this call is part of
// an annotated assignment, to closer match the order of any unions written in the type // an annotated assignment, to closer match the order of any unions written in the type
@ -2563,10 +2587,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
} }
} }
self.specialization = self self.specialization = Some(builder.build(generic_context, *self.call_expression_tcx));
.signature
.generic_context
.map(|gc| builder.build(gc, *self.call_expression_tcx));
} }
fn check_argument_type( fn check_argument_type(
@ -2590,7 +2611,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
// constraint set that we get from this assignability check, instead of inferring and // constraint set that we get from this assignability check, instead of inferring and
// building them in an earlier separate step. // building them in an earlier separate step.
if argument_type if argument_type
.when_assignable_to(self.db, expected_ty) .when_assignable_to(self.db, expected_ty, self.inferable_typevars)
.is_never_satisfied() .is_never_satisfied()
{ {
let positional = matches!(argument, Argument::Positional | Argument::Synthetic) let positional = matches!(argument, Argument::Positional | Argument::Synthetic)
@ -2719,7 +2740,14 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
return; return;
}; };
if !key_type.is_assignable_to(self.db, KnownClass::Str.to_instance(self.db)) { if !key_type
.when_assignable_to(
self.db,
KnownClass::Str.to_instance(self.db),
self.inferable_typevars,
)
.is_always_satisfied()
{
self.errors.push(BindingError::InvalidKeyType { self.errors.push(BindingError::InvalidKeyType {
argument_index: adjusted_argument_index, argument_index: adjusted_argument_index,
provided_ty: key_type, provided_ty: key_type,
@ -2754,8 +2782,8 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
} }
} }
fn finish(self) -> Option<Specialization<'db>> { fn finish(self) -> (InferableTypeVars<'db, 'db>, Option<Specialization<'db>>) {
self.specialization (self.inferable_typevars, self.specialization)
} }
} }
@ -2819,6 +2847,9 @@ pub(crate) struct Binding<'db> {
/// Return type of the call. /// Return type of the call.
return_ty: Type<'db>, return_ty: Type<'db>,
/// The inferable typevars in this signature.
inferable_typevars: InferableTypeVars<'db, 'db>,
/// The specialization that was inferred from the argument types, if the callable is generic. /// The specialization that was inferred from the argument types, if the callable is generic.
specialization: Option<Specialization<'db>>, specialization: Option<Specialization<'db>>,
@ -2845,6 +2876,7 @@ impl<'db> Binding<'db> {
callable_type: signature_type, callable_type: signature_type,
signature_type, signature_type,
return_ty: Type::unknown(), return_ty: Type::unknown(),
inferable_typevars: InferableTypeVars::None,
specialization: None, specialization: None,
argument_matches: Box::from([]), argument_matches: Box::from([]),
variadic_argument_matched_to_variadic_parameter: false, variadic_argument_matched_to_variadic_parameter: false,
@ -2916,7 +2948,7 @@ impl<'db> Binding<'db> {
checker.infer_specialization(); checker.infer_specialization();
checker.check_argument_types(); checker.check_argument_types();
self.specialization = checker.finish(); (self.inferable_typevars, self.specialization) = checker.finish();
if let Some(specialization) = self.specialization { if let Some(specialization) = self.specialization {
self.return_ty = self.return_ty.apply_specialization(db, specialization); self.return_ty = self.return_ty.apply_specialization(db, specialization);
} }
@ -3010,6 +3042,7 @@ impl<'db> Binding<'db> {
fn snapshot(&self) -> BindingSnapshot<'db> { fn snapshot(&self) -> BindingSnapshot<'db> {
BindingSnapshot { BindingSnapshot {
return_ty: self.return_ty, return_ty: self.return_ty,
inferable_typevars: self.inferable_typevars,
specialization: self.specialization, specialization: self.specialization,
argument_matches: self.argument_matches.clone(), argument_matches: self.argument_matches.clone(),
parameter_tys: self.parameter_tys.clone(), parameter_tys: self.parameter_tys.clone(),
@ -3020,6 +3053,7 @@ impl<'db> Binding<'db> {
fn restore(&mut self, snapshot: BindingSnapshot<'db>) { fn restore(&mut self, snapshot: BindingSnapshot<'db>) {
let BindingSnapshot { let BindingSnapshot {
return_ty, return_ty,
inferable_typevars,
specialization, specialization,
argument_matches, argument_matches,
parameter_tys, parameter_tys,
@ -3027,6 +3061,7 @@ impl<'db> Binding<'db> {
} = snapshot; } = snapshot;
self.return_ty = return_ty; self.return_ty = return_ty;
self.inferable_typevars = inferable_typevars;
self.specialization = specialization; self.specialization = specialization;
self.argument_matches = argument_matches; self.argument_matches = argument_matches;
self.parameter_tys = parameter_tys; self.parameter_tys = parameter_tys;
@ -3046,6 +3081,7 @@ impl<'db> Binding<'db> {
/// Resets the state of this binding to its initial state. /// Resets the state of this binding to its initial state.
fn reset(&mut self) { fn reset(&mut self) {
self.return_ty = Type::unknown(); self.return_ty = Type::unknown();
self.inferable_typevars = InferableTypeVars::None;
self.specialization = None; self.specialization = None;
self.argument_matches = Box::from([]); self.argument_matches = Box::from([]);
self.parameter_tys = Box::from([]); self.parameter_tys = Box::from([]);
@ -3056,6 +3092,7 @@ impl<'db> Binding<'db> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct BindingSnapshot<'db> { struct BindingSnapshot<'db> {
return_ty: Type<'db>, return_ty: Type<'db>,
inferable_typevars: InferableTypeVars<'db, 'db>,
specialization: Option<Specialization<'db>>, specialization: Option<Specialization<'db>>,
argument_matches: Box<[MatchedArgument<'db>]>, argument_matches: Box<[MatchedArgument<'db>]>,
parameter_tys: Box<[Option<Type<'db>>]>, parameter_tys: Box<[Option<Type<'db>>]>,
@ -3095,6 +3132,7 @@ impl<'db> CallableBindingSnapshot<'db> {
// ... and update the snapshot with the current state of the binding. // ... and update the snapshot with the current state of the binding.
snapshot.return_ty = binding.return_ty; snapshot.return_ty = binding.return_ty;
snapshot.inferable_typevars = binding.inferable_typevars;
snapshot.specialization = binding.specialization; snapshot.specialization = binding.specialization;
snapshot snapshot
.argument_matches .argument_matches

View file

@ -22,7 +22,7 @@ use crate::types::diagnostic::INVALID_TYPE_ALIAS_TYPE;
use crate::types::enums::enum_metadata; use crate::types::enums::enum_metadata;
use crate::types::function::{DataclassTransformerParams, KnownFunction}; use crate::types::function::{DataclassTransformerParams, KnownFunction};
use crate::types::generics::{ use crate::types::generics::{
GenericContext, Specialization, walk_generic_context, walk_specialization, GenericContext, InferableTypeVars, Specialization, walk_generic_context, walk_specialization,
}; };
use crate::types::infer::nearest_enclosing_class; use crate::types::infer::nearest_enclosing_class;
use crate::types::member::{Member, class_member}; use crate::types::member::{Member, class_member};
@ -540,17 +540,20 @@ impl<'db> ClassType<'db> {
/// Return `true` if `other` is present in this class's MRO. /// Return `true` if `other` is present in this class's MRO.
pub(super) fn is_subclass_of(self, db: &'db dyn Db, other: ClassType<'db>) -> bool { pub(super) fn is_subclass_of(self, db: &'db dyn Db, other: ClassType<'db>) -> bool {
self.when_subclass_of(db, other).is_always_satisfied() self.when_subclass_of(db, other, InferableTypeVars::None)
.is_always_satisfied()
} }
pub(super) fn when_subclass_of( pub(super) fn when_subclass_of(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: ClassType<'db>, other: ClassType<'db>,
inferable: InferableTypeVars<'_, 'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
self.has_relation_to_impl( self.has_relation_to_impl(
db, db,
other, other,
inferable,
TypeRelation::Subtyping, TypeRelation::Subtyping,
&HasRelationToVisitor::default(), &HasRelationToVisitor::default(),
&IsDisjointVisitor::default(), &IsDisjointVisitor::default(),
@ -561,6 +564,7 @@ impl<'db> ClassType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -586,6 +590,7 @@ impl<'db> ClassType<'db> {
base.specialization(db).has_relation_to_impl( base.specialization(db).has_relation_to_impl(
db, db,
other.specialization(db), other.specialization(db),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -610,6 +615,7 @@ impl<'db> ClassType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: ClassType<'db>, other: ClassType<'db>,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
if self == other { if self == other {
@ -628,6 +634,7 @@ impl<'db> ClassType<'db> {
this.specialization(db).is_equivalent_to_impl( this.specialization(db).is_equivalent_to_impl(
db, db,
other.specialization(db), other.specialization(db),
inferable,
visitor, visitor,
) )
}) })

View file

@ -73,7 +73,7 @@ use crate::types::diagnostic::{
report_runtime_check_against_non_runtime_checkable_protocol, report_runtime_check_against_non_runtime_checkable_protocol,
}; };
use crate::types::display::DisplaySettings; use crate::types::display::DisplaySettings;
use crate::types::generics::GenericContext; use crate::types::generics::{GenericContext, InferableTypeVars};
use crate::types::ide_support::all_members; use crate::types::ide_support::all_members;
use crate::types::narrow::ClassInfoConstraintFunction; use crate::types::narrow::ClassInfoConstraintFunction;
use crate::types::signatures::{CallableSignature, Signature}; use crate::types::signatures::{CallableSignature, Signature};
@ -81,9 +81,9 @@ use crate::types::visitor::any_over_type;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase,
ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext, TypeMapping, NormalizedVisitor, SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext,
TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature, TypeMapping, TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature,
}; };
use crate::{Db, FxOrderSet, ModuleName, resolve_module}; use crate::{Db, FxOrderSet, ModuleName, resolve_module};
@ -959,46 +959,42 @@ impl<'db> FunctionType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
match relation {
TypeRelation::Subtyping | TypeRelation::Redundancy => {
ConstraintSet::from(self.is_subtype_of(db, other))
}
TypeRelation::Assignability => ConstraintSet::from(self.is_assignable_to(db, other)),
}
}
pub(crate) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
// A function type is the subtype of itself, and not of any other function type. However, // A function type is the subtype of itself, and not of any other function type. However,
// our representation of a function type includes any specialization that should be applied // our representation of a function type includes any specialization that should be applied
// to the signature. Different specializations of the same function type are only subtypes // to the signature. Different specializations of the same function type are only subtypes
// of each other if they result in subtype signatures. // of each other if they result in subtype signatures.
if self.normalized(db) == other.normalized(db) { if matches!(relation, TypeRelation::Subtyping | TypeRelation::Redundancy)
return true; && self.normalized(db) == other.normalized(db)
} {
if self.literal(db) != other.literal(db) { return ConstraintSet::from(true);
return false;
}
let self_signature = self.signature(db);
let other_signature = other.signature(db);
self_signature.is_subtype_of(db, other_signature)
} }
pub(crate) fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool { if self.literal(db) != other.literal(db) {
// A function type is assignable to itself, and not to any other function type. However, return ConstraintSet::from(false);
// our representation of a function type includes any specialization that should be applied }
// to the signature. Different specializations of the same function type are only
// assignable to each other if they result in assignable signatures. let self_signature = self.signature(db);
self.literal(db) == other.literal(db) let other_signature = other.signature(db);
&& self.signature(db).is_assignable_to(db, other.signature(db)) self_signature.has_relation_to_impl(
db,
other_signature,
inferable,
relation,
relation_visitor,
disjointness_visitor,
)
} }
pub(crate) fn is_equivalent_to_impl( pub(crate) fn is_equivalent_to_impl(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
if self.normalized(db) == other.normalized(db) { if self.normalized(db) == other.normalized(db) {
@ -1009,7 +1005,7 @@ impl<'db> FunctionType<'db> {
} }
let self_signature = self.signature(db); let self_signature = self.signature(db);
let other_signature = other.signature(db); let other_signature = other.signature(db);
self_signature.is_equivalent_to_impl(db, other_signature, visitor) self_signature.is_equivalent_to_impl(db, other_signature, inferable, visitor)
} }
pub(crate) fn find_legacy_typevars_impl( pub(crate) fn find_legacy_typevars_impl(

View file

@ -1,4 +1,4 @@
use crate::types::constraints::ConstraintSet; use std::marker::PhantomData;
use itertools::Itertools; use itertools::Itertools;
use ruff_python_ast as ast; use ruff_python_ast as ast;
@ -9,6 +9,7 @@ use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind, ScopeId};
use crate::semantic_index::{SemanticIndex, semantic_index}; use crate::semantic_index::{SemanticIndex, semantic_index};
use crate::types::class::ClassType; use crate::types::class::ClassType;
use crate::types::class_base::ClassBase; use crate::types::class_base::ClassBase;
use crate::types::constraints::ConstraintSet;
use crate::types::infer::infer_definition_types; use crate::types::infer::infer_definition_types;
use crate::types::instance::{Protocol, ProtocolInstanceType}; use crate::types::instance::{Protocol, ProtocolInstanceType};
use crate::types::signatures::{Parameter, Parameters, Signature}; use crate::types::signatures::{Parameter, Parameters, Signature};
@ -140,6 +141,16 @@ pub(crate) fn typing_self<'db>(
.map(typevar_to_type) .map(typevar_to_type)
} }
#[derive(Clone, Copy, Debug)]
pub(crate) enum InferableTypeVars<'a, 'db> {
None,
// TODO: This variant isn't used, and only exists so that we can include the 'a and 'db in the
// type definition. They will be used soon when we start creating real InferableTypeVars
// instances.
#[expect(unused)]
Unused(PhantomData<&'a &'db ()>),
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub struct GenericContextTypeVar<'db> { pub struct GenericContextTypeVar<'db> {
bound_typevar: BoundTypeVarInstance<'db>, bound_typevar: BoundTypeVarInstance<'db>,
@ -593,12 +604,14 @@ pub(super) fn walk_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Si
} }
} }
#[expect(clippy::too_many_arguments)]
fn is_subtype_in_invariant_position<'db>( fn is_subtype_in_invariant_position<'db>(
db: &'db dyn Db, db: &'db dyn Db,
derived_type: &Type<'db>, derived_type: &Type<'db>,
derived_materialization: MaterializationKind, derived_materialization: MaterializationKind,
base_type: &Type<'db>, base_type: &Type<'db>,
base_materialization: MaterializationKind, base_materialization: MaterializationKind,
inferable: InferableTypeVars<'_, 'db>,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
@ -623,6 +636,7 @@ fn is_subtype_in_invariant_position<'db>(
derived.has_relation_to_impl( derived.has_relation_to_impl(
db, db,
base, base,
inferable,
TypeRelation::Subtyping, TypeRelation::Subtyping,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -675,6 +689,7 @@ fn has_relation_in_invariant_position<'db>(
derived_materialization: Option<MaterializationKind>, derived_materialization: Option<MaterializationKind>,
base_type: &Type<'db>, base_type: &Type<'db>,
base_materialization: Option<MaterializationKind>, base_materialization: Option<MaterializationKind>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -688,6 +703,7 @@ fn has_relation_in_invariant_position<'db>(
derived_mat, derived_mat,
base_type, base_type,
base_mat, base_mat,
inferable,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
), ),
@ -707,6 +723,7 @@ fn has_relation_in_invariant_position<'db>(
.has_relation_to_impl( .has_relation_to_impl(
db, db,
*base_type, *base_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -715,6 +732,7 @@ fn has_relation_in_invariant_position<'db>(
base_type.has_relation_to_impl( base_type.has_relation_to_impl(
db, db,
*derived_type, *derived_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -728,6 +746,7 @@ fn has_relation_in_invariant_position<'db>(
MaterializationKind::Top, MaterializationKind::Top,
base_type, base_type,
base_mat, base_mat,
inferable,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
) )
@ -739,6 +758,7 @@ fn has_relation_in_invariant_position<'db>(
derived_mat, derived_mat,
base_type, base_type,
MaterializationKind::Bottom, MaterializationKind::Bottom,
inferable,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
) )
@ -750,6 +770,7 @@ fn has_relation_in_invariant_position<'db>(
MaterializationKind::Bottom, MaterializationKind::Bottom,
base_type, base_type,
base_mat, base_mat,
inferable,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
), ),
@ -759,6 +780,7 @@ fn has_relation_in_invariant_position<'db>(
derived_mat, derived_mat,
base_type, base_type,
MaterializationKind::Top, MaterializationKind::Top,
inferable,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
), ),
@ -1002,6 +1024,7 @@ impl<'db> Specialization<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -1016,6 +1039,7 @@ impl<'db> Specialization<'db> {
return self_tuple.has_relation_to_impl( return self_tuple.has_relation_to_impl(
db, db,
other_tuple, other_tuple,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -1043,6 +1067,7 @@ impl<'db> Specialization<'db> {
self_materialization_kind, self_materialization_kind,
other_type, other_type,
other_materialization_kind, other_materialization_kind,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -1050,6 +1075,7 @@ impl<'db> Specialization<'db> {
TypeVarVariance::Covariant => self_type.has_relation_to_impl( TypeVarVariance::Covariant => self_type.has_relation_to_impl(
db, db,
*other_type, *other_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -1057,6 +1083,7 @@ impl<'db> Specialization<'db> {
TypeVarVariance::Contravariant => other_type.has_relation_to_impl( TypeVarVariance::Contravariant => other_type.has_relation_to_impl(
db, db,
*self_type, *self_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -1075,6 +1102,7 @@ impl<'db> Specialization<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Specialization<'db>, other: Specialization<'db>,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
if self.materialization_kind(db) != other.materialization_kind(db) { if self.materialization_kind(db) != other.materialization_kind(db) {
@ -1100,7 +1128,7 @@ impl<'db> Specialization<'db> {
TypeVarVariance::Invariant TypeVarVariance::Invariant
| TypeVarVariance::Covariant | TypeVarVariance::Covariant
| TypeVarVariance::Contravariant => { | TypeVarVariance::Contravariant => {
self_type.is_equivalent_to_impl(db, *other_type, visitor) self_type.is_equivalent_to_impl(db, *other_type, inferable, visitor)
} }
TypeVarVariance::Bivariant => ConstraintSet::from(true), TypeVarVariance::Bivariant => ConstraintSet::from(true),
}; };
@ -1113,7 +1141,8 @@ impl<'db> Specialization<'db> {
(Some(_), None) | (None, Some(_)) => return ConstraintSet::from(false), (Some(_), None) | (None, Some(_)) => return ConstraintSet::from(false),
(None, None) => {} (None, None) => {}
(Some(self_tuple), Some(other_tuple)) => { (Some(self_tuple), Some(other_tuple)) => {
let compatible = self_tuple.is_equivalent_to_impl(db, other_tuple, visitor); let compatible =
self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor);
if result.intersect(db, compatible).is_never_satisfied() { if result.intersect(db, compatible).is_never_satisfied() {
return result; return result;
} }
@ -1168,13 +1197,15 @@ impl<'db> PartialSpecialization<'_, 'db> {
/// specialization of a generic function. /// specialization of a generic function.
pub(crate) struct SpecializationBuilder<'db> { pub(crate) struct SpecializationBuilder<'db> {
db: &'db dyn Db, db: &'db dyn Db,
inferable: InferableTypeVars<'db, 'db>,
types: FxHashMap<BoundTypeVarIdentity<'db>, Type<'db>>, types: FxHashMap<BoundTypeVarIdentity<'db>, Type<'db>>,
} }
impl<'db> SpecializationBuilder<'db> { impl<'db> SpecializationBuilder<'db> {
pub(crate) fn new(db: &'db dyn Db) -> Self { pub(crate) fn new(db: &'db dyn Db, inferable: InferableTypeVars<'db, 'db>) -> Self {
Self { Self {
db, db,
inferable,
types: FxHashMap::default(), types: FxHashMap::default(),
} }
} }
@ -1244,14 +1275,16 @@ impl<'db> SpecializationBuilder<'db> {
// without specializing `T` to `None`. // without specializing `T` to `None`.
if !matches!(formal, Type::ProtocolInstance(_)) if !matches!(formal, Type::ProtocolInstance(_))
&& !actual.is_never() && !actual.is_never()
&& actual.is_subtype_of(self.db, formal) && actual
.when_subtype_of(self.db, formal, self.inferable)
.is_always_satisfied()
{ {
return Ok(()); return Ok(());
} }
// For example, if `formal` is `list[T]` and `actual` is `list[int] | None`, we want to specialize `T` to `int`. // For example, if `formal` is `list[T]` and `actual` is `list[int] | None`, we want to specialize `T` to `int`.
// So, here we remove the union elements that are not related to `formal`. // So, here we remove the union elements that are not related to `formal`.
actual = actual.filter_disjoint_elements(self.db, formal); actual = actual.filter_disjoint_elements(self.db, formal, self.inferable);
match (formal, actual) { match (formal, actual) {
// TODO: We haven't implemented a full unification solver yet. If typevars appear in // TODO: We haven't implemented a full unification solver yet. If typevars appear in
@ -1324,7 +1357,10 @@ impl<'db> SpecializationBuilder<'db> {
(Type::TypeVar(bound_typevar), ty) | (ty, Type::TypeVar(bound_typevar)) => { (Type::TypeVar(bound_typevar), ty) | (ty, Type::TypeVar(bound_typevar)) => {
match bound_typevar.typevar(self.db).bound_or_constraints(self.db) { match bound_typevar.typevar(self.db).bound_or_constraints(self.db) {
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => { Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
if !ty.is_assignable_to(self.db, bound) { if !ty
.when_assignable_to(self.db, bound, self.inferable)
.is_always_satisfied()
{
return Err(SpecializationError::MismatchedBound { return Err(SpecializationError::MismatchedBound {
bound_typevar, bound_typevar,
argument: ty, argument: ty,
@ -1334,7 +1370,10 @@ impl<'db> SpecializationBuilder<'db> {
} }
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
for constraint in constraints.elements(self.db) { for constraint in constraints.elements(self.db) {
if ty.is_assignable_to(self.db, *constraint) { if ty
.when_assignable_to(self.db, *constraint, self.inferable)
.is_always_satisfied()
{
self.add_type_mapping(bound_typevar, *constraint); self.add_type_mapping(bound_typevar, *constraint);
return Ok(()); return Ok(());
} }

View file

@ -75,8 +75,10 @@ use crate::types::diagnostic::{
use crate::types::function::{ use crate::types::function::{
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral, FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
}; };
use crate::types::generics::{GenericContext, bind_typevar, enclosing_generic_contexts}; use crate::types::generics::{
use crate::types::generics::{LegacyGenericBase, SpecializationBuilder}; GenericContext, InferableTypeVars, LegacyGenericBase, SpecializationBuilder, bind_typevar,
enclosing_generic_contexts,
};
use crate::types::infer::nearest_enclosing_function; use crate::types::infer::nearest_enclosing_function;
use crate::types::instance::SliceLiteral; use crate::types::instance::SliceLiteral;
use crate::types::mro::MroErrorKind; use crate::types::mro::MroErrorKind;
@ -5964,11 +5966,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
return None; return None;
}; };
// TODO: Use the list of inferable typevars from the generic context of the collection
// class.
let inferable = InferableTypeVars::None;
let tcx = tcx.map_annotation(|annotation| { let tcx = tcx.map_annotation(|annotation| {
// Remove any union elements of `annotation` that are not related to `collection_ty`. // Remove any union elements of `annotation` that are not related to `collection_ty`.
// e.g. `annotation: list[int] | None => list[int]` if `collection_ty: list` // e.g. `annotation: list[int] | None => list[int]` if `collection_ty: list`
let collection_ty = collection_class.to_instance(self.db()); let collection_ty = collection_class.to_instance(self.db());
annotation.filter_disjoint_elements(self.db(), collection_ty) annotation.filter_disjoint_elements(self.db(), collection_ty, inferable)
}); });
// Extract the annotated type of `T`, if provided. // Extract the annotated type of `T`, if provided.
@ -5977,7 +5982,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.map(|specialization| specialization.types(self.db())); .map(|specialization| specialization.types(self.db()));
// Create a set of constraints to infer a precise type for `T`. // Create a set of constraints to infer a precise type for `T`.
let mut builder = SpecializationBuilder::new(self.db()); let mut builder = SpecializationBuilder::new(self.db(), inferable);
match annotated_elt_tys { match annotated_elt_tys {
// The annotated type acts as a constraint for `T`. // The annotated type acts as a constraint for `T`.

View file

@ -9,7 +9,7 @@ use crate::place::PlaceAndQualifiers;
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
use crate::types::enums::is_single_member_enum; use crate::types::enums::is_single_member_enum;
use crate::types::generics::walk_specialization; use crate::types::generics::{InferableTypeVars, walk_specialization};
use crate::types::protocol_class::walk_protocol_interface; use crate::types::protocol_class::walk_protocol_interface;
use crate::types::tuple::{TupleSpec, TupleType}; use crate::types::tuple::{TupleSpec, TupleType};
use crate::types::{ use crate::types::{
@ -121,6 +121,7 @@ impl<'db> Type<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
protocol: ProtocolInstanceType<'db>, protocol: ProtocolInstanceType<'db>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -129,6 +130,7 @@ impl<'db> Type<'db> {
self_protocol.interface(db).has_relation_to_impl( self_protocol.interface(db).has_relation_to_impl(
db, db,
protocol.interface(db), protocol.interface(db),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -142,6 +144,7 @@ impl<'db> Type<'db> {
member.is_satisfied_by( member.is_satisfied_by(
db, db,
self, self,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -173,6 +176,7 @@ impl<'db> Type<'db> {
type_to_test.has_relation_to_impl( type_to_test.has_relation_to_impl(
db, db,
Type::NominalInstance(nominal_instance), Type::NominalInstance(nominal_instance),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -360,6 +364,7 @@ impl<'db> NominalInstanceType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -372,6 +377,7 @@ impl<'db> NominalInstanceType<'db> {
) => tuple1.has_relation_to_impl( ) => tuple1.has_relation_to_impl(
db, db,
tuple2, tuple2,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -379,6 +385,7 @@ impl<'db> NominalInstanceType<'db> {
_ => self.class(db).has_relation_to_impl( _ => self.class(db).has_relation_to_impl(
db, db,
other.class(db), other.class(db),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -390,18 +397,19 @@ impl<'db> NominalInstanceType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
match (self.0, other.0) { match (self.0, other.0) {
( (
NominalInstanceInner::ExactTuple(tuple1), NominalInstanceInner::ExactTuple(tuple1),
NominalInstanceInner::ExactTuple(tuple2), NominalInstanceInner::ExactTuple(tuple2),
) => tuple1.is_equivalent_to_impl(db, tuple2, visitor), ) => tuple1.is_equivalent_to_impl(db, tuple2, inferable, visitor),
(NominalInstanceInner::Object, NominalInstanceInner::Object) => { (NominalInstanceInner::Object, NominalInstanceInner::Object) => {
ConstraintSet::from(true) ConstraintSet::from(true)
} }
(NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => { (NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => {
class1.is_equivalent_to_impl(db, class2, visitor) class1.is_equivalent_to_impl(db, class2, inferable, visitor)
} }
_ => ConstraintSet::from(false), _ => ConstraintSet::from(false),
} }
@ -411,6 +419,7 @@ impl<'db> NominalInstanceType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
@ -423,6 +432,7 @@ impl<'db> NominalInstanceType<'db> {
let compatible = self_spec.is_disjoint_from_impl( let compatible = self_spec.is_disjoint_from_impl(
db, db,
&other_spec, &other_spec,
inferable,
disjointness_visitor, disjointness_visitor,
relation_visitor, relation_visitor,
); );
@ -650,6 +660,7 @@ impl<'db> ProtocolInstanceType<'db> {
.satisfies_protocol( .satisfies_protocol(
db, db,
protocol, protocol,
InferableTypeVars::None,
TypeRelation::Subtyping, TypeRelation::Subtyping,
&HasRelationToVisitor::default(), &HasRelationToVisitor::default(),
&IsDisjointVisitor::default(), &IsDisjointVisitor::default(),
@ -708,6 +719,7 @@ impl<'db> ProtocolInstanceType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
_inferable: InferableTypeVars<'_, 'db>,
_visitor: &IsEquivalentVisitor<'db>, _visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
if self == other { if self == other {
@ -729,6 +741,7 @@ impl<'db> ProtocolInstanceType<'db> {
self, self,
_db: &'db dyn Db, _db: &'db dyn Db,
_other: Self, _other: Self,
_inferable: InferableTypeVars<'_, 'db>,
_visitor: &IsDisjointVisitor<'db>, _visitor: &IsDisjointVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
ConstraintSet::from(false) ConstraintSet::from(false)

View file

@ -22,6 +22,7 @@ use crate::{
constraints::{ConstraintSet, IteratorConstraintsExtension, OptionConstraintsExtension}, constraints::{ConstraintSet, IteratorConstraintsExtension, OptionConstraintsExtension},
context::InferContext, context::InferContext,
diagnostic::report_undeclared_protocol_member, diagnostic::report_undeclared_protocol_member,
generics::InferableTypeVars,
signatures::{Parameter, Parameters}, signatures::{Parameter, Parameters},
todo_type, todo_type,
}, },
@ -235,6 +236,7 @@ impl<'db> ProtocolInterface<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -276,6 +278,7 @@ impl<'db> ProtocolInterface<'db> {
our_type.has_relation_to_impl( our_type.has_relation_to_impl(
db, db,
Type::Callable(other_type.bind_self(db)), Type::Callable(other_type.bind_self(db)),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -288,6 +291,7 @@ impl<'db> ProtocolInterface<'db> {
) => our_method.bind_self(db).has_relation_to_impl( ) => our_method.bind_self(db).has_relation_to_impl(
db, db,
other_method.bind_self(db), other_method.bind_self(db),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -300,6 +304,7 @@ impl<'db> ProtocolInterface<'db> {
.has_relation_to_impl( .has_relation_to_impl(
db, db,
other_type, other_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -308,6 +313,7 @@ impl<'db> ProtocolInterface<'db> {
other_type.has_relation_to_impl( other_type.has_relation_to_impl(
db, db,
our_type, our_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -605,6 +611,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: Type<'db>, other: Type<'db>,
inferable: InferableTypeVars<'_, 'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
@ -613,9 +620,13 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
ProtocolMemberKind::Property(_) | ProtocolMemberKind::Method(_) => { ProtocolMemberKind::Property(_) | ProtocolMemberKind::Method(_) => {
ConstraintSet::from(false) ConstraintSet::from(false)
} }
ProtocolMemberKind::Other(ty) => { ProtocolMemberKind::Other(ty) => ty.is_disjoint_from_impl(
ty.is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor) db,
} other,
inferable,
disjointness_visitor,
relation_visitor,
),
} }
} }
@ -625,6 +636,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: Type<'db>, other: Type<'db>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -664,6 +676,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
attribute_type.has_relation_to_impl( attribute_type.has_relation_to_impl(
db, db,
Type::Callable(method.bind_self(db)), Type::Callable(method.bind_self(db)),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -684,6 +697,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
.has_relation_to_impl( .has_relation_to_impl(
db, db,
attribute_type, attribute_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -692,6 +706,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
attribute_type.has_relation_to_impl( attribute_type.has_relation_to_impl(
db, db,
*member_type, *member_type,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,

View file

@ -23,7 +23,9 @@ use super::{
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
use crate::types::function::FunctionType; use crate::types::function::FunctionType;
use crate::types::generics::{GenericContext, typing_self, walk_generic_context}; use crate::types::generics::{
GenericContext, InferableTypeVars, typing_self, walk_generic_context,
};
use crate::types::infer::nearest_enclosing_class; use crate::types::infer::nearest_enclosing_class;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral, ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral,
@ -174,41 +176,27 @@ impl<'db> CallableSignature<'db> {
} }
} }
/// Check whether this callable type is a subtype of another callable type. fn is_subtype_of_impl(
/// &self,
/// See [`Type::is_subtype_of`] for more details. db: &'db dyn Db,
pub(crate) fn is_subtype_of(&self, db: &'db dyn Db, other: &Self) -> bool { other: &Self,
self.is_subtype_of_impl(db, other).is_always_satisfied() inferable: InferableTypeVars<'_, 'db>,
} ) -> ConstraintSet<'db> {
fn is_subtype_of_impl(&self, db: &'db dyn Db, other: &Self) -> ConstraintSet<'db> {
self.has_relation_to_impl( self.has_relation_to_impl(
db, db,
other, other,
inferable,
TypeRelation::Subtyping, TypeRelation::Subtyping,
&HasRelationToVisitor::default(), &HasRelationToVisitor::default(),
&IsDisjointVisitor::default(), &IsDisjointVisitor::default(),
) )
} }
/// Check whether this callable type is assignable to another callable type.
///
/// See [`Type::is_assignable_to`] for more details.
pub(crate) fn is_assignable_to(&self, db: &'db dyn Db, other: &Self) -> bool {
self.has_relation_to_impl(
db,
other,
TypeRelation::Assignability,
&HasRelationToVisitor::default(),
&IsDisjointVisitor::default(),
)
.is_always_satisfied()
}
pub(crate) fn has_relation_to_impl( pub(crate) fn has_relation_to_impl(
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Self, other: &Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -217,6 +205,7 @@ impl<'db> CallableSignature<'db> {
db, db,
&self.overloads, &self.overloads,
&other.overloads, &other.overloads,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -229,6 +218,7 @@ impl<'db> CallableSignature<'db> {
db: &'db dyn Db, db: &'db dyn Db,
self_signatures: &[Signature<'db>], self_signatures: &[Signature<'db>],
other_signatures: &[Signature<'db>], other_signatures: &[Signature<'db>],
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -239,6 +229,7 @@ impl<'db> CallableSignature<'db> {
self_signature.has_relation_to_impl( self_signature.has_relation_to_impl(
db, db,
other_signature, other_signature,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -251,6 +242,7 @@ impl<'db> CallableSignature<'db> {
db, db,
std::slice::from_ref(self_signature), std::slice::from_ref(self_signature),
other_signatures, other_signatures,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -263,6 +255,7 @@ impl<'db> CallableSignature<'db> {
db, db,
self_signatures, self_signatures,
std::slice::from_ref(other_signature), std::slice::from_ref(other_signature),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -275,6 +268,7 @@ impl<'db> CallableSignature<'db> {
db, db,
self_signatures, self_signatures,
std::slice::from_ref(other_signature), std::slice::from_ref(other_signature),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -290,20 +284,21 @@ impl<'db> CallableSignature<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Self, other: &Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
match (self.overloads.as_slice(), other.overloads.as_slice()) { match (self.overloads.as_slice(), other.overloads.as_slice()) {
([self_signature], [other_signature]) => { ([self_signature], [other_signature]) => {
// Common case: both callable types contain a single signature, use the custom // Common case: both callable types contain a single signature, use the custom
// equivalence check instead of delegating it to the subtype check. // equivalence check instead of delegating it to the subtype check.
self_signature.is_equivalent_to_impl(db, other_signature, visitor) self_signature.is_equivalent_to_impl(db, other_signature, inferable, visitor)
} }
(_, _) => { (_, _) => {
if self == other { if self == other {
return ConstraintSet::from(true); return ConstraintSet::from(true);
} }
self.is_subtype_of_impl(db, other) self.is_subtype_of_impl(db, other, inferable)
.and(db, || other.is_subtype_of_impl(db, self)) .and(db, || other.is_subtype_of_impl(db, self, inferable))
} }
} }
} }
@ -619,6 +614,7 @@ impl<'db> Signature<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Signature<'db>, other: &Signature<'db>,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
let mut result = ConstraintSet::from(true); let mut result = ConstraintSet::from(true);
@ -626,7 +622,10 @@ impl<'db> Signature<'db> {
let self_type = self_type.unwrap_or(Type::unknown()); let self_type = self_type.unwrap_or(Type::unknown());
let other_type = other_type.unwrap_or(Type::unknown()); let other_type = other_type.unwrap_or(Type::unknown());
!result !result
.intersect(db, self_type.is_equivalent_to_impl(db, other_type, visitor)) .intersect(
db,
self_type.is_equivalent_to_impl(db, other_type, inferable, visitor),
)
.is_never_satisfied() .is_never_satisfied()
}; };
@ -702,6 +701,7 @@ impl<'db> Signature<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Signature<'db>, other: &Signature<'db>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -777,6 +777,7 @@ impl<'db> Signature<'db> {
type1.has_relation_to_impl( type1.has_relation_to_impl(
db, db,
type2, type2,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,

View file

@ -1,6 +1,7 @@
use crate::place::PlaceAndQualifiers; use crate::place::PlaceAndQualifiers;
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::types::constraints::ConstraintSet; use crate::types::constraints::ConstraintSet;
use crate::types::generics::InferableTypeVars;
use crate::types::variance::VarianceInferable; use crate::types::variance::VarianceInferable;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType, ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType,
@ -135,6 +136,7 @@ impl<'db> SubclassOfType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: SubclassOfType<'db>, other: SubclassOfType<'db>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -157,6 +159,7 @@ impl<'db> SubclassOfType<'db> {
.has_relation_to_impl( .has_relation_to_impl(
db, db,
other_class, other_class,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -171,6 +174,7 @@ impl<'db> SubclassOfType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
_inferable: InferableTypeVars<'_, 'db>,
_visitor: &IsDisjointVisitor<'db>, _visitor: &IsDisjointVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
match (self.subclass_of, other.subclass_of) { match (self.subclass_of, other.subclass_of) {

View file

@ -24,6 +24,7 @@ use itertools::{Either, EitherOrBoth, Itertools};
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::types::class::{ClassType, KnownClass}; use crate::types::class::{ClassType, KnownClass};
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
use crate::types::generics::InferableTypeVars;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation,
@ -258,6 +259,7 @@ impl<'db> TupleType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -265,6 +267,7 @@ impl<'db> TupleType<'db> {
self.tuple(db).has_relation_to_impl( self.tuple(db).has_relation_to_impl(
db, db,
other.tuple(db), other.tuple(db),
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -275,10 +278,11 @@ impl<'db> TupleType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
other: Self, other: Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
self.tuple(db) self.tuple(db)
.is_equivalent_to_impl(db, other.tuple(db), visitor) .is_equivalent_to_impl(db, other.tuple(db), inferable, visitor)
} }
pub(crate) fn is_single_valued(self, db: &'db dyn Db) -> bool { pub(crate) fn is_single_valued(self, db: &'db dyn Db) -> bool {
@ -442,6 +446,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Tuple<Type<'db>>, other: &Tuple<Type<'db>>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -453,6 +458,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
self_ty.has_relation_to_impl( self_ty.has_relation_to_impl(
db, db,
*other_ty, *other_ty,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -473,6 +479,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
let element_constraints = self_ty.has_relation_to_impl( let element_constraints = self_ty.has_relation_to_impl(
db, db,
*other_ty, *other_ty,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -491,6 +498,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
let element_constraints = self_ty.has_relation_to_impl( let element_constraints = self_ty.has_relation_to_impl(
db, db,
*other_ty, *other_ty,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -510,6 +518,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
self_ty.has_relation_to_impl( self_ty.has_relation_to_impl(
db, db,
other.variable, other.variable,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -524,13 +533,14 @@ impl<'db> FixedLengthTuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Self, other: &Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
ConstraintSet::from(self.0.len() == other.0.len()).and(db, || { ConstraintSet::from(self.0.len() == other.0.len()).and(db, || {
(self.0.iter()) (self.0.iter())
.zip(&other.0) .zip(&other.0)
.when_all(db, |(self_ty, other_ty)| { .when_all(db, |(self_ty, other_ty)| {
self_ty.is_equivalent_to_impl(db, *other_ty, visitor) self_ty.is_equivalent_to_impl(db, *other_ty, inferable, visitor)
}) })
}) })
} }
@ -793,6 +803,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Tuple<Type<'db>>, other: &Tuple<Type<'db>>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -825,6 +836,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
let element_constraints = self_ty.has_relation_to_impl( let element_constraints = self_ty.has_relation_to_impl(
db, db,
other_ty, other_ty,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -844,6 +856,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
let element_constraints = self_ty.has_relation_to_impl( let element_constraints = self_ty.has_relation_to_impl(
db, db,
other_ty, other_ty,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -884,6 +897,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
EitherOrBoth::Both(self_ty, other_ty) => self_ty.has_relation_to_impl( EitherOrBoth::Both(self_ty, other_ty) => self_ty.has_relation_to_impl(
db, db,
other_ty, other_ty,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -891,6 +905,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl( EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl(
db, db,
other.variable, other.variable,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -918,6 +933,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
EitherOrBoth::Both(self_ty, other_ty) => self_ty.has_relation_to_impl( EitherOrBoth::Both(self_ty, other_ty) => self_ty.has_relation_to_impl(
db, db,
*other_ty, *other_ty,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -925,6 +941,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl( EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl(
db, db,
other.variable, other.variable,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -945,6 +962,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
self.variable.has_relation_to_impl( self.variable.has_relation_to_impl(
db, db,
other.variable, other.variable,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -958,16 +976,17 @@ impl<'db> VariableLengthTuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Self, other: &Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
self.variable self.variable
.is_equivalent_to_impl(db, other.variable, visitor) .is_equivalent_to_impl(db, other.variable, inferable, visitor)
.and(db, || { .and(db, || {
(self.prenormalized_prefix_elements(db, None)) (self.prenormalized_prefix_elements(db, None))
.zip_longest(other.prenormalized_prefix_elements(db, None)) .zip_longest(other.prenormalized_prefix_elements(db, None))
.when_all(db, |pair| match pair { .when_all(db, |pair| match pair {
EitherOrBoth::Both(self_ty, other_ty) => { EitherOrBoth::Both(self_ty, other_ty) => {
self_ty.is_equivalent_to_impl(db, other_ty, visitor) self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
} }
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => { EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
ConstraintSet::from(false) ConstraintSet::from(false)
@ -979,7 +998,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
.zip_longest(other.prenormalized_suffix_elements(db, None)) .zip_longest(other.prenormalized_suffix_elements(db, None))
.when_all(db, |pair| match pair { .when_all(db, |pair| match pair {
EitherOrBoth::Both(self_ty, other_ty) => { EitherOrBoth::Both(self_ty, other_ty) => {
self_ty.is_equivalent_to_impl(db, other_ty, visitor) self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
} }
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => { EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
ConstraintSet::from(false) ConstraintSet::from(false)
@ -1170,6 +1189,7 @@ impl<'db> Tuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Self, other: &Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation, relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
@ -1178,6 +1198,7 @@ impl<'db> Tuple<Type<'db>> {
Tuple::Fixed(self_tuple) => self_tuple.has_relation_to_impl( Tuple::Fixed(self_tuple) => self_tuple.has_relation_to_impl(
db, db,
other, other,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -1185,6 +1206,7 @@ impl<'db> Tuple<Type<'db>> {
Tuple::Variable(self_tuple) => self_tuple.has_relation_to_impl( Tuple::Variable(self_tuple) => self_tuple.has_relation_to_impl(
db, db,
other, other,
inferable,
relation, relation,
relation_visitor, relation_visitor,
disjointness_visitor, disjointness_visitor,
@ -1196,14 +1218,15 @@ impl<'db> Tuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Self, other: &Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>, visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
match (self, other) { match (self, other) {
(Tuple::Fixed(self_tuple), Tuple::Fixed(other_tuple)) => { (Tuple::Fixed(self_tuple), Tuple::Fixed(other_tuple)) => {
self_tuple.is_equivalent_to_impl(db, other_tuple, visitor) self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor)
} }
(Tuple::Variable(self_tuple), Tuple::Variable(other_tuple)) => { (Tuple::Variable(self_tuple), Tuple::Variable(other_tuple)) => {
self_tuple.is_equivalent_to_impl(db, other_tuple, visitor) self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor)
} }
(Tuple::Fixed(_), Tuple::Variable(_)) | (Tuple::Variable(_), Tuple::Fixed(_)) => { (Tuple::Fixed(_), Tuple::Variable(_)) | (Tuple::Variable(_), Tuple::Fixed(_)) => {
ConstraintSet::from(false) ConstraintSet::from(false)
@ -1215,6 +1238,7 @@ impl<'db> Tuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
other: &Self, other: &Self,
inferable: InferableTypeVars<'_, 'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
) -> ConstraintSet<'db> { ) -> ConstraintSet<'db> {
@ -1234,6 +1258,7 @@ impl<'db> Tuple<Type<'db>> {
db: &'db dyn Db, db: &'db dyn Db,
a: impl IntoIterator<Item = &'s Type<'db>>, a: impl IntoIterator<Item = &'s Type<'db>>,
b: impl IntoIterator<Item = &'s Type<'db>>, b: impl IntoIterator<Item = &'s Type<'db>>,
inferable: InferableTypeVars<'_, 'db>,
disjointness_visitor: &IsDisjointVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>,
relation_visitor: &HasRelationToVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>,
) -> ConstraintSet<'db> ) -> ConstraintSet<'db>
@ -1244,6 +1269,7 @@ impl<'db> Tuple<Type<'db>> {
self_element.is_disjoint_from_impl( self_element.is_disjoint_from_impl(
db, db,
*other_element, *other_element,
inferable,
disjointness_visitor, disjointness_visitor,
relation_visitor, relation_visitor,
) )
@ -1255,6 +1281,7 @@ impl<'db> Tuple<Type<'db>> {
db, db,
self_tuple.elements(), self_tuple.elements(),
other_tuple.elements(), other_tuple.elements(),
inferable,
disjointness_visitor, disjointness_visitor,
relation_visitor, relation_visitor,
), ),
@ -1266,6 +1293,7 @@ impl<'db> Tuple<Type<'db>> {
db, db,
self_tuple.prefix_elements(), self_tuple.prefix_elements(),
other_tuple.prefix_elements(), other_tuple.prefix_elements(),
inferable,
disjointness_visitor, disjointness_visitor,
relation_visitor, relation_visitor,
) )
@ -1274,6 +1302,7 @@ impl<'db> Tuple<Type<'db>> {
db, db,
self_tuple.suffix_elements().rev(), self_tuple.suffix_elements().rev(),
other_tuple.suffix_elements().rev(), other_tuple.suffix_elements().rev(),
inferable,
disjointness_visitor, disjointness_visitor,
relation_visitor, relation_visitor,
) )
@ -1284,6 +1313,7 @@ impl<'db> Tuple<Type<'db>> {
db, db,
fixed.elements(), fixed.elements(),
variable.prefix_elements(), variable.prefix_elements(),
inferable,
disjointness_visitor, disjointness_visitor,
relation_visitor, relation_visitor,
) )
@ -1292,6 +1322,7 @@ impl<'db> Tuple<Type<'db>> {
db, db,
fixed.elements().rev(), fixed.elements().rev(),
variable.suffix_elements().rev(), variable.suffix_elements().rev(),
inferable,
disjointness_visitor, disjointness_visitor,
relation_visitor, relation_visitor,
) )