mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-27 10:26:26 +00:00
[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:
parent
85ff4f3eef
commit
8817ea5c84
11 changed files with 580 additions and 185 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue