Skip to content

Commit b83bb70

Browse files
committed
[Constraint solver] Look through optional binding for overload sets.
When we’re trying to find the overload set corresponding to a particular type variable, look through “optional object of” constraints that represent the use of ? binding or ! forcing. This allows us to find overload sets when referring to, e.g., @objc optional protocol requirements.
1 parent fd50d94 commit b83bb70

File tree

4 files changed

+95
-22
lines changed

4 files changed

+95
-22
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4952,7 +4952,9 @@ Type ConstraintSystem::simplifyAppliedOverloads(
49524952
fnTypeVar = getRepresentative(fnTypeVar);
49534953

49544954
// Dig out the disjunction that describes this overload.
4955-
auto disjunction = getUnboundBindOverloadDisjunction(fnTypeVar);
4955+
unsigned numOptionalUnwraps = 0;
4956+
auto disjunction =
4957+
getUnboundBindOverloadDisjunction(fnTypeVar, &numOptionalUnwraps);
49564958
if (!disjunction) return fnType;
49574959

49584960
/// The common result type amongst all function overloads.
@@ -5013,6 +5015,13 @@ Type ConstraintSystem::simplifyAppliedOverloads(
50135015
return true;
50145016
}
50155017

5018+
// Account for any optional unwrapping/binding
5019+
for (unsigned i : range(numOptionalUnwraps)) {
5020+
(void)i;
5021+
if (Type objectType = choiceType->getOptionalObjectType())
5022+
choiceType = objectType;
5023+
}
5024+
50165025
// If we have a function type, we can compute a common result type.
50175026
updateCommonResultType(choiceType);
50185027
return true;

lib/Sema/CSSolver.cpp

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,32 +1650,67 @@ bool ConstraintSystem::haveTypeInformationForAllArguments(
16501650
}
16511651

16521652
Constraint *ConstraintSystem::getUnboundBindOverloadDisjunction(
1653-
TypeVariableType *tyvar) {
1653+
TypeVariableType *tyvar, unsigned *numOptionalUnwraps) {
1654+
if (numOptionalUnwraps)
1655+
*numOptionalUnwraps = 0;
1656+
16541657
auto *rep = getRepresentative(tyvar);
16551658
assert(!getFixedType(rep));
16561659

1657-
llvm::SetVector<Constraint *> disjunctions;
1658-
getConstraintGraph().gatherConstraints(
1659-
rep, disjunctions, ConstraintGraph::GatheringKind::EquivalenceClass,
1660-
[this, rep](Constraint *match) {
1661-
if (match->getKind() != ConstraintKind::Disjunction ||
1662-
match->getNestedConstraints().front()->getKind() !=
1663-
ConstraintKind::BindOverload)
1664-
return false;
1665-
1666-
auto lhsTypeVar =
1667-
match->getNestedConstraints().front()->getFirstType()
1668-
->getAs<TypeVariableType>();
1669-
if (!lhsTypeVar)
1670-
return false;
1671-
1672-
return getRepresentative(lhsTypeVar) == rep;
1673-
});
1660+
SmallPtrSet<TypeVariableType *, 4> visitedVars;
1661+
while (visitedVars.insert(rep).second) {
1662+
// Look for a disjunction that binds this type variable to an overload set.
1663+
TypeVariableType *optionalObjectTypeVar = nullptr;
1664+
llvm::SetVector<Constraint *> disjunctions;
1665+
getConstraintGraph().gatherConstraints(
1666+
rep, disjunctions, ConstraintGraph::GatheringKind::EquivalenceClass,
1667+
[this, rep, &optionalObjectTypeVar](Constraint *match) {
1668+
// If we have an "optional object of" constraint where the right-hand
1669+
// side is this type variable, we may need to follow that type
1670+
// variable to find the disjunction.
1671+
if (match->getKind() == ConstraintKind::OptionalObject) {
1672+
auto rhsTypeVar = match->getSecondType()->getAs<TypeVariableType>();
1673+
if (rhsTypeVar && getRepresentative(rhsTypeVar) == rep) {
1674+
optionalObjectTypeVar =
1675+
match->getFirstType()->getAs<TypeVariableType>();
1676+
}
1677+
return false;
1678+
}
16741679

1675-
if (disjunctions.empty())
1680+
// We only care about disjunctions of overload bindings.
1681+
if (match->getKind() != ConstraintKind::Disjunction ||
1682+
match->getNestedConstraints().front()->getKind() !=
1683+
ConstraintKind::BindOverload)
1684+
return false;
1685+
1686+
auto lhsTypeVar =
1687+
match->getNestedConstraints().front()->getFirstType()
1688+
->getAs<TypeVariableType>();
1689+
if (!lhsTypeVar)
1690+
return false;
1691+
1692+
return getRepresentative(lhsTypeVar) == rep;
1693+
});
1694+
1695+
// If we found a disjunction, return it.
1696+
if (!disjunctions.empty())
1697+
return disjunctions[0];
1698+
1699+
// If we found an "optional object of" constraint, follow it.
1700+
if (optionalObjectTypeVar && !getFixedType(optionalObjectTypeVar)) {
1701+
if (numOptionalUnwraps)
1702+
++*numOptionalUnwraps;
1703+
1704+
tyvar = optionalObjectTypeVar;
1705+
rep = getRepresentative(tyvar);
1706+
continue;
1707+
}
1708+
1709+
// There is nowhere else to look.
16761710
return nullptr;
1711+
}
16771712

1678-
return disjunctions[0];
1713+
return nullptr;
16791714
}
16801715

16811716
/// solely resolved by an overload set.

lib/Sema/ConstraintSystem.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3166,7 +3166,14 @@ class ConstraintSystem {
31663166
// bind overloads associated with it. This may return null in cases where
31673167
// the disjunction has either not been created or binds the type variable
31683168
// in some manner other than by binding overloads.
3169-
Constraint *getUnboundBindOverloadDisjunction(TypeVariableType *tyvar);
3169+
///
3170+
/// \param numOptionalUnwraps If non-null, this will receive the number
3171+
/// of "optional object of" constraints that this function looked through
3172+
/// to uncover the disjunction. The actual overloads will have this number
3173+
/// of optionals wrapping the type.
3174+
Constraint *getUnboundBindOverloadDisjunction(
3175+
TypeVariableType *tyvar,
3176+
unsigned *numOptionalUnwraps = nullptr);
31703177

31713178
private:
31723179
/// Given a type variable that might represent an overload set, retrieve
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s -debug-constraints 2>%t.err
2+
// RUN: %FileCheck %s < %t.err
3+
4+
// REQUIRES: objc_interop
5+
6+
import Foundation
7+
8+
@objc protocol P {
9+
func foo(_ i: Int) -> Double
10+
func foo(_ d: Double) -> Double
11+
12+
@objc optional func opt(_ i: Int) -> Int
13+
@objc optional func opt(_ d: Double) -> Int
14+
}
15+
16+
func testOptional(obj: P) {
17+
// CHECK: common result type for {{.*}} is Int
18+
_ = obj.opt?(1)
19+
20+
// CHECK: common result type for {{.*}} is Int
21+
_ = obj.opt!(1)
22+
}

0 commit comments

Comments
 (0)