Skip to content

Commit 9a4255e

Browse files
authored
Merge pull request #9717 from xedin/rdar-32034560
[ConstraintSystem] Prevent `shrink` from solving "too complex" sub-expressions
2 parents 5bcf8a9 + fea7bcf commit 9a4255e

File tree

3 files changed

+59
-7
lines changed

3 files changed

+59
-7
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,8 @@ ConstraintSystem::solveSingle(FreeTypeVariableBinding allowFreeTypeVariables) {
14611461
return std::move(solutions[0]);
14621462
}
14631463

1464-
bool ConstraintSystem::Candidate::solve() {
1464+
bool ConstraintSystem::Candidate::solve(
1465+
llvm::SmallDenseSet<Expr *> &shrunkExprs) {
14651466
// Don't attempt to solve candidate if there is closure
14661467
// expression involved, because it's handled specially
14671468
// by parent constraint system (e.g. parameter lists).
@@ -1504,6 +1505,11 @@ bool ConstraintSystem::Candidate::solve() {
15041505
return true;
15051506
}
15061507

1508+
// If this candidate is too complex given the number
1509+
// of the domains we have reduced so far, let's bail out early.
1510+
if (isTooComplexGiven(&cs, shrunkExprs))
1511+
return false;
1512+
15071513
if (TC.getLangOpts().DebugConstraintSolver) {
15081514
auto &log = cs.getASTContext().TypeCheckerDebug->getStream();
15091515
log << "--- Solving candidate for shrinking at ";
@@ -1557,7 +1563,7 @@ bool ConstraintSystem::Candidate::solve() {
15571563
}
15581564

15591565
// Record found solutions as suggestions.
1560-
this->applySolutions(solutions);
1566+
this->applySolutions(solutions, shrunkExprs);
15611567

15621568
// Let's double-check if we have any implicit expressions
15631569
// with type variables and nullify their types.
@@ -1569,7 +1575,8 @@ bool ConstraintSystem::Candidate::solve() {
15691575
}
15701576

15711577
void ConstraintSystem::Candidate::applySolutions(
1572-
llvm::SmallVectorImpl<Solution> &solutions) const {
1578+
llvm::SmallVectorImpl<Solution> &solutions,
1579+
llvm::SmallDenseSet<Expr *> &shrunkExprs) const {
15731580
// A collection of OSRs with their newly reduced domains,
15741581
// it's domains are sets because multiple solutions can have the same
15751582
// choice for one of the type variables, and we want no duplication.
@@ -1616,6 +1623,9 @@ void ConstraintSystem::Candidate::applySolutions(
16161623
= TC.Context.AllocateUninitialized<ValueDecl *>(choices.size());
16171624
std::uninitialized_copy(choices.begin(), choices.end(), decls.begin());
16181625
OSR->setDecls(decls);
1626+
1627+
// Record successfully shrunk expression.
1628+
shrunkExprs.insert(OSR);
16191629
}
16201630
}
16211631

@@ -1684,7 +1694,8 @@ void ConstraintSystem::shrink(Expr *expr) {
16841694
auto func = applyExpr->getFn();
16851695
// Let's record this function application for post-processing
16861696
// as well as if it contains overload set, see walkToExprPost.
1687-
ApplyExprs.push_back({applyExpr, isa<OverloadSetRefExpr>(func)});
1697+
ApplyExprs.push_back(
1698+
{applyExpr, isa<OverloadSetRefExpr>(func) || isa<TypeExpr>(func)});
16881699
}
16891700

16901701
return { true, expr };
@@ -1918,11 +1929,12 @@ void ConstraintSystem::shrink(Expr *expr) {
19181929
// so we can start solving them separately.
19191930
expr->walk(collector);
19201931

1932+
llvm::SmallDenseSet<Expr *> shrunkExprs;
19211933
for (auto &candidate : collector.Candidates) {
19221934
// If there are no results, let's forget everything we know about the
19231935
// system so far. This actually is ok, because some of the expressions
19241936
// might require manual salvaging.
1925-
if (candidate.solve()) {
1937+
if (candidate.solve(shrunkExprs)) {
19261938
// Let's restore all of the original OSR domains for this sub-expression,
19271939
// this means that we can still make forward progress with solving of the
19281940
// top sub-expressions.
@@ -1933,6 +1945,7 @@ void ConstraintSystem::shrink(Expr *expr) {
19331945
return childExpr;
19341946

19351947
OSR->setDecls(domain->getSecond());
1948+
shrunkExprs.erase(OSR);
19361949
}
19371950

19381951
return childExpr;

lib/Sema/ConstraintSystem.h

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,15 +1000,45 @@ class ConstraintSystem {
10001000
/// \brief Try to solve this candidate sub-expression
10011001
/// and re-write it's OSR domains afterwards.
10021002
///
1003+
/// \param shrunkExprs The set of expressions which
1004+
/// domains have been successfully shrunk so far.
1005+
///
10031006
/// \returns true on solver failure, false otherwise.
1004-
bool solve();
1007+
bool solve(llvm::SmallDenseSet<Expr *> &shrunkExprs);
10051008

10061009
/// \brief Apply solutions found by solver as reduced OSR sets for
10071010
/// for current and all of it's sub-expressions.
10081011
///
10091012
/// \param solutions The solutions found by running solver on the
10101013
/// this candidate expression.
1011-
void applySolutions(llvm::SmallVectorImpl<Solution> &solutions) const;
1014+
///
1015+
/// \param shrunkExprs The set of expressions which
1016+
/// domains have been successfully shrunk so far.
1017+
void applySolutions(llvm::SmallVectorImpl<Solution> &solutions,
1018+
llvm::SmallDenseSet<Expr *> &shrunkExprs) const;
1019+
1020+
/// Check if attempt at solving of the candidate makes sense given
1021+
/// the current conditions - number of shrunk domains which is related
1022+
/// to the given candidate over the total number of disjunctions present.
1023+
static bool isTooComplexGiven(ConstraintSystem *const cs,
1024+
llvm::SmallDenseSet<Expr *> &shrunkExprs) {
1025+
SmallVector<Constraint *, 8> disjunctions;
1026+
cs->collectDisjunctions(disjunctions);
1027+
1028+
unsigned unsolvedDisjunctions = disjunctions.size();
1029+
for (auto *disjunction : disjunctions) {
1030+
auto *locator = disjunction->getLocator();
1031+
if (!locator)
1032+
continue;
1033+
1034+
if (auto *anchor = locator->getAnchor()) {
1035+
if (shrunkExprs.count(anchor) > 0)
1036+
--unsolvedDisjunctions;
1037+
}
1038+
}
1039+
1040+
return unsolvedDisjunctions >= 5;
1041+
}
10121042
};
10131043

10141044
/// \brief Describes the current solver state.

test/Sema/complex_expressions.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,12 @@ let sr3668Dict2: [Int: (Int, Int) -> Bool] =
117117
8: { $0 != $1 }, 9: { $0 != $1 }, 10: { $0 != $1 }, 11: { $0 != $1 },
118118
12: { $0 != $1 }, 13: { $0 != $1 }, 14: { $0 != $1 }, 15: { $0 != $1 },
119119
16: { $0 != $1 }, 17: { $0 != $1 }, 18: { $0 != $1 }, 19: { $0 != $1 } ]
120+
121+
// rdar://problem/32034560 - type-checker hangs trying to solve expression
122+
struct R32034560 {
123+
private var R32034560: Array<Array<UInt32>>
124+
private func foo(x: UInt32) -> UInt32 {
125+
return ((self.R32034560[0][Int(x >> 24) & 0xFF] &+ self.R32034560[1][Int(x >> 16) & 0xFF]) ^ self.R32034560[2][Int(x >> 8) & 0xFF]) &+ self.R32034560[3][Int(x & 0xFF)]
126+
// expected-error@-1 {{expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions}}
127+
}
128+
}

0 commit comments

Comments
 (0)