From 11fea869c17b77310d2b766b51eee6dc01c096ff Mon Sep 17 00:00:00 2001 From: Dmitri Hrybenko Date: Thu, 20 Mar 2014 11:44:59 +0000 Subject: [PATCH] Change 'switch' not to fall through between empty cases and always require at least one statement per case rdar://16301313 Swift SVN r15266 --- docs/LangRefNew.rst | 27 +- include/swift/AST/DiagnosticsParse.def | 8 +- include/swift/AST/Stmt.h | 166 +++++------ include/swift/Parse/Parser.h | 4 +- lib/AST/ASTDumper.cpp | 24 +- lib/AST/ASTPrinter.cpp | 39 ++- lib/AST/ASTWalker.cpp | 18 +- lib/AST/LookupVisibleDecls.cpp | 5 +- lib/AST/NameLookup.cpp | 13 +- lib/AST/Stmt.cpp | 74 ++--- lib/Parse/ParseStmt.cpp | 279 +++++++++--------- lib/SILGen/SILGenPattern.cpp | 8 +- .../DerivedConformanceEquatableHashable.cpp | 37 +-- .../DerivedConformanceRawRepresentable.cpp | 28 +- lib/Sema/TypeCheckStmt.cpp | 46 +-- utils/buildbot-release-notes.txt | 33 +++ 16 files changed, 402 insertions(+), 407 deletions(-) diff --git a/docs/LangRefNew.rst b/docs/LangRefNew.rst index 3dee5b74c11..45613737632 100644 --- a/docs/LangRefNew.rst +++ b/docs/LangRefNew.rst @@ -1250,9 +1250,9 @@ The 'continue' statement transfers control back to the start of the enclosing .. code-block:: none stmt-switch ::= 'switch' expr-basic '{' stmt-switch-case* '}' - stmt-switch-case ::= (case-label+ | default-label) brace-item* + stmt-switch-case ::= (case-label | default-label) brace-item+ - case-label ::= 'case' pattern (',' pattern)* ('where' expr)? ':' + case-label ::= 'case' pattern ('where' expr)? (',' pattern ('where' expr)?)* ':' default-label ::= 'default' ':' 'switch' statements branch on the value of an expression by :ref:`pattern @@ -1260,15 +1260,15 @@ matching `. The subject expression of the switch is evaluated and tested against the patterns in its ``case`` labels in source order. When a pattern is found that matches the value, control is transferred into the matching ``case`` block. ``case`` labels may declare multiple patterns -separated by commas, and multiple ``case`` labels may cover a case block. Case -labels may optionally specify a *guard* expression, introduced by the ``where`` -keyword; if present, control is transferred to the case only if the subject -value both matches one of its patterns and the guard expression evaluates to -true. Patterns are tested "as if" in source order; if multiple cases can match -a value, control is transferred only to the first matching case. The actual -execution order of pattern matching operations, and in particular the -evaluation order of :ref:`expression patterns `, is -unspecified. +separated by commas. Only a single ``case`` labels may precede a block of +code. Case labels may optionally specify a *guard* expression, introduced by +the ``where`` keyword; if present, control is transferred to the case only if +the subject value both matches the corresponding pattern and the guard +expression evaluates to true. Patterns are tested "as if" in source order; if +multiple cases can match a value, control is transferred only to the first +matching case. The actual execution order of pattern matching operations, and +in particular the evaluation order of :ref:`expression patterns +`, is unspecified. A switch may also contain a ``default`` block. If present, it receives control if no cases match the subject value. The ``default`` block must appear at the @@ -1291,9 +1291,12 @@ transfer control among case blocks. :ref:`break ` and :ref:`continue ` within a switch will break or continue out of an enclosing 'while' or 'for' loop, not out of the 'switch' itself. +At least one ``brace-item`` is required in every case or default block. It is +allowed to be a no-op. + :: - func classifyPoint(point:(Int, Int)) { + func classifyPoint(point: (Int, Int)) { switch point { case (0, 0): println("origin") diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index dcf90539bb2..07b4295f42f 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -554,7 +554,7 @@ ERROR(init_expected_self,type_parsing,PointsToFirstBadToken, // Pattern parsing diagnostics //------------------------------------------------------------------------------ -ERROR(expected_pattern,pattern_parsing,none, +ERROR(expected_pattern,pattern_parsing,PointsToFirstBadToken, "expected pattern", ()) ERROR(expected_pattern_is_keyword,pattern_parsing,none, "keyword '%0' cannot be used as an identifier", (StringRef)) @@ -690,15 +690,15 @@ ERROR(expected_case_where_expr,stmt_parsing,PointsToFirstBadToken, "expected expression for 'where' guard of 'case'", ()) ERROR(expected_case_colon,stmt_parsing,PointsToFirstBadToken, "expected ':' after '%0'", (StringRef)) -ERROR(default_with_other_labels,stmt_parsing,none, - "'default' cannot appear with other 'case' or 'default' labels over the same block", - ()) ERROR(default_with_where,stmt_parsing,none, "'default' cannot be used with a 'where' guard expression", ()) ERROR(var_binding_with_multiple_case_patterns,stmt_parsing,none, "'case' labels with multiple patterns cannot declare variables", ()) +ERROR(case_stmt_without_body,stmt_parsing,none, + "'case' label in a 'switch' should have at least one executable " + "statement", ()) //------------------------------------------------------------------------------ // Expression parsing diagnostics diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 627ba1adebc..22b660cd42a 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -418,125 +418,101 @@ public: return S->getKind() == StmtKind::ForEach; } }; - -/// A label used at the head of a 'case' block. Each 'case' label may have one -/// or more comma-separated patterns. The 'case' may also optionally have a -/// 'where' guard expression. 'default' is allowed as an alternate spelling of -/// 'case _'. -/// -/// Some examples: -/// -/// case 1: -/// case 2, 3: -/// case Foo(var x, var y) where x < y: -/// default: -class CaseLabel { - SourceLoc CaseLoc; - SourceLoc ColonLoc; + +/// A pattern and an optional guard expression used in a 'case' statement. +class CaseLabelItem { + Pattern *CasePattern; SourceLoc WhereLoc; llvm::PointerIntPair GuardExprAndIsDefault; - unsigned NumPatterns; - - CaseLabel(bool isDefault, - SourceLoc caseLoc, ArrayRef patterns, - SourceLoc whereLoc, Expr *guardExpr, - SourceLoc colonLoc); - - Pattern **getPatternsBuffer() { - return reinterpret_cast(this + 1); - } - const Pattern * const *getPatternsBuffer() const { - return reinterpret_cast(this + 1); - } - - /// CaseLabels should be allocated with an ASTContext using create. - void *operator new(size_t) = delete; - void operator delete(void*) = delete; - + public: - static CaseLabel *create(ASTContext &C, - bool isDefault, - SourceLoc caseLoc, - ArrayRef patterns, - SourceLoc whereLoc, - Expr * /*nullable*/ guardExpr, - SourceLoc colonLoc); - - SourceLoc getLoc() const { return CaseLoc; } - SourceLoc getCaseLoc() const { return CaseLoc; } - SourceLoc getColonLoc() const { return ColonLoc; } + CaseLabelItem(const CaseLabelItem &) = default; + + CaseLabelItem(bool IsDefault, Pattern *CasePattern, SourceLoc WhereLoc, + Expr *GuardExpr) + : CasePattern(CasePattern), WhereLoc(WhereLoc), + GuardExprAndIsDefault(GuardExpr, IsDefault) {} + SourceLoc getWhereLoc() const { return WhereLoc; } - - SourceRange getSourceRange() const { - return {CaseLoc, ColonLoc}; - } - - MutableArrayRef getPatterns() { - return {getPatternsBuffer(), NumPatterns}; - } - ArrayRef getPatterns() const { - return {getPatternsBuffer(), NumPatterns}; - } - + + SourceRange getSourceRange() const; + + Pattern *getPattern() { return CasePattern; } + const Pattern *getPattern() const { return CasePattern; } + void setPattern(Pattern *CasePattern) { this->CasePattern = CasePattern; } + /// Return the guard expression if present, or null if the case label has /// no guard. - Expr *getGuardExpr() const { return GuardExprAndIsDefault.getPointer(); } + Expr *getGuardExpr() { return GuardExprAndIsDefault.getPointer(); } + const Expr *getGuardExpr() const { + return GuardExprAndIsDefault.getPointer(); + } void setGuardExpr(Expr *e) { GuardExprAndIsDefault.setPointer(e); } - + /// Returns true if this is syntactically a 'default' label. bool isDefault() const { return GuardExprAndIsDefault.getInt(); } }; - -/// A 'case' or 'default' block of a switch statement. Only valid as the -/// substatement of a SwitchStmt. A case block begins either with one or more -/// CaseLabels or a single 'default' label. -class CaseStmt : public Stmt { - llvm::PointerIntPair BodyAndHasBoundDecls; - unsigned NumCaseLabels; - CaseLabel * const *getCaseLabelsBuffer() const { - return reinterpret_cast(this + 1); +/// A 'case' or 'default' block of a switch statement. Only valid as the +/// substatement of a SwitchStmt. A case block begins either with one or more +/// CaseLabelItems or a single 'default' label. +/// +/// Some examples: +/// \code +/// case 1: +/// case 2, 3: +/// case Foo(var x, var y) where x < y: +/// case 2 where foo(), 3 where bar(): +/// default: +/// \endcode + +class CaseStmt : public Stmt { + SourceLoc CaseLoc; + SourceLoc ColonLoc; + + llvm::PointerIntPair BodyAndHasBoundDecls; + unsigned NumPatterns; + + const CaseLabelItem *getCaseLabelItemsBuffer() const { + return reinterpret_cast(this + 1); } - CaseLabel **getCaseLabelsBuffer() { - return reinterpret_cast(this + 1); + CaseLabelItem *getCaseLabelItemsBuffer() { + return reinterpret_cast(this + 1); } - - CaseStmt(ArrayRef Labels, bool hasBoundDecls, Stmt *Body, + + CaseStmt(SourceLoc CaseLoc, ArrayRef CaseLabelItems, + bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, Optional Implicit); - + public: - static CaseStmt *create(ASTContext &C, - ArrayRef Labels, - bool hasBoundDecls, - Stmt *Body, + static CaseStmt *create(ASTContext &C, SourceLoc CaseLoc, + ArrayRef CaseLabelItems, + bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, Optional Implicit = {}); - - ArrayRef getCaseLabels() const { - return {getCaseLabelsBuffer(), NumCaseLabels}; + + ArrayRef getCaseLabelItems() const { + return { getCaseLabelItemsBuffer(), NumPatterns }; } - + MutableArrayRef getMutableCaseLabelItems() { + return { getCaseLabelItemsBuffer(), NumPatterns }; + } + Stmt *getBody() const { return BodyAndHasBoundDecls.getPointer(); } void setBody(Stmt *body) { BodyAndHasBoundDecls.setPointer(body); } - + /// True if the case block declares any patterns with local variable bindings. bool hasBoundDecls() const { return BodyAndHasBoundDecls.getInt(); } - + /// Get the source location of the 'case' or 'default' of the first label. - SourceLoc getLoc() const { - return getCaseLabels()[0]->getLoc(); - } - + SourceLoc getLoc() const { return CaseLoc; } + SourceRange getSourceRange() const { - return {getLoc(), getBody()->getEndLoc()}; - } - - bool isDefault() { - return getCaseLabels()[0]->isDefault(); - } - - static bool classof(const Stmt *S) { - return S->getKind() == StmtKind::Case; + return { getLoc(), getBody()->getEndLoc() }; } + + bool isDefault() { return getCaseLabelItems()[0].isDefault(); } + + static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Case; } }; /// Switch statement. diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index a26eff81275..79856f42e55 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -943,9 +943,7 @@ public: ParserResult parseStmtForEach(SourceLoc ForLoc); ParserResult parseStmtSwitch(); ParserResult parseStmtCase(); - ParserStatus parseStmtCaseLabels(SmallVectorImpl &labels, - SmallVectorImpl &boundDecls); - + /// Evaluate the conditional configuration expression of an #if statement bool evaluateConfigConditionExpr(Expr *configExpr); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index b62452793e2..985875f2843 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -91,7 +91,9 @@ namespace { void printRec(Expr *E) { E->print(OS, Indent + 2); } void printRec(Stmt *S) { S->print(OS, Indent + 2); } void printRec(TypeRepr *T); - void printRec(Pattern *P) { PrintPattern(OS, Indent+2).visit(P); } + void printRec(const Pattern *P) { + PrintPattern(OS, Indent+2).visit(const_cast(P)); + } raw_ostream &printCommon(Pattern *P, const char *Name) { OS.indent(Indent) << '('; @@ -792,7 +794,9 @@ public: void printRec(Decl *D) { D->dump(OS, Indent + 2); } void printRec(Expr *E) { E->print(OS, Indent + 2); } - void printRec(Pattern *P) { PrintPattern(OS, Indent+2).visit(P); } + void printRec(const Pattern *P) { + PrintPattern(OS, Indent+2).visit(const_cast(P)); + } void printRec(StmtCondition C) { if (auto E = C.dyn_cast()) @@ -924,16 +928,16 @@ public: } void visitCaseStmt(CaseStmt *S) { OS.indent(Indent) << "(case_stmt"; - for (CaseLabel *label : S->getCaseLabels()) { + for (const auto &LabelItem : S->getCaseLabelItems()) { OS << '\n'; - OS.indent(Indent+2) << "(case_label"; - for (Pattern *p : label->getPatterns()) { + OS.indent(Indent + 2) << "(case_label_item"; + if (auto *CasePattern = LabelItem.getPattern()) { OS << '\n'; - printRec(p); + printRec(CasePattern); } - if (Expr *guard = label->getGuardExpr()) { + if (auto *Guard = LabelItem.getGuardExpr()) { OS << '\n'; - guard->print(OS, Indent+4); + Guard->print(OS, Indent+4); } OS << ')'; } @@ -981,7 +985,9 @@ public: void printRec(Decl *D) { D->dump(OS, Indent + 2); } void printRec(Stmt *S) { S->print(OS, Indent + 2); } - void printRec(Pattern *P) { PrintPattern(OS, Indent+2).visit(P); } + void printRec(const Pattern *P) { + PrintPattern(OS, Indent+2).visit(const_cast(P)); + } void printRec(TypeRepr *T); raw_ostream &printCommon(Expr *E, const char *C) { diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 8e8c6014bac..dc6d5dea934 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -1061,28 +1061,25 @@ void PrintAST::visitSwitchStmt(SwitchStmt *stmt) { Printer << "}"; } -void PrintAST::visitCaseStmt(CaseStmt *stmt) { - auto printCaseLabel = [&](CaseLabel *label) { - if (label->isDefault()) { - Printer << "default"; - // '_' pattern is implicit and doesn't need to be printed. - } else { - Printer << "case "; - interleave(label->getPatterns(), - [&](Pattern *p) { printPattern(p); }, - [&] { Printer << ", "; }); - } - if (label->getGuardExpr()) { - Printer << " where "; - // FIXME: print guard expr - } - Printer << ":\n"; - }; +void PrintAST::visitCaseStmt(CaseStmt *CS) { + if (CS->isDefault()) { + Printer << "default"; + } else { + auto PrintCaseLabelItem = [&](const CaseLabelItem &CLI) { + if (auto *P = CLI.getPattern()) + printPattern(P); + if (CLI.getGuardExpr()) { + Printer << " where "; + // FIXME: print guard expr + } + }; + Printer << "case "; + interleave(CS->getCaseLabelItems(), PrintCaseLabelItem, + [&] { Printer << ", "; }); + } + Printer << ":\n"; - for (auto *label : stmt->getCaseLabels()) - printCaseLabel(label); - - printBraceStmtElements(cast(stmt->getBody())); + printBraceStmtElements(cast(CS->getBody())); } void Decl::print(raw_ostream &os) const { diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 93c64ea20e3..5b261966b1f 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -937,16 +937,14 @@ Stmt *Traversal::visitSwitchStmt(SwitchStmt *S) { } Stmt *Traversal::visitCaseStmt(CaseStmt *S) { - for (CaseLabel *label : S->getCaseLabels()) { - for (Pattern *&pattern : label->getPatterns()) { - if (Pattern *newPattern = doIt(pattern)) - pattern = newPattern; - else - return nullptr; - } - if (label->getGuardExpr()) { - if (Expr *newGuard = doIt(label->getGuardExpr())) - label->setGuardExpr(newGuard); + for (auto &CLI : S->getMutableCaseLabelItems()) { + if (auto *newPattern = doIt(CLI.getPattern())) + CLI.setPattern(newPattern); + else + return nullptr; + if (CLI.getGuardExpr()) { + if (auto *newGuard = doIt(CLI.getGuardExpr())) + CLI.setGuardExpr(newGuard); else return nullptr; } diff --git a/lib/AST/LookupVisibleDecls.cpp b/lib/AST/LookupVisibleDecls.cpp index 1cb1819f085..1aea66d4c6d 100644 --- a/lib/AST/LookupVisibleDecls.cpp +++ b/lib/AST/LookupVisibleDecls.cpp @@ -623,9 +623,8 @@ struct FindLocalVal : public StmtVisitor { void visitCaseStmt(CaseStmt *S) { if (!isReferencePointInRange(S->getSourceRange())) return; - for (auto Label : S->getCaseLabels()) { - for (auto P : Label->getPatterns()) - checkPattern(P, DeclVisibilityKind::LocalVariable); + for (const auto &CLI : S->getCaseLabelItems()) { + checkPattern(CLI.getPattern(), DeclVisibilityKind::LocalVariable); } visit(S->getBody()); } diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index a87e4824f9a..6142d54676b 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -187,7 +187,7 @@ struct FindLocalVal : public StmtVisitor { } } - void checkPattern(Pattern *Pat) { + void checkPattern(const Pattern *Pat) { switch (Pat->getKind()) { case PatternKind::Tuple: for (auto &field : cast(Pat)->getFields()) @@ -201,7 +201,7 @@ struct FindLocalVal : public StmtVisitor { case PatternKind::Named: return checkValueDecl(cast(Pat)->getDecl()); case PatternKind::NominalType: { - for (auto &elt : cast(Pat)->getMutableElements()) + for (const auto &elt : cast(Pat)->getElements()) checkPattern(elt.getSubPattern()); return; } @@ -305,11 +305,10 @@ struct FindLocalVal : public StmtVisitor { void visitCaseStmt(CaseStmt *S) { if (!IntersectsRange(S->getSourceRange())) return; - for (auto Label : S->getCaseLabels()) { - for (auto P : Label->getPatterns()) { - if (!IntersectsRange(P->getSourceRange())) - checkPattern(P); - } + for (const auto &CLI : S->getCaseLabelItems()) { + auto *P = CLI.getPattern(); + if (!IntersectsRange(P->getSourceRange())) + checkPattern(P); } visit(S->getBody()); } diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index f35c9f33cf1..bb54105bae4 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -18,7 +18,9 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/Pattern.h" #include "llvm/ADT/PointerUnion.h" + using namespace swift; //===----------------------------------------------------------------------===// @@ -117,55 +119,37 @@ SourceRange DoWhileStmt::getSourceRange() const { return SourceRange(DoLoc, Cond->getEndLoc()); } -CaseLabel::CaseLabel(bool isDefault, - SourceLoc caseLoc, ArrayRef patterns, - SourceLoc whereLoc, Expr *guardExpr, - SourceLoc colonLoc) - : CaseLoc(caseLoc), ColonLoc(colonLoc), WhereLoc(whereLoc), - GuardExprAndIsDefault(guardExpr, isDefault), - NumPatterns(patterns.size()) -{ - MutableArrayRef patternBuf{getPatternsBuffer(), NumPatterns}; - +SourceRange CaseLabelItem::getSourceRange() const { + if (auto *E = getGuardExpr()) + return { CasePattern->getStartLoc(), E->getEndLoc() }; + return CasePattern->getSourceRange(); +} + +CaseStmt::CaseStmt(SourceLoc CaseLoc, ArrayRef CaseLabelItems, + bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, + Optional Implicit) + : Stmt(StmtKind::Case, getDefaultImplicitFlag(Implicit, CaseLoc)), + CaseLoc(CaseLoc), ColonLoc(ColonLoc), + BodyAndHasBoundDecls(Body, HasBoundDecls), + NumPatterns(CaseLabelItems.size()) { + assert(NumPatterns > 0 && "case block must have at least one pattern"); + MutableArrayRef Items{ getCaseLabelItemsBuffer(), + NumPatterns }; + for (unsigned i = 0; i < NumPatterns; ++i) { - patternBuf[i] = patterns[i]; + new (&Items[i]) CaseLabelItem(CaseLabelItems[i]); } } -CaseLabel *CaseLabel::create(ASTContext &C, bool isDefault, - SourceLoc caseLoc, ArrayRef patterns, - SourceLoc whereLoc, Expr *guardExpr, - SourceLoc colonLoc) { - void *buf = C.Allocate(sizeof(CaseLabel) + sizeof(Pattern*) * patterns.size(), - alignof(CaseLabel)); - return ::new (buf) CaseLabel(isDefault, - caseLoc, patterns, - whereLoc, guardExpr, colonLoc); -} - -CaseStmt::CaseStmt(ArrayRef Labels, bool HasBoundDecls, Stmt *Body, - Optional implicit) - : Stmt(StmtKind::Case, - getDefaultImplicitFlag(implicit, Labels[0]->getCaseLoc())), - BodyAndHasBoundDecls(Body, HasBoundDecls), - NumCaseLabels(Labels.size()) -{ - assert(NumCaseLabels > 0 && "case block must have at least one label"); - MutableArrayRef buf{getCaseLabelsBuffer(), NumCaseLabels}; - - for (unsigned i = 0; i < NumCaseLabels; ++i) { - buf[i] = Labels[i]; - } -} - -CaseStmt *CaseStmt::create(ASTContext &C, - ArrayRef Labels, - bool HasBoundDecls, - Stmt *Body, - Optional implicit) { - void *p = C.Allocate(sizeof(CaseStmt) + Labels.size() * sizeof(CaseLabel*), - alignof(CaseStmt)); - return ::new (p) CaseStmt(Labels, HasBoundDecls, Body, implicit); +CaseStmt *CaseStmt::create(ASTContext &C, SourceLoc CaseLoc, + ArrayRef CaseLabelItems, + bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, + Optional Implicit) { + void *Mem = C.Allocate(sizeof(CaseStmt) + + CaseLabelItems.size() * sizeof(CaseLabelItem), + alignof(CaseStmt)); + return ::new (Mem) CaseStmt(CaseLoc, CaseLabelItems, HasBoundDecls, ColonLoc, + Body, Implicit); } SwitchStmt *SwitchStmt::create(SourceLoc SwitchLoc, diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 455184e5fd3..8dababb908c 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1333,172 +1333,171 @@ ParserResult Parser::parseStmtSwitch() { } namespace { - class CollectVarsAndAddToScope : public ASTWalker { - public: - Parser &TheParser; - SmallVectorImpl &Decls; - - CollectVarsAndAddToScope(Parser &P, SmallVectorImpl &Decls) - : TheParser(P), Decls(Decls) {} - - Pattern *walkToPatternPost(Pattern *P) override { - // Handle vars. - if (auto *Named = dyn_cast(P)) { - VarDecl *VD = Named->getDecl(); - Decls.push_back(VD); - TheParser.addToScope(VD); - } - return P; +class CollectVarsAndAddToScope : public ASTWalker { +public: + Parser &TheParser; + SmallVectorImpl &Decls; + + CollectVarsAndAddToScope(Parser &P, SmallVectorImpl &Decls) + : TheParser(P), Decls(Decls) {} + + Pattern *walkToPatternPost(Pattern *P) override { + // Handle vars. + if (auto *Named = dyn_cast(P)) { + VarDecl *VD = Named->getDecl(); + Decls.push_back(VD); + TheParser.addToScope(VD); } - }; -} - -ParserStatus Parser::parseStmtCaseLabels(SmallVectorImpl &labels, - SmallVectorImpl &boundDecls) { - // We must have at least one case label. - assert(Tok.is(tok::kw_case) || Tok.is(tok::kw_default)); + return P; + } +}; +} // unnamed namespace +static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc, + SmallVectorImpl &LabelItems, + SmallVectorImpl &BoundDecls, + SourceLoc &ColonLoc) { ParserStatus Status; - bool parsedDefault = false; - bool parsedOtherLabelWithDefault = false; + CaseLoc = P.consumeToken(tok::kw_case); + do { - // 'default' should label a block by itself. - if (parsedDefault && !parsedOtherLabelWithDefault) { - diagnose(Tok, diag::default_with_other_labels); - parsedOtherLabelWithDefault = true; - } - - // case-label ::= 'case' matching-pattern (',' matching-pattern)* - // ('where' expr)? ':' - if (Tok.is(tok::kw_case)) { - SourceLoc caseLoc = consumeToken(); - - // Parse comma-separated patterns. - SmallVector patterns; - do { - if (CodeCompletion) { - if (Tok.is(tok::code_complete)) { - CodeCompletion->completeCaseStmtBeginning(); - consumeToken(); - continue; - } - if (Tok.is(tok::period) && peekToken().is(tok::code_complete)) { - consumeToken(); - CodeCompletion->completeCaseStmtDotPrefix(); - consumeToken(); - continue; - } - } - - ParserResult pattern = parseMatchingPattern(); - Status |= pattern; - if (pattern.isNonNull()) { - // Add variable bindings from the pattern to the case scope. We have - // to do this with a full AST walk, because the freshly parsed pattern - // represents tuples and var patterns as tupleexprs and - // unresolved_pattern_expr nodes, instead of as proper pattern nodes. - pattern.get()->walk(CollectVarsAndAddToScope(*this, boundDecls)); - patterns.push_back(pattern.get()); - } - } while (consumeIf(tok::comma)); - - // Parse an optional 'where' guard. - SourceLoc whereLoc; - Expr *guardExpr = nullptr; - - if (Tok.is(tok::kw_where)) { - whereLoc = consumeToken(); - ParserResult guard = parseExpr(diag::expected_case_where_expr); - Status |= guard; - if (guard.isNonNull()) - guardExpr = guard.get(); + ParserResult CasePattern; + if (P.CodeCompletion) { + if (P.Tok.is(tok::code_complete)) { + CasePattern = + makeParserErrorResult(new (P.Context) AnyPattern(SourceLoc())); + P.CodeCompletion->completeCaseStmtBeginning(); + P.consumeToken(); + } + if (P.Tok.is(tok::period) && P.peekToken().is(tok::code_complete)) { + CasePattern = + makeParserErrorResult(new (P.Context) AnyPattern(SourceLoc())); + P.consumeToken(); + P.CodeCompletion->completeCaseStmtDotPrefix(); + P.consumeToken(); } - - SourceLoc colonLoc = Tok.getLoc(); - if (!Tok.is(tok::colon)) - diagnose(Tok, diag::expected_case_colon, "case"); - else - colonLoc = consumeToken(); - - auto label = CaseLabel::create(Context, /*isDefault*/false, - caseLoc, patterns, whereLoc, guardExpr, - colonLoc); - labels.push_back(label); - continue; } - - // case-label ::= 'default' ':' - - // 'default' should label a block by itself. - if (!labels.empty() && !parsedOtherLabelWithDefault) { - diagnose(Tok, diag::default_with_other_labels); - parsedOtherLabelWithDefault = true; + + if (CasePattern.isNull()) + CasePattern = P.parseMatchingPattern(); + + if (CasePattern.isNull()) + CasePattern = + makeParserErrorResult(new (P.Context) AnyPattern(P.PreviousLoc)); + + Status |= CasePattern; + if (CasePattern.isNonNull()) { + // Add variable bindings from the pattern to the case scope. We have + // to do this with a full AST walk, because the freshly parsed pattern + // represents tuples and var patterns as tupleexprs and + // unresolved_pattern_expr nodes, instead of as proper pattern nodes. + CasePattern.get()->walk(CollectVarsAndAddToScope(P, BoundDecls)); } - - parsedDefault = true; - SourceLoc defaultLoc = consumeToken(tok::kw_default); - - // We don't allow 'where' guards on a 'default' block. For recovery - // parse one if present. - SourceLoc whereLoc; - Expr *guardExpr = nullptr; - if (Tok.is(tok::kw_where)) { - diagnose(Tok, diag::default_with_where); - whereLoc = consumeToken(); - ParserResult guard = parseExpr(diag::expected_case_where_expr); - Status |= guard; - if (guard.isNonNull()) - guardExpr = guard.get(); + + // Parse an optional 'where' guard. + SourceLoc WhereLoc; + ParserResult Guard; + if (P.Tok.is(tok::kw_where)) { + WhereLoc = P.consumeToken(tok::kw_where); + Guard = P.parseExpr(diag::expected_case_where_expr); + Status |= Guard; } - - SourceLoc colonLoc = Tok.getLoc(); - if (!Tok.is(tok::colon)) - diagnose(Tok, diag::expected_case_colon, "default"); - else - consumeToken(tok::colon); - - // Create an implicit AnyPattern to represent the default match. - auto any = new (Context) AnyPattern(defaultLoc); - auto label = CaseLabel::create(Context, /*isDefault*/true, - defaultLoc, any, whereLoc, guardExpr, - colonLoc); - labels.push_back(label); - } while (Tok.is(tok::kw_case) || Tok.is(tok::kw_default)); + + LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false, + CasePattern.get(), WhereLoc, + Guard.getPtrOrNull())); + } while (P.consumeIf(tok::comma)); + + ColonLoc = P.Tok.getLoc(); + if (!P.Tok.is(tok::colon)) { + P.diagnose(P.Tok, diag::expected_case_colon, "case"); + Status.setIsParseError(); + } else + P.consumeToken(tok::colon); + + return Status; +} + +static ParserStatus +parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc, + SmallVectorImpl &LabelItems, + SourceLoc &ColonLoc) { + ParserStatus Status; + + CaseLoc = P.consumeToken(tok::kw_default); + + // We don't allow 'where' guards on a 'default' block. For recovery + // parse one if present. + SourceLoc WhereLoc; + ParserResult Guard; + if (P.Tok.is(tok::kw_where)) { + P.diagnose(P.Tok, diag::default_with_where); + WhereLoc = P.consumeToken(tok::kw_where); + Guard = P.parseExpr(diag::expected_case_where_expr); + Status |= Guard; + } + + ColonLoc = P.Tok.getLoc(); + if (!P.Tok.is(tok::colon)) { + P.diagnose(P.Tok, diag::expected_case_colon, "default"); + Status.setIsParseError(); + } else + P.consumeToken(tok::colon); + + // Create an implicit AnyPattern to represent the default match. + auto Any = new (P.Context) AnyPattern(CaseLoc); + LabelItems.push_back( + CaseLabelItem(/*IsDefault=*/true, Any, WhereLoc, Guard.getPtrOrNull())); + return Status; } -// stmt-case ::= case-label+ brace-item* ParserResult Parser::parseStmtCase() { // A case block has its own scope for variables bound out of the pattern. - Scope scope(this, ScopeKind::CaseVars); + Scope S(this, ScopeKind::CaseVars); ParserStatus Status; - SmallVector labels; - SmallVector boundDecls; - Status |= parseStmtCaseLabels(labels, boundDecls); + SmallVector CaseLabelItems; + SmallVector BoundDecls; - assert(!labels.empty() && "did not parse any labels?!"); + SourceLoc CaseLoc; + SourceLoc ColonLoc; + if (Tok.is(tok::kw_case)) { + Status |= + ::parseStmtCase(*this, CaseLoc, CaseLabelItems, BoundDecls, ColonLoc); + } else { + Status |= parseStmtCaseDefault(*this, CaseLoc, CaseLabelItems, ColonLoc); + } + + assert(!CaseLabelItems.empty() && "did not parse any labels?!"); // Case blocks with multiple patterns cannot bind variables. - if (!boundDecls.empty() - && (labels.size() > 1 || labels[0]->getPatterns().size() > 1)) - diagnose(boundDecls[0]->getLoc(), + if (!BoundDecls.empty() && CaseLabelItems.size() > 1) + diagnose(BoundDecls[0]->getLoc(), diag::var_binding_with_multiple_case_patterns); - SmallVector bodyItems; + SmallVector BodyItems; - SourceLoc startOfBody = Tok.getLoc(); - Status |= parseBraceItems(bodyItems, BraceItemListKind::Case); - BraceStmt *body; - if (bodyItems.empty()) { - body = BraceStmt::create(Context, PreviousLoc, ArrayRef(), + SourceLoc StartOfBody = Tok.getLoc(); + if (Tok.isNot(tok::kw_case) && Tok.isNot(tok::kw_default) && + Tok.isNot(tok::r_brace)) { + Status |= parseBraceItems(BodyItems, BraceItemListKind::Case); + } else if (Status.isSuccess()) { + diagnose(CaseLoc, diag::case_stmt_without_body) + .highlight(SourceRange(CaseLoc, ColonLoc)); + } + BraceStmt *Body; + if (BodyItems.empty()) { + Body = BraceStmt::create(Context, PreviousLoc, ArrayRef(), PreviousLoc, /*implicit=*/true); } else { - body = BraceStmt::create(Context, startOfBody, bodyItems, PreviousLoc); + Body = BraceStmt::create(Context, StartOfBody, BodyItems, PreviousLoc); } return makeParserResult( - Status, CaseStmt::create(Context, labels, !boundDecls.empty(), body)); + Status, CaseStmt::create(Context, CaseLoc, CaseLabelItems, + !BoundDecls.empty(), ColonLoc, Body)); } + diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index 716ef674949..433529fdded 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -1818,10 +1818,10 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) { for (auto *caseBlock : S->getCases()) { caseMap[caseBlock] = {}; - for (auto *label : caseBlock->getCaseLabels()) - for (auto *pattern : label->getPatterns()) - clauses.addRow(caseBlock, label->getGuardExpr(), pattern, - CleanupsDepth::invalid(), contBB); + for (const auto &labelItem : caseBlock->getCaseLabelItems()) + clauses.addRow(caseBlock, const_cast(labelItem.getGuardExpr()), + labelItem.getPattern(), + CleanupsDepth::invalid(), contBB); } // Bind variable bindings from the topmost pattern nodes. diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 0ab7700e946..bd15b0ef98a 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -138,7 +138,7 @@ deriveEquatable_enum_eq(TypeChecker &tc, EnumDecl *enumDecl) { eqDecl->setDerivedForTypeDecl(enumDecl); SmallVector cases; - SmallVector caseLabels; + SmallVector caseLabelItems; for (auto elt : enumDecl->getAllElements()) { assert(!elt->hasArgumentType() @@ -156,32 +156,33 @@ deriveEquatable_enum_eq(TypeChecker &tc, EnumDecl *enumDecl) { }; auto tuplePat = TuplePattern::create(C, SourceLoc(), tupleElts, SourceLoc()); tuplePat->setImplicit(); - - caseLabels.push_back(CaseLabel::create(C, /*isDefault*/false, - SourceLoc(), tuplePat, SourceLoc(), - nullptr, SourceLoc())); - + + caseLabelItems.push_back( + CaseLabelItem(/*IsDefault=*/false, tuplePat, SourceLoc(), nullptr)); } { Expr *trueExpr = getTrueExpr(C); auto returnStmt = new (C) ReturnStmt(SourceLoc(), trueExpr); BraceStmt* body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), SourceLoc()); - cases.push_back(CaseStmt::create(C, caseLabels, /*hasBoundDecls*/false, body)); + cases.push_back( + CaseStmt::create(C, SourceLoc(), caseLabelItems, + /*HasBoundDecls=*/false, SourceLoc(), body)); } { auto any = new (C) AnyPattern(SourceLoc()); any->setImplicit(); - - auto label = CaseLabel::create(C, /*isDefault*/ true, - SourceLoc(), any, SourceLoc(), - nullptr, SourceLoc()); - + + auto labelItem = + CaseLabelItem(/*IsDefault=*/true, any, SourceLoc(), nullptr); + Expr *falseExpr = getFalseExpr(C); auto returnStmt = new (C) ReturnStmt(SourceLoc(), falseExpr); BraceStmt* body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), SourceLoc()); - cases.push_back(CaseStmt::create(C, label, /*hasBoundDecls*/false, body)); + cases.push_back( + CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, + SourceLoc(), body)); } auto aRef = new (C) DeclRefExpr(aParam.first, SourceLoc(), /*implicit*/ true); @@ -330,9 +331,8 @@ deriveHashable_enum_hashValue(TypeChecker &tc, EnumDecl *enumDecl) { SourceLoc(), SourceLoc(), Identifier(), elt, nullptr); pat->setImplicit(); - auto label = CaseLabel::create(C, /*isDefault*/false, - SourceLoc(), - pat, SourceLoc(), nullptr, SourceLoc()); + auto labelItem = + CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr); llvm::SmallString<8> indexVal; APInt(32, index++).toString(indexVal, 10, /*signed*/ false); @@ -347,8 +347,9 @@ deriveHashable_enum_hashValue(TypeChecker &tc, EnumDecl *enumDecl) { indexExpr, /*implicit*/ true); auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), SourceLoc()); - cases.push_back(CaseStmt::create(C, label, /*hasBoundDecls*/false, - body)); + cases.push_back( + CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, + SourceLoc(), body)); } auto selfRef = new (C) DeclRefExpr(selfDecl, SourceLoc(), /*implicit*/true); auto switchStmt = SwitchStmt::create(SourceLoc(), selfRef, diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index 237406ba02a..ae076e84d75 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -147,9 +147,8 @@ static FuncDecl *deriveRawRepresentable_toRaw(TypeChecker &tc, SourceLoc(), SourceLoc(), Identifier(), elt, nullptr); pat->setImplicit(); - auto label = CaseLabel::create(C, /*isDefault*/false, - SourceLoc(), - pat, SourceLoc(), nullptr, SourceLoc()); + auto labelItem = + CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr); auto returnExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr()); auto returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr); @@ -157,7 +156,9 @@ static FuncDecl *deriveRawRepresentable_toRaw(TypeChecker &tc, auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), SourceLoc()); - cases.push_back(CaseStmt::create(C, label, /*hasBoundDecls*/false, body)); + cases.push_back( + CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, + SourceLoc(), body)); } auto selfRef = new (C) DeclRefExpr(selfDecl, SourceLoc(), /*implicit*/true); auto switchStmt = SwitchStmt::create(SourceLoc(), selfRef, @@ -280,9 +281,8 @@ static FuncDecl *deriveRawRepresentable_fromRaw(TypeChecker &tc, nullptr, nullptr); litPat->setImplicit(); - auto label = CaseLabel::create(C, /*isDefault*/false, - SourceLoc(), litPat, SourceLoc(), - nullptr, SourceLoc()); + auto labelItem = + CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), nullptr); auto eltRef = new (C) DeclRefExpr(elt, SourceLoc(), /*implicit*/true); auto metaTyRef = new (C) MetatypeExpr(nullptr, SourceLoc(), metaTy); @@ -292,14 +292,15 @@ static FuncDecl *deriveRawRepresentable_fromRaw(TypeChecker &tc, auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), SourceLoc()); - cases.push_back(CaseStmt::create(C, label, /*hasBoundDecls*/false, body)); + cases.push_back( + CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, + SourceLoc(), body)); } auto anyPat = new (C) AnyPattern(SourceLoc()); anyPat->setImplicit(); - auto dfltLabel = CaseLabel::create(C, /*isDefault*/true, SourceLoc(), - anyPat, SourceLoc(), nullptr, - SourceLoc()); + auto dfltLabelItem = + CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), nullptr); auto optionalRef = new (C) DeclRefExpr(C.getOptionalDecl(), SourceLoc(), /*implicit*/true); @@ -310,8 +311,9 @@ static FuncDecl *deriveRawRepresentable_fromRaw(TypeChecker &tc, auto dfltReturnStmt = new (C) ReturnStmt(SourceLoc(), dfltReturnExpr); auto dfltBody = BraceStmt::create(C, SourceLoc(), ASTNode(dfltReturnStmt), SourceLoc()); - cases.push_back(CaseStmt::create(C, dfltLabel, /*hasBoundDecls*/false, - dfltBody)); + cases.push_back( + CaseStmt::create(C, SourceLoc(), dfltLabelItem, /*HasBoundDecls=*/false, + SourceLoc(), dfltBody)); auto rawRef = new (C) DeclRefExpr(rawDecl, SourceLoc(), /*implicit*/true); auto switchStmt = SwitchStmt::create(SourceLoc(), rawRef, SourceLoc(), diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 0f06269d850..ce885e01a48 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -523,34 +523,34 @@ public: // final case block, it is invalid. FallthroughDest = i+1 == e ? nullptr : S->getCases()[i+1]; - for (auto *caseLabel : caseBlock->getCaseLabels()) { - // Resolve the patterns in the label. - for (auto *&pattern : caseLabel->getPatterns()) { - if (auto *newPattern = TC.resolvePattern(pattern, DC)) { - pattern = newPattern; - } else { - hadTypeError = true; - continue; - } - - // Coerce the pattern to the subject's type. - if (TC.coercePatternToType(pattern, DC, subjectType, None)) { - // If that failed, mark any variables binding pieces of the pattern - // as invalid to silence follow-on errors. - pattern->forEachVariable([&](VarDecl *VD) { - VD->overwriteType(ErrorType::get(TC.Context)); - VD->setInvalid(); - }); - hadTypeError = true; - } + for (auto &labelItem : caseBlock->getMutableCaseLabelItems()) { + // Resolve the pattern in the label. + Pattern *pattern = labelItem.getPattern(); + if (auto *newPattern = TC.resolvePattern(pattern, DC)) { + pattern = newPattern; + } else { + hadTypeError = true; + continue; } - + + // Coerce the pattern to the subject's type. + if (TC.coercePatternToType(pattern, DC, subjectType, None)) { + // If that failed, mark any variables binding pieces of the pattern + // as invalid to silence follow-on errors. + pattern->forEachVariable([&](VarDecl *VD) { + VD->overwriteType(ErrorType::get(TC.Context)); + VD->setInvalid(); + }); + hadTypeError = true; + } + labelItem.setPattern(pattern); + // Check the guard expression, if present. - if (auto *guard = caseLabel->getGuardExpr()) { + if (auto *guard = labelItem.getGuardExpr()) { if (TC.typeCheckCondition(guard, DC)) hadTypeError = true; else - caseLabel->setGuardExpr(guard); + labelItem.setGuardExpr(guard); } } diff --git a/utils/buildbot-release-notes.txt b/utils/buildbot-release-notes.txt index 9ee5a731599..92bef686441 100644 --- a/utils/buildbot-release-notes.txt +++ b/utils/buildbot-release-notes.txt @@ -1,3 +1,36 @@ +2014-03-26 +---------- + +* 'switch' now always requires a statement after a 'case' or 'default'. + + Old syntax: + + switch x { + case .A: + case .B(1): + println(".A or .B(1)") + default: + // Ignore it. + } + + New syntax: + + switch x { + case .A, .B(1): + println(".A or .B(1)") + default: + () // Ignore it. + } + + The following syntax can be used to introduce guard expressions for patterns + inside the 'case': + + switch x { + case .A where isFoo(), + .B(1) where isBar(): + ... + } + 2014-03-19 ----------