Skip to content

Commit 46d945f

Browse files
committed
[ConstraintSystem] Avoid exploring too much search space when call arguments are holes
Detect that disjunction is going to be applied to arguments which don't provide any additional contextual information and allow only a single choice to be attempted in such case to avoid triggering exponential behavior in the solver. The problem is most visible with operators e.g. ```swift .foo == .bar || 1 == .baz ``` If neither member could be contextually determined and solver was allowed to attempt all of the overloads for `==` and `||` that would lead to exponential behavior (because each has 30+ overloads) and generation of hundreds of partial solutions. Resolves: rdar://problem/56400265
1 parent 2897daa commit 46d945f

File tree

3 files changed

+52
-27
lines changed

3 files changed

+52
-27
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7644,6 +7644,38 @@ Type ConstraintSystem::simplifyAppliedOverloads(
76447644
getUnboundBindOverloadDisjunction(fnTypeVar, &numOptionalUnwraps);
76457645
if (!disjunction) return fnType;
76467646

7647+
if (shouldAttemptFixes()) {
7648+
auto arguments = argFnType->getParams();
7649+
bool allHoles =
7650+
arguments.size() > 0 &&
7651+
llvm::all_of(arguments, [&](const AnyFunctionType::Param &arg) -> bool {
7652+
auto argType = arg.getPlainType();
7653+
if (argType->isHole())
7654+
return true;
7655+
7656+
if (auto *typeVar = argType->getAs<TypeVariableType>())
7657+
return hasFixFor(typeVar->getImpl().getLocator());
7658+
7659+
return false;
7660+
});
7661+
7662+
// If all of the arguments are holes, let's disable all but one
7663+
// overload to make sure holes don't cause performance problems
7664+
// because hole could be bound to any type.
7665+
if (allHoles) {
7666+
auto choices = disjunction->getNestedConstraints();
7667+
for (auto *choice : choices.slice(1))
7668+
choice->setDisabled();
7669+
}
7670+
7671+
// Don't attempt further optimization in "diagnostic mode" because
7672+
// in such mode we'd like to attempt all of the available overloads
7673+
// regardless of problems related to missing or extraneous labels
7674+
// and/or arguments.
7675+
if (solverState)
7676+
return fnTypeVar;
7677+
}
7678+
76477679
/// The common result type amongst all function overloads.
76487680
Type commonResultType;
76497681
auto updateCommonResultType = [&](Type choiceType) {
@@ -7842,20 +7874,14 @@ ConstraintSystem::simplifyApplicableFnConstraint(
78427874

78437875
};
78447876

7845-
// Don't attempt this optimization in "diagnostic mode" because
7846-
// in such mode we'd like to attempt all of the available
7847-
// overloads regardless of problems related to missing or
7848-
// extraneous labels and/or arguments.
7849-
if (!(solverState && shouldAttemptFixes())) {
7850-
// If the right-hand side is a type variable,
7851-
// try to simplify the overload set.
7852-
if (auto typeVar = desugar2->getAs<TypeVariableType>()) {
7853-
Type newType2 = simplifyAppliedOverloads(typeVar, func1, locator);
7854-
if (!newType2)
7855-
return SolutionKind::Error;
7877+
// If the right-hand side is a type variable,
7878+
// try to simplify the overload set.
7879+
if (auto typeVar = desugar2->getAs<TypeVariableType>()) {
7880+
Type newType2 = simplifyAppliedOverloads(typeVar, func1, locator);
7881+
if (!newType2)
7882+
return SolutionKind::Error;
78567883

7857-
desugar2 = newType2->getDesugaredType();
7858-
}
7884+
desugar2 = newType2->getDesugaredType();
78597885
}
78607886

78617887
// If right-hand side is a type variable, the constraint is unsolved.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %scale-test --begin 1 --end 10 --step 1 --select NumLeafScopes %s --expected-exit-code 1 -Xfrontend=-solver-expression-time-threshold=1
2+
// REQUIRES: asserts,no_asan
3+
4+
class God {
5+
public func isEqual(_ other: God) -> Bool {
6+
return (
7+
(self.form == other.form)
8+
%for i in range(1, N):
9+
&& (self.form == other.form)
10+
%end
11+
)
12+
}
13+
}

validation-test/Sema/type_checker_perf/slow/rdar54926602.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)