build multiple constraints on a path into union/intersection

This commit is contained in:
Douglas Creager 2025-12-16 08:01:55 -05:00
parent e79986abb0
commit 18ac8e6a14
2 changed files with 54 additions and 26 deletions

View File

@ -693,6 +693,11 @@ impl<'db> IntersectionBuilder<'db> {
self.add_positive_impl(ty, &mut vec![]) self.add_positive_impl(ty, &mut vec![])
} }
pub(crate) fn add_positive_in_place(&mut self, ty: Type<'db>) {
let this = std::mem::replace(self, Self::empty(self.db));
*self = this.add_positive(ty);
}
pub(crate) fn add_positive_impl( pub(crate) fn add_positive_impl(
mut self, mut self,
ty: Type<'db>, ty: Type<'db>,

View File

@ -19,11 +19,11 @@ use crate::types::variance::VarianceInferable;
use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion_guard}; use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion_guard};
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarIdentity, BoundTypeVarInstance, ApplyTypeMappingVisitor, BindingContext, BoundTypeVarIdentity, BoundTypeVarInstance,
ClassLiteral, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, ClassLiteral, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IntersectionBuilder,
IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind,
Type, TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarIdentity, NormalizedVisitor, Type, TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints,
TypeVarInstance, TypeVarKind, TypeVarVariance, UnionType, declaration_type, TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionBuilder, UnionType,
walk_type_var_bounds, declaration_type, walk_type_var_bounds,
}; };
use crate::{Db, FxOrderMap, FxOrderSet}; use crate::{Db, FxOrderMap, FxOrderSet};
@ -1584,6 +1584,20 @@ impl<'db> SpecializationBuilder<'db> {
constraints: ConstraintSet<'db>, constraints: ConstraintSet<'db>,
mut f: impl FnMut(TypeVarAssignment<'db>) -> Option<Type<'db>>, mut f: impl FnMut(TypeVarAssignment<'db>) -> Option<Type<'db>>,
) { ) {
struct Bounds<'db> {
lower: UnionBuilder<'db>,
upper: IntersectionBuilder<'db>,
}
impl<'db> Bounds<'db> {
fn new(db: &'db dyn Db) -> Self {
Self {
lower: UnionBuilder::new(db),
upper: IntersectionBuilder::new(db),
}
}
}
let constraints = constraints.limit_to_valid_specializations(self.db); let constraints = constraints.limit_to_valid_specializations(self.db);
let mut sorted_paths = Vec::new(); let mut sorted_paths = Vec::new();
constraints.for_each_path(self.db, |path| { constraints.for_each_path(self.db, |path| {
@ -1597,35 +1611,44 @@ impl<'db> SpecializationBuilder<'db> {
source_orders1.cmp(source_orders2) source_orders1.cmp(source_orders2)
}); });
let mut mappings: FxHashMap<BoundTypeVarInstance<'db>, Bounds<'db>> = FxHashMap::default();
for path in sorted_paths { for path in sorted_paths {
mappings.clear();
for (constraint, _) in path { for (constraint, _) in path {
let typevar = constraint.typevar(self.db); let typevar = constraint.typevar(self.db);
let lower = constraint.lower(self.db); let lower = constraint.lower(self.db);
let upper = constraint.upper(self.db); let upper = constraint.upper(self.db);
if !upper.is_object() { let bounds = mappings
let variance = formal.variance_of(self.db, typevar); .entry(typevar)
self.add_type_mapping(typevar, upper, variance, &mut f); .or_insert_with(|| Bounds::new(self.db));
} else if !lower.is_never() { bounds.lower.add_in_place(lower);
let variance = formal.variance_of(self.db, typevar); bounds.upper.add_positive_in_place(upper);
self.add_type_mapping(typevar, lower, variance, &mut f);
}
if let Type::TypeVar(lower_bound_typevar) = lower { if let Type::TypeVar(lower_bound_typevar) = lower {
let variance = formal.variance_of(self.db, lower_bound_typevar); let bounds = mappings
self.add_type_mapping( .entry(lower_bound_typevar)
lower_bound_typevar, .or_insert_with(|| Bounds::new(self.db));
Type::TypeVar(typevar), bounds.upper.add_positive_in_place(Type::TypeVar(typevar));
variance,
&mut f,
);
} }
if let Type::TypeVar(upper_bound_typevar) = upper { if let Type::TypeVar(upper_bound_typevar) = upper {
let variance = formal.variance_of(self.db, upper_bound_typevar); let bounds = mappings
self.add_type_mapping( .entry(upper_bound_typevar)
upper_bound_typevar, .or_insert_with(|| Bounds::new(self.db));
Type::TypeVar(typevar), bounds.lower.add_in_place(Type::TypeVar(typevar));
variance, }
&mut f, }
);
for (bound_typevar, bounds) in mappings.drain() {
let variance = formal.variance_of(self.db, bound_typevar);
let upper = bounds.upper.build();
if !upper.is_object() {
self.add_type_mapping(bound_typevar, upper, variance, &mut f);
continue;
}
let lower = bounds.lower.build();
if !lower.is_never() {
self.add_type_mapping(bound_typevar, lower, variance, &mut f);
} }
} }
} }