Skip to content

Commit ca9ddad

Browse files
committed
Introduce do expressions
1 parent ded74db commit ca9ddad

21 files changed

+1097
-47
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,9 @@ ERROR(single_value_stmt_must_be_unlabeled,none,
12561256
ERROR(if_expr_must_be_syntactically_exhaustive,none,
12571257
"'if' must have an unconditional 'else' to be used as expression",
12581258
())
1259+
ERROR(do_catch_expr_must_be_syntactically_exhaustive,none,
1260+
"'do catch' must have an unconditional 'catch' to be used as expression",
1261+
())
12591262
ERROR(single_value_stmt_branch_empty,none,
12601263
"expected expression in branch of '%0' expression",
12611264
(StmtKind))
@@ -1269,8 +1272,8 @@ ERROR(cannot_jump_in_single_value_stmt,none,
12691272
ERROR(effect_marker_on_single_value_stmt,none,
12701273
"'%0' may not be used on '%1' expression", (StringRef, StmtKind))
12711274
ERROR(out_of_place_then_stmt,none,
1272-
"'then' may only appear as the last statement in an 'if' or 'switch' "
1273-
"expression", ())
1275+
"'then' may only appear as the last statement in an 'if', 'switch', or "
1276+
"'do' expression", ())
12741277

12751278
ERROR(did_not_call_function_value,none,
12761279
"function value was used as a property; add () to call it",

include/swift/AST/Expr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6070,7 +6070,7 @@ class KeyPathDotExpr : public Expr {
60706070
class SingleValueStmtExpr : public Expr {
60716071
public:
60726072
enum class Kind {
6073-
If, Switch
6073+
If, Switch, Do, DoCatch
60746074
};
60756075

60766076
private:

include/swift/AST/Stmt.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,9 @@ class DoCatchStmt final
14171417
return {getTrailingObjects<CaseStmt *>(), Bits.DoCatchStmt.NumCatches};
14181418
}
14191419

1420+
/// Retrieve the complete set of branches for this do-catch statement.
1421+
ArrayRef<Stmt *> getBranches(SmallVectorImpl<Stmt *> &scratch) const;
1422+
14201423
/// Does this statement contain a syntactically exhaustive catch
14211424
/// clause?
14221425
///

include/swift/AST/TypeCheckRequests.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3926,6 +3926,10 @@ class IsSingleValueStmtResult {
39263926
/// The statement is an 'if' statement without an unconditional 'else'.
39273927
NonExhaustiveIf,
39283928

3929+
/// The statement is a 'do catch' statement without an unconditional
3930+
/// 'catch'.
3931+
NonExhaustiveDoCatch,
3932+
39293933
/// There is no branch that produces a resulting value.
39303934
NoResult,
39313935

@@ -3982,6 +3986,9 @@ class IsSingleValueStmtResult {
39823986
static IsSingleValueStmtResult nonExhaustiveIf() {
39833987
return IsSingleValueStmtResult(Kind::NonExhaustiveIf);
39843988
}
3989+
static IsSingleValueStmtResult nonExhaustiveDoCatch() {
3990+
return IsSingleValueStmtResult(Kind::NonExhaustiveDoCatch);
3991+
}
39853992
static IsSingleValueStmtResult noResult() {
39863993
return IsSingleValueStmtResult(Kind::NoResult);
39873994
}

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ EXPERIMENTAL_FEATURE(PlaygroundExtendedCallbacks, true)
229229
/// Enable 'then' statements.
230230
EXPERIMENTAL_FEATURE(ThenStatements, false)
231231

232+
/// Enable 'do' expressions.
233+
EXPERIMENTAL_FEATURE(DoExpressions, false)
234+
232235
/// Enable the `@_rawLayout` attribute.
233236
EXPERIMENTAL_FEATURE(RawLayout, true)
234237

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,6 +3539,10 @@ static bool usesFeatureThenStatements(Decl *decl) {
35393539
return false;
35403540
}
35413541

3542+
static bool usesFeatureDoExpressions(Decl *decl) {
3543+
return false;
3544+
}
3545+
35423546
static bool usesFeatureNewCxxMethodSafetyHeuristics(Decl *decl) {
35433547
return decl->hasClangNode();
35443548
}

lib/AST/ASTVerifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,7 @@ class Verifier : public ASTWalker {
12271227
break;
12281228
case Kind::UnterminatedBranches:
12291229
case Kind::NonExhaustiveIf:
1230+
case Kind::NonExhaustiveDoCatch:
12301231
case Kind::UnhandledStmt:
12311232
case Kind::CircularReference:
12321233
case Kind::HasLabel:

lib/AST/Expr.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,6 +2472,8 @@ SingleValueStmtExpr *SingleValueStmtExpr::create(ASTContext &ctx, Stmt *S,
24722472

24732473
SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(
24742474
ASTContext &ctx, Stmt *S, DeclContext *DC, bool mustBeExpr) {
2475+
assert(!(isa<DoStmt>(S) || isa<DoCatchStmt>(S)) ||
2476+
ctx.LangOpts.hasFeature(Feature::DoExpressions));
24752477
auto *SVE = create(ctx, S, DC);
24762478

24772479
// Attempt to wrap any branches that can be wrapped.
@@ -2483,12 +2485,15 @@ SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(
24832485

24842486
if (auto *S = BS->getSingleActiveStatement()) {
24852487
if (mustBeExpr) {
2486-
// If this must be an expression, we can eagerly wrap any exhaustive if
2487-
// and switch branch.
2488+
// If this must be an expression, we can eagerly wrap any exhaustive if,
2489+
// switch, and do statement.
24882490
if (auto *IS = dyn_cast<IfStmt>(S)) {
24892491
if (!IS->isSyntacticallyExhaustive())
24902492
continue;
2491-
} else if (!isa<SwitchStmt>(S)) {
2493+
} else if (auto *DCS = dyn_cast<DoCatchStmt>(S)) {
2494+
if (!DCS->isSyntacticallyExhaustive())
2495+
continue;
2496+
} else if (!isa<SwitchStmt>(S) && !isa<DoStmt>(S)) {
24922497
continue;
24932498
}
24942499
} else {
@@ -2571,18 +2576,28 @@ SingleValueStmtExpr::Kind SingleValueStmtExpr::getStmtKind() const {
25712576
return Kind::If;
25722577
case StmtKind::Switch:
25732578
return Kind::Switch;
2579+
case StmtKind::Do:
2580+
return Kind::Do;
2581+
case StmtKind::DoCatch:
2582+
return Kind::DoCatch;
25742583
default:
25752584
llvm_unreachable("Unhandled kind!");
25762585
}
25772586
}
25782587

25792588
ArrayRef<Stmt *>
25802589
SingleValueStmtExpr::getBranches(SmallVectorImpl<Stmt *> &scratch) const {
2590+
assert(scratch.empty());
25812591
switch (getStmtKind()) {
25822592
case Kind::If:
25832593
return cast<IfStmt>(getStmt())->getBranches(scratch);
25842594
case Kind::Switch:
25852595
return cast<SwitchStmt>(getStmt())->getBranches(scratch);
2596+
case Kind::Do:
2597+
scratch.push_back(cast<DoStmt>(getStmt())->getBody());
2598+
return scratch;
2599+
case Kind::DoCatch:
2600+
return cast<DoCatchStmt>(getStmt())->getBranches(scratch);
25862601
}
25872602
llvm_unreachable("Unhandled case in switch!");
25882603
}

lib/AST/Stmt.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,15 @@ SwitchStmt::getBranches(SmallVectorImpl<Stmt *> &scratch) const {
850850
return scratch;
851851
}
852852

853+
ArrayRef<Stmt *>
854+
DoCatchStmt::getBranches(SmallVectorImpl<Stmt *> &scratch) const {
855+
assert(scratch.empty());
856+
scratch.push_back(getBody());
857+
for (auto *CS : getCatches())
858+
scratch.push_back(CS->getBody());
859+
return scratch;
860+
}
861+
853862
// See swift/Basic/Statistic.h for declaration: this enables tracing Stmts, is
854863
// defined here to avoid too much layering violation / circular linkage
855864
// dependency.

lib/ASTGen/Sources/ASTGen/SourceFile.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ extension Parser.ExperimentalFeatures {
3737
}
3838
mapFeature(.ThenStatements, to: .thenStatements)
3939
mapFeature(.TypedThrows, to: .typedThrows)
40+
mapFeature(.DoExpressions, to: .doExpressions)
4041
}
4142
}
4243

lib/Parse/ParseExpr.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,11 +592,13 @@ ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
592592
// First check to see if we have the start of a regex literal `/.../`.
593593
tryLexRegexLiteral(/*forUnappliedOperator*/ false);
594594

595-
// Try parse an 'if' or 'switch' as an expression. Note we do this here in
596-
// parseExprUnary as we don't allow postfix syntax to hang off such
595+
// Try parse 'if', 'switch', and 'do' as expressions. Note we do this here
596+
// in parseExprUnary as we don't allow postfix syntax to hang off such
597597
// expressions to avoid ambiguities such as postfix '.member', which can
598598
// currently be parsed as a static dot member for a result builder.
599-
if (Tok.isAny(tok::kw_if, tok::kw_switch)) {
599+
if (Tok.isAny(tok::kw_if, tok::kw_switch) ||
600+
(Tok.is(tok::kw_do) &&
601+
Context.LangOpts.hasFeature(Feature::DoExpressions))) {
600602
auto Result = parseStmt();
601603
Expr *E = nullptr;
602604
if (Result.isNonNull()) {

lib/Parse/ParseStmt.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ bool Parser::isStartOfStmt(bool preferExpr) {
7878
// "try" cannot actually start any statements, but we parse it there for
7979
// better recovery in cases like 'try return'.
8080

81-
// For 'if' and 'switch' we can parse as an expression.
82-
if (peekToken().isAny(tok::kw_if, tok::kw_switch)) {
81+
// For 'if', 'switch', and 'do' we can parse as an expression.
82+
if (peekToken().isAny(tok::kw_if, tok::kw_switch) ||
83+
(peekToken().is(tok::kw_do) &&
84+
Context.LangOpts.hasFeature(Feature::DoExpressions))) {
8385
return false;
8486
}
8587
Parser::BacktrackingScope backtrack(*this);
@@ -157,7 +159,10 @@ ParserStatus Parser::parseExprOrStmt(ASTNode &Result) {
157159
// We could also achieve this by more eagerly attempting to parse an 'if'
158160
// or 'switch' as an expression when in statement position, but that
159161
// could result in less useful recovery behavior.
160-
if ((isa<IfStmt>(S) || isa<SwitchStmt>(S)) && Tok.is(tok::kw_as)) {
162+
if ((isa<IfStmt>(S) || isa<SwitchStmt>(S) ||
163+
((isa<DoStmt>(S) || isa<DoCatchStmt>(S)) &&
164+
Context.LangOpts.hasFeature(Feature::DoExpressions))) &&
165+
Tok.is(tok::kw_as)) {
161166
auto *SVE = SingleValueStmtExpr::createWithWrappedBranches(
162167
Context, S, CurDeclContext, /*mustBeExpr*/ true);
163168
auto As = parseExprAs();
@@ -778,8 +783,10 @@ ParserResult<Stmt> Parser::parseStmtReturn(SourceLoc tryLoc) {
778783
tok::pound_else, tok::pound_elseif)) {
779784
return false;
780785
}
781-
// Allowed for if/switch expressions.
782-
if (Tok.isAny(tok::kw_if, tok::kw_switch)) {
786+
// Allowed for if/switch/do expressions.
787+
if (Tok.isAny(tok::kw_if, tok::kw_switch) ||
788+
(Tok.is(tok::kw_do) &&
789+
Context.LangOpts.hasFeature(Feature::DoExpressions))) {
783790
return true;
784791
}
785792
if (isStartOfStmt(/*preferExpr*/ true) || isStartOfSwiftDecl())

lib/Sema/CSSyntacticElement.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,14 +1023,23 @@ class SyntacticElementConstraintGenerator
10231023

10241024
SmallVector<ElementInfo, 4> elements;
10251025

1026-
// First, let's record a body of `do` statement.
1027-
elements.push_back(makeElement(doStmt->getBody(), doLoc));
1026+
// First, let's record a body of `do` statement. Note we need to add a
1027+
// SyntaticElement locator path element here to avoid treating the inner
1028+
// brace conjunction as being isolated if 'doLoc' is for an isolated
1029+
// conjunction (as is the case with 'do' expressions).
1030+
auto *doBodyLoc = cs.getConstraintLocator(
1031+
doLoc, LocatorPathElt::SyntacticElement(doStmt->getBody()));
1032+
elements.push_back(makeElement(doStmt->getBody(), doBodyLoc));
10281033

10291034
// After that has been type-checked, let's switch to
10301035
// individual `catch` statements.
10311036
for (auto *catchStmt : doStmt->getCatches())
10321037
elements.push_back(makeElement(catchStmt, doLoc));
10331038

1039+
// Inject a join if we have one.
1040+
if (auto *join = context.ElementJoin.getPtrOrNull())
1041+
elements.push_back(makeJoinElement(cs, join, locator));
1042+
10341043
createConjunction(elements, doLoc);
10351044
}
10361045

@@ -1219,6 +1228,10 @@ class SyntacticElementConstraintGenerator
12191228
contextInfo.value_or(ContextualTypeInfo()), isDiscarded));
12201229
}
12211230

1231+
// Inject a join if we have one.
1232+
if (auto *join = context.ElementJoin.getPtrOrNull())
1233+
elements.push_back(makeJoinElement(cs, join, locator));
1234+
12221235
createConjunction(elements, locator);
12231236
}
12241237

lib/Sema/ConstraintLocator.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,8 @@ bool ConstraintLocator::isForSingleValueStmtConjunction() const {
666666
}
667667

668668
bool ConstraintLocator::isForSingleValueStmtConjunctionOrBrace() const {
669-
if (!isExpr<SingleValueStmtExpr>(getAnchor()))
669+
auto *SVE = getAsExpr<SingleValueStmtExpr>(getAnchor());
670+
if (!SVE)
670671
return false;
671672

672673
auto path = getPath();
@@ -677,11 +678,17 @@ bool ConstraintLocator::isForSingleValueStmtConjunctionOrBrace() const {
677678
continue;
678679
}
679680

680-
// Ignore a SyntaticElement path element for a case statement of a switch.
681+
// Ignore a SyntaticElement path element for a case statement of a switch,
682+
// or the catch of a do-catch, or the brace of a do-statement.
681683
if (auto elt = path.back().getAs<LocatorPathElt::SyntacticElement>()) {
682684
if (elt->getElement().isStmt(StmtKind::Case)) {
683685
path = path.drop_back();
684-
continue;
686+
break;
687+
}
688+
if (elt->getElement().isStmt(StmtKind::Brace) &&
689+
isa<DoCatchStmt>(SVE->getStmt())) {
690+
path = path.drop_back();
691+
break;
685692
}
686693
}
687694
break;

lib/Sema/MiscDiagnostics.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,6 +3947,11 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
39473947
diag::if_expr_must_be_syntactically_exhaustive);
39483948
break;
39493949
}
3950+
case IsSingleValueStmtResult::Kind::NonExhaustiveDoCatch: {
3951+
Diags.diagnose(S->getStartLoc(),
3952+
diag::do_catch_expr_must_be_syntactically_exhaustive);
3953+
break;
3954+
}
39503955
case IsSingleValueStmtResult::Kind::HasLabel: {
39513956
// FIXME: We should offer a fix-it to remove (currently we don't track
39523957
// the colon SourceLoc).

lib/Sema/TypeCheckStmt.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2994,14 +2994,11 @@ IsSingleValueStmtRequest::evaluate(Evaluator &eval, const Stmt *S,
29942994
assert(_ctx);
29952995
auto &ctx = *_ctx;
29962996

2997-
if (!isa<IfStmt>(S) && !isa<SwitchStmt>(S))
2998-
return IsSingleValueStmtResult::unhandledStmt();
2999-
30002997
// Statements must be unlabeled.
3001-
auto *LS = cast<LabeledStmt>(S);
3002-
if (LS->getLabelInfo())
3003-
return IsSingleValueStmtResult::hasLabel();
3004-
2998+
if (auto *LS = dyn_cast<LabeledStmt>(S)) {
2999+
if (LS->getLabelInfo())
3000+
return IsSingleValueStmtResult::hasLabel();
3001+
}
30053002
if (auto *IS = dyn_cast<IfStmt>(S)) {
30063003
// Must be exhaustive.
30073004
if (!IS->isSyntacticallyExhaustive())
@@ -3014,7 +3011,23 @@ IsSingleValueStmtRequest::evaluate(Evaluator &eval, const Stmt *S,
30143011
SmallVector<Stmt *, 4> scratch;
30153012
return areBranchesValidForSingleValueStmt(ctx, SS->getBranches(scratch));
30163013
}
3017-
llvm_unreachable("Unhandled case");
3014+
if (auto *DS = dyn_cast<DoStmt>(S)) {
3015+
if (!ctx.LangOpts.hasFeature(Feature::DoExpressions))
3016+
return IsSingleValueStmtResult::unhandledStmt();
3017+
3018+
return areBranchesValidForSingleValueStmt(ctx, DS->getBody());
3019+
}
3020+
if (auto *DCS = dyn_cast<DoCatchStmt>(S)) {
3021+
if (!ctx.LangOpts.hasFeature(Feature::DoExpressions))
3022+
return IsSingleValueStmtResult::unhandledStmt();
3023+
3024+
if (!DCS->isSyntacticallyExhaustive())
3025+
return IsSingleValueStmtResult::nonExhaustiveDoCatch();
3026+
3027+
SmallVector<Stmt *, 4> scratch;
3028+
return areBranchesValidForSingleValueStmt(ctx, DCS->getBranches(scratch));
3029+
}
3030+
return IsSingleValueStmtResult::unhandledStmt();
30183031
}
30193032

30203033
void swift::checkUnknownAttrRestrictions(

0 commit comments

Comments
 (0)