[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::{
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::tuple::{TupleLength, TupleType};
use crate::types::{
@ -597,7 +599,8 @@ impl<'db> Bindings<'db> {
Type::FunctionLiteral(function_type) => match function_type.known(db) {
Some(KnownFunction::IsEquivalentTo) => {
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);
overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked),
@ -607,7 +610,8 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::IsSubtypeOf) => {
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);
overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked),
@ -617,7 +621,8 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::IsAssignableTo) => {
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);
overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked),
@ -627,7 +632,8 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::IsDisjointFrom) => {
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);
overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked),
@ -1407,7 +1413,10 @@ impl<'db> CallableBinding<'db> {
let parameter_type = overload.signature.parameters()[*parameter_index]
.annotated_type()
.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;
break 'overload;
}
@ -1633,7 +1642,14 @@ impl<'db> CallableBinding<'db> {
.unwrap_or(Type::unknown());
let first_parameter_type = &mut first_parameter_types[parameter_index];
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);
}
} else {
@ -1750,7 +1766,12 @@ impl<'db> CallableBinding<'db> {
matching_overloads.all(|(_, overload)| {
overload
.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 {
// No matching overload
@ -2461,6 +2482,7 @@ struct ArgumentTypeChecker<'a, 'db> {
call_expression_tcx: &'a TypeContext<'db>,
errors: &'a mut Vec<BindingError<'db>>,
inferable_typevars: InferableTypeVars<'db, 'db>,
specialization: Option<Specialization<'db>>,
}
@ -2482,6 +2504,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
parameter_tys,
call_expression_tcx,
errors,
inferable_typevars: InferableTypeVars::None,
specialization: None,
}
}
@ -2514,11 +2537,12 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
}
fn infer_specialization(&mut self) {
if self.signature.generic_context.is_none() {
let Some(generic_context) = self.signature.generic_context else {
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
// 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
.signature
.generic_context
.map(|gc| builder.build(gc, *self.call_expression_tcx));
self.specialization = Some(builder.build(generic_context, *self.call_expression_tcx));
}
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
// building them in an earlier separate step.
if argument_type
.when_assignable_to(self.db, expected_ty)
.when_assignable_to(self.db, expected_ty, self.inferable_typevars)
.is_never_satisfied()
{
let positional = matches!(argument, Argument::Positional | Argument::Synthetic)
@ -2719,7 +2740,14 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
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 {
argument_index: adjusted_argument_index,
provided_ty: key_type,
@ -2754,8 +2782,8 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
}
}
fn finish(self) -> Option<Specialization<'db>> {
self.specialization
fn finish(self) -> (InferableTypeVars<'db, 'db>, Option<Specialization<'db>>) {
(self.inferable_typevars, self.specialization)
}
}
@ -2819,6 +2847,9 @@ pub(crate) struct Binding<'db> {
/// Return type of the call.
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.
specialization: Option<Specialization<'db>>,
@ -2845,6 +2876,7 @@ impl<'db> Binding<'db> {
callable_type: signature_type,
signature_type,
return_ty: Type::unknown(),
inferable_typevars: InferableTypeVars::None,
specialization: None,
argument_matches: Box::from([]),
variadic_argument_matched_to_variadic_parameter: false,
@ -2916,7 +2948,7 @@ impl<'db> Binding<'db> {
checker.infer_specialization();
checker.check_argument_types();
self.specialization = checker.finish();
(self.inferable_typevars, self.specialization) = checker.finish();
if let Some(specialization) = self.specialization {
self.return_ty = self.return_ty.apply_specialization(db, specialization);
}
@ -3010,6 +3042,7 @@ impl<'db> Binding<'db> {
fn snapshot(&self) -> BindingSnapshot<'db> {
BindingSnapshot {
return_ty: self.return_ty,
inferable_typevars: self.inferable_typevars,
specialization: self.specialization,
argument_matches: self.argument_matches.clone(),
parameter_tys: self.parameter_tys.clone(),
@ -3020,6 +3053,7 @@ impl<'db> Binding<'db> {
fn restore(&mut self, snapshot: BindingSnapshot<'db>) {
let BindingSnapshot {
return_ty,
inferable_typevars,
specialization,
argument_matches,
parameter_tys,
@ -3027,6 +3061,7 @@ impl<'db> Binding<'db> {
} = snapshot;
self.return_ty = return_ty;
self.inferable_typevars = inferable_typevars;
self.specialization = specialization;
self.argument_matches = argument_matches;
self.parameter_tys = parameter_tys;
@ -3046,6 +3081,7 @@ impl<'db> Binding<'db> {
/// Resets the state of this binding to its initial state.
fn reset(&mut self) {
self.return_ty = Type::unknown();
self.inferable_typevars = InferableTypeVars::None;
self.specialization = None;
self.argument_matches = Box::from([]);
self.parameter_tys = Box::from([]);
@ -3056,6 +3092,7 @@ impl<'db> Binding<'db> {
#[derive(Clone, Debug)]
struct BindingSnapshot<'db> {
return_ty: Type<'db>,
inferable_typevars: InferableTypeVars<'db, 'db>,
specialization: Option<Specialization<'db>>,
argument_matches: Box<[MatchedArgument<'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.
snapshot.return_ty = binding.return_ty;
snapshot.inferable_typevars = binding.inferable_typevars;
snapshot.specialization = binding.specialization;
snapshot
.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::function::{DataclassTransformerParams, KnownFunction};
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::member::{Member, class_member};
@ -540,17 +540,20 @@ impl<'db> ClassType<'db> {
/// 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 {
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(
self,
db: &'db dyn Db,
other: ClassType<'db>,
inferable: InferableTypeVars<'_, 'db>,
) -> ConstraintSet<'db> {
self.has_relation_to_impl(
db,
other,
inferable,
TypeRelation::Subtyping,
&HasRelationToVisitor::default(),
&IsDisjointVisitor::default(),
@ -561,6 +564,7 @@ impl<'db> ClassType<'db> {
self,
db: &'db dyn Db,
other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
@ -586,6 +590,7 @@ impl<'db> ClassType<'db> {
base.specialization(db).has_relation_to_impl(
db,
other.specialization(db),
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -610,6 +615,7 @@ impl<'db> ClassType<'db> {
self,
db: &'db dyn Db,
other: ClassType<'db>,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> {
if self == other {
@ -628,6 +634,7 @@ impl<'db> ClassType<'db> {
this.specialization(db).is_equivalent_to_impl(
db,
other.specialization(db),
inferable,
visitor,
)
})

View file

@ -73,7 +73,7 @@ use crate::types::diagnostic::{
report_runtime_check_against_non_runtime_checkable_protocol,
};
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::narrow::ClassInfoConstraintFunction;
use crate::types::signatures::{CallableSignature, Signature};
@ -81,9 +81,9 @@ use crate::types::visitor::any_over_type;
use crate::types::{
ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase,
ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor,
SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext, TypeMapping,
TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature,
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
NormalizedVisitor, SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext,
TypeMapping, TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature,
};
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
@ -959,46 +959,42 @@ impl<'db> FunctionType<'db> {
self,
db: &'db dyn Db,
other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation,
_visitor: &HasRelationToVisitor<'db>,
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'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,
// 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
// of each other if they result in subtype signatures.
if self.normalized(db) == other.normalized(db) {
return true;
if matches!(relation, TypeRelation::Subtyping | TypeRelation::Redundancy)
&& self.normalized(db) == other.normalized(db)
{
return ConstraintSet::from(true);
}
if self.literal(db) != other.literal(db) {
return false;
return ConstraintSet::from(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 {
// A function type is assignable to itself, and not to any other function type. However,
// 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.
self.literal(db) == other.literal(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(
self,
db: &'db dyn Db,
other: Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> {
if self.normalized(db) == other.normalized(db) {
@ -1009,7 +1005,7 @@ impl<'db> FunctionType<'db> {
}
let self_signature = self.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(

View file

@ -1,4 +1,4 @@
use crate::types::constraints::ConstraintSet;
use std::marker::PhantomData;
use itertools::Itertools;
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::types::class::ClassType;
use crate::types::class_base::ClassBase;
use crate::types::constraints::ConstraintSet;
use crate::types::infer::infer_definition_types;
use crate::types::instance::{Protocol, ProtocolInstanceType};
use crate::types::signatures::{Parameter, Parameters, Signature};
@ -140,6 +141,16 @@ pub(crate) fn typing_self<'db>(
.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)]
pub struct GenericContextTypeVar<'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>(
db: &'db dyn Db,
derived_type: &Type<'db>,
derived_materialization: MaterializationKind,
base_type: &Type<'db>,
base_materialization: MaterializationKind,
inferable: InferableTypeVars<'_, 'db>,
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
) -> ConstraintSet<'db> {
@ -623,6 +636,7 @@ fn is_subtype_in_invariant_position<'db>(
derived.has_relation_to_impl(
db,
base,
inferable,
TypeRelation::Subtyping,
relation_visitor,
disjointness_visitor,
@ -675,6 +689,7 @@ fn has_relation_in_invariant_position<'db>(
derived_materialization: Option<MaterializationKind>,
base_type: &Type<'db>,
base_materialization: Option<MaterializationKind>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
@ -688,6 +703,7 @@ fn has_relation_in_invariant_position<'db>(
derived_mat,
base_type,
base_mat,
inferable,
relation_visitor,
disjointness_visitor,
),
@ -707,6 +723,7 @@ fn has_relation_in_invariant_position<'db>(
.has_relation_to_impl(
db,
*base_type,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -715,6 +732,7 @@ fn has_relation_in_invariant_position<'db>(
base_type.has_relation_to_impl(
db,
*derived_type,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -728,6 +746,7 @@ fn has_relation_in_invariant_position<'db>(
MaterializationKind::Top,
base_type,
base_mat,
inferable,
relation_visitor,
disjointness_visitor,
)
@ -739,6 +758,7 @@ fn has_relation_in_invariant_position<'db>(
derived_mat,
base_type,
MaterializationKind::Bottom,
inferable,
relation_visitor,
disjointness_visitor,
)
@ -750,6 +770,7 @@ fn has_relation_in_invariant_position<'db>(
MaterializationKind::Bottom,
base_type,
base_mat,
inferable,
relation_visitor,
disjointness_visitor,
),
@ -759,6 +780,7 @@ fn has_relation_in_invariant_position<'db>(
derived_mat,
base_type,
MaterializationKind::Top,
inferable,
relation_visitor,
disjointness_visitor,
),
@ -1002,6 +1024,7 @@ impl<'db> Specialization<'db> {
self,
db: &'db dyn Db,
other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
@ -1016,6 +1039,7 @@ impl<'db> Specialization<'db> {
return self_tuple.has_relation_to_impl(
db,
other_tuple,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -1043,6 +1067,7 @@ impl<'db> Specialization<'db> {
self_materialization_kind,
other_type,
other_materialization_kind,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -1050,6 +1075,7 @@ impl<'db> Specialization<'db> {
TypeVarVariance::Covariant => self_type.has_relation_to_impl(
db,
*other_type,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -1057,6 +1083,7 @@ impl<'db> Specialization<'db> {
TypeVarVariance::Contravariant => other_type.has_relation_to_impl(
db,
*self_type,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -1075,6 +1102,7 @@ impl<'db> Specialization<'db> {
self,
db: &'db dyn Db,
other: Specialization<'db>,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> {
if self.materialization_kind(db) != other.materialization_kind(db) {
@ -1100,7 +1128,7 @@ impl<'db> Specialization<'db> {
TypeVarVariance::Invariant
| TypeVarVariance::Covariant
| 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),
};
@ -1113,7 +1141,8 @@ impl<'db> Specialization<'db> {
(Some(_), None) | (None, Some(_)) => return ConstraintSet::from(false),
(None, None) => {}
(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() {
return result;
}
@ -1168,13 +1197,15 @@ impl<'db> PartialSpecialization<'_, 'db> {
/// specialization of a generic function.
pub(crate) struct SpecializationBuilder<'db> {
db: &'db dyn Db,
inferable: InferableTypeVars<'db, 'db>,
types: FxHashMap<BoundTypeVarIdentity<'db>, Type<'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 {
db,
inferable,
types: FxHashMap::default(),
}
}
@ -1244,14 +1275,16 @@ impl<'db> SpecializationBuilder<'db> {
// without specializing `T` to `None`.
if !matches!(formal, Type::ProtocolInstance(_))
&& !actual.is_never()
&& actual.is_subtype_of(self.db, formal)
&& actual
.when_subtype_of(self.db, formal, self.inferable)
.is_always_satisfied()
{
return Ok(());
}
// 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`.
actual = actual.filter_disjoint_elements(self.db, formal);
actual = actual.filter_disjoint_elements(self.db, formal, self.inferable);
match (formal, actual) {
// 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)) => {
match bound_typevar.typevar(self.db).bound_or_constraints(self.db) {
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 {
bound_typevar,
argument: ty,
@ -1334,7 +1370,10 @@ impl<'db> SpecializationBuilder<'db> {
}
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
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);
return Ok(());
}

View file

@ -75,8 +75,10 @@ use crate::types::diagnostic::{
use crate::types::function::{
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
};
use crate::types::generics::{GenericContext, bind_typevar, enclosing_generic_contexts};
use crate::types::generics::{LegacyGenericBase, SpecializationBuilder};
use crate::types::generics::{
GenericContext, InferableTypeVars, LegacyGenericBase, SpecializationBuilder, bind_typevar,
enclosing_generic_contexts,
};
use crate::types::infer::nearest_enclosing_function;
use crate::types::instance::SliceLiteral;
use crate::types::mro::MroErrorKind;
@ -5964,11 +5966,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
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| {
// 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`
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.
@ -5977,7 +5982,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.map(|specialization| specialization.types(self.db()));
// 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 {
// 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::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
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::tuple::{TupleSpec, TupleType};
use crate::types::{
@ -121,6 +121,7 @@ impl<'db> Type<'db> {
self,
db: &'db dyn Db,
protocol: ProtocolInstanceType<'db>,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
@ -129,6 +130,7 @@ impl<'db> Type<'db> {
self_protocol.interface(db).has_relation_to_impl(
db,
protocol.interface(db),
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -142,6 +144,7 @@ impl<'db> Type<'db> {
member.is_satisfied_by(
db,
self,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -173,6 +176,7 @@ impl<'db> Type<'db> {
type_to_test.has_relation_to_impl(
db,
Type::NominalInstance(nominal_instance),
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -360,6 +364,7 @@ impl<'db> NominalInstanceType<'db> {
self,
db: &'db dyn Db,
other: Self,
inferable: InferableTypeVars<'_, 'db>,
relation: TypeRelation,
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
@ -372,6 +377,7 @@ impl<'db> NominalInstanceType<'db> {
) => tuple1.has_relation_to_impl(
db,
tuple2,
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -379,6 +385,7 @@ impl<'db> NominalInstanceType<'db> {
_ => self.class(db).has_relation_to_impl(
db,
other.class(db),
inferable,
relation,
relation_visitor,
disjointness_visitor,
@ -390,18 +397,19 @@ impl<'db> NominalInstanceType<'db> {
self,
db: &'db dyn Db,
other: Self,
inferable: InferableTypeVars<'_, 'db>,
visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> {
match (self.0, other.0) {
(
NominalInstanceInner::ExactTuple(tuple1),
NominalInstanceInner::ExactTuple(tuple2),
) => tuple1.is_equivalent_to_impl(db, tuple2, visitor),
) => tuple1.is_equivalent_to_impl(db, tuple2, inferable, visitor),
(NominalInstanceInner::Object, NominalInstanceInner::Object) => {
ConstraintSet::from(true)
}
(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),
}
@ -411,6 +419,7 @@ impl<'db> NominalInstanceType<'db> {
self,
db: &'db dyn Db,
other: Self,
inferable: InferableTypeVars<'_, 'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
relation_visitor: &HasRelationToVisitor<'db>,
) -> ConstraintSet<'db> {
@ -423,6 +432,7 @@ impl<'db> NominalInstanceType<'db> {
let compatible = self_spec.is_disjoint_from_impl(
db,
&other_spec,
inferable,
disjointness_visitor,
relation_visitor,
);
@ -650,6 +660,7 @@ impl<'db> ProtocolInstanceType<'db> {
.satisfies_protocol(
db,
protocol,
InferableTypeVars::None,
TypeRelation::Subtyping,
&HasRelationToVisitor::default(),
&IsDisjointVisitor::default(),
@ -708,6 +719,7 @@ impl<'db> ProtocolInstanceType<'db> {
self,
db: &'db dyn Db,
other: Self,
_inferable: InferableTypeVars<'_, 'db>,
_visitor: &IsEquivalentVisitor<'db>,
) -> ConstraintSet<'db> {
if self == other {
@ -729,6 +741,7 @@ impl<'db> ProtocolInstanceType<'db> {
self,
_db: &'db dyn Db,
_other: Self,
_inferable: InferableTypeVars<'_, 'db>,
_visitor: &IsDisjointVisitor<'db>,
) -> ConstraintSet<'db> {
ConstraintSet::from(false)

View file

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

View file

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

View file

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