@@ -68,7 +68,7 @@ pub(crate) use crate::types::narrow::infer_narrowing_constraint;
6868use crate::types::newtype::NewType;
6969pub(crate) use crate::types::signatures::{Parameter, Parameters};
7070use crate::types::signatures::{ParameterForm, walk_signature};
71- use crate::types::tuple::{TupleSpec, TupleSpecBuilder};
71+ use crate::types::tuple::{Tuple, TupleSpec, TupleSpecBuilder};
7272pub(crate) use crate::types::typed_dict::{TypedDictParams, TypedDictType, walk_typed_dict_type};
7373pub use crate::types::variance::TypeVarVariance;
7474use crate::types::variance::VarianceInferable;
@@ -5401,9 +5401,9 @@ impl<'db> Type<'db> {
54015401 Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
54025402 bound.try_bool_impl(db, allow_short_circuit, visitor)?
54035403 }
5404- Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
5405- try_union(constraints)?
5406- }
5404+ Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
5405+ .as_type(db)
5406+ .try_bool_impl(db, allow_short_circuit, visitor)?,
54075407 }
54085408 }
54095409
@@ -6453,7 +6453,7 @@ impl<'db> Type<'db> {
64536453 TypeVarBoundOrConstraints::UpperBound(bound) => {
64546454 non_async_special_case(db, bound)
64556455 }
6456- TypeVarBoundOrConstraints::Constraints(union ) => non_async_special_case(db, Type::Union(union )),
6456+ TypeVarBoundOrConstraints::Constraints(constraints ) => non_async_special_case(db, constraints.as_type(db )),
64576457 },
64586458 Type::Union(union) => {
64596459 let elements = union.elements(db);
@@ -9594,7 +9594,7 @@ impl<'db> TypeVarInstance<'db> {
95949594 TypeVarBoundOrConstraints::UpperBound(upper_bound.to_instance(db)?)
95959595 }
95969596 TypeVarBoundOrConstraints::Constraints(constraints) => {
9597- TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?.as_union()? )
9597+ TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?)
95989598 }
95999599 };
96009600 let identity = TypeVarIdentity::new(
@@ -9645,22 +9645,30 @@ impl<'db> TypeVarInstance<'db> {
96459645 fn lazy_constraints(self, db: &'db dyn Db) -> Option<TypeVarBoundOrConstraints<'db>> {
96469646 let definition = self.definition(db)?;
96479647 let module = parsed_module(db, definition.file(db)).load(db);
9648- let ty = match definition.kind(db) {
9648+ let constraints = match definition.kind(db) {
96499649 // PEP 695 typevar
96509650 DefinitionKind::TypeVar(typevar) => {
96519651 let typevar_node = typevar.node(&module);
9652- definition_expression_type(db, definition, typevar_node.bound.as_ref()?)
9653- .as_union()?
9652+ let bound =
9653+ definition_expression_type(db, definition, typevar_node.bound.as_ref()?);
9654+ let constraints = if let Some(tuple) = bound
9655+ .as_nominal_instance()
9656+ .and_then(|instance| instance.tuple_spec(db))
9657+ {
9658+ if let Tuple::Fixed(tuple) = tuple.into_owned() {
9659+ tuple.owned_elements()
9660+ } else {
9661+ vec![Type::unknown()].into_boxed_slice()
9662+ }
9663+ } else {
9664+ vec![Type::unknown()].into_boxed_slice()
9665+ };
9666+ TypeVarConstraints::new(db, constraints)
96549667 }
96559668 // legacy typevar
96569669 DefinitionKind::Assignment(assignment) => {
96579670 let call_expr = assignment.value(&module).as_call_expr()?;
9658- // We don't use `UnionType::from_elements` or `UnionBuilder` here,
9659- // because we don't want to simplify the list of constraints as we would with
9660- // an actual union type.
9661- // TODO: We probably shouldn't use `UnionType` to store these at all? TypeVar
9662- // constraints are not a union.
9663- UnionType::new(
9671+ TypeVarConstraints::new(
96649672 db,
96659673 call_expr
96669674 .arguments
@@ -9669,12 +9677,11 @@ impl<'db> TypeVarInstance<'db> {
96699677 .skip(1)
96709678 .map(|arg| definition_expression_type(db, definition, arg))
96719679 .collect::<Box<_>>(),
9672- RecursivelyDefined::No,
96739680 )
96749681 }
96759682 _ => return None,
96769683 };
9677- Some(TypeVarBoundOrConstraints::Constraints(ty ))
9684+ Some(TypeVarBoundOrConstraints::Constraints(constraints ))
96789685 }
96799686
96809687 #[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
1008610093 }
1008710094}
1008810095
10096+ /// Type variable constraints (e.g. `T: (int, str)`).
10097+ /// This is structurally identical to [`UnionType`], except that it does not perform simplification and preserves the element types.
10098+ #[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
10099+ pub struct TypeVarConstraints<'db> {
10100+ #[returns(ref)]
10101+ elements: Box<[Type<'db>]>,
10102+ }
10103+
10104+ impl get_size2::GetSize for TypeVarConstraints<'_> {}
10105+
10106+ fn walk_type_var_constraints<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
10107+ db: &'db dyn Db,
10108+ constraints: TypeVarConstraints<'db>,
10109+ visitor: &V,
10110+ ) {
10111+ for ty in constraints.elements(db) {
10112+ visitor.visit_type(db, *ty);
10113+ }
10114+ }
10115+
10116+ impl<'db> TypeVarConstraints<'db> {
10117+ fn as_type(self, db: &'db dyn Db) -> Type<'db> {
10118+ let mut builder = UnionBuilder::new(db);
10119+ for ty in self.elements(db) {
10120+ builder = builder.add(*ty);
10121+ }
10122+ builder.build()
10123+ }
10124+
10125+ fn to_instance(self, db: &'db dyn Db) -> Option<TypeVarConstraints<'db>> {
10126+ let mut instance_elements = Vec::new();
10127+ for ty in self.elements(db) {
10128+ instance_elements.push(ty.to_instance(db)?);
10129+ }
10130+ Some(TypeVarConstraints::new(
10131+ db,
10132+ instance_elements.into_boxed_slice(),
10133+ ))
10134+ }
10135+
10136+ fn map(self, db: &'db dyn Db, transform_fn: impl FnMut(&Type<'db>) -> Type<'db>) -> Self {
10137+ let mapped = self
10138+ .elements(db)
10139+ .iter()
10140+ .map(transform_fn)
10141+ .collect::<Box<_>>();
10142+ TypeVarConstraints::new(db, mapped)
10143+ }
10144+
10145+ pub(crate) fn map_with_boundness_and_qualifiers(
10146+ self,
10147+ db: &'db dyn Db,
10148+ mut transform_fn: impl FnMut(&Type<'db>) -> PlaceAndQualifiers<'db>,
10149+ ) -> PlaceAndQualifiers<'db> {
10150+ let mut builder = UnionBuilder::new(db);
10151+ let mut qualifiers = TypeQualifiers::empty();
10152+
10153+ let mut all_unbound = true;
10154+ let mut possibly_unbound = false;
10155+ let mut origin = TypeOrigin::Declared;
10156+ for ty in self.elements(db) {
10157+ let PlaceAndQualifiers {
10158+ place: ty_member,
10159+ qualifiers: new_qualifiers,
10160+ } = transform_fn(ty);
10161+ qualifiers |= new_qualifiers;
10162+ match ty_member {
10163+ Place::Undefined => {
10164+ possibly_unbound = true;
10165+ }
10166+ Place::Defined(ty_member, member_origin, member_boundness) => {
10167+ origin = origin.merge(member_origin);
10168+ if member_boundness == Definedness::PossiblyUndefined {
10169+ possibly_unbound = true;
10170+ }
10171+
10172+ all_unbound = false;
10173+ builder = builder.add(ty_member);
10174+ }
10175+ }
10176+ }
10177+ PlaceAndQualifiers {
10178+ place: if all_unbound {
10179+ Place::Undefined
10180+ } else {
10181+ Place::Defined(
10182+ builder.build(),
10183+ origin,
10184+ if possibly_unbound {
10185+ Definedness::PossiblyUndefined
10186+ } else {
10187+ Definedness::AlwaysDefined
10188+ },
10189+ )
10190+ },
10191+ qualifiers,
10192+ }
10193+ }
10194+
10195+ fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
10196+ let normalized = self
10197+ .elements(db)
10198+ .iter()
10199+ .map(|ty| ty.normalized_impl(db, visitor))
10200+ .collect::<Box<_>>();
10201+ TypeVarConstraints::new(db, normalized)
10202+ }
10203+
10204+ fn materialize_impl(
10205+ self,
10206+ db: &'db dyn Db,
10207+ materialization_kind: MaterializationKind,
10208+ visitor: &ApplyTypeMappingVisitor<'db>,
10209+ ) -> Self {
10210+ let materialized = self
10211+ .elements(db)
10212+ .iter()
10213+ .map(|ty| ty.materialize(db, materialization_kind, visitor))
10214+ .collect::<Box<_>>();
10215+ TypeVarConstraints::new(db, materialized)
10216+ }
10217+ }
10218+
1008910219#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
1009010220pub enum TypeVarBoundOrConstraints<'db> {
1009110221 UpperBound(Type<'db>),
10092- Constraints(UnionType <'db>),
10222+ Constraints(TypeVarConstraints <'db>),
1009310223}
1009410224
1009510225fn 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>(
1010010230 match bounds {
1010110231 TypeVarBoundOrConstraints::UpperBound(bound) => visitor.visit_type(db, bound),
1010210232 TypeVarBoundOrConstraints::Constraints(constraints) => {
10103- visitor.visit_union_type (db, constraints);
10233+ walk_type_var_constraints (db, constraints, visitor );
1010410234 }
1010510235 }
1010610236}
@@ -10112,18 +10242,7 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
1011210242 TypeVarBoundOrConstraints::UpperBound(bound.normalized_impl(db, visitor))
1011310243 }
1011410244 TypeVarBoundOrConstraints::Constraints(constraints) => {
10115- // Constraints are a non-normalized union by design (it's not really a union at
10116- // all, we are just using a union to store the types). Normalize the types but not
10117- // the containing union.
10118- TypeVarBoundOrConstraints::Constraints(UnionType::new(
10119- db,
10120- constraints
10121- .elements(db)
10122- .iter()
10123- .map(|ty| ty.normalized_impl(db, visitor))
10124- .collect::<Box<_>>(),
10125- constraints.recursively_defined(db),
10126- ))
10245+ TypeVarBoundOrConstraints::Constraints(constraints.normalized_impl(db, visitor))
1012710246 }
1012810247 }
1012910248 }
@@ -10147,14 +10266,13 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
1014710266 // Normalize each constraint with its corresponding previous constraint
1014810267 let current_elements = constraints.elements(db);
1014910268 let prev_elements = prev_constraints.elements(db);
10150- TypeVarBoundOrConstraints::Constraints(UnionType ::new(
10269+ TypeVarBoundOrConstraints::Constraints(TypeVarConstraints ::new(
1015110270 db,
1015210271 current_elements
1015310272 .iter()
1015410273 .zip(prev_elements.iter())
1015510274 .map(|(ty, prev_ty)| ty.cycle_normalized(db, *prev_ty, cycle))
1015610275 .collect::<Box<_>>(),
10157- constraints.recursively_defined(db),
1015810276 ))
1015910277 }
1016010278 // The choice of whether it's an upper bound or constraints is purely syntactic and
@@ -10175,15 +10293,9 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
1017510293 TypeVarBoundOrConstraints::UpperBound(bound.recursive_type_normalized(db, cycle))
1017610294 }
1017710295 TypeVarBoundOrConstraints::Constraints(constraints) => {
10178- TypeVarBoundOrConstraints::Constraints(UnionType::new(
10179- db,
10180- constraints
10181- .elements(db)
10182- .iter()
10183- .map(|ty| ty.recursive_type_normalized(db, cycle))
10184- .collect::<Box<_>>(),
10185- constraints.recursively_defined(db),
10186- ))
10296+ TypeVarBoundOrConstraints::Constraints(
10297+ constraints.map(db, |ty| ty.recursive_type_normalized(db, cycle)),
10298+ )
1018710299 }
1018810300 }
1018910301 }
@@ -10199,14 +10311,10 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
1019910311 bound.materialize(db, materialization_kind, visitor),
1020010312 ),
1020110313 TypeVarBoundOrConstraints::Constraints(constraints) => {
10202- TypeVarBoundOrConstraints::Constraints(UnionType::new (
10314+ TypeVarBoundOrConstraints::Constraints(constraints.materialize_impl (
1020310315 db,
10204- constraints
10205- .elements(db)
10206- .iter()
10207- .map(|ty| ty.materialize(db, materialization_kind, visitor))
10208- .collect::<Box<_>>(),
10209- RecursivelyDefined::No,
10316+ materialization_kind,
10317+ visitor,
1021010318 ))
1021110319 }
1021210320 }
0 commit comments