Skip to content

Commit 47353c2

Browse files
committed
[ConstraintSolver] Skip performance hacks in presence of unavailable overloads
Limit the scope of the performance hacks which currently exist in the solver even further by disallowing it to skip generic overlaods or stop in case when previous solutions involve unavailable overloads, which otherwise might lead to producing incorrect overall solutions.
1 parent 3d7ec38 commit 47353c2

File tree

3 files changed

+88
-35
lines changed

3 files changed

+88
-35
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7155,11 +7155,22 @@ bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
71557155

71567156
bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) {
71577157
// Coerce the input to whatever type is specified by the CoerceExpr.
7158-
if (!typeCheckChildIndependently(CE->getSubExpr(),
7159-
CE->getCastTypeLoc().getType(),
7160-
CTP_CoerceOperand))
7158+
auto expr = typeCheckChildIndependently(CE->getSubExpr(),
7159+
CE->getCastTypeLoc().getType(),
7160+
CTP_CoerceOperand);
7161+
if (!expr)
71617162
return true;
71627163

7164+
auto ref = expr->getReferencedDecl();
7165+
if (auto *decl = ref.getDecl()) {
7166+
// Without explicit coercion we might end up
7167+
// type-checking sub-expression as unavaible
7168+
// declaration, let's try to diagnose that here.
7169+
if (AvailableAttr::isUnavailable(decl))
7170+
return CS.TC.diagnoseExplicitUnavailability(
7171+
decl, expr->getSourceRange(), CS.DC, dyn_cast<ApplyExpr>(expr));
7172+
}
7173+
71637174
return false;
71647175
}
71657176

lib/Sema/CSSolver.cpp

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,6 +1706,44 @@ void ConstraintSystem::collectDisjunctions(
17061706
}
17071707
}
17081708

1709+
/// \brief Check if the given disjunction choice should be attempted by solver.
1710+
static bool shouldSkipDisjunctionChoice(ConstraintSystem &cs,
1711+
DisjunctionChoice &choice,
1712+
Optional<Score> &bestNonGenericScore) {
1713+
if (choice->isDisabled()) {
1714+
auto &TC = cs.TC;
1715+
if (TC.getLangOpts().DebugConstraintSolver) {
1716+
auto &log = cs.getASTContext().TypeCheckerDebug->getStream();
1717+
log.indent(cs.solverState->depth)
1718+
<< "(skipping ";
1719+
choice->print(log, &TC.Context.SourceMgr);
1720+
log << '\n';
1721+
}
1722+
1723+
return true;
1724+
}
1725+
1726+
// Don't attempt to solve for generic operators if we already have
1727+
// a non-generic solution.
1728+
1729+
// FIXME: Less-horrible but still horrible hack to attempt to
1730+
// speed things up. Skip the generic operators if we
1731+
// already have a solution involving non-generic operators,
1732+
// but continue looking for a better non-generic operator
1733+
// solution.
1734+
if (bestNonGenericScore && choice.isGenericOperatorOrUnavailable()) {
1735+
auto &score = bestNonGenericScore->Data;
1736+
// Let's skip generic overload choices only in case if
1737+
// non-generic score indicates that there were no forced
1738+
// unwrappings of optional(s) and no unavailable overload
1739+
// choices present in the solution.
1740+
if (score[SK_ForceUnchecked] == 0 && score[SK_Unavailable] == 0)
1741+
return true;
1742+
}
1743+
1744+
return false;
1745+
}
1746+
17091747
bool ConstraintSystem::solveSimplified(
17101748
SmallVectorImpl<Solution> &solutions,
17111749
FreeTypeVariableBinding allowFreeTypeVariables) {
@@ -1789,47 +1827,29 @@ bool ConstraintSystem::solveSimplified(
17891827
auto afterDisjunction = InactiveConstraints.erase(disjunction);
17901828
CG.removeConstraint(disjunction);
17911829

1792-
Score initialScore = CurrentScore;
1793-
Optional<DisjunctionChoice> lastSolvedChoice;
1830+
Optional<std::pair<DisjunctionChoice, Score>> lastSolvedChoice;
17941831
Optional<Score> bestNonGenericScore;
17951832

17961833
++solverState->NumDisjunctions;
17971834
auto constraints = disjunction->getNestedConstraints();
17981835
// Try each of the constraints within the disjunction.
17991836
for (auto index : indices(constraints)) {
18001837
auto currentChoice = DisjunctionChoice(this, constraints[index]);
1801-
if (currentChoice.isDisabled()) {
1802-
if (TC.getLangOpts().DebugConstraintSolver) {
1803-
auto &log = getASTContext().TypeCheckerDebug->getStream();
1804-
log.indent(solverState->depth)
1805-
<< "(skipping ";
1806-
currentChoice->print(log, &TC.Context.SourceMgr);
1807-
log << '\n';
1808-
}
1838+
if (shouldSkipDisjunctionChoice(*this, currentChoice, bestNonGenericScore))
18091839
continue;
1810-
}
1811-
1812-
// Don't attempt to solve for generic operators if we already have
1813-
// a non-generic solution.
1814-
1815-
// FIXME: Less-horrible but still horrible hack to attempt to
1816-
// speed things up. Skip the generic operators if we
1817-
// already have a solution involving non-generic operators,
1818-
// but continue looking for a better non-generic operator
1819-
// solution.
1820-
if (bestNonGenericScore && currentChoice.isGenericOperatorOrUnavailable()) {
1821-
// If non-generic solution increased the score by applying any
1822-
// fixes or restrictions to the solution, let's not skip generic
1823-
// overloads because they could produce a better solution.
1824-
if (bestNonGenericScore <= initialScore)
1825-
continue;
1826-
}
18271840

18281841
// We already have a solution; check whether we should
18291842
// short-circuit the disjunction.
18301843
if (lastSolvedChoice) {
1831-
auto *lastChoice = lastSolvedChoice->getConstraint();
1832-
if (shortCircuitDisjunctionAt(&currentChoice, lastChoice,
1844+
auto *lastChoice = lastSolvedChoice->first.getConstraint();
1845+
auto &score = lastSolvedChoice->second;
1846+
bool hasUnavailableOverloads = score.Data[SK_Unavailable] > 0;
1847+
1848+
// Attempt to short-circuit disjunction only if
1849+
// score indicates that there are no unavailable
1850+
// overload choices present in the solution.
1851+
if (!hasUnavailableOverloads &&
1852+
shortCircuitDisjunctionAt(&currentChoice, lastChoice,
18331853
getASTContext()))
18341854
break;
18351855
}
@@ -1864,7 +1884,7 @@ bool ConstraintSystem::solveSimplified(
18641884
bestNonGenericScore = score;
18651885
}
18661886

1867-
lastSolvedChoice = currentChoice;
1887+
lastSolvedChoice = {currentChoice, *score};
18681888

18691889
// If we see a tuple-to-tuple conversion that succeeded, we're done.
18701890
// FIXME: This should be more general.
@@ -1894,8 +1914,24 @@ Optional<Score>
18941914
DisjunctionChoice::solve(SmallVectorImpl<Solution> &solutions,
18951915
FreeTypeVariableBinding allowFreeTypeVariables) {
18961916
CS->simplifyDisjunctionChoice(Choice);
1897-
bool failed = CS->solveRec(solutions, allowFreeTypeVariables);
1898-
return failed ? None : Optional<Score>(CS->CurrentScore);
1917+
1918+
if (CS->solveRec(solutions, allowFreeTypeVariables))
1919+
return None;
1920+
1921+
assert (!solutions.empty());
1922+
1923+
Score bestScore = solutions.front().getFixedScore();
1924+
1925+
if (solutions.size() == 1)
1926+
return bestScore;
1927+
1928+
for (unsigned i = 1, n = solutions.size(); i != n; ++i) {
1929+
auto &score = solutions[i].getFixedScore();
1930+
if (score < bestScore)
1931+
bestScore = score;
1932+
}
1933+
1934+
return bestScore;
18991935
}
19001936

19011937
bool DisjunctionChoice::isGenericOperatorOrUnavailable() const {

test/Constraints/diagnostics_swift4.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,9 @@ class R<K: Hashable, V> {
4949
dict.forEach(body)
5050
}
5151
}
52+
53+
// Make sure that solver doesn't try to form solutions with available overloads when better generic choices are present.
54+
infix operator +=+ : AdditionPrecedence
55+
func +=+(_ lhs: Int, _ rhs: Int) -> Bool { return lhs == rhs }
56+
func +=+<T: BinaryInteger>(_ lhs: T, _ rhs: Int) -> Bool { return lhs == rhs }
57+
let _ = DoubleWidth<Int>(Int.min) - 1 +=+ Int.min // Ok

0 commit comments

Comments
 (0)