Skip to content

Commit c6a5f59

Browse files
committed
[ConstraintSystem] Attempt types linked via subtyping in reverse order (supertypes first)
If there is a subtype relationship between N types we have to make sure that type chain is attempted in reverse order because we can't infer bindings transitively through type variables and attempting it in any other way would miss potential bindings across the chain.
1 parent 25433b3 commit c6a5f59

File tree

3 files changed

+52
-0
lines changed

3 files changed

+52
-0
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,11 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint(
347347

348348
result.InvolvesTypeVariables = true;
349349

350+
if (constraint->getKind() == ConstraintKind::Subtype &&
351+
kind == AllowedBindingKind::Subtypes) {
352+
result.SubtypeOf.insert(bindingTypeVar);
353+
}
354+
350355
// If we've already set addOptionalSupertypeBindings, or we aren't
351356
// allowing supertype bindings, we're done.
352357
if (addOptionalSupertypeBindings || kind != AllowedBindingKind::Supertypes)

lib/Sema/ConstraintSystem.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3677,6 +3677,13 @@ class ConstraintSystem {
36773677
/// A set of all constraints which contribute to pontential bindings.
36783678
llvm::SmallPtrSet<Constraint *, 8> Sources;
36793679

3680+
/// A set of all not-yet-resolved type variables this type variable
3681+
/// is a subtype of. This is used to determine ordering inside a
3682+
/// chain of subtypes because binding inference algorithm can't,
3683+
/// at the moment, determine bindings transitively through supertype
3684+
/// type variables.
3685+
llvm::SmallPtrSet<TypeVariableType *, 4> SubtypeOf;
3686+
36803687
PotentialBindings(TypeVariableType *typeVar)
36813688
: TypeVar(typeVar), PotentiallyIncomplete(isGenericParameter()) {}
36823689

@@ -3713,6 +3720,20 @@ class ConstraintSystem {
37133720
if (x.NumDefaultableBindings != y.NumDefaultableBindings)
37143721
return x.NumDefaultableBindings < y.NumDefaultableBindings;
37153722

3723+
// If neither type variable is a "hole" let's check whether
3724+
// there is a subtype relationship between them and prefer
3725+
// type variable which represents superclass first in order
3726+
// for "subtype" type variable to attempt more bindings later.
3727+
// This is required because algorithm can't currently infer
3728+
// bindings for subtype transitively through superclass ones.
3729+
if (!(x.IsHole && y.IsHole)) {
3730+
if (x.SubtypeOf.count(y.TypeVar))
3731+
return false;
3732+
3733+
if (y.SubtypeOf.count(x.TypeVar))
3734+
return true;
3735+
}
3736+
37163737
// As a last resort, let's check if the bindings are
37173738
// potentially incomplete, and if so, let's de-prioritize them.
37183739
return x.PotentiallyIncomplete < y.PotentiallyIncomplete;

test/Generics/deduction.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,29 @@ prefix func +-<T>(_: T) where T: Sequence, T.Element == Int {}
342342

343343
+-"hello"
344344
// expected-error@-1 {{operator function '+-' requires the types 'String.Element' (aka 'Character') and 'Int' be equivalent}}
345+
346+
func test_transitive_subtype_deduction_for_generic_params() {
347+
class A {}
348+
349+
func foo<T: A>(_: [(String, (T) -> Void)]) {}
350+
351+
func bar<U>(_: @escaping (U) -> Void) -> (U) -> Void {
352+
return { _ in }
353+
}
354+
355+
// Here we have:
356+
// - `W subtype of A`
357+
// - `W subtype of U`
358+
//
359+
// Type variable associated with `U` has to be attempted
360+
// first because solver can't infer bindings for `W` transitively
361+
// through `U`.
362+
func baz<W: A>(_ arr: [(String, (W) -> Void)]) {
363+
foo(arr.map { ($0.0, bar($0.1)) }) // Ok
364+
}
365+
366+
func fiz<T>(_ a: T, _ op: (T, T) -> Bool, _ b: T) {}
367+
func biz(_ v: Int32) {
368+
fiz(v, !=, -1) // Ok because -1 literal should be inferred as Int32
369+
}
370+
}

0 commit comments

Comments
 (0)