Skip to content

Commit f683d84

Browse files
authored
Merge pull request #4485 from xedin/dpc
[Type checker] Introduce directional path consistency algorithm
2 parents 1f6670f + de51b01 commit f683d84

File tree

6 files changed

+552
-43
lines changed

6 files changed

+552
-43
lines changed

include/swift/AST/Expr.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,11 @@ class OverloadSetRefExpr : public Expr {
14151415

14161416
public:
14171417
ArrayRef<ValueDecl*> getDecls() const { return Decls; }
1418-
1418+
1419+
void setDecls(ArrayRef<ValueDecl *> domain) {
1420+
Decls = domain;
1421+
}
1422+
14191423
/// getBaseType - Determine the type of the base object provided for the
14201424
/// given overload set, which is only non-null when dealing with an overloaded
14211425
/// member reference.

lib/Sema/CSSolver.cpp

Lines changed: 318 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "llvm/Support/SaveAndRestore.h"
2121
#include <memory>
2222
#include <tuple>
23+
#include <stack>
24+
#include <queue>
2325
using namespace swift;
2426
using namespace constraints;
2527

@@ -1333,6 +1335,321 @@ ConstraintSystem::solveSingle(FreeTypeVariableBinding allowFreeTypeVariables) {
13331335
return std::move(solutions[0]);
13341336
}
13351337

1338+
bool ConstraintSystem::Candidate::solve() {
1339+
// Cleanup after constraint system generation/solving,
1340+
// because it would assign types to expressions, which
1341+
// might interfere with solving higher-level expressions.
1342+
ExprCleaner cleaner(E);
1343+
1344+
// Allocate new constraint system for sub-expression.
1345+
ConstraintSystem cs(TC, DC, None);
1346+
1347+
// Set contextual type if present. This is done before constraint generation
1348+
// to give a "hint" to that operation about possible optimizations.
1349+
if (!CT.isNull())
1350+
cs.setContextualType(E, CT, CTP);
1351+
1352+
// Generate constraints for the new system.
1353+
if (auto generatedExpr = cs.generateConstraints(E)) {
1354+
E = generatedExpr;
1355+
} else {
1356+
// Failure to generate constraint system for sub-expression means we can't
1357+
// continue solving sub-expressions.
1358+
return true;
1359+
}
1360+
1361+
// If there is contextual type present, add an explicit "conversion"
1362+
// constraint to the system.
1363+
if (!CT.isNull()) {
1364+
auto constraintKind = ConstraintKind::Conversion;
1365+
if (CTP == CTP_CallArgument)
1366+
constraintKind = ConstraintKind::ArgumentConversion;
1367+
1368+
cs.addConstraint(constraintKind, E->getType(), CT.getType(),
1369+
cs.getConstraintLocator(E), /*isFavored=*/true);
1370+
}
1371+
1372+
// Try to solve the system and record all available solutions.
1373+
llvm::SmallVector<Solution, 2> solutions;
1374+
{
1375+
SolverState state(cs);
1376+
cs.solverState = &state;
1377+
1378+
// Use solveRec() instead of solve() in here, because solve()
1379+
// would try to deduce the best solution, which we don't
1380+
// really want. Instead, we want the reduced set of domain choices.
1381+
cs.solveRec(solutions, FreeTypeVariableBinding::Allow);
1382+
1383+
cs.solverState = nullptr;
1384+
}
1385+
1386+
// No solutions for the sub-expression means that either main expression
1387+
// needs salvaging or it's inconsistent (read: doesn't have solutions).
1388+
if (solutions.empty())
1389+
return true;
1390+
1391+
// Record found solutions as suggestions.
1392+
this->applySolutions(solutions);
1393+
return false;
1394+
}
1395+
1396+
void ConstraintSystem::Candidate::applySolutions(
1397+
llvm::SmallVectorImpl<Solution> &solutions) const {
1398+
// A collection of OSRs with their newly reduced domains,
1399+
// it's domains are sets because multiple solutions can have the same
1400+
// choice for one of the type variables, and we want no duplication.
1401+
llvm::SmallDenseMap<OverloadSetRefExpr *, llvm::SmallSet<ValueDecl *, 2>>
1402+
domains;
1403+
for (auto &solution : solutions) {
1404+
for (auto choice : solution.overloadChoices) {
1405+
// Some of the choices might not have locators.
1406+
if (!choice.getFirst())
1407+
continue;
1408+
1409+
auto anchor = choice.getFirst()->getAnchor();
1410+
// Anchor is not available or expression is not an overload set.
1411+
if (!anchor || !isa<OverloadSetRefExpr>(anchor))
1412+
continue;
1413+
1414+
auto OSR = cast<OverloadSetRefExpr>(anchor);
1415+
auto overload = choice.getSecond().choice;
1416+
auto type = overload.getDecl()->getInterfaceType();
1417+
1418+
// One of the solutions has polymorphic type assigned with one of it's
1419+
// type variables. Such functions can only be properly resolved
1420+
// via complete expression, so we'll have to forget solutions
1421+
// we have already recorded. They might not include all viable overload
1422+
// choices.
1423+
if (type->is<GenericFunctionType>()) {
1424+
return;
1425+
}
1426+
1427+
domains[OSR].insert(overload.getDecl());
1428+
}
1429+
}
1430+
1431+
// Reduce the domains.
1432+
for (auto &domain : domains) {
1433+
auto OSR = domain.getFirst();
1434+
auto &choices = domain.getSecond();
1435+
1436+
// If the domain wasn't reduced, skip it.
1437+
if (OSR->getDecls().size() == choices.size()) continue;
1438+
1439+
// Update the expression with the reduced domain.
1440+
MutableArrayRef<ValueDecl *> decls
1441+
= TC.Context.AllocateUninitialized<ValueDecl *>(choices.size());
1442+
std::uninitialized_copy(choices.begin(), choices.end(), decls.begin());
1443+
OSR->setDecls(decls);
1444+
}
1445+
}
1446+
1447+
void ConstraintSystem::shrink(Expr *expr) {
1448+
typedef llvm::SmallDenseMap<Expr *, ArrayRef<ValueDecl *>> DomainMap;
1449+
1450+
// A collection of original domains of all of the expressions,
1451+
// so they can be restored in case of failure.
1452+
DomainMap domains;
1453+
1454+
struct ExprCollector : public ASTWalker {
1455+
// The primary constraint system.
1456+
ConstraintSystem &CS;
1457+
1458+
// All of the sub-expressions of certain type (binary/unary/calls) in
1459+
// depth-first order.
1460+
std::queue<Candidate> &SubExprs;
1461+
1462+
// Counts the number of overload sets present in the tree so far.
1463+
// Note that the traversal is depth-first.
1464+
std::stack<std::pair<ApplyExpr *, unsigned>,
1465+
llvm::SmallVector<std::pair<ApplyExpr *, unsigned>, 4>>
1466+
ApplyExprs;
1467+
1468+
// A collection of original domains of all of the expressions,
1469+
// so they can be restored in case of failure.
1470+
DomainMap &Domains;
1471+
1472+
ExprCollector(ConstraintSystem &cs,
1473+
std::queue<Candidate> &container,
1474+
DomainMap &domains)
1475+
: CS(cs), SubExprs(container), Domains(domains) { }
1476+
1477+
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
1478+
// A dictionary expression is just a set of tuples; try to solve ones
1479+
// that have overload sets.
1480+
if (auto dictionaryExpr = dyn_cast<DictionaryExpr>(expr)) {
1481+
for (auto element : dictionaryExpr->getElements()) {
1482+
unsigned numOverlaods = 0;
1483+
element->walk(OverloadSetCounter(numOverlaods));
1484+
1485+
// There are no overload sets in the element; skip it.
1486+
if (numOverlaods == 0)
1487+
continue;
1488+
1489+
// FIXME: Could we avoid creating a separate dictionary expression
1490+
// here by introducing a contextual type on the element?
1491+
auto dict = DictionaryExpr::create(CS.getASTContext(),
1492+
dictionaryExpr->getLBracketLoc(),
1493+
{ element },
1494+
dictionaryExpr->getRBracketLoc(),
1495+
dictionaryExpr->getType());
1496+
1497+
// Make each of the dictionary elements an independent dictionary,
1498+
// such makes it easy to type-check everything separately.
1499+
SubExprs.push(Candidate(CS, dict));
1500+
}
1501+
1502+
// Don't try to walk into the dictionary.
1503+
return { false, expr };
1504+
}
1505+
1506+
// Let's not attempt to type-check closures or default values,
1507+
// which has already been type checked anyway.
1508+
if (isa<ClosureExpr>(expr) || isa<DefaultValueExpr>(expr)) {
1509+
return { false, expr };
1510+
}
1511+
1512+
// Coerce to type expressions are only viable if they have
1513+
// a single child expression.
1514+
if (auto coerceExpr = dyn_cast<CoerceExpr>(expr)) {
1515+
if (!coerceExpr->getSubExpr()) {
1516+
return { false, expr };
1517+
}
1518+
}
1519+
1520+
if (auto OSR = dyn_cast<OverloadSetRefExpr>(expr)) {
1521+
Domains[OSR] = OSR->getDecls();
1522+
}
1523+
1524+
if (auto applyExpr = dyn_cast<ApplyExpr>(expr)) {
1525+
auto func = applyExpr->getFn();
1526+
// Let's record this function application for post-processing
1527+
// as well as if it contains overload set, see walkToExprPost.
1528+
ApplyExprs.push({ applyExpr, isa<OverloadSetRefExpr>(func) });
1529+
}
1530+
1531+
return { true, expr };
1532+
}
1533+
1534+
Expr *walkToExprPost(Expr *expr) override {
1535+
if (!isa<ApplyExpr>(expr))
1536+
return expr;
1537+
1538+
unsigned numOverloadSets = 0;
1539+
// Let's count how many overload sets do we have.
1540+
while (!ApplyExprs.empty()) {
1541+
auto application = ApplyExprs.top();
1542+
auto applyExpr = application.first;
1543+
1544+
// Add overload sets tracked by current expression.
1545+
numOverloadSets += application.second;
1546+
ApplyExprs.pop();
1547+
1548+
// We've found the current expression, so record the number of
1549+
// overloads.
1550+
if (expr == applyExpr) {
1551+
ApplyExprs.push({ applyExpr, numOverloadSets });
1552+
break;
1553+
}
1554+
}
1555+
1556+
// If there are fewer than two overloads in the chain
1557+
// there is no point of solving this expression,
1558+
// because we won't be able to reduce it's domain.
1559+
if (numOverloadSets > 1)
1560+
SubExprs.push(Candidate(CS, expr));
1561+
1562+
return expr;
1563+
}
1564+
};
1565+
1566+
std::queue<Candidate> expressions;
1567+
ExprCollector collector(*this, expressions, domains);
1568+
1569+
// Collect all of the binary/unary and call sub-expressions
1570+
// so we can start solving them separately.
1571+
expr->walk(collector);
1572+
1573+
while (!expressions.empty()) {
1574+
auto &candidate = expressions.front();
1575+
1576+
// If there are no results, let's forget everything we know about the
1577+
// system so far. This actually is ok, because some of the expressions
1578+
// might require manual salvaging.
1579+
if (candidate.solve()) {
1580+
// Let's restore all of the original OSR domains.
1581+
for (auto &domain : domains) {
1582+
if (auto OSR = dyn_cast<OverloadSetRefExpr>(domain.getFirst())) {
1583+
OSR->setDecls(domain.getSecond());
1584+
}
1585+
}
1586+
break;
1587+
}
1588+
1589+
expressions.pop();
1590+
}
1591+
}
1592+
1593+
ConstraintSystem::SolutionKind
1594+
ConstraintSystem::solve(Expr *&expr,
1595+
Type convertType,
1596+
ExprTypeCheckListener *listener,
1597+
SmallVectorImpl<Solution> &solutions,
1598+
FreeTypeVariableBinding allowFreeTypeVariables) {
1599+
assert(!solverState && "use solveRec for recursive calls");
1600+
1601+
// Try to shrink the system by reducing disjunction domains. This
1602+
// goes through every sub-expression and generate it's own sub-system, to
1603+
// try to reduce the domains of those subexpressions.
1604+
shrink(expr);
1605+
1606+
// Generate constraints for the main system.
1607+
if (auto generatedExpr = generateConstraints(expr))
1608+
expr = generatedExpr;
1609+
else {
1610+
return SolutionKind::Error;
1611+
}
1612+
1613+
// If there is a type that we're expected to convert to, add the conversion
1614+
// constraint.
1615+
if (convertType) {
1616+
auto constraintKind = ConstraintKind::Conversion;
1617+
if (getContextualTypePurpose() == CTP_CallArgument)
1618+
constraintKind = ConstraintKind::ArgumentConversion;
1619+
1620+
if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) {
1621+
convertType = convertType.transform([&](Type type) -> Type {
1622+
if (type->is<UnresolvedType>())
1623+
return createTypeVariable(getConstraintLocator(expr), 0);
1624+
return type;
1625+
});
1626+
}
1627+
1628+
addConstraint(constraintKind, expr->getType(), convertType,
1629+
getConstraintLocator(expr), /*isFavored*/ true);
1630+
}
1631+
1632+
// Notify the listener that we've built the constraint system.
1633+
if (listener && listener->builtConstraints(*this, expr)) {
1634+
return SolutionKind::Error;
1635+
}
1636+
1637+
if (TC.getLangOpts().DebugConstraintSolver) {
1638+
auto &log = getASTContext().TypeCheckerDebug->getStream();
1639+
log << "---Initial constraints for the given expression---\n";
1640+
expr->print(log);
1641+
log << "\n";
1642+
print(log);
1643+
}
1644+
1645+
// Try to solve the constraint system using computed suggestions.
1646+
solve(solutions, allowFreeTypeVariables);
1647+
1648+
// If there are no solutions let's mark system as unsolved,
1649+
// and solved otherwise even if there are multiple solutions still present.
1650+
return solutions.empty() ? SolutionKind::Unsolved : SolutionKind::Solved;
1651+
}
1652+
13361653
bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
13371654
FreeTypeVariableBinding allowFreeTypeVariables) {
13381655
assert(!solverState && "use solveRec for recursive calls");
@@ -1356,7 +1673,7 @@ bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
13561673

13571674
// Remove the solver state.
13581675
this->solverState = nullptr;
1359-
1676+
13601677
// We fail if there is no solution.
13611678
return solutions.empty();
13621679
}

0 commit comments

Comments
 (0)