mirror of https://github.com/astral-sh/ruff
[ty] fix panic when instantiating a type variable with invalid constraints (#21663)
This commit is contained in:
parent
10de342991
commit
1951f1bbb8
|
|
@ -0,0 +1,6 @@
|
||||||
|
class C[T: (A, B)]:
|
||||||
|
def f(foo: T):
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except foo:
|
||||||
|
pass
|
||||||
|
|
@ -68,7 +68,7 @@ pub(crate) use crate::types::narrow::infer_narrowing_constraint;
|
||||||
use crate::types::newtype::NewType;
|
use crate::types::newtype::NewType;
|
||||||
pub(crate) use crate::types::signatures::{Parameter, Parameters};
|
pub(crate) use crate::types::signatures::{Parameter, Parameters};
|
||||||
use crate::types::signatures::{ParameterForm, walk_signature};
|
use crate::types::signatures::{ParameterForm, walk_signature};
|
||||||
use crate::types::tuple::{TupleSpec, TupleSpecBuilder};
|
use crate::types::tuple::{Tuple, TupleSpec, TupleSpecBuilder};
|
||||||
pub(crate) use crate::types::typed_dict::{TypedDictParams, TypedDictType, walk_typed_dict_type};
|
pub(crate) use crate::types::typed_dict::{TypedDictParams, TypedDictType, walk_typed_dict_type};
|
||||||
pub use crate::types::variance::TypeVarVariance;
|
pub use crate::types::variance::TypeVarVariance;
|
||||||
use crate::types::variance::VarianceInferable;
|
use crate::types::variance::VarianceInferable;
|
||||||
|
|
@ -5401,9 +5401,9 @@ impl<'db> Type<'db> {
|
||||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||||
bound.try_bool_impl(db, allow_short_circuit, visitor)?
|
bound.try_bool_impl(db, allow_short_circuit, visitor)?
|
||||||
}
|
}
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
||||||
try_union(constraints)?
|
.as_type(db)
|
||||||
}
|
.try_bool_impl(db, allow_short_circuit, visitor)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6453,7 +6453,7 @@ impl<'db> Type<'db> {
|
||||||
TypeVarBoundOrConstraints::UpperBound(bound) => {
|
TypeVarBoundOrConstraints::UpperBound(bound) => {
|
||||||
non_async_special_case(db, bound)
|
non_async_special_case(db, bound)
|
||||||
}
|
}
|
||||||
TypeVarBoundOrConstraints::Constraints(union) => non_async_special_case(db, Type::Union(union)),
|
TypeVarBoundOrConstraints::Constraints(constraints) => non_async_special_case(db, constraints.as_type(db)),
|
||||||
},
|
},
|
||||||
Type::Union(union) => {
|
Type::Union(union) => {
|
||||||
let elements = union.elements(db);
|
let elements = union.elements(db);
|
||||||
|
|
@ -9594,7 +9594,7 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
TypeVarBoundOrConstraints::UpperBound(upper_bound.to_instance(db)?)
|
TypeVarBoundOrConstraints::UpperBound(upper_bound.to_instance(db)?)
|
||||||
}
|
}
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?.as_union()?)
|
TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let identity = TypeVarIdentity::new(
|
let identity = TypeVarIdentity::new(
|
||||||
|
|
@ -9645,22 +9645,30 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
fn lazy_constraints(self, db: &'db dyn Db) -> Option<TypeVarBoundOrConstraints<'db>> {
|
fn lazy_constraints(self, db: &'db dyn Db) -> Option<TypeVarBoundOrConstraints<'db>> {
|
||||||
let definition = self.definition(db)?;
|
let definition = self.definition(db)?;
|
||||||
let module = parsed_module(db, definition.file(db)).load(db);
|
let module = parsed_module(db, definition.file(db)).load(db);
|
||||||
let ty = match definition.kind(db) {
|
let constraints = match definition.kind(db) {
|
||||||
// PEP 695 typevar
|
// PEP 695 typevar
|
||||||
DefinitionKind::TypeVar(typevar) => {
|
DefinitionKind::TypeVar(typevar) => {
|
||||||
let typevar_node = typevar.node(&module);
|
let typevar_node = typevar.node(&module);
|
||||||
definition_expression_type(db, definition, typevar_node.bound.as_ref()?)
|
let bound =
|
||||||
.as_union()?
|
definition_expression_type(db, definition, typevar_node.bound.as_ref()?);
|
||||||
|
let constraints = if let Some(tuple) = bound
|
||||||
|
.as_nominal_instance()
|
||||||
|
.and_then(|instance| instance.tuple_spec(db))
|
||||||
|
{
|
||||||
|
if let Tuple::Fixed(tuple) = tuple.into_owned() {
|
||||||
|
tuple.owned_elements()
|
||||||
|
} else {
|
||||||
|
vec![Type::unknown()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec![Type::unknown()].into_boxed_slice()
|
||||||
|
};
|
||||||
|
TypeVarConstraints::new(db, constraints)
|
||||||
}
|
}
|
||||||
// legacy typevar
|
// legacy typevar
|
||||||
DefinitionKind::Assignment(assignment) => {
|
DefinitionKind::Assignment(assignment) => {
|
||||||
let call_expr = assignment.value(&module).as_call_expr()?;
|
let call_expr = assignment.value(&module).as_call_expr()?;
|
||||||
// We don't use `UnionType::from_elements` or `UnionBuilder` here,
|
TypeVarConstraints::new(
|
||||||
// because we don't want to simplify the list of constraints as we would with
|
|
||||||
// an actual union type.
|
|
||||||
// TODO: We probably shouldn't use `UnionType` to store these at all? TypeVar
|
|
||||||
// constraints are not a union.
|
|
||||||
UnionType::new(
|
|
||||||
db,
|
db,
|
||||||
call_expr
|
call_expr
|
||||||
.arguments
|
.arguments
|
||||||
|
|
@ -9669,12 +9677,11 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|arg| definition_expression_type(db, definition, arg))
|
.map(|arg| definition_expression_type(db, definition, arg))
|
||||||
.collect::<Box<_>>(),
|
.collect::<Box<_>>(),
|
||||||
RecursivelyDefined::No,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(ty))
|
Some(TypeVarBoundOrConstraints::Constraints(constraints))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::tracked(cycle_fn=lazy_default_cycle_recover, cycle_initial=lazy_default_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
#[salsa::tracked(cycle_fn=lazy_default_cycle_recover, cycle_initial=lazy_default_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||||
|
|
@ -10086,10 +10093,133 @@ impl<'db> From<TypeVarBoundOrConstraints<'db>> for TypeVarBoundOrConstraintsEval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type variable constraints (e.g. `T: (int, str)`).
|
||||||
|
/// This is structurally identical to [`UnionType`], except that it does not perform simplification and preserves the element types.
|
||||||
|
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||||
|
pub struct TypeVarConstraints<'db> {
|
||||||
|
#[returns(ref)]
|
||||||
|
elements: Box<[Type<'db>]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl get_size2::GetSize for TypeVarConstraints<'_> {}
|
||||||
|
|
||||||
|
fn walk_type_var_constraints<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
constraints: TypeVarConstraints<'db>,
|
||||||
|
visitor: &V,
|
||||||
|
) {
|
||||||
|
for ty in constraints.elements(db) {
|
||||||
|
visitor.visit_type(db, *ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> TypeVarConstraints<'db> {
|
||||||
|
fn as_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
|
let mut builder = UnionBuilder::new(db);
|
||||||
|
for ty in self.elements(db) {
|
||||||
|
builder = builder.add(*ty);
|
||||||
|
}
|
||||||
|
builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_instance(self, db: &'db dyn Db) -> Option<TypeVarConstraints<'db>> {
|
||||||
|
let mut instance_elements = Vec::new();
|
||||||
|
for ty in self.elements(db) {
|
||||||
|
instance_elements.push(ty.to_instance(db)?);
|
||||||
|
}
|
||||||
|
Some(TypeVarConstraints::new(
|
||||||
|
db,
|
||||||
|
instance_elements.into_boxed_slice(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map(self, db: &'db dyn Db, transform_fn: impl FnMut(&Type<'db>) -> Type<'db>) -> Self {
|
||||||
|
let mapped = self
|
||||||
|
.elements(db)
|
||||||
|
.iter()
|
||||||
|
.map(transform_fn)
|
||||||
|
.collect::<Box<_>>();
|
||||||
|
TypeVarConstraints::new(db, mapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn map_with_boundness_and_qualifiers(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
mut transform_fn: impl FnMut(&Type<'db>) -> PlaceAndQualifiers<'db>,
|
||||||
|
) -> PlaceAndQualifiers<'db> {
|
||||||
|
let mut builder = UnionBuilder::new(db);
|
||||||
|
let mut qualifiers = TypeQualifiers::empty();
|
||||||
|
|
||||||
|
let mut all_unbound = true;
|
||||||
|
let mut possibly_unbound = false;
|
||||||
|
let mut origin = TypeOrigin::Declared;
|
||||||
|
for ty in self.elements(db) {
|
||||||
|
let PlaceAndQualifiers {
|
||||||
|
place: ty_member,
|
||||||
|
qualifiers: new_qualifiers,
|
||||||
|
} = transform_fn(ty);
|
||||||
|
qualifiers |= new_qualifiers;
|
||||||
|
match ty_member {
|
||||||
|
Place::Undefined => {
|
||||||
|
possibly_unbound = true;
|
||||||
|
}
|
||||||
|
Place::Defined(ty_member, member_origin, member_boundness) => {
|
||||||
|
origin = origin.merge(member_origin);
|
||||||
|
if member_boundness == Definedness::PossiblyUndefined {
|
||||||
|
possibly_unbound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
all_unbound = false;
|
||||||
|
builder = builder.add(ty_member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlaceAndQualifiers {
|
||||||
|
place: if all_unbound {
|
||||||
|
Place::Undefined
|
||||||
|
} else {
|
||||||
|
Place::Defined(
|
||||||
|
builder.build(),
|
||||||
|
origin,
|
||||||
|
if possibly_unbound {
|
||||||
|
Definedness::PossiblyUndefined
|
||||||
|
} else {
|
||||||
|
Definedness::AlwaysDefined
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
qualifiers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||||
|
let normalized = self
|
||||||
|
.elements(db)
|
||||||
|
.iter()
|
||||||
|
.map(|ty| ty.normalized_impl(db, visitor))
|
||||||
|
.collect::<Box<_>>();
|
||||||
|
TypeVarConstraints::new(db, normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn materialize_impl(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
materialization_kind: MaterializationKind,
|
||||||
|
visitor: &ApplyTypeMappingVisitor<'db>,
|
||||||
|
) -> Self {
|
||||||
|
let materialized = self
|
||||||
|
.elements(db)
|
||||||
|
.iter()
|
||||||
|
.map(|ty| ty.materialize(db, materialization_kind, visitor))
|
||||||
|
.collect::<Box<_>>();
|
||||||
|
TypeVarConstraints::new(db, materialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||||
pub enum TypeVarBoundOrConstraints<'db> {
|
pub enum TypeVarBoundOrConstraints<'db> {
|
||||||
UpperBound(Type<'db>),
|
UpperBound(Type<'db>),
|
||||||
Constraints(UnionType<'db>),
|
Constraints(TypeVarConstraints<'db>),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_type_var_bounds<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
fn walk_type_var_bounds<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
|
|
@ -10100,7 +10230,7 @@ fn walk_type_var_bounds<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
match bounds {
|
match bounds {
|
||||||
TypeVarBoundOrConstraints::UpperBound(bound) => visitor.visit_type(db, bound),
|
TypeVarBoundOrConstraints::UpperBound(bound) => visitor.visit_type(db, bound),
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||||
visitor.visit_union_type(db, constraints);
|
walk_type_var_constraints(db, constraints, visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10112,18 +10242,7 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
|
||||||
TypeVarBoundOrConstraints::UpperBound(bound.normalized_impl(db, visitor))
|
TypeVarBoundOrConstraints::UpperBound(bound.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||||
// Constraints are a non-normalized union by design (it's not really a union at
|
TypeVarBoundOrConstraints::Constraints(constraints.normalized_impl(db, visitor))
|
||||||
// all, we are just using a union to store the types). Normalize the types but not
|
|
||||||
// the containing union.
|
|
||||||
TypeVarBoundOrConstraints::Constraints(UnionType::new(
|
|
||||||
db,
|
|
||||||
constraints
|
|
||||||
.elements(db)
|
|
||||||
.iter()
|
|
||||||
.map(|ty| ty.normalized_impl(db, visitor))
|
|
||||||
.collect::<Box<_>>(),
|
|
||||||
constraints.recursively_defined(db),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10147,14 +10266,13 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
|
||||||
// Normalize each constraint with its corresponding previous constraint
|
// Normalize each constraint with its corresponding previous constraint
|
||||||
let current_elements = constraints.elements(db);
|
let current_elements = constraints.elements(db);
|
||||||
let prev_elements = prev_constraints.elements(db);
|
let prev_elements = prev_constraints.elements(db);
|
||||||
TypeVarBoundOrConstraints::Constraints(UnionType::new(
|
TypeVarBoundOrConstraints::Constraints(TypeVarConstraints::new(
|
||||||
db,
|
db,
|
||||||
current_elements
|
current_elements
|
||||||
.iter()
|
.iter()
|
||||||
.zip(prev_elements.iter())
|
.zip(prev_elements.iter())
|
||||||
.map(|(ty, prev_ty)| ty.cycle_normalized(db, *prev_ty, cycle))
|
.map(|(ty, prev_ty)| ty.cycle_normalized(db, *prev_ty, cycle))
|
||||||
.collect::<Box<_>>(),
|
.collect::<Box<_>>(),
|
||||||
constraints.recursively_defined(db),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// The choice of whether it's an upper bound or constraints is purely syntactic and
|
// The choice of whether it's an upper bound or constraints is purely syntactic and
|
||||||
|
|
@ -10175,15 +10293,9 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
|
||||||
TypeVarBoundOrConstraints::UpperBound(bound.recursive_type_normalized(db, cycle))
|
TypeVarBoundOrConstraints::UpperBound(bound.recursive_type_normalized(db, cycle))
|
||||||
}
|
}
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||||
TypeVarBoundOrConstraints::Constraints(UnionType::new(
|
TypeVarBoundOrConstraints::Constraints(
|
||||||
db,
|
constraints.map(db, |ty| ty.recursive_type_normalized(db, cycle)),
|
||||||
constraints
|
)
|
||||||
.elements(db)
|
|
||||||
.iter()
|
|
||||||
.map(|ty| ty.recursive_type_normalized(db, cycle))
|
|
||||||
.collect::<Box<_>>(),
|
|
||||||
constraints.recursively_defined(db),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10199,14 +10311,10 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
|
||||||
bound.materialize(db, materialization_kind, visitor),
|
bound.materialize(db, materialization_kind, visitor),
|
||||||
),
|
),
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||||
TypeVarBoundOrConstraints::Constraints(UnionType::new(
|
TypeVarBoundOrConstraints::Constraints(constraints.materialize_impl(
|
||||||
db,
|
db,
|
||||||
constraints
|
materialization_kind,
|
||||||
.elements(db)
|
visitor,
|
||||||
.iter()
|
|
||||||
.map(|ty| ty.materialize(db, materialization_kind, visitor))
|
|
||||||
.collect::<Box<_>>(),
|
|
||||||
RecursivelyDefined::No,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ impl<'db> BoundSuperError<'db> {
|
||||||
.map(|c| c.display(db))
|
.map(|c| c.display(db))
|
||||||
.join(", ")
|
.join(", ")
|
||||||
));
|
));
|
||||||
Type::Union(constraints)
|
constraints.as_type(db)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
diagnostic.info(format_args!(
|
diagnostic.info(format_args!(
|
||||||
|
|
@ -374,7 +374,7 @@ impl<'db> BoundSuperType<'db> {
|
||||||
delegate_with_error_mapped(bound, Some(type_var))
|
delegate_with_error_mapped(bound, Some(type_var))
|
||||||
}
|
}
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||||
delegate_with_error_mapped(Type::Union(constraints), Some(type_var))
|
delegate_with_error_mapped(constraints.as_type(db), Some(type_var))
|
||||||
}
|
}
|
||||||
None => delegate_with_error_mapped(Type::object(), Some(type_var)),
|
None => delegate_with_error_mapped(Type::object(), Some(type_var)),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1255,7 +1255,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
speculative = speculative.add_positive(bound);
|
speculative = speculative.add_positive(bound);
|
||||||
}
|
}
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||||
speculative = speculative.add_positive(Type::Union(constraints));
|
speculative = speculative.add_positive(constraints.as_type(db));
|
||||||
}
|
}
|
||||||
// TypeVars without a bound or constraint implicitly have `object` as their
|
// TypeVars without a bound or constraint implicitly have `object` as their
|
||||||
// upper bound, and it is always a no-op to add `object` to an intersection.
|
// upper bound, and it is always a no-op to add `object` to an intersection.
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ use crate::semantic_index::{
|
||||||
ApplicableConstraints, EnclosingSnapshotResult, SemanticIndex, place_table,
|
ApplicableConstraints, EnclosingSnapshotResult, SemanticIndex, place_table,
|
||||||
};
|
};
|
||||||
use crate::subscript::{PyIndex, PySlice};
|
use crate::subscript::{PyIndex, PySlice};
|
||||||
use crate::types::builder::RecursivelyDefined;
|
|
||||||
use crate::types::call::bind::{CallableDescription, MatchingOverloadIndex};
|
use crate::types::call::bind::{CallableDescription, MatchingOverloadIndex};
|
||||||
use crate::types::call::{Binding, Bindings, CallArguments, CallError, CallErrorKind};
|
use crate::types::call::{Binding, Bindings, CallArguments, CallError, CallErrorKind};
|
||||||
use crate::types::class::{CodeGeneratorKind, FieldKind, MetaclassErrorKind, MethodDecorator};
|
use crate::types::class::{CodeGeneratorKind, FieldKind, MetaclassErrorKind, MethodDecorator};
|
||||||
|
|
@ -3274,19 +3273,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
std::mem::replace(&mut self.deferred_state, DeferredExpressionState::Deferred);
|
std::mem::replace(&mut self.deferred_state, DeferredExpressionState::Deferred);
|
||||||
match bound.as_deref() {
|
match bound.as_deref() {
|
||||||
Some(expr @ ast::Expr::Tuple(ast::ExprTuple { elts, .. })) => {
|
Some(expr @ ast::Expr::Tuple(ast::ExprTuple { elts, .. })) => {
|
||||||
// We don't use UnionType::from_elements or UnionBuilder here, because we don't
|
// Here, we interpret `bound` as a heterogeneous tuple and convert it to `TypeVarConstraints` in `TypeVarInstance::lazy_constraints`.
|
||||||
// want to simplify the list of constraints like we do with the elements of an
|
let tuple_ty = Type::heterogeneous_tuple(
|
||||||
// actual union type.
|
|
||||||
// TODO: Consider using a new `OneOfType` connective here instead, since that
|
|
||||||
// more accurately represents the actual semantics of typevar constraints.
|
|
||||||
let ty = Type::Union(UnionType::new(
|
|
||||||
self.db(),
|
self.db(),
|
||||||
elts.iter()
|
elts.iter()
|
||||||
.map(|expr| self.infer_type_expression(expr))
|
.map(|expr| self.infer_type_expression(expr))
|
||||||
.collect::<Box<[_]>>(),
|
.collect::<Box<[_]>>(),
|
||||||
RecursivelyDefined::No,
|
);
|
||||||
));
|
self.store_expression_type(expr, tuple_ty);
|
||||||
self.store_expression_type(expr, ty);
|
|
||||||
}
|
}
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
self.infer_type_expression(expr);
|
self.infer_type_expression(expr);
|
||||||
|
|
@ -11521,7 +11515,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
if provided_type
|
if provided_type
|
||||||
.when_assignable_to(
|
.when_assignable_to(
|
||||||
db,
|
db,
|
||||||
Type::Union(constraints),
|
constraints.as_type(db),
|
||||||
InferableTypeVars::None,
|
InferableTypeVars::None,
|
||||||
)
|
)
|
||||||
.is_never_satisfied(db)
|
.is_never_satisfied(db)
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ impl ClassInfoConstraintFunction {
|
||||||
self.generate_constraint(db, bound)
|
self.generate_constraint(db, bound)
|
||||||
}
|
}
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||||
self.generate_constraint(db, Type::Union(constraints))
|
self.generate_constraint(db, constraints.as_type(db))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType,
|
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType,
|
||||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, KnownClass,
|
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, KnownClass,
|
||||||
MaterializationKind, MemberLookupPolicy, NormalizedVisitor, SpecialFormType, Type, TypeContext,
|
MaterializationKind, MemberLookupPolicy, NormalizedVisitor, SpecialFormType, Type, TypeContext,
|
||||||
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, UnionType, todo_type,
|
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, todo_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
|
|
@ -190,7 +190,9 @@ impl<'db> SubclassOfType<'db> {
|
||||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound,
|
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound,
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(union)) => Type::Union(union),
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||||
|
constraints.as_type(db)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -351,7 +353,7 @@ impl<'db> SubclassOfInner<'db> {
|
||||||
.and_then(|subclass_of| subclass_of.into_class(db))
|
.and_then(|subclass_of| subclass_of.into_class(db))
|
||||||
}
|
}
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||||
match constraints.elements(db) {
|
match &**constraints.elements(db) {
|
||||||
[bound] => Self::try_from_instance(db, *bound)
|
[bound] => Self::try_from_instance(db, *bound)
|
||||||
.and_then(|subclass_of| subclass_of.into_class(db)),
|
.and_then(|subclass_of| subclass_of.into_class(db)),
|
||||||
_ => Some(ClassType::object(db)),
|
_ => Some(ClassType::object(db)),
|
||||||
|
|
@ -416,20 +418,10 @@ impl<'db> SubclassOfInner<'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||||
let constraints_types = constraints
|
TypeVarBoundOrConstraints::Constraints(constraints.map(db, |constraint| {
|
||||||
.elements(db)
|
SubclassOfType::try_from_instance(db, *constraint)
|
||||||
.iter()
|
.unwrap_or(SubclassOfType::subclass_of_unknown())
|
||||||
.map(|constraint| {
|
}))
|
||||||
SubclassOfType::try_from_instance(db, *constraint)
|
|
||||||
.unwrap_or(SubclassOfType::subclass_of_unknown())
|
|
||||||
})
|
|
||||||
.collect::<Box<_>>();
|
|
||||||
|
|
||||||
TypeVarBoundOrConstraints::Constraints(UnionType::new(
|
|
||||||
db,
|
|
||||||
constraints_types,
|
|
||||||
constraints.recursively_defined(db),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -349,6 +349,10 @@ impl<T> FixedLengthTuple<T> {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn owned_elements(self) -> Box<[T]> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn elements(&self) -> impl DoubleEndedIterator<Item = &T> + ExactSizeIterator + '_ {
|
pub(crate) fn elements(&self) -> impl DoubleEndedIterator<Item = &T> + ExactSizeIterator + '_ {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue