Skip to content

Commit 61a4148

Browse files
committed
[CS] Generalize implied result handling
Track the implied result exprs in the constraint system, and allow arbitrary propagation of implied results down if/switch expression branches. This is required for allowing implied results in non-single-expression closures.
1 parent d73e394 commit 61a4148

File tree

9 files changed

+146
-100
lines changed

9 files changed

+146
-100
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,6 @@ namespace constraints {
102102
class ConstraintSystem;
103103
enum class ConversionRestrictionKind;
104104

105-
/// The kind of SingleValueStmtExpr branch the locator identifies.
106-
enum class SingleValueStmtBranchKind {
107-
/// Explicitly written 'then <expr>'.
108-
Explicit,
109-
110-
/// Implicitly written '<expr>'.
111-
Implicit,
112-
113-
/// Implicitly written '<expr>' in a single expr closure body.
114-
ImplicitInSingleExprClosure
115-
};
116-
117105
/// Locates a given constraint within the expression being
118106
/// type-checked, which may refer down into subexpressions and parts of
119107
/// the types of those subexpressions.
@@ -301,6 +289,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
301289
/// Determine whether this locator points to the contextual type.
302290
bool isForContextualType() const;
303291

292+
/// Determine whether this locator points to the contextual type for a given
293+
/// purpose.
294+
bool isForContextualType(ContextualTypePurpose ctp) const;
295+
304296
/// Determine whether this locator points to the assignment expression.
305297
bool isForAssignment() const;
306298

@@ -325,8 +317,8 @@ class ConstraintLocator : public llvm::FoldingSetNode {
325317
bool isForSingleValueStmtConjunctionOrBrace() const;
326318

327319
/// Whether this locator identifies a conversion for a SingleValueStmtExpr
328-
/// branch, and if so, the kind of branch.
329-
llvm::Optional<SingleValueStmtBranchKind> isForSingleValueStmtBranch() const;
320+
/// branch.
321+
bool isForSingleValueStmtBranch() const;
330322

331323
/// If the locator in question is for a pattern match, returns the pattern,
332324
/// otherwise \c nullptr.
@@ -818,21 +810,6 @@ class LocatorPathElt::TypeParameterRequirement final
818810
}
819811
};
820812

