Skip to content

Commit 07424a2

Browse files
xedinrudkx
authored andcommitted
[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. (cherry picked from commit 73318a2)
1 parent e6bdd11 commit 07424a2

File tree

3 files changed

+190
-56
lines changed

3 files changed

+190
-56
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 173 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,13 +1358,6 @@ bool ConstraintSystem::Candidate::solve() {
13581358
// Allocate new constraint system for sub-expression.
13591359
ConstraintSystem cs(TC, DC, None);
13601360

1361-
// Set contextual type if present. This is done before constraint generation
1362-
// to give a "hint" to that operation about possible optimizations.
1363-
auto CT = IsPrimary ? CS.getContextualType() : CS.getContextualType(E);
1364-
if (!CT.isNull())
1365-
cs.setContextualType(E, CS.getContextualTypeLoc(),
1366-
CS.getContextualTypePurpose());
1367-
13681361
// Generate constraints for the new system.
13691362
if (auto generatedExpr = cs.generateConstraints(E)) {
13701363
E = generatedExpr;
@@ -1378,7 +1371,7 @@ bool ConstraintSystem::Candidate::solve() {
13781371
// constraint to the system.
13791372
if (!CT.isNull()) {
13801373
auto constraintKind = ConstraintKind::Conversion;
1381-
if (CS.getContextualTypePurpose() == CTP_CallArgument)
1374+
if (CTP == CTP_CallArgument)
13821375
constraintKind = ConstraintKind::ArgumentConversion;
13831376

13841377
cs.addConstraint(constraintKind, E->getType(), CT,
@@ -1492,52 +1485,22 @@ void ConstraintSystem::shrink(Expr *expr) {
14921485
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
14931486
// A dictionary expression is just a set of tuples; try to solve ones
14941487
// that have overload sets.
1495-
if (auto dictionaryExpr = dyn_cast<DictionaryExpr>(expr)) {
1496-
bool isPrimaryExpr = expr == PrimaryExpr;
1497-
for (auto element : dictionaryExpr->getElements()) {
1498-
unsigned numOverloads = 0;
1499-
element->walk(OverloadSetCounter(numOverloads));
1500-
1501-
// There are no overload sets in the element; skip it.
1502-
if (numOverloads == 0)
1503-
continue;
1504-
1505-
// FIXME: Could we avoid creating a separate dictionary expression
1506-
// here by introducing a contextual type on the element?
1507-
auto dict = DictionaryExpr::create(CS.getASTContext(),
1508-
dictionaryExpr->getLBracketLoc(),
1509-
{ element },
1510-
dictionaryExpr->getRBracketLoc(),
1511-
dictionaryExpr->getType());
1512-
1513-
// Make each of the dictionary elements an independent dictionary,
1514-
// such makes it easy to type-check everything separately.
1515-
Candidates.push_back(Candidate(CS, dict, isPrimaryExpr));
1516-
}
1517-
1488+
if (auto collectionExpr = dyn_cast<CollectionExpr>(expr)) {
1489+
visitCollectionExpr(collectionExpr, CS.getContextualType(expr),
1490+
CS.getContextualTypePurpose());
15181491
// Don't try to walk into the dictionary.
1519-
return { false, expr };
1520-
}
1521-
1522-
// Consider all of the collections to be candidates,
1523-
// FIXME: try to split collections into parts for simplified solving.
1524-
if (isa<CollectionExpr>(expr)) {
1525-
Candidates.push_back(Candidate(CS, expr, false));
15261492
return {false, expr};
15271493
}
15281494

15291495
// Let's not attempt to type-check closures, which has already been
15301496
// type checked anyway.
15311497
if (isa<ClosureExpr>(expr)) {
1532-
return { false, expr };
1498+
return {false, expr};
15331499
}
15341500

1535-
// Coerce to type expressions are only viable if they have
1536-
// a single child expression.
15371501
if (auto coerceExpr = dyn_cast<CoerceExpr>(expr)) {
1538-
if (!coerceExpr->getSubExpr()) {
1539-
return { false, expr };
1540-
}
1502+
visitCoerceExpr(coerceExpr);
1503+
return {false, expr};
15411504
}
15421505

15431506
if (auto OSR = dyn_cast<OverloadSetRefExpr>(expr)) {
@@ -1555,12 +1518,24 @@ void ConstraintSystem::shrink(Expr *expr) {
15551518
}
15561519

15571520
Expr *walkToExprPost(Expr *expr) override {
1558-
// If there are sub-expressions to consider and
1559-
// contextual type is involved, let's add top-most expression
1560-
// to the queue just to make sure that we didn't miss any solutions.
1561-
if (expr == PrimaryExpr && !Candidates.empty()) {
1562-
if (!CS.getContextualType().isNull()) {
1563-
Candidates.push_back(Candidate(CS, expr, true));
1521+
if (expr == PrimaryExpr) {
1522+
// If this is primary expression and there are no candidates
1523+
// to be solved, let's not record it, because it's going to be
1524+
// solved irregardless.
1525+
if (Candidates.empty())
1526+
return expr;
1527+
1528+
auto contextualType = CS.getContextualType();
1529+
// If there is a contextual type set for this expression.
1530+
if (!contextualType.isNull()) {
1531+
Candidates.push_back(Candidate(CS, expr, contextualType,
1532+
CS.getContextualTypePurpose()));
1533+
return expr;
1534+
}
1535+
1536+
// Or it's a function application with other candidates present.
1537+
if (isa<ApplyExpr>(expr)) {
1538+
Candidates.push_back(Candidate(CS, expr));
15641539
return expr;
15651540
}
15661541
}
@@ -1590,10 +1565,157 @@ void ConstraintSystem::shrink(Expr *expr) {
15901565
// there is no point of solving this expression,
15911566
// because we won't be able to reduce its domain.
15921567
if (numOverloadSets > 1)
1593-
Candidates.push_back(Candidate(CS, expr, expr == PrimaryExpr));
1568+
Candidates.push_back(Candidate(CS, expr));
15941569

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

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

lib/Sema/ConstraintSystem.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,15 +1024,17 @@ class ConstraintSystem {
10241024
/// to reduce scopes of the overload sets (disjunctions) in the system.
10251025
class Candidate {
10261026
Expr *E;
1027-
bool IsPrimary;
1028-
1029-
ConstraintSystem &CS;
10301027
TypeChecker &TC;
10311028
DeclContext *DC;
10321029

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

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

test/Sema/complex_expressions.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,13 @@ 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>
99+
let v7: [UInt32] = [55 * 8, 0]

0 commit comments

Comments
 (0)