diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 3eb227b647..32ee4e4c42 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -1,7 +1,5 @@ use std::borrow::Cow; -use crate::types::constraints::ConstraintSet; - use itertools::Itertools; use ruff_db::parsed::ParsedModuleRef; use ruff_python_ast as ast; @@ -12,6 +10,7 @@ use crate::semantic_index::definition::Definition; use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind}; 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}; @@ -20,7 +19,7 @@ use crate::types::{ ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, - TypeVarVariance, UnionType, binding_type, declaration_type, + TypeVarVariance, UnionBuilder, UnionType, binding_type, declaration_type, }; use crate::{Db, FxOrderSet}; @@ -1113,16 +1112,34 @@ impl<'db> SpecializationBuilder<'db> { self.add_type_mapping(bound_typevar, ty); } Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { + // If the type is assignable to _all_ of the constraints, then we + // specialize the typevar to the type itself. If the type is assignable to + // some but not all of them, we specialize the type to the union of the + // constraints that it was assignable to. Otherwise it's a specialization + // error. + let mut builder = UnionBuilder::new(self.db); + let mut when_all_assignable = ConstraintSet::from(true); + let mut any_assignable = false; for constraint in constraints.elements(self.db) { - if ty.is_assignable_to(self.db, *constraint) { - self.add_type_mapping(bound_typevar, *constraint); - return Ok(()); + let when_assignable: ConstraintSet = + ty.when_assignable_to(self.db, *constraint); + if when_assignable.is_always_satisfied() { + builder = builder.add(*constraint); + any_assignable = true; } + when_all_assignable.intersect(self.db, &when_assignable); + } + + if when_all_assignable.is_always_satisfied() { + self.add_type_mapping(bound_typevar, ty); + } else if any_assignable { + self.add_type_mapping(bound_typevar, builder.build()); + } else { + return Err(SpecializationError::MismatchedConstraint { + bound_typevar, + argument: ty, + }); } - return Err(SpecializationError::MismatchedConstraint { - bound_typevar, - argument: ty, - }); } _ => { self.add_type_mapping(bound_typevar, ty);