do this at the overloads level

This commit is contained in:
Douglas Creager 2025-12-05 18:41:37 -05:00
parent 61381522e4
commit c60560f39d
2 changed files with 90 additions and 90 deletions

View File

@ -1749,19 +1749,17 @@ impl<'db> SpecializationBuilder<'db> {
return Ok(());
};
for formal_signature in &formal_callable.signatures(self.db).overloads {
for actual_callable in actual_callables.as_slice() {
for actual_signature in &actual_callable.signatures(self.db).overloads {
let when = formal_signature.when_constraint_set_assignable_to(
let when = formal_callable
.signatures(self.db)
.when_constraint_set_assignable_to(
self.db,
actual_signature,
actual_callable.signatures(self.db),
self.inferable,
);
self.add_type_mappings_from_constraint_set(when, polarity, &mut f);
}
}
}
}
// TODO: Add more forms that we can structurally induct into: type[C], callables
_ => {}

View File

@ -398,6 +398,31 @@ impl<'db> CallableSignature<'db> {
)
}
fn signatures_is_single_paramspec(
signatures: &[Signature<'db>],
) -> Option<BoundTypeVarInstance<'db>> {
let [signature] = signatures else {
return None;
};
signature.parameters.as_paramspec()
}
pub(crate) fn when_constraint_set_assignable_to(
&self,
db: &'db dyn Db,
other: &Self,
inferable: InferableTypeVars<'_, 'db>,
) -> ConstraintSet<'db> {
self.has_relation_to_impl(
db,
other,
inferable,
TypeRelation::ConstraintSetAssignability,
&HasRelationToVisitor::default(),
&IsDisjointVisitor::default(),
)
}
/// Implementation of subtyping and assignability between two, possible overloaded, callable
/// types.
fn has_relation_to_inner(
@ -409,6 +434,62 @@ impl<'db> CallableSignature<'db> {
relation_visitor: &HasRelationToVisitor<'db>,
disjointness_visitor: &IsDisjointVisitor<'db>,
) -> ConstraintSet<'db> {
if relation.is_constraint_set_assignability() {
// TODO: Oof, maybe ParamSpec needs to live at CallableSignature, not Signature?
let self_is_single_paramspec = Self::signatures_is_single_paramspec(self_signatures);
let other_is_single_paramspec = Self::signatures_is_single_paramspec(other_signatures);
match (self_is_single_paramspec, other_is_single_paramspec) {
(Some(self_bound_typevar), Some(other_bound_typevar)) => {
return ConstraintSet::constrain_typevar(
db,
self_bound_typevar,
Type::TypeVar(other_bound_typevar),
Type::TypeVar(other_bound_typevar),
relation,
);
}
(Some(self_bound_typevar), None) => {
let upper =
Type::Callable(CallableType::new(
db,
CallableSignature::from_overloads(other_signatures.iter().map(
|signature| Signature::new(signature.parameters().clone(), None),
)),
CallableTypeKind::ParamSpecValue,
));
return ConstraintSet::constrain_typevar(
db,
self_bound_typevar,
Type::Never,
upper,
relation,
);
}
(None, Some(other_bound_typevar)) => {
let lower =
Type::Callable(CallableType::new(
db,
CallableSignature::from_overloads(self_signatures.iter().map(
|signature| Signature::new(signature.parameters().clone(), None),
)),
CallableTypeKind::ParamSpecValue,
));
return ConstraintSet::constrain_typevar(
db,
other_bound_typevar,
lower,
Type::object(),
relation,
);
}
(None, None) => {}
}
}
match (self_signatures, other_signatures) {
([self_signature], [other_signature]) => {
// Base case: both callable types contain a single signature.
@ -964,22 +1045,6 @@ impl<'db> Signature<'db> {
result
}
pub(crate) fn when_constraint_set_assignable_to(
&self,
db: &'db dyn Db,
other: &Signature<'db>,
inferable: InferableTypeVars<'_, 'db>,
) -> ConstraintSet<'db> {
self.has_relation_to_impl(
db,
other,
inferable,
TypeRelation::ConstraintSetAssignability,
&HasRelationToVisitor::default(),
&IsDisjointVisitor::default(),
)
}
/// Implementation of subtyping and assignability for signature.
fn has_relation_to_impl(
&self,
@ -1145,69 +1210,6 @@ impl<'db> Signature<'db> {
return result;
}
// If either parameter list is a ParamSpec, then we can create a constraint set that holds
// when the ParamSpec is assignable to the other parameter list.
if relation.is_constraint_set_assignability() {
match (
self.parameters.as_paramspec(),
other.parameters.as_paramspec(),
) {
(Some(self_bound_typevar), Some(other_bound_typevar)) => {
result.intersect(
db,
ConstraintSet::constrain_typevar(
db,
self_bound_typevar,
Type::TypeVar(other_bound_typevar),
Type::TypeVar(other_bound_typevar),
relation,
),
);
return result;
}
(Some(self_bound_typevar), None) => {
let upper = Type::Callable(CallableType::new(
db,
CallableSignature::single(Signature::new(other.parameters.clone(), None)),
CallableTypeKind::ParamSpecValue,
));
result.intersect(
db,
ConstraintSet::constrain_typevar(
db,
self_bound_typevar,
Type::Never,
upper,
relation,
),
);
return result;
}
(None, Some(other_bound_typevar)) => {
let lower = Type::Callable(CallableType::new(
db,
CallableSignature::single(Signature::new(self.parameters.clone(), None)),
CallableTypeKind::ParamSpecValue,
));
result.intersect(
db,
ConstraintSet::constrain_typevar(
db,
other_bound_typevar,
lower,
Type::object(),
relation,
),
);
return result;
}
(None, None) => {}
}
}
let mut parameters = ParametersZip {
current_self: None,
current_other: None,