Skip to content

Commit 44882d2

Browse files
committed
[Constraint solver] Fix Swift 3 compatibility issue with overloaded operators.
Recent changes to the stdlib resulted in some expressions involving literals and operators that have both generic and non-generic overloads to become ambiguous. The ambiguity is due to an old performance hack in the solver which unfortunately needs to remain in place at the moment to avoid regressing expression type checker performance. This commit changes the performance hack a bit in that instead of stopping visiting the options in a disjunction as soon as we have a solution involving non-generic operators and come across a constraint involving generic operators, we instead continue visiting the elements in the disjunction, but just skip over the generic operators. Fixes rdar://problem/31695865 and rdar://problem/31698831.
1 parent 5c52400 commit 44882d2

File tree

2 files changed

+46
-22
lines changed

2 files changed

+46
-22
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2348,27 +2348,6 @@ static bool shortCircuitDisjunctionAt(Constraint *constraint,
23482348
if (constraint->getKind() == ConstraintKind::CheckedCast)
23492349
return true;
23502350

2351-
// Binding an operator overloading to a generic operator is weaker than
2352-
// binding to a non-generic operator, always.
2353-
// FIXME: this is a hack to improve performance when we're dealing with
2354-
// overloaded operators.
2355-
if (constraint->getKind() == ConstraintKind::BindOverload &&
2356-
constraint->getOverloadChoice().getKind() == OverloadChoiceKind::Decl &&
2357-
constraint->getOverloadChoice().getDecl()->isOperator() &&
2358-
successfulConstraint->getKind() == ConstraintKind::BindOverload &&
2359-
successfulConstraint->getOverloadChoice().getKind()
2360-
== OverloadChoiceKind::Decl &&
2361-
successfulConstraint->getOverloadChoice().getDecl()->isOperator()) {
2362-
auto decl = constraint->getOverloadChoice().getDecl();
2363-
auto successfulDecl = successfulConstraint->getOverloadChoice().getDecl();
2364-
auto &ctx = decl->getASTContext();
2365-
if (decl->getInterfaceType()->is<GenericFunctionType>() &&
2366-
!successfulDecl->getInterfaceType()->is<GenericFunctionType>() &&
2367-
(!successfulDecl->getAttrs().isUnavailable(ctx) ||
2368-
decl->getAttrs().isUnavailable(ctx)))
2369-
return true;
2370-
}
2371-
23722351
return false;
23732352
}
23742353

@@ -2486,6 +2465,21 @@ static unsigned countUnboundTypes(ConstraintSystem &CS,
24862465
return count;
24872466
}
24882467

2468+
// Is this constraint a bind overload to a generic operator or one
2469+
// that is unavailable?
2470+
static bool isGenericOperatorOrUnavailable(Constraint *constraint) {
2471+
if (constraint->getKind() != ConstraintKind::BindOverload ||
2472+
constraint->getOverloadChoice().getKind() != OverloadChoiceKind::Decl ||
2473+
!constraint->getOverloadChoice().getDecl()->isOperator())
2474+
return false;
2475+
2476+
auto decl = constraint->getOverloadChoice().getDecl();
2477+
auto &ctx = decl->getASTContext();
2478+
2479+
return decl->getInterfaceType()->is<GenericFunctionType>() ||
2480+
decl->getAttrs().isUnavailable(ctx);
2481+
}
2482+
24892483
bool ConstraintSystem::solveSimplified(
24902484
SmallVectorImpl<Solution> &solutions,
24912485
FreeTypeVariableBinding allowFreeTypeVariables) {
@@ -2620,6 +2614,7 @@ bool ConstraintSystem::solveSimplified(
26202614

26212615
// Try each of the constraints within the disjunction.
26222616
Constraint *firstSolvedConstraint = nullptr;
2617+
Constraint *firstNonGenericOperatorSolution = nullptr;
26232618
++solverState->NumDisjunctions;
26242619
auto constraints = disjunction->getNestedConstraints();
26252620
for (auto index : indices(constraints)) {
@@ -2635,12 +2630,24 @@ bool ConstraintSystem::solveSimplified(
26352630
continue;
26362631
}
26372632

2633+
// Don't attempt to solve for generic operators if we already have
2634+
// a non-generic solution.
2635+
2636+
// FIXME: Less-horrible but still horrible hack to attempt to
2637+
// speed things up. Skip the generic operators if we
2638+
// already have a solution involving non-generic operators,
2639+
// but continue looking for a better non-generic operator
2640+
// solution.
2641+
if (firstNonGenericOperatorSolution &&
2642+
isGenericOperatorOrUnavailable(constraint))
2643+
continue;
2644+
26382645
// We already have a solution; check whether we should
26392646
// short-circuit the disjunction.
26402647
if (firstSolvedConstraint &&
26412648
shortCircuitDisjunctionAt(constraint, firstSolvedConstraint))
26422649
break;
2643-
2650+
26442651
// If the expression was deemed "too complex", stop now and salvage.
26452652
if (getExpressionTooComplex(solutions))
26462653
break;
@@ -2686,6 +2693,10 @@ bool ConstraintSystem::solveSimplified(
26862693
solverState->addGeneratedConstraint(constraint);
26872694

26882695
if (!solveRec(solutions, allowFreeTypeVariables)) {
2696+
if (!firstNonGenericOperatorSolution &&
2697+
!isGenericOperatorOrUnavailable(constraint))
2698+
firstNonGenericOperatorSolution = constraint;
2699+
26892700
firstSolvedConstraint = constraint;
26902701

26912702
// If we see a tuple-to-tuple conversion that succeeded, we're done.

test/Constraints/overload.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,16 @@ func test_f6() {
209209
let _: (X1a) -> Void = f6
210210
let _: (X1a) -> X6 = X6.init
211211
}
212+
213+
func curry<LHS, RHS, R>(_ f: @escaping (LHS, RHS) -> R) -> (LHS) -> (RHS) -> R {
214+
return { lhs in { rhs in f(lhs, rhs) } }
215+
}
216+
217+
// We need to have an alternative version of this to ensure that there's an overload disjunction created.
218+
func curry<F, S, T, R>(_ f: @escaping (F, S, T) -> R) -> (F) -> (S) -> (T) -> R {
219+
return { fst in { snd in { thd in f(fst, snd, thd) } } }
220+
}
221+
222+
// Ensure that we consider these unambiguous
223+
let _ = curry(+)(1)
224+
let _ = [0].reduce(0, +)

0 commit comments

Comments
 (0)