fix constrained typevar inference

This commit is contained in:
Douglas Creager 2025-09-02 13:49:35 -04:00
parent b1a987318f
commit 7abff51010

View file

@ -1,7 +1,5 @@
use std::borrow::Cow; use std::borrow::Cow;
use crate::types::constraints::ConstraintSet;
use itertools::Itertools; use itertools::Itertools;
use ruff_db::parsed::ParsedModuleRef; use ruff_db::parsed::ParsedModuleRef;
use ruff_python_ast as ast; 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::semantic_index::scope::{FileScopeId, NodeWithScopeKind};
use crate::types::class::ClassType; use crate::types::class::ClassType;
use crate::types::class_base::ClassBase; use crate::types::class_base::ClassBase;
use crate::types::constraints::ConstraintSet;
use crate::types::infer::infer_definition_types; use crate::types::infer::infer_definition_types;
use crate::types::instance::{Protocol, ProtocolInstanceType}; use crate::types::instance::{Protocol, ProtocolInstanceType};
use crate::types::signatures::{Parameter, Parameters, Signature}; use crate::types::signatures::{Parameter, Parameters, Signature};
@ -20,7 +19,7 @@ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor,
Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
TypeVarVariance, UnionType, binding_type, declaration_type, TypeVarVariance, UnionBuilder, UnionType, binding_type, declaration_type,
}; };
use crate::{Db, FxOrderSet}; use crate::{Db, FxOrderSet};
@ -1113,17 +1112,35 @@ impl<'db> SpecializationBuilder<'db> {
self.add_type_mapping(bound_typevar, ty); self.add_type_mapping(bound_typevar, ty);
} }
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { 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) { for constraint in constraints.elements(self.db) {
if ty.is_assignable_to(self.db, *constraint) { let when_assignable: ConstraintSet =
self.add_type_mapping(bound_typevar, *constraint); ty.when_assignable_to(self.db, *constraint);
return Ok(()); 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 { return Err(SpecializationError::MismatchedConstraint {
bound_typevar, bound_typevar,
argument: ty, argument: ty,
}); });
} }
}
_ => { _ => {
self.add_type_mapping(bound_typevar, ty); self.add_type_mapping(bound_typevar, ty);
} }