Skip to content

Commit 042ccdd

Browse files
authored
Merge pull request #68344 from hamishknight/do-it
2 parents 03b6623 + 5cbdfb8 commit 042ccdd

23 files changed

+1134
-73
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,9 @@ ERROR(single_value_stmt_must_be_unlabeled,none,
12591259
ERROR(if_expr_must_be_syntactically_exhaustive,none,
12601260
"'if' must have an unconditional 'else' to be used as expression",
12611261
())
1262+
ERROR(do_catch_expr_must_be_syntactically_exhaustive,none,
1263+
"'do catch' must have an unconditional 'catch' to be used as expression",
1264+
())
12621265
ERROR(single_value_stmt_branch_empty,none,
12631266
"expected expression in branch of '%0' expression",
12641267
(StmtKind))
@@ -1272,8 +1275,8 @@ ERROR(cannot_jump_in_single_value_stmt,none,
12721275
ERROR(effect_marker_on_single_value_stmt,none,
12731276
"'%0' may not be used on '%1' expression", (StringRef, StmtKind))
12741277
ERROR(out_of_place_then_stmt,none,
1275-
"'then' may only appear as the last statement in an 'if' or 'switch' "
1276-
"expression", ())
1278+
"'then' may only appear as the last statement in an 'if', 'switch', or "
1279+
"'do' expression", ())
12771280

12781281
ERROR(did_not_call_function_value,none,
12791282
"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
@@ -6075,7 +6075,7 @@ class KeyPathDotExpr : public Expr {
60756075
class SingleValueStmtExpr : public Expr {
60766076
public:
60776077
enum class Kind {
6078-
If, Switch
6078+
If, Switch, Do, DoCatch
60796079
};
60806080

60816081
private:

include/swift/AST/Stmt.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ class alignas(8) Stmt : public ASTAllocated<Stmt> {
144144

145145
/// Whether the statement can produce a single value, and as such may be
146146
/// treated as an expression.
147-
IsSingleValueStmtResult mayProduceSingleValue(Evaluator &eval) const;
148147
IsSingleValueStmtResult mayProduceSingleValue(ASTContext &ctx) const;
149148

150149
/// isImplicit - Determines whether this statement was implicitly-generated,
@@ -1418,6 +1417,9 @@ class DoCatchStmt final
14181417
return {getTrailingObjects<CaseStmt *>(), Bits.DoCatchStmt.NumCatches};
14191418
}
14201419

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

include/swift/AST/TypeCheckRequests.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,6 +3947,10 @@ class IsSingleValueStmtResult {
39473947
/// The statement is an 'if' statement without an unconditional 'else'.
39483948
NonExhaustiveIf,
39493949

3950+
/// The statement is a 'do catch' statement without an unconditional
3951+
/// 'catch'.
3952+
NonExhaustiveDoCatch,
3953+
39503954
/// There is no branch that produces a resulting value.
39513955
NoResult,
39523956

@@ -4003,6 +4007,9 @@ class IsSingleValueStmtResult {
40034007
static IsSingleValueStmtResult nonExhaustiveIf() {
40044008
return IsSingleValueStmtResult(Kind::NonExhaustiveIf);
40054009
}
4010+
static IsSingleValueStmtResult nonExhaustiveDoCatch() {
4011+
return IsSingleValueStmtResult(Kind::NonExhaustiveDoCatch);
4012+
}
40064013
static IsSingleValueStmtResult noResult() {
40074014
return IsSingleValueStmtResult(Kind::NoResult);
40084015
}
@@ -4039,18 +4046,21 @@ class IsSingleValueStmtResult {
40394046
};
40404047

40414048
/// Computes whether a given statement can be treated as a SingleValueStmtExpr.
4049+
///
4050+
// TODO: We ought to consider storing a reference to the ASTContext on the
4051+
// evaluator.
40424052
class IsSingleValueStmtRequest
40434053
: public SimpleRequest<IsSingleValueStmtRequest,
4044-
IsSingleValueStmtResult(const Stmt *),
4054+
IsSingleValueStmtResult(const Stmt *, ASTContext *),
40454055
RequestFlags::Cached> {
40464056
public:
40474057
using SimpleRequest::SimpleRequest;
40484058

40494059
private:
40504060
friend SimpleRequest;
40514061

4052-
IsSingleValueStmtResult
4053-
evaluate(Evaluator &evaluator, const Stmt *stmt) const;
4062+
IsSingleValueStmtResult evaluate(Evaluator &evaluator, const Stmt *stmt,
4063+
ASTContext *ctx) const;
40544064

40554065
public:
40564066
bool isCached() const { return true; }

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ SWIFT_REQUEST(TypeChecker, PreCheckReturnStmtRequest,
448448
Stmt *(ReturnStmt *, DeclContext *),
449449
Cached, NoLocationInfo)
450450
SWIFT_REQUEST(TypeChecker, IsSingleValueStmtRequest,
451-
IsSingleValueStmtResult(const Stmt *),
451+
IsSingleValueStmtResult(const Stmt *, ASTContext *),
452452
Cached, NoLocationInfo)
453453
SWIFT_REQUEST(TypeChecker, MacroDefinitionRequest,
454454
MacroDefinition(MacroDecl *),

include/swift/Basic/Features.def

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

235+
/// Enable 'do' expressions.
236+
EXPERIMENTAL_FEATURE(DoExpressions, false)
237+
235238
/// Enable the `@_rawLayout` attribute.
236239
EXPERIMENTAL_FEATURE(RawLayout, true)
237240

lib/AST/ASTPrinter.cpp

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

3566+
static bool usesFeatureDoExpressions(Decl *decl) {
3567+
return false;
3568+
}
3569+
35663570
static bool usesFeatureNewCxxMethodSafetyHeuristics(Decl *decl) {
35673571
return decl->hasClangNode();
35683572
}

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
@@ -2477,6 +2477,8 @@ SingleValueStmtExpr *SingleValueStmtExpr::create(ASTContext &ctx, Stmt *S,
24772477

24782478
SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(
24792479
ASTContext &ctx, Stmt *S, DeclContext *DC, bool mustBeExpr) {
2480+
assert(!(isa<DoStmt>(S) || isa<DoCatchStmt>(S)) ||
2481+
ctx.LangOpts.hasFeature(Feature::DoExpressions));
24802482
auto *SVE = create(ctx, S, DC);
24812483

24822484
// Attempt to wrap any branches that can be wrapped.
@@ -2488,12 +2490,15 @@ SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(
24882490

24892491
if (auto *S = BS->getSingleActiveStatement()) {
24902492
if (mustBeExpr) {
2491-
// If this must be an expression, we can eagerly wrap any exhaustive if
2492-
// and switch branch.
2493+
// If this must be an expression, we can eagerly wrap any exhaustive if,
2494+
// switch, and do statement.
24932495
if (auto *IS = dyn_cast<IfStmt>(S)) {
24942496
if (!IS->isSyntacticallyExhaustive())
24952497
continue;
2496-
} else if (!isa<SwitchStmt>(S)) {
2498+
} else if (auto *DCS = dyn_cast<DoCatchStmt>(S)) {
2499+
if (!DCS->isSyntacticallyExhaustive())
2500+
continue;
2501+
} else if (!isa<SwitchStmt>(S) && !isa<DoStmt>(S)) {
24972502
continue;
24982503
}
24992504
} else {
@@ -2576,18 +2581,28 @@ SingleValueStmtExpr::Kind SingleValueStmtExpr::getStmtKind() const {
25762581
return Kind::If;
25772582
case StmtKind::Switch:
25782583
return Kind::Switch;
2584+
case StmtKind::Do:
2585+
return Kind::Do;
2586+
case StmtKind::DoCatch:
2587+
return Kind::DoCatch;
25792588
default:
25802589
llvm_unreachable("Unhandled kind!");
25812590
}
25822591
}
25832592

25842593
ArrayRef<Stmt *>
25852594
SingleValueStmtExpr::getBranches(SmallVectorImpl<Stmt *> &scratch) const {
2595+
assert(scratch.empty());
25862596
switch (getStmtKind()) {
25872597
case Kind::If:
25882598
return cast<IfStmt>(getStmt())->getBranches(scratch);
25892599
case Kind::Switch:
25902600
return cast<SwitchStmt>(getStmt())->getBranches(scratch);
2601+
case Kind::Do:
2602+
scratch.push_back(cast<DoStmt>(getStmt())->getBody());
2603+
return scratch;
2604+
case Kind::DoCatch:
2605+
return cast<DoCatchStmt>(getStmt())->getBranches(scratch);
25912606
}
25922607
llvm_unreachable("Unhandled case in switch!");
25932608
}

lib/AST/Stmt.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -333,13 +333,9 @@ Stmt *BraceStmt::getSingleActiveStatement() const {
333333
return getSingleActiveElement().dyn_cast<Stmt *>();
334334
}
335335

336-
IsSingleValueStmtResult Stmt::mayProduceSingleValue(Evaluator &eval) const {
337-
return evaluateOrDefault(eval, IsSingleValueStmtRequest{this},
338-
IsSingleValueStmtResult::circularReference());
339-
}
340-
341336
IsSingleValueStmtResult Stmt::mayProduceSingleValue(ASTContext &ctx) const {
342-
return mayProduceSingleValue(ctx.evaluator);
337+
return evaluateOrDefault(ctx.evaluator, IsSingleValueStmtRequest{this, &ctx},
338+
IsSingleValueStmtResult::circularReference());
343339
}
344340

345341
SourceLoc ReturnStmt::getStartLoc() const {
@@ -863,6 +859,15 @@ SwitchStmt::getBranches(SmallVectorImpl<Stmt *> &scratch) const {
863859
return scratch;
864860
}
865861

862+
ArrayRef<Stmt *>
863+
DoCatchStmt::getBranches(SmallVectorImpl<Stmt *> &scratch) const {
864+
assert(scratch.empty());
865+
scratch.push_back(getBody());
866+
for (auto *CS : getCatches())
867+
scratch.push_back(CS->getBody());
868+
return scratch;
869+
}
870+
866871
// See swift/Basic/Statistic.h for declaration: this enables tracing Stmts, is
867872
// defined here to avoid too much layering violation / circular linkage
868873
// 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: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,9 @@ class SyntacticElementConstraintGenerator
507507
SyntacticElementContext context;
508508
ConstraintLocator *locator;
509509

510+
/// Whether a conjunction was generated.
511+
bool generatedConjunction = false;
512+
510513
public:
511514
/// Whether an error was encountered while generating constraints.
512515
bool hadError = false;
@@ -519,6 +522,16 @@ class SyntacticElementConstraintGenerator
519522
void createConjunction(ArrayRef<ElementInfo> elements,
520523
ConstraintLocator *locator, bool isIsolated = false,
521524
ArrayRef<TypeVariableType *> extraTypeVars = {}) {
525+
assert(!generatedConjunction && "Already generated conjunction");
526+
generatedConjunction = true;
527+
528+
// Inject a join if we have one.
529+
SmallVector<ElementInfo, 4> scratch;
530+
if (auto *join = context.ElementJoin.getPtrOrNull()) {
531+
scratch.append(elements.begin(), elements.end());
532+
scratch.push_back(makeJoinElement(cs, join, locator));
533+
elements = scratch;
534+
}
522535
::createConjunction(cs, context.getAsDeclContext(), elements, locator,
523536
isIsolated, extraTypeVars);
524537
}
@@ -865,10 +878,6 @@ class SyntacticElementConstraintGenerator
865878
elements.push_back(makeElement(ifStmt->getElseStmt(), elseLoc));
866879
}
867880

868-
// Inject a join if we have one.
869-
if (auto *join = context.ElementJoin.getPtrOrNull())
870-
elements.push_back(makeJoinElement(cs, join, locator));
871-
872881
createConjunction(elements, locator);
873882
}
874883

@@ -1010,10 +1019,6 @@ class SyntacticElementConstraintGenerator
10101019
elements.push_back(makeElement(rawCase, switchLoc));
10111020
}
10121021

1013-
// Inject a join if we have one.
1014-
if (auto *join = context.ElementJoin.getPtrOrNull())
1015-
elements.push_back(makeJoinElement(cs, join, switchLoc));
1016-
10171022
createConjunction(elements, switchLoc);
10181023
}
10191024

@@ -1023,8 +1028,13 @@ class SyntacticElementConstraintGenerator
10231028

10241029
SmallVector<ElementInfo, 4> elements;
10251030

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

10291039
// After that has been type-checked, let's switch to
10301040
// individual `catch` statements.

lib/Sema/ConstraintLocator.cpp

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

673673
bool ConstraintLocator::isForSingleValueStmtConjunctionOrBrace() const {
674-
if (!isExpr<SingleValueStmtExpr>(getAnchor()))
674+
auto *SVE = getAsExpr<SingleValueStmtExpr>(getAnchor());
675+
if (!SVE)
675676
return false;
676677

677678
auto path = getPath();
@@ -682,11 +683,17 @@ bool ConstraintLocator::isForSingleValueStmtConjunctionOrBrace() const {
682683
continue;
683684
}
684685

685-
// Ignore a SyntaticElement path element for a case statement of a switch.
686+
// Ignore a SyntaticElement path element for a case statement of a switch,
687+
// or the catch of a do-catch, or the brace of a do-statement.
686688
if (auto elt = path.back().getAs<LocatorPathElt::SyntacticElement>()) {
687689
if (elt->getElement().isStmt(StmtKind::Case)) {
688690
path = path.drop_back();
689-
continue;
691+
break;
692+
}
693+
if (elt->getElement().isStmt(StmtKind::Brace) &&
694+
isa<DoCatchStmt>(SVE->getStmt())) {
695+
path = path.drop_back();
696+
break;
690697
}
691698
}
692699
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).

0 commit comments

Comments
 (0)