Skip to content

Commit f590a1b

Browse files
committed
[Type Checker] Extend Path Consistency algorithm to cover collections
ExprCollector is extended to cover all generic collections instead of only dictionary expressions. Contextual type propagation is extended to support partial solving of collections embedded into coerce expressions.
1 parent 99ac807 commit f590a1b

File tree

3 files changed

+188
-56
lines changed

3 files changed

+188
-56
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 172 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,13 +1342,6 @@ bool ConstraintSystem::Candidate::solve() {
13421342
// Allocate new constraint system for sub-expression.
13431343
ConstraintSystem cs(TC, DC, None);
13441344

1345-
// Set contextual type if present. This is done before constraint generation
1346-
// to give a "hint" to that operation about possible optimizations.
1347-
auto CT = IsPrimary ? CS.getContextualType() : CS.getContextualType(E);
1348-
if (!CT.isNull())
1349-
cs.setContextualType(E, CS.getContextualTypeLoc(),
1350-
CS.getContextualTypePurpose());
1351-
13521345
// Generate constraints for the new system.
13531346
if (auto generatedExpr = cs.generateConstraints(E)) {
13541347
E = generatedExpr;
@@ -1362,7 +1355,7 @@ bool ConstraintSystem::Candidate::solve() {
13621355
// constraint to the system.
13631356
if (!CT.isNull()) {
13641357
auto constraintKind = ConstraintKind::Conversion;
1365-
if (CS.getContextualTypePurpose() == CTP_CallArgument)
1358+
if (CTP == CTP_CallArgument)
13661359
constraintKind = ConstraintKind::ArgumentConversion;
13671360

13681361
cs.addConstraint(constraintKind, E->getType(), CT,
@@ -1476,52 +1469,21 @@ void ConstraintSystem::shrink(Expr *expr) {
14761469
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
14771470
// A dictionary expression is just a set of tuples; try to solve ones
14781471
// that have overload sets.
1479-
if (auto dictionaryExpr = dyn_cast<DictionaryExpr>(expr)) {
1480-
bool isPrimaryExpr = expr == PrimaryExpr;
1481-
for (auto element : dictionaryExpr->getElements()) {
1482-
unsigned numOverloads = 0;
1483-
element->walk(OverloadSetCounter(numOverloads));
1484-
1485-
// There are no overload sets in the element; skip it.
1486-
if (numOverloads == 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-
Candidates.push_back(Candidate(CS, dict, isPrimaryExpr));
1500-
}
1501-
1472+
if (auto collectionExpr = dyn_cast<CollectionExpr>(expr)) {
1473+
visitCollectionExpr(collectionExpr);
15021474
// Don't try to walk into the dictionary.
1503-
return { false, expr };
1504-
}
1505-
1506-
// Consider all of the collections to be candidates,
1507-
// FIXME: try to split collections into parts for simplified solving.
1508-
if (isa<CollectionExpr>(expr)) {
1509-
Candidates.push_back(Candidate(CS, expr, false));
15101475
return {false, expr};
15111476
}
15121477

15131478
// Let's not attempt to type-check closures, which has already been
15141479
// type checked anyway.
15151480
if (isa<ClosureExpr>(expr)) {
1516-
return { false, expr };
1481+
return {false, expr};
15171482
}
15181483

1519-
// Coerce to type expressions are only viable if they have
1520-
// a single child expression.
15211484
if (auto coerceExpr = dyn_cast<CoerceExpr>(expr)) {
1522-
if (!coerceExpr->getSubExpr()) {
1523-
return { false, expr };
1524-
}
1485+
visitCoerceExpr(coerceExpr);
1486+
return {false, expr};
15251487
}
15261488

15271489
if (auto OSR = dyn_cast<OverloadSetRefExpr>(expr)) {
@@ -1539,12 +1501,24 @@ void ConstraintSystem::shrink(Expr *expr) {
15391501
}
15401502

15411503
Expr *walkToExprPost(Expr *expr) override {
1542-
// If there are sub-expressions to consider and
1543-
// contextual type is involved, let's add top-most expression
1544-
// to the queue just to make sure that we didn't miss any solutions.
1545-
if (expr == PrimaryExpr && !Candidates.empty()) {
1546-
if (!CS.getContextualType().isNull()) {
1547-
Candidates.push_back(Candidate(CS, expr, true));
1504+
if (expr == PrimaryExpr) {
1505+
// If this is primary expression and there are no candidates
1506+
// to be solved, let's not record it, because it's going to be
1507+
// solved irregardless.
1508+
if (Candidates.empty())
1509+
return expr;
1510+
1511+
auto contextualType = CS.getContextualType();
1512+
// If there is a contextual type set for this expression.
1513+
if (!contextualType.isNull()) {
1514+
Candidates.push_back(Candidate(CS, expr, contextualType,
1515+
CS.getContextualTypePurpose()));
1516+
return expr;
1517+
}
1518+
1519+
// Or it's a function application with other candidates present.
1520+
if (isa<ApplyExpr>(expr)) {
1521+
Candidates.push_back(Candidate(CS, expr));
15481522
return expr;
15491523
}
15501524
}
@@ -1574,10 +1548,157 @@ void ConstraintSystem::shrink(Expr *expr) {
15741548
// there is no point of solving this expression,
15751549
// because we won't be able to reduce its domain.
15761550
if (numOverloadSets > 1)
1577-
Candidates.push_back(Candidate(CS, expr, expr == PrimaryExpr));
1551+
Candidates.push_back(Candidate(CS, expr));
15781552

15791553
return expr;
15801554
}
1555+
1556+
private:
1557+
/// \brief Extract type of the element from given collection type.
1558+
///
1559+
/// \param collection The type of the collection container.
1560+
///
1561+
/// \returns ErrorType on failure, properly constructed type otherwise.
1562+
Type extractElementType(Type collection) {
1563+
auto &ctx = CS.getASTContext();
1564+
if (collection.isNull() || collection->is<ErrorType>())
1565+
return ErrorType::get(ctx);
1566+
1567+
auto base = collection.getPointer();
1568+
auto isInvalidType = [](Type type) -> bool {
1569+
return type.isNull() || type->hasUnresolvedType() ||
1570+
type->is<ErrorType>();
1571+
};
1572+
1573+
// Array type.
1574+
if (auto array = dyn_cast<ArraySliceType>(base)) {
1575+
auto elementType = array->getBaseType();
1576+
// If base type is invalid let's return error type.
1577+
return isInvalidType(elementType) ? ErrorType::get(ctx) : elementType;
1578+
}
1579+
1580+
// Map or Set or any other associated collection type.
1581+
if (auto boundGeneric = dyn_cast<BoundGenericType>(base)) {
1582+
if (boundGeneric->hasUnresolvedType())
1583+
return ErrorType::get(ctx);
1584+
1585+
llvm::SmallVector<TupleTypeElt, 2> params;
1586+
for (auto &type : boundGeneric->getGenericArgs()) {
1587+
// One of the generic arguments in invalid or unresolved.
1588+
if (isInvalidType(type))
1589+
return ErrorType::get(ctx);
1590+
1591+
params.push_back(type);
1592+
}
1593+
1594+
// If there is just one parameter, let's return it directly.
1595+
if (params.size() == 1)
1596+
return params[0].getType();
1597+
1598+
return TupleType::get(params, ctx);
1599+
}
1600+
1601+
return ErrorType::get(ctx);
1602+
}
1603+
1604+
bool isSuitableCollection(TypeRepr *collectionTypeRepr) {
1605+
// Only generic identifier, array or dictionary.
1606+
switch (collectionTypeRepr->getKind()) {
1607+
case TypeReprKind::GenericIdent:
1608+
case TypeReprKind::Array:
1609+
case TypeReprKind::Dictionary:
1610+
return true;
1611+
1612+
default:
1613+
return false;
1614+
}
1615+
}
1616+
1617+
void visitCoerceExpr(CoerceExpr *coerceExpr) {
1618+
auto subExpr = coerceExpr->getSubExpr();
1619+
// Coerce expression is valid only if it has sub-expression.
1620+
if (!subExpr) return;
1621+
1622+
unsigned numOverloadSets = 0;
1623+
subExpr->forEachChildExpr([&](Expr *childExpr) -> Expr * {
1624+
if (isa<OverloadSetRefExpr>(childExpr)) {
1625+
++numOverloadSets;
1626+
return childExpr;
1627+
}
1628+
1629+
if (auto nestedCoerceExpr = dyn_cast<CoerceExpr>(childExpr)) {
1630+
visitCoerceExpr(nestedCoerceExpr);
1631+
// Don't walk inside of nested coercion expression directly,
1632+
// that is be done by recursive call to visitCoerceExpr.
1633+
return nullptr;
1634+
}
1635+
1636+
// If sub-expression we are trying to coerce to type is a collection,
1637+
// let's allow collector discover it with assigned contextual type
1638+
// of coercion, which allows collections to be solved in parts.
1639+
if (auto collectionExpr = dyn_cast<CollectionExpr>(childExpr)) {
1640+
auto castTypeLoc = coerceExpr->getCastTypeLoc();
1641+
auto typeRepr = castTypeLoc.getTypeRepr();
1642+
1643+
if (typeRepr && isSuitableCollection(typeRepr)) {
1644+
// Clone representative to avoid modifying in-place,
1645+
// FIXME: We should try and silently resolve the type here,
1646+
// instead of cloning representative.
1647+
auto coercionRepr = typeRepr->clone(CS.getASTContext());
1648+
// Let's try to resolve coercion type from cloned representative.
1649+
auto coercionType = CS.TC.resolveType(coercionRepr, CS.DC,
1650+
TypeResolutionOptions());
1651+
1652+
// Looks like coercion type is invalid, let's skip this sub-tree.
1653+
if (coercionType->is<ErrorType>())
1654+
return nullptr;
1655+
1656+
// Visit collection expression inline.
1657+
visitCollectionExpr(collectionExpr, coercionType,
1658+
CTP_CoerceOperand);
1659+
}
1660+
}
1661+
1662+
return childExpr;
1663+
});
1664+
1665+
// It's going to be inefficient to try and solve
1666+
// coercion in parts, so let's just make it a candidate directly,
1667+
// if it contains at least a single overload set.
1668+
1669+
if (numOverloadSets > 0)
1670+
Candidates.push_back(Candidate(CS, coerceExpr));
1671+
}
1672+
1673+
void visitCollectionExpr(CollectionExpr *collectionExpr,
1674+
Type contextualType = Type(),
1675+
ContextualTypePurpose CTP = CTP_Unused) {
1676+
// If there is a contextual type set for this collection,
1677+
// let's propagate it to the candidate.
1678+
if (!contextualType.isNull()) {
1679+
auto elementType = extractElementType(contextualType);
1680+
// If we couldn't deduce element type for the collection, let's
1681+
// not attempt to solve it.
1682+
if (elementType->is<ErrorType>())
1683+
return;
1684+
1685+
contextualType = elementType;
1686+
}
1687+
1688+
for (auto element : collectionExpr->getElements()) {
1689+
unsigned numOverloads = 0;
1690+
element->walk(OverloadSetCounter(numOverloads));
1691+
1692+
// There are no overload sets in the element; skip it.
1693+
if (numOverloads == 0)
1694+
continue;
1695+
1696+
// Record each of the collection elements, which passed
1697+
// number of overload sets rule, as a candidate for solving
1698+
// with contextual type of the collection.
1699+
Candidates.push_back(Candidate(CS, element, contextualType, CTP));
1700+
}
1701+
}
15811702
};
15821703

15831704
ExprCollector collector(expr, *this, domains);

lib/Sema/ConstraintSystem.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,15 +1022,17 @@ class ConstraintSystem {
10221022
/// to reduce scopes of the overload sets (disjunctions) in the system.
10231023
class Candidate {
10241024
Expr *E;
1025-
bool IsPrimary;
1026-
1027-
ConstraintSystem &CS;
10281025
TypeChecker &TC;
10291026
DeclContext *DC;
10301027

1028+
// Contextual Information.
1029+
Type CT;
1030+
ContextualTypePurpose CTP;
1031+
10311032
public:
1032-
Candidate(ConstraintSystem &cs, Expr *expr, bool primaryExpr)
1033-
: E(expr), IsPrimary(primaryExpr), CS(cs), TC(cs.TC), DC(cs.DC) {}
1033+
Candidate(ConstraintSystem &cs, Expr *expr, Type ct = Type(),
1034+
ContextualTypePurpose ctp = ContextualTypePurpose::CTP_Unused)
1035+
: E(expr), TC(cs.TC), DC(cs.DC), CT(ct), CTP(ctp) {}
10341036

10351037
/// \brief Return underlaying expression.
10361038
Expr *getExpr() const { return E; }

test/Sema/complex_expressions.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,12 @@ struct P {
8787
func sr1794(pt: P, p0: P, p1: P) -> Bool {
8888
return (pt.x - p0.x) * (p1.y - p0.y) - (pt.y - p0.y) * (p1.x - p0.x) < 0.0
8989
}
90+
91+
// Tests for partial contextual type application in sub-expressions
92+
93+
let v1 = (1 - 2 / 3 * 6) as UInt
94+
let v2 = (([1 + 2 * 3, 4, 5])) as [UInt]
95+
let v3 = ["hello": 1 + 2, "world": 3 + 4 + 5 * 3] as Dictionary<String, UInt>
96+
let v4 = [1 + 2 + 3, 4] as [UInt32] + [2 * 3] as [UInt32]
97+
let v5 = ([1 + 2 + 3, 4] as [UInt32]) + ([2 * 3] as [UInt32])
98+
let v6 = [1 + 2 + 3, 4] as Set<UInt32>

0 commit comments

Comments
 (0)