821-
class LocatorPathElt::ClosureBody final : public StoredIntegerElement<1> {
822-
public:
823-
ClosureBody(bool hasImpliedReturn)
824-
: StoredIntegerElement(ConstraintLocator::ClosureBody, hasImpliedReturn) {}
825-
826-
/// Indicates whether body of the closure has an implied `return` statement,
827-
/// this is the case for single expression bodies where the `return` was not
828-
/// written explicitly.
829-
bool hasImpliedReturn() const { return bool(getValue()); }
830-
831-
static bool classof(const LocatorPathElt *elt) {
832-
return elt->getKind() == ConstraintLocator::ClosureBody;
833-
}
834-
};
835-
836813
class LocatorPathElt::Witness final : public StoredPointerElement<ValueDecl> {
837814
public:
838815
Witness(ValueDecl *witness)

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ SIMPLE_LOCATOR_PATH_ELT(ClosureThrownError)
6161
/// closure returns.
6262
///
6363
/// The type of a closure body.
64-
CUSTOM_LOCATOR_PATH_ELT(ClosureBody)
64+
SIMPLE_LOCATOR_PATH_ELT(ClosureBody)
6565

6666
/// The lookup for a constructor member.
6767
SIMPLE_LOCATOR_PATH_ELT(ConstructorMember)

include/swift/Sema/ConstraintSystem.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,15 @@ struct PotentialThrowSite {
14681468
ConstraintLocator *locator;
14691469
};
14701470

1471+
enum class ImpliedResultKind {
1472+
/// A regular implied result, this applies to e.g implied 'then' statements
1473+
/// outside of closures.
1474+
Regular,
1475+
1476+
/// An implied result for a closure, e.g a single expression body.
1477+
ForClosure
1478+
};
1479+
14711480
/// A complete solution to a constraint system.
14721481
///
14731482
/// A solution to a constraint system consists of type variable bindings to
@@ -1515,6 +1524,11 @@ class Solution {
15151524
/// to make the solution work.
15161525
std::vector<ConstraintFix *> Fixes;
15171526

1527+
/// Maps expressions for implied results (e.g implicit 'then' statements,
1528+
/// implicit 'return' statements in single expression body closures) to their
1529+
/// result kind.
1530+
llvm::MapVector<const Expr *, ImpliedResultKind> ImpliedResults;
1531+
15181532
/// For locators associated with call expressions, the trailing closure
15191533
/// matching rule and parameter bindings that were applied.
15201534
llvm::MapVector<ConstraintLocator *, MatchCallArgumentResult>
@@ -1671,6 +1685,16 @@ class Solution {
16711685
return DisjunctionChoices.find(locator)->second;
16721686
}
16731687

1688+
/// Whether the given expression is the implied result for either a ReturnStmt
1689+
/// or ThenStmt, and if so, the kind of implied result.
1690+
llvm::Optional<ImpliedResultKind> isImpliedResult(const Expr *E) const {
1691+
auto result = ImpliedResults.find(E);
1692+
if (result == ImpliedResults.end())
1693+
return llvm::None;
1694+
1695+
return result->second;
1696+
}
1697+
16741698
/// Retrieve the fixed score of this solution
16751699
const Score &getFixedScore() const { return FixedScore; }
16761700

@@ -2196,6 +2220,11 @@ class ConstraintSystem {
21962220
/// from declared parameters/result and body.
21972221
llvm::MapVector<const ClosureExpr *, FunctionType *> ClosureTypes;
21982222

2223+
/// Maps expressions for implied results (e.g implicit 'then' statements,
2224+
/// implicit 'return' statements in single expression body closures) to their
2225+
/// result kind.
2226+
llvm::MapVector<const Expr *, ImpliedResultKind> ImpliedResults;
2227+
21992228
/// This is a *global* list of all result builder bodies that have
22002229
/// been determined to be incorrect by failing constraint generation.
22012230
///
@@ -2869,6 +2898,9 @@ class ConstraintSystem {
28692898
/// The length of \c ClosureTypes.
28702899
unsigned numInferredClosureTypes;
28712900

2901+
/// The length of \c ImpliedResults.
2902+
unsigned numImpliedResults;
2903+
28722904
/// The length of \c contextualTypes.
28732905
unsigned numContextualTypes;
28742906

@@ -3077,6 +3109,24 @@ class ConstraintSystem {
30773109
return !IgnoredArguments.empty();
30783110
}
30793111

3112+
/// Record an implied result for a ReturnStmt or ThenStmt.
3113+
void recordImpliedResult(const Expr *E, ImpliedResultKind kind) {
3114+
assert(E);
3115+
auto inserted = ImpliedResults.insert({E, kind}).second;
3116+
assert(inserted && "Duplicate implied result?");
3117+
(void)inserted;
3118+
}
3119+
3120+
/// Whether the given expression is the implied result for either a ReturnStmt
3121+
/// or ThenStmt, and if so, the kind of implied result.
3122+
llvm::Optional<ImpliedResultKind> isImpliedResult(const Expr *E) const {
3123+
auto result = ImpliedResults.find(E);
3124+
if (result == ImpliedResults.end())
3125+
return llvm::None;
3126+
3127+
return result->second;
3128+
}
3129+
30803130
void setClosureType(const ClosureExpr *closure, FunctionType *type) {
30813131
assert(closure);
30823132
assert(type && "Expected non-null type");

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9638,6 +9638,7 @@ ExprWalker::rewriteTarget(SyntacticElementTarget target) {
96389638
if (solution.simplifyType(convertType)->isVoid()) {
96399639
auto contextPurpose = cs.getContextualTypePurpose(target.getAsExpr());
96409640
if (contextPurpose == CTP_ImpliedReturnStmt ||
9641+
contextPurpose == CTP_ClosureResult ||
96419642
contextPurpose == CTP_SingleValueStmtBranch) {
96429643
return false;
96439644
}

lib/Sema/CSSimplify.cpp

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7854,22 +7854,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
78547854
}
78557855

78567856
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
7857-
// literals and expressions representing an implicit return type of the single
7858-
// expression functions.
7857+
// literals and expressions representing an implied result of closures and
7858+
// if/switch expressions.
78597859
if (auto elt = locator.last()) {
78607860
if (kind >= ConstraintKind::Subtype &&
78617861
(type1->isUninhabited() || type2->isVoid())) {
7862-
// A conversion from closure body type to its signature result type.
7863-
if (auto resultElt = elt->getAs<LocatorPathElt::ClosureBody>()) {
7864-
// If a single statement closure has explicit `return` let's
7865-
// forbid conversion to `Void` and report an error instead to
7866-
// honor user's intent.
7867-
if (type1->isUninhabited() || resultElt->hasImpliedReturn()) {
7868-
increaseScore(SK_FunctionConversion, locator);
7869-
return getTypeMatchSuccess();
7870-
}
7871-
}
7872-
78737862
// Function with an implied `return`, e.g a single expression body.
78747863
if (auto contextualType = elt->getAs<LocatorPathElt::ContextualType>()) {
78757864
if (contextualType->isFor(CTP_ImpliedReturnStmt)) {
@@ -7878,26 +7867,34 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
78787867
}
78797868
}
78807869

7881-
// We also need to propagate this conversion into the branches for single
7882-
// value statements.
7870+
// Implied results can occur for closure bodies, returns, and if/switch
7871+
// expression branches.
78837872
//
7884-
// As with the previous checks, we only allow the Void conversion in
7885-
// an implicit single-expression closure. In the more general case for
7886-
// implicit branches, we only allow the Never conversion. For explicit
7887-
// branches, no conversions are allowed.
7873+
// We only allow the Void conversion for implied results in a closure
7874+
// context. In the more general case, we only allow the Never conversion.
7875+
// For explicit branches, no conversions are allowed, unless this is for
7876+
// a single expression body closure, in which case we still allow the
7877+
// Never conversion.
78887878
auto *loc = getConstraintLocator(locator);
7889-
if (auto branchKind = loc->isForSingleValueStmtBranch()) {
7879+
if (elt->is<LocatorPathElt::ClosureBody>() ||
7880+
loc->isForContextualType(CTP_ClosureResult) ||
7881+
loc->isForSingleValueStmtBranch()) {
78907882
bool allowConversion = false;
7891-
switch (*branchKind) {
7892-
case SingleValueStmtBranchKind::Explicit:
7893-
allowConversion = false;
7894-
break;
7895-
case SingleValueStmtBranchKind::Implicit:
7896-
allowConversion = type1->isUninhabited();
7897-
break;
7898-
case SingleValueStmtBranchKind::ImplicitInSingleExprClosure:
7899-
allowConversion = true;
7900-
break;
7883+
if (auto *E = getAsExpr(simplifyLocatorToAnchor(loc))) {
7884+
if (auto kind = isImpliedResult(E)) {
7885+
switch (*kind) {
7886+
case ImpliedResultKind::Regular:
7887+
allowConversion = type1->isUninhabited();
7888+
break;
7889+
case ImpliedResultKind::ForClosure:
7890+
allowConversion = true;
7891+
break;
7892+
}
7893+
} else if (elt->is<LocatorPathElt::ClosureBody>()) {
7894+
// Even if explicit, we always allow uninhabited types in single
7895+
// expression closures.
7896+
allowConversion = type1->isUninhabited();
7897+
}
79017898
}
79027899
if (allowConversion) {
79037900
increaseScore(SK_FunctionConversion, locator);

lib/Sema/CSSolver.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ Solution ConstraintSystem::finalize() {
148148
(void)inserted;
149149
}
150150

151+
// Remember implied results.
152+
for (auto impliedResult : ImpliedResults)
153+
solution.ImpliedResults.insert(impliedResult);
154+
151155
// Remember the opened types.
152156
for (const auto &opened : OpenedTypes) {
153157
// We shouldn't ever register opened types multiple times,
@@ -294,6 +298,10 @@ void ConstraintSystem::applySolution(const Solution &solution) {
294298
argumentMatchingChoices.insert(argumentMatch);
295299
}
296300

301+
// Remember implied results.
302+
for (auto impliedResult : solution.ImpliedResults)
303+
ImpliedResults.insert(impliedResult);
304+
297305
// Register the solution's opened types.
298306
for (const auto &opened : solution.OpenedTypes) {
299307
OpenedTypes.insert(opened);
@@ -667,6 +675,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
667675
numAppliedPropertyWrappers = cs.appliedPropertyWrappers.size();
668676
numResolvedOverloads = cs.ResolvedOverloads.size();
669677
numInferredClosureTypes = cs.ClosureTypes.size();
678+
numImpliedResults = cs.ImpliedResults.size();
670679
numContextualTypes = cs.contextualTypes.size();
671680
numTargets = cs.targets.size();
672681
numCaseLabelItems = cs.caseLabelItems.size();
@@ -789,6 +798,9 @@ ConstraintSystem::SolverScope::~SolverScope() {
789798
// Remove any inferred closure types (e.g. used in result builder body).
790799
truncate(cs.ClosureTypes, numInferredClosureTypes);
791800

801+
// Remove any implied results.
802+
truncate(cs.ImpliedResults, numImpliedResults);
803+
792804
// Remove any contextual types.
793805
truncate(cs.contextualTypes, numContextualTypes);
794806

lib/Sema/CSSyntacticElement.cpp

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,14 @@ class SyntacticElementConstraintGenerator
12421242
}
12431243

12441244
void visitReturnStmt(ReturnStmt *returnStmt) {
1245+
// Record an implied result if we have one.
1246+
if (returnStmt->isImplied()) {
1247+
auto kind = context.getAsClosureExpr() ? ImpliedResultKind::ForClosure
1248+
: ImpliedResultKind::Regular;
1249+
auto *result = returnStmt->getResult();
1250+
cs.recordImpliedResult(result, kind);
1251+
}
1252+
12451253
Expr *resultExpr;
12461254
if (returnStmt->hasResult()) {
12471255
resultExpr = returnStmt->getResult();
@@ -1316,11 +1324,9 @@ class SyntacticElementConstraintGenerator
13161324
// on the closure itself. Otherwise we use the default contextual type
13171325
// locator, which will be created for us.
13181326
ConstraintLocator *loc = nullptr;
1319-
if (context.isSingleExpressionClosure(cs) && returnStmt->hasResult()) {
1320-
loc = cs.getConstraintLocator(
1321-
closure, {LocatorPathElt::ClosureBody(
1322-
/*hasImpliedReturn=*/returnStmt->isImplied())});
1323-
}
1327+
if (context.isSingleExpressionClosure(cs) && returnStmt->hasResult())
1328+
loc = cs.getConstraintLocator(closure, {LocatorPathElt::ClosureBody()});
1329+
13241330
return {cs.getClosureType(closure)->getResult(), CTP_ClosureResult, loc};
13251331
}
13261332

@@ -1461,18 +1467,30 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) {
14611467
Type resultTy = createTypeVariable(loc, /*options*/ 0);
14621468
setType(E, resultTy);
14631469

1470+
// Propagate the implied result kind from the if/switch expression itself
1471+
// into the branches.
1472+
auto impliedResultKind =
1473+
isImpliedResult(E).value_or(ImpliedResultKind::Regular);
1474+
14641475
// Assign contextual types for each of the result exprs.
1465-
SmallVector<Expr *, 4> scratch;
1466-
auto branches = E->getResultExprs(scratch);
1476+
SmallVector<ThenStmt *, 4> scratch;
1477+
auto branches = E->getThenStmts(scratch);
14671478
for (auto idx : indices(branches)) {
1468-
auto *branch = branches[idx];
1479+
auto *thenStmt = branches[idx];
1480+
auto *result = thenStmt->getResult();
1481+
1482+
// If we have an implicit 'then' statement, record it as an implied result.
1483+
// TODO: Should we track 'implied' as a separate bit on ThenStmt? Currently
1484+
// it's the same as being implicit, but may not always be.
1485+
if (thenStmt->isImplicit())
1486+
recordImpliedResult(result, impliedResultKind);
14691487

14701488
auto ctpElt = LocatorPathElt::ContextualType(CTP_SingleValueStmtBranch);
14711489
auto *loc = getConstraintLocator(
14721490
E, {LocatorPathElt::SingleValueStmtResult(idx), ctpElt});
14731491

14741492
ContextualTypeInfo info(resultTy, CTP_SingleValueStmtBranch, loc);
1475-
setContextualInfo(branch, info);
1493+
setContextualInfo(result, info);
14761494
}
14771495

14781496
TypeJoinExpr *join = nullptr;
@@ -1488,9 +1506,9 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) {
14881506
ctx, resultTy, E, AllocationArena::ConstraintSolver);
14891507
}
14901508

1491-
// If this is the single expression body of a closure, we need to account
1492-
// for the fact that the result type may be bound to Void. This is necessary
1493-
// to correctly handle the following case:
1509+
// If this is an implied return in a closure, we need to account for the fact
1510+
// that the result type may be bound to Void. This is necessary to correctly
1511+
// handle the following case:
14941512
//
14951513
// func foo<T>(_ fn: () -> T) {}
14961514
// foo {
@@ -1514,12 +1532,11 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) {
15141532
// need to do this with 'return if'. We also don't need to do it for function
15151533
// decls, as we proactively avoid transforming the if/switch into an
15161534
// expression if the result is known to be Void.
1517-
if (auto *CE = dyn_cast<ClosureExpr>(E->getDeclContext())) {
1518-
if (CE->hasSingleExpressionBody() && !hasExplicitResult(CE) &&
1519-
CE->getSingleExpressionBody()->getSemanticsProvidingExpr() == E) {
1520-
assert(!getAppliedResultBuilderTransform(CE) &&
1521-
"Should have applied the builder with statement semantics");
1522-
1535+
if (impliedResultKind == ImpliedResultKind::ForClosure) {
1536+
auto *CE = cast<ClosureExpr>(E->getDeclContext());
1537+
assert(!getAppliedResultBuilderTransform(CE) &&
1538+
"Should have applied the builder with statement semantics");
1539+
if (getParentExpr(E) == CE) {
15231540
// We may not have a closure type if we're solving a sub-expression
15241541
// independently for e.g code completion.
15251542
// TODO: This won't be necessary once we stop doing the fallback

0 commit comments

Comments
 (0)