Change 'switch' not to fall through between empty cases and always require at

least one statement per case

rdar://16301313


Swift SVN r15266
This commit is contained in:
Dmitri Hrybenko
2014-03-20 11:44:59 +00:00
parent bf2568f8e9
commit 11fea869c1
16 changed files with 402 additions and 407 deletions

View File

@@ -1250,9 +1250,9 @@ The 'continue' statement transfers control back to the start of the enclosing
.. code-block:: none .. code-block:: none
stmt-switch ::= 'switch' expr-basic '{' stmt-switch-case* '}' 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' ':' default-label ::= 'default' ':'
'switch' statements branch on the value of an expression by :ref:`pattern 'switch' statements branch on the value of an expression by :ref:`pattern
@@ -1260,15 +1260,15 @@ matching <langref.pattern>`. The subject expression of the switch is evaluated
and tested against the patterns in its ``case`` labels in source order. When a 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 pattern is found that matches the value, control is transferred into the
matching ``case`` block. ``case`` labels may declare multiple patterns matching ``case`` block. ``case`` labels may declare multiple patterns
separated by commas, and multiple ``case`` labels may cover a case block. Case separated by commas. Only a single ``case`` labels may precede a block of
labels may optionally specify a *guard* expression, introduced by the ``where`` code. Case labels may optionally specify a *guard* expression, introduced by
keyword; if present, control is transferred to the case only if the subject the ``where`` keyword; if present, control is transferred to the case only if
value both matches one of its patterns and the guard expression evaluates to the subject value both matches the corresponding pattern and the guard
true. Patterns are tested "as if" in source order; if multiple cases can match expression evaluates to true. Patterns are tested "as if" in source order; if
a value, control is transferred only to the first matching case. The actual multiple cases can match a value, control is transferred only to the first
execution order of pattern matching operations, and in particular the matching case. The actual execution order of pattern matching operations, and
evaluation order of :ref:`expression patterns <langref.pattern.expr>`, is in particular the evaluation order of :ref:`expression patterns
unspecified. <langref.pattern.expr>`, is unspecified.
A switch may also contain a ``default`` block. If present, it receives control 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 if no cases match the subject value. The ``default`` block must appear at the
@@ -1291,6 +1291,9 @@ transfer control among case blocks. :ref:`break <langref.stmt.break>` and
:ref:`continue <langref.stmt.continue>` within a switch will break or continue :ref:`continue <langref.stmt.continue>` within a switch will break or continue
out of an enclosing 'while' or 'for' loop, not out of the 'switch' itself. 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)) {

View File

@@ -554,7 +554,7 @@ ERROR(init_expected_self,type_parsing,PointsToFirstBadToken,
// Pattern parsing diagnostics // Pattern parsing diagnostics
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
ERROR(expected_pattern,pattern_parsing,none, ERROR(expected_pattern,pattern_parsing,PointsToFirstBadToken,
"expected pattern", ()) "expected pattern", ())
ERROR(expected_pattern_is_keyword,pattern_parsing,none, ERROR(expected_pattern_is_keyword,pattern_parsing,none,
"keyword '%0' cannot be used as an identifier", (StringRef)) "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'", ()) "expected expression for 'where' guard of 'case'", ())
ERROR(expected_case_colon,stmt_parsing,PointsToFirstBadToken, ERROR(expected_case_colon,stmt_parsing,PointsToFirstBadToken,
"expected ':' after '%0'", (StringRef)) "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, ERROR(default_with_where,stmt_parsing,none,
"'default' cannot be used with a 'where' guard expression", "'default' cannot be used with a 'where' guard expression",
()) ())
ERROR(var_binding_with_multiple_case_patterns,stmt_parsing,none, ERROR(var_binding_with_multiple_case_patterns,stmt_parsing,none,
"'case' labels with multiple patterns cannot declare variables", "'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 // Expression parsing diagnostics

View File

@@ -419,68 +419,34 @@ public:
} }
}; };
/// A label used at the head of a 'case' block. Each 'case' label may have one /// A pattern and an optional guard expression used in a 'case' statement.
/// or more comma-separated patterns. The 'case' may also optionally have a class CaseLabelItem {
/// 'where' guard expression. 'default' is allowed as an alternate spelling of Pattern *CasePattern;
/// 'case _'.
///
/// Some examples:
///
/// case 1:
/// case 2, 3:
/// case Foo(var x, var y) where x < y:
/// default:
class CaseLabel {
SourceLoc CaseLoc;
SourceLoc ColonLoc;
SourceLoc WhereLoc; SourceLoc WhereLoc;
llvm::PointerIntPair<Expr *, 1, bool> GuardExprAndIsDefault; llvm::PointerIntPair<Expr *, 1, bool> GuardExprAndIsDefault;
unsigned NumPatterns;
CaseLabel(bool isDefault,
SourceLoc caseLoc, ArrayRef<Pattern*> patterns,
SourceLoc whereLoc, Expr *guardExpr,
SourceLoc colonLoc);
Pattern **getPatternsBuffer() {
return reinterpret_cast<Pattern**>(this + 1);
}
const Pattern * const *getPatternsBuffer() const {
return reinterpret_cast<const Pattern * const *>(this + 1);
}
/// CaseLabels should be allocated with an ASTContext using create.
void *operator new(size_t) = delete;
void operator delete(void*) = delete;
public: public:
static CaseLabel *create(ASTContext &C, CaseLabelItem(const CaseLabelItem &) = default;
bool isDefault,
SourceLoc caseLoc, CaseLabelItem(bool IsDefault, Pattern *CasePattern, SourceLoc WhereLoc,
ArrayRef<Pattern*> patterns, Expr *GuardExpr)
SourceLoc whereLoc, : CasePattern(CasePattern), WhereLoc(WhereLoc),
Expr * /*nullable*/ guardExpr, GuardExprAndIsDefault(GuardExpr, IsDefault) {}
SourceLoc colonLoc);
SourceLoc getLoc() const { return CaseLoc; }
SourceLoc getCaseLoc() const { return CaseLoc; }
SourceLoc getColonLoc() const { return ColonLoc; }
SourceLoc getWhereLoc() const { return WhereLoc; } SourceLoc getWhereLoc() const { return WhereLoc; }
SourceRange getSourceRange() const { SourceRange getSourceRange() const;
return {CaseLoc, ColonLoc};
}
MutableArrayRef<Pattern*> getPatterns() { Pattern *getPattern() { return CasePattern; }
return {getPatternsBuffer(), NumPatterns}; const Pattern *getPattern() const { return CasePattern; }
} void setPattern(Pattern *CasePattern) { this->CasePattern = CasePattern; }
ArrayRef<const Pattern *> getPatterns() const {
return {getPatternsBuffer(), NumPatterns};
}
/// Return the guard expression if present, or null if the case label has /// Return the guard expression if present, or null if the case label has
/// no guard. /// 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); } void setGuardExpr(Expr *e) { GuardExprAndIsDefault.setPointer(e); }
/// Returns true if this is syntactically a 'default' label. /// Returns true if this is syntactically a 'default' label.
@@ -489,30 +455,46 @@ public:
/// A 'case' or 'default' block of a switch statement. Only valid as the /// 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 /// substatement of a SwitchStmt. A case block begins either with one or more
/// CaseLabels or a single 'default' label. /// 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 { class CaseStmt : public Stmt {
SourceLoc CaseLoc;
SourceLoc ColonLoc;
llvm::PointerIntPair<Stmt *, 1, bool> BodyAndHasBoundDecls; llvm::PointerIntPair<Stmt *, 1, bool> BodyAndHasBoundDecls;
unsigned NumCaseLabels; unsigned NumPatterns;
CaseLabel * const *getCaseLabelsBuffer() const { const CaseLabelItem *getCaseLabelItemsBuffer() const {
return reinterpret_cast<CaseLabel * const *>(this + 1); return reinterpret_cast<const CaseLabelItem *>(this + 1);
} }
CaseLabel **getCaseLabelsBuffer() { CaseLabelItem *getCaseLabelItemsBuffer() {
return reinterpret_cast<CaseLabel **>(this + 1); return reinterpret_cast<CaseLabelItem *>(this + 1);
} }
CaseStmt(ArrayRef<CaseLabel*> Labels, bool hasBoundDecls, Stmt *Body, CaseStmt(SourceLoc CaseLoc, ArrayRef<CaseLabelItem> CaseLabelItems,
bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body,
Optional<bool> Implicit); Optional<bool> Implicit);
public: public:
static CaseStmt *create(ASTContext &C, static CaseStmt *create(ASTContext &C, SourceLoc CaseLoc,
ArrayRef<CaseLabel*> Labels, ArrayRef<CaseLabelItem> CaseLabelItems,
bool hasBoundDecls, bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body,
Stmt *Body,
Optional<bool> Implicit = {}); Optional<bool> Implicit = {});
ArrayRef<CaseLabel *> getCaseLabels() const { ArrayRef<CaseLabelItem> getCaseLabelItems() const {
return {getCaseLabelsBuffer(), NumCaseLabels}; return { getCaseLabelItemsBuffer(), NumPatterns };
}
MutableArrayRef<CaseLabelItem> getMutableCaseLabelItems() {
return { getCaseLabelItemsBuffer(), NumPatterns };
} }
Stmt *getBody() const { return BodyAndHasBoundDecls.getPointer(); } Stmt *getBody() const { return BodyAndHasBoundDecls.getPointer(); }
@@ -522,21 +504,15 @@ public:
bool hasBoundDecls() const { return BodyAndHasBoundDecls.getInt(); } bool hasBoundDecls() const { return BodyAndHasBoundDecls.getInt(); }
/// Get the source location of the 'case' or 'default' of the first label. /// Get the source location of the 'case' or 'default' of the first label.
SourceLoc getLoc() const { SourceLoc getLoc() const { return CaseLoc; }
return getCaseLabels()[0]->getLoc();
}
SourceRange getSourceRange() const { SourceRange getSourceRange() const {
return { getLoc(), getBody()->getEndLoc() }; return { getLoc(), getBody()->getEndLoc() };
} }
bool isDefault() { bool isDefault() { return getCaseLabelItems()[0].isDefault(); }
return getCaseLabels()[0]->isDefault();
}
static bool classof(const Stmt *S) { static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Case; }
return S->getKind() == StmtKind::Case;
}
}; };
/// Switch statement. /// Switch statement.

View File

@@ -943,8 +943,6 @@ public:
ParserResult<Stmt> parseStmtForEach(SourceLoc ForLoc); ParserResult<Stmt> parseStmtForEach(SourceLoc ForLoc);
ParserResult<Stmt> parseStmtSwitch(); ParserResult<Stmt> parseStmtSwitch();
ParserResult<CaseStmt> parseStmtCase(); ParserResult<CaseStmt> parseStmtCase();
ParserStatus parseStmtCaseLabels(SmallVectorImpl<CaseLabel*> &labels,
SmallVectorImpl<Decl *> &boundDecls);
/// Evaluate the conditional configuration expression of an #if statement /// Evaluate the conditional configuration expression of an #if statement
bool evaluateConfigConditionExpr(Expr *configExpr); bool evaluateConfigConditionExpr(Expr *configExpr);

View File

@@ -91,7 +91,9 @@ namespace {
void printRec(Expr *E) { E->print(OS, Indent + 2); } void printRec(Expr *E) { E->print(OS, Indent + 2); }
void printRec(Stmt *S) { S->print(OS, Indent + 2); } void printRec(Stmt *S) { S->print(OS, Indent + 2); }
void printRec(TypeRepr *T); 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<Pattern *>(P));
}
raw_ostream &printCommon(Pattern *P, const char *Name) { raw_ostream &printCommon(Pattern *P, const char *Name) {
OS.indent(Indent) << '('; OS.indent(Indent) << '(';
@@ -792,7 +794,9 @@ public:
void printRec(Decl *D) { D->dump(OS, Indent + 2); } void printRec(Decl *D) { D->dump(OS, Indent + 2); }
void printRec(Expr *E) { E->print(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<Pattern *>(P));
}
void printRec(StmtCondition C) { void printRec(StmtCondition C) {
if (auto E = C.dyn_cast<Expr*>()) if (auto E = C.dyn_cast<Expr*>())
@@ -924,16 +928,16 @@ public:
} }
void visitCaseStmt(CaseStmt *S) { void visitCaseStmt(CaseStmt *S) {
OS.indent(Indent) << "(case_stmt"; OS.indent(Indent) << "(case_stmt";
for (CaseLabel *label : S->getCaseLabels()) { for (const auto &LabelItem : S->getCaseLabelItems()) {
OS << '\n'; OS << '\n';
OS.indent(Indent+2) << "(case_label"; OS.indent(Indent + 2) << "(case_label_item";
for (Pattern *p : label->getPatterns()) { if (auto *CasePattern = LabelItem.getPattern()) {
OS << '\n'; OS << '\n';
printRec(p); printRec(CasePattern);
} }
if (Expr *guard = label->getGuardExpr()) { if (auto *Guard = LabelItem.getGuardExpr()) {
OS << '\n'; OS << '\n';
guard->print(OS, Indent+4); Guard->print(OS, Indent+4);
} }
OS << ')'; OS << ')';
} }
@@ -981,7 +985,9 @@ public:
void printRec(Decl *D) { D->dump(OS, Indent + 2); } void printRec(Decl *D) { D->dump(OS, Indent + 2); }
void printRec(Stmt *S) { S->print(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<Pattern *>(P));
}
void printRec(TypeRepr *T); void printRec(TypeRepr *T);
raw_ostream &printCommon(Expr *E, const char *C) { raw_ostream &printCommon(Expr *E, const char *C) {

View File

@@ -1061,28 +1061,25 @@ void PrintAST::visitSwitchStmt(SwitchStmt *stmt) {
Printer << "}"; Printer << "}";
} }
void PrintAST::visitCaseStmt(CaseStmt *stmt) { void PrintAST::visitCaseStmt(CaseStmt *CS) {
auto printCaseLabel = [&](CaseLabel *label) { if (CS->isDefault()) {
if (label->isDefault()) {
Printer << "default"; Printer << "default";
// '_' pattern is implicit and doesn't need to be printed.
} else { } else {
Printer << "case "; auto PrintCaseLabelItem = [&](const CaseLabelItem &CLI) {
interleave(label->getPatterns(), if (auto *P = CLI.getPattern())
[&](Pattern *p) { printPattern(p); }, printPattern(P);
[&] { Printer << ", "; }); if (CLI.getGuardExpr()) {
}
if (label->getGuardExpr()) {
Printer << " where "; Printer << " where ";
// FIXME: print guard expr // FIXME: print guard expr
} }
Printer << ":\n";
}; };
Printer << "case ";
interleave(CS->getCaseLabelItems(), PrintCaseLabelItem,
[&] { Printer << ", "; });
}
Printer << ":\n";
for (auto *label : stmt->getCaseLabels()) printBraceStmtElements(cast<BraceStmt>(CS->getBody()));
printCaseLabel(label);
printBraceStmtElements(cast<BraceStmt>(stmt->getBody()));
} }
void Decl::print(raw_ostream &os) const { void Decl::print(raw_ostream &os) const {

View File

@@ -937,16 +937,14 @@ Stmt *Traversal::visitSwitchStmt(SwitchStmt *S) {
} }
Stmt *Traversal::visitCaseStmt(CaseStmt *S) { Stmt *Traversal::visitCaseStmt(CaseStmt *S) {
for (CaseLabel *label : S->getCaseLabels()) { for (auto &CLI : S->getMutableCaseLabelItems()) {
for (Pattern *&pattern : label->getPatterns()) { if (auto *newPattern = doIt(CLI.getPattern()))
if (Pattern *newPattern = doIt(pattern)) CLI.setPattern(newPattern);
pattern = newPattern;
else else
return nullptr; return nullptr;
} if (CLI.getGuardExpr()) {
if (label->getGuardExpr()) { if (auto *newGuard = doIt(CLI.getGuardExpr()))
if (Expr *newGuard = doIt(label->getGuardExpr())) CLI.setGuardExpr(newGuard);
label->setGuardExpr(newGuard);
else else
return nullptr; return nullptr;
} }

View File

@@ -623,9 +623,8 @@ struct FindLocalVal : public StmtVisitor<FindLocalVal> {
void visitCaseStmt(CaseStmt *S) { void visitCaseStmt(CaseStmt *S) {
if (!isReferencePointInRange(S->getSourceRange())) if (!isReferencePointInRange(S->getSourceRange()))
return; return;
for (auto Label : S->getCaseLabels()) { for (const auto &CLI : S->getCaseLabelItems()) {
for (auto P : Label->getPatterns()) checkPattern(CLI.getPattern(), DeclVisibilityKind::LocalVariable);
checkPattern(P, DeclVisibilityKind::LocalVariable);
} }
visit(S->getBody()); visit(S->getBody());
} }

View File

@@ -187,7 +187,7 @@ struct FindLocalVal : public StmtVisitor<FindLocalVal> {
} }
} }
void checkPattern(Pattern *Pat) { void checkPattern(const Pattern *Pat) {
switch (Pat->getKind()) { switch (Pat->getKind()) {
case PatternKind::Tuple: case PatternKind::Tuple:
for (auto &field : cast<TuplePattern>(Pat)->getFields()) for (auto &field : cast<TuplePattern>(Pat)->getFields())
@@ -201,7 +201,7 @@ struct FindLocalVal : public StmtVisitor<FindLocalVal> {
case PatternKind::Named: case PatternKind::Named:
return checkValueDecl(cast<NamedPattern>(Pat)->getDecl()); return checkValueDecl(cast<NamedPattern>(Pat)->getDecl());
case PatternKind::NominalType: { case PatternKind::NominalType: {
for (auto &elt : cast<NominalTypePattern>(Pat)->getMutableElements()) for (const auto &elt : cast<NominalTypePattern>(Pat)->getElements())
checkPattern(elt.getSubPattern()); checkPattern(elt.getSubPattern());
return; return;
} }
@@ -305,12 +305,11 @@ struct FindLocalVal : public StmtVisitor<FindLocalVal> {
void visitCaseStmt(CaseStmt *S) { void visitCaseStmt(CaseStmt *S) {
if (!IntersectsRange(S->getSourceRange())) if (!IntersectsRange(S->getSourceRange()))
return; return;
for (auto Label : S->getCaseLabels()) { for (const auto &CLI : S->getCaseLabelItems()) {
for (auto P : Label->getPatterns()) { auto *P = CLI.getPattern();
if (!IntersectsRange(P->getSourceRange())) if (!IntersectsRange(P->getSourceRange()))
checkPattern(P); checkPattern(P);
} }
}
visit(S->getBody()); visit(S->getBody());
} }
}; };

View File

@@ -18,7 +18,9 @@
#include "swift/AST/ASTContext.h" #include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h" #include "swift/AST/Decl.h"
#include "swift/AST/Expr.h" #include "swift/AST/Expr.h"
#include "swift/AST/Pattern.h"
#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/PointerUnion.h"
using namespace swift; using namespace swift;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -117,55 +119,37 @@ SourceRange DoWhileStmt::getSourceRange() const {
return SourceRange(DoLoc, Cond->getEndLoc()); return SourceRange(DoLoc, Cond->getEndLoc());
} }
CaseLabel::CaseLabel(bool isDefault, SourceRange CaseLabelItem::getSourceRange() const {
SourceLoc caseLoc, ArrayRef<Pattern*> patterns, if (auto *E = getGuardExpr())
SourceLoc whereLoc, Expr *guardExpr, return { CasePattern->getStartLoc(), E->getEndLoc() };
SourceLoc colonLoc) return CasePattern->getSourceRange();
: CaseLoc(caseLoc), ColonLoc(colonLoc), WhereLoc(whereLoc), }
GuardExprAndIsDefault(guardExpr, isDefault),
NumPatterns(patterns.size()) CaseStmt::CaseStmt(SourceLoc CaseLoc, ArrayRef<CaseLabelItem> CaseLabelItems,
{ bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body,
MutableArrayRef<Pattern*> patternBuf{getPatternsBuffer(), NumPatterns}; Optional<bool> 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<CaseLabelItem> Items{ getCaseLabelItemsBuffer(),
NumPatterns };
for (unsigned i = 0; i < NumPatterns; ++i) { for (unsigned i = 0; i < NumPatterns; ++i) {
patternBuf[i] = patterns[i]; new (&Items[i]) CaseLabelItem(CaseLabelItems[i]);
} }
} }
CaseLabel *CaseLabel::create(ASTContext &C, bool isDefault, CaseStmt *CaseStmt::create(ASTContext &C, SourceLoc CaseLoc,
SourceLoc caseLoc, ArrayRef<Pattern *> patterns, ArrayRef<CaseLabelItem> CaseLabelItems,
SourceLoc whereLoc, Expr *guardExpr, bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body,
SourceLoc colonLoc) { Optional<bool> Implicit) {
void *buf = C.Allocate(sizeof(CaseLabel) + sizeof(Pattern*) * patterns.size(), void *Mem = C.Allocate(sizeof(CaseStmt) +
alignof(CaseLabel)); CaseLabelItems.size() * sizeof(CaseLabelItem),
return ::new (buf) CaseLabel(isDefault,
caseLoc, patterns,
whereLoc, guardExpr, colonLoc);
}
CaseStmt::CaseStmt(ArrayRef<CaseLabel*> Labels, bool HasBoundDecls, Stmt *Body,
Optional<bool> 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<CaseLabel*> buf{getCaseLabelsBuffer(), NumCaseLabels};
for (unsigned i = 0; i < NumCaseLabels; ++i) {
buf[i] = Labels[i];
}
}
CaseStmt *CaseStmt::create(ASTContext &C,
ArrayRef<CaseLabel*> Labels,
bool HasBoundDecls,
Stmt *Body,
Optional<bool> implicit) {
void *p = C.Allocate(sizeof(CaseStmt) + Labels.size() * sizeof(CaseLabel*),
alignof(CaseStmt)); alignof(CaseStmt));
return ::new (p) CaseStmt(Labels, HasBoundDecls, Body, implicit); return ::new (Mem) CaseStmt(CaseLoc, CaseLabelItems, HasBoundDecls, ColonLoc,
Body, Implicit);
} }
SwitchStmt *SwitchStmt::create(SourceLoc SwitchLoc, SwitchStmt *SwitchStmt::create(SourceLoc SwitchLoc,

View File

@@ -1351,154 +1351,153 @@ namespace {
return P; return P;
} }
}; };
} } // unnamed namespace
ParserStatus Parser::parseStmtCaseLabels(SmallVectorImpl<CaseLabel *> &labels,
SmallVectorImpl<Decl *> &boundDecls) {
// We must have at least one case label.
assert(Tok.is(tok::kw_case) || Tok.is(tok::kw_default));
static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc,
SmallVectorImpl<CaseLabelItem> &LabelItems,
SmallVectorImpl<Decl *> &BoundDecls,
SourceLoc &ColonLoc) {
ParserStatus Status; ParserStatus Status;
bool parsedDefault = false; CaseLoc = P.consumeToken(tok::kw_case);
bool parsedOtherLabelWithDefault = false;
do { do {
// 'default' should label a block by itself. ParserResult<Pattern> CasePattern;
if (parsedDefault && !parsedOtherLabelWithDefault) { if (P.CodeCompletion) {
diagnose(Tok, diag::default_with_other_labels); if (P.Tok.is(tok::code_complete)) {
parsedOtherLabelWithDefault = true; CasePattern =
makeParserErrorResult(new (P.Context) AnyPattern(SourceLoc()));
P.CodeCompletion->completeCaseStmtBeginning();
P.consumeToken();
} }
if (P.Tok.is(tok::period) && P.peekToken().is(tok::code_complete)) {
// case-label ::= 'case' matching-pattern (',' matching-pattern)* CasePattern =
// ('where' expr)? ':' makeParserErrorResult(new (P.Context) AnyPattern(SourceLoc()));
if (Tok.is(tok::kw_case)) { P.consumeToken();
SourceLoc caseLoc = consumeToken(); P.CodeCompletion->completeCaseStmtDotPrefix();
P.consumeToken();
// Parse comma-separated patterns.
SmallVector<Pattern *, 2> 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> pattern = parseMatchingPattern(); if (CasePattern.isNull())
Status |= pattern; CasePattern = P.parseMatchingPattern();
if (pattern.isNonNull()) {
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 // 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 // to do this with a full AST walk, because the freshly parsed pattern
// represents tuples and var patterns as tupleexprs and // represents tuples and var patterns as tupleexprs and
// unresolved_pattern_expr nodes, instead of as proper pattern nodes. // unresolved_pattern_expr nodes, instead of as proper pattern nodes.
pattern.get()->walk(CollectVarsAndAddToScope(*this, boundDecls)); CasePattern.get()->walk(CollectVarsAndAddToScope(P, BoundDecls));
patterns.push_back(pattern.get());
} }
} while (consumeIf(tok::comma));
// Parse an optional 'where' guard. // Parse an optional 'where' guard.
SourceLoc whereLoc; SourceLoc WhereLoc;
Expr *guardExpr = nullptr; ParserResult<Expr> Guard;
if (P.Tok.is(tok::kw_where)) {
if (Tok.is(tok::kw_where)) { WhereLoc = P.consumeToken(tok::kw_where);
whereLoc = consumeToken(); Guard = P.parseExpr(diag::expected_case_where_expr);
ParserResult<Expr> guard = parseExpr(diag::expected_case_where_expr); Status |= Guard;
Status |= guard;
if (guard.isNonNull())
guardExpr = guard.get();
} }
SourceLoc colonLoc = Tok.getLoc(); LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false,
if (!Tok.is(tok::colon)) CasePattern.get(), WhereLoc,
diagnose(Tok, diag::expected_case_colon, "case"); Guard.getPtrOrNull()));
else } while (P.consumeIf(tok::comma));
colonLoc = consumeToken();
auto label = CaseLabel::create(Context, /*isDefault*/false, ColonLoc = P.Tok.getLoc();
caseLoc, patterns, whereLoc, guardExpr, if (!P.Tok.is(tok::colon)) {
colonLoc); P.diagnose(P.Tok, diag::expected_case_colon, "case");
labels.push_back(label); Status.setIsParseError();
continue; } else
} P.consumeToken(tok::colon);
return Status;
}
static ParserStatus
parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc,
SmallVectorImpl<CaseLabelItem> &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<Expr> 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()));
// case-label ::= 'default' ':'
// 'default' should label a block by itself.
if (!labels.empty() && !parsedOtherLabelWithDefault) {
diagnose(Tok, diag::default_with_other_labels);
parsedOtherLabelWithDefault = true;
}
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<Expr> guard = parseExpr(diag::expected_case_where_expr);
Status |= guard;
if (guard.isNonNull())
guardExpr = guard.get();
}
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));
return Status; return Status;
} }
// stmt-case ::= case-label+ brace-item*
ParserResult<CaseStmt> Parser::parseStmtCase() { ParserResult<CaseStmt> Parser::parseStmtCase() {
// A case block has its own scope for variables bound out of the pattern. // 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; ParserStatus Status;
SmallVector<CaseLabel*, 2> labels; SmallVector<CaseLabelItem, 2> CaseLabelItems;
SmallVector<Decl*, 4> boundDecls; SmallVector<Decl *, 4> BoundDecls;
Status |= parseStmtCaseLabels(labels, 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. // Case blocks with multiple patterns cannot bind variables.
if (!boundDecls.empty() if (!BoundDecls.empty() && CaseLabelItems.size() > 1)
&& (labels.size() > 1 || labels[0]->getPatterns().size() > 1)) diagnose(BoundDecls[0]->getLoc(),
diagnose(boundDecls[0]->getLoc(),
diag::var_binding_with_multiple_case_patterns); diag::var_binding_with_multiple_case_patterns);
SmallVector<ASTNode, 8> bodyItems; SmallVector<ASTNode, 8> BodyItems;
SourceLoc startOfBody = Tok.getLoc(); SourceLoc StartOfBody = Tok.getLoc();
Status |= parseBraceItems(bodyItems, BraceItemListKind::Case); if (Tok.isNot(tok::kw_case) && Tok.isNot(tok::kw_default) &&
BraceStmt *body; Tok.isNot(tok::r_brace)) {
if (bodyItems.empty()) { Status |= parseBraceItems(BodyItems, BraceItemListKind::Case);
body = BraceStmt::create(Context, PreviousLoc, ArrayRef<ASTNode>(), } 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<ASTNode>(),
PreviousLoc, /*implicit=*/true); PreviousLoc, /*implicit=*/true);
} else { } else {
body = BraceStmt::create(Context, startOfBody, bodyItems, PreviousLoc); Body = BraceStmt::create(Context, StartOfBody, BodyItems, PreviousLoc);
} }
return makeParserResult( return makeParserResult(
Status, CaseStmt::create(Context, labels, !boundDecls.empty(), body)); Status, CaseStmt::create(Context, CaseLoc, CaseLabelItems,
!BoundDecls.empty(), ColonLoc, Body));
} }

View File

@@ -1818,9 +1818,9 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
for (auto *caseBlock : S->getCases()) { for (auto *caseBlock : S->getCases()) {
caseMap[caseBlock] = {}; caseMap[caseBlock] = {};
for (auto *label : caseBlock->getCaseLabels()) for (const auto &labelItem : caseBlock->getCaseLabelItems())
for (auto *pattern : label->getPatterns()) clauses.addRow(caseBlock, const_cast<Expr *>(labelItem.getGuardExpr()),
clauses.addRow(caseBlock, label->getGuardExpr(), pattern, labelItem.getPattern(),
CleanupsDepth::invalid(), contBB); CleanupsDepth::invalid(), contBB);
} }

View File

@@ -138,7 +138,7 @@ deriveEquatable_enum_eq(TypeChecker &tc, EnumDecl *enumDecl) {
eqDecl->setDerivedForTypeDecl(enumDecl); eqDecl->setDerivedForTypeDecl(enumDecl);
SmallVector<CaseStmt*, 4> cases; SmallVector<CaseStmt*, 4> cases;
SmallVector<CaseLabel*, 4> caseLabels; SmallVector<CaseLabelItem, 4> caseLabelItems;
for (auto elt : enumDecl->getAllElements()) { for (auto elt : enumDecl->getAllElements()) {
assert(!elt->hasArgumentType() assert(!elt->hasArgumentType()
@@ -157,31 +157,32 @@ deriveEquatable_enum_eq(TypeChecker &tc, EnumDecl *enumDecl) {
auto tuplePat = TuplePattern::create(C, SourceLoc(), tupleElts, SourceLoc()); auto tuplePat = TuplePattern::create(C, SourceLoc(), tupleElts, SourceLoc());
tuplePat->setImplicit(); tuplePat->setImplicit();
caseLabels.push_back(CaseLabel::create(C, /*isDefault*/false, caseLabelItems.push_back(
SourceLoc(), tuplePat, SourceLoc(), CaseLabelItem(/*IsDefault=*/false, tuplePat, SourceLoc(), nullptr));
nullptr, SourceLoc()));
} }
{ {
Expr *trueExpr = getTrueExpr(C); Expr *trueExpr = getTrueExpr(C);
auto returnStmt = new (C) ReturnStmt(SourceLoc(), trueExpr); auto returnStmt = new (C) ReturnStmt(SourceLoc(), trueExpr);
BraceStmt* body = BraceStmt::create(C, SourceLoc(), BraceStmt* body = BraceStmt::create(C, SourceLoc(),
ASTNode(returnStmt), 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()); auto any = new (C) AnyPattern(SourceLoc());
any->setImplicit(); any->setImplicit();
auto label = CaseLabel::create(C, /*isDefault*/ true, auto labelItem =
SourceLoc(), any, SourceLoc(), CaseLabelItem(/*IsDefault=*/true, any, SourceLoc(), nullptr);
nullptr, SourceLoc());
Expr *falseExpr = getFalseExpr(C); Expr *falseExpr = getFalseExpr(C);
auto returnStmt = new (C) ReturnStmt(SourceLoc(), falseExpr); auto returnStmt = new (C) ReturnStmt(SourceLoc(), falseExpr);
BraceStmt* body = BraceStmt::create(C, SourceLoc(), BraceStmt* body = BraceStmt::create(C, SourceLoc(),
ASTNode(returnStmt), 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); 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); SourceLoc(), SourceLoc(), Identifier(), elt, nullptr);
pat->setImplicit(); pat->setImplicit();
auto label = CaseLabel::create(C, /*isDefault*/false, auto labelItem =
SourceLoc(), CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr);
pat, SourceLoc(), nullptr, SourceLoc());
llvm::SmallString<8> indexVal; llvm::SmallString<8> indexVal;
APInt(32, index++).toString(indexVal, 10, /*signed*/ false); APInt(32, index++).toString(indexVal, 10, /*signed*/ false);
@@ -347,8 +347,9 @@ deriveHashable_enum_hashValue(TypeChecker &tc, EnumDecl *enumDecl) {
indexExpr, /*implicit*/ true); indexExpr, /*implicit*/ true);
auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr),
SourceLoc()); SourceLoc());
cases.push_back(CaseStmt::create(C, label, /*hasBoundDecls*/false, cases.push_back(
body)); CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false,
SourceLoc(), body));
} }
auto selfRef = new (C) DeclRefExpr(selfDecl, SourceLoc(), /*implicit*/true); auto selfRef = new (C) DeclRefExpr(selfDecl, SourceLoc(), /*implicit*/true);
auto switchStmt = SwitchStmt::create(SourceLoc(), selfRef, auto switchStmt = SwitchStmt::create(SourceLoc(), selfRef,

View File

@@ -147,9 +147,8 @@ static FuncDecl *deriveRawRepresentable_toRaw(TypeChecker &tc,
SourceLoc(), SourceLoc(), Identifier(), elt, nullptr); SourceLoc(), SourceLoc(), Identifier(), elt, nullptr);
pat->setImplicit(); pat->setImplicit();
auto label = CaseLabel::create(C, /*isDefault*/false, auto labelItem =
SourceLoc(), CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr);
pat, SourceLoc(), nullptr, SourceLoc());
auto returnExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr()); auto returnExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr());
auto returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr); auto returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr);
@@ -157,7 +156,9 @@ static FuncDecl *deriveRawRepresentable_toRaw(TypeChecker &tc,
auto body = BraceStmt::create(C, SourceLoc(), auto body = BraceStmt::create(C, SourceLoc(),
ASTNode(returnStmt), 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 selfRef = new (C) DeclRefExpr(selfDecl, SourceLoc(), /*implicit*/true);
auto switchStmt = SwitchStmt::create(SourceLoc(), selfRef, auto switchStmt = SwitchStmt::create(SourceLoc(), selfRef,
@@ -280,9 +281,8 @@ static FuncDecl *deriveRawRepresentable_fromRaw(TypeChecker &tc,
nullptr, nullptr); nullptr, nullptr);
litPat->setImplicit(); litPat->setImplicit();
auto label = CaseLabel::create(C, /*isDefault*/false, auto labelItem =
SourceLoc(), litPat, SourceLoc(), CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), nullptr);
nullptr, SourceLoc());
auto eltRef = new (C) DeclRefExpr(elt, SourceLoc(), /*implicit*/true); auto eltRef = new (C) DeclRefExpr(elt, SourceLoc(), /*implicit*/true);
auto metaTyRef = new (C) MetatypeExpr(nullptr, SourceLoc(), metaTy); auto metaTyRef = new (C) MetatypeExpr(nullptr, SourceLoc(), metaTy);
@@ -292,14 +292,15 @@ static FuncDecl *deriveRawRepresentable_fromRaw(TypeChecker &tc,
auto body = BraceStmt::create(C, SourceLoc(), auto body = BraceStmt::create(C, SourceLoc(),
ASTNode(returnStmt), 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()); auto anyPat = new (C) AnyPattern(SourceLoc());
anyPat->setImplicit(); anyPat->setImplicit();
auto dfltLabel = CaseLabel::create(C, /*isDefault*/true, SourceLoc(), auto dfltLabelItem =
anyPat, SourceLoc(), nullptr, CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), nullptr);
SourceLoc());
auto optionalRef = new (C) DeclRefExpr(C.getOptionalDecl(), auto optionalRef = new (C) DeclRefExpr(C.getOptionalDecl(),
SourceLoc(), /*implicit*/true); SourceLoc(), /*implicit*/true);
@@ -310,8 +311,9 @@ static FuncDecl *deriveRawRepresentable_fromRaw(TypeChecker &tc,
auto dfltReturnStmt = new (C) ReturnStmt(SourceLoc(), dfltReturnExpr); auto dfltReturnStmt = new (C) ReturnStmt(SourceLoc(), dfltReturnExpr);
auto dfltBody = BraceStmt::create(C, SourceLoc(), auto dfltBody = BraceStmt::create(C, SourceLoc(),
ASTNode(dfltReturnStmt), SourceLoc()); ASTNode(dfltReturnStmt), SourceLoc());
cases.push_back(CaseStmt::create(C, dfltLabel, /*hasBoundDecls*/false, cases.push_back(
dfltBody)); CaseStmt::create(C, SourceLoc(), dfltLabelItem, /*HasBoundDecls=*/false,
SourceLoc(), dfltBody));
auto rawRef = new (C) DeclRefExpr(rawDecl, SourceLoc(), /*implicit*/true); auto rawRef = new (C) DeclRefExpr(rawDecl, SourceLoc(), /*implicit*/true);
auto switchStmt = SwitchStmt::create(SourceLoc(), rawRef, SourceLoc(), auto switchStmt = SwitchStmt::create(SourceLoc(), rawRef, SourceLoc(),

View File

@@ -523,9 +523,9 @@ public:
// final case block, it is invalid. // final case block, it is invalid.
FallthroughDest = i+1 == e ? nullptr : S->getCases()[i+1]; FallthroughDest = i+1 == e ? nullptr : S->getCases()[i+1];
for (auto *caseLabel : caseBlock->getCaseLabels()) { for (auto &labelItem : caseBlock->getMutableCaseLabelItems()) {
// Resolve the patterns in the label. // Resolve the pattern in the label.
for (auto *&pattern : caseLabel->getPatterns()) { Pattern *pattern = labelItem.getPattern();
if (auto *newPattern = TC.resolvePattern(pattern, DC)) { if (auto *newPattern = TC.resolvePattern(pattern, DC)) {
pattern = newPattern; pattern = newPattern;
} else { } else {
@@ -543,14 +543,14 @@ public:
}); });
hadTypeError = true; hadTypeError = true;
} }
} labelItem.setPattern(pattern);
// Check the guard expression, if present. // Check the guard expression, if present.
if (auto *guard = caseLabel->getGuardExpr()) { if (auto *guard = labelItem.getGuardExpr()) {
if (TC.typeCheckCondition(guard, DC)) if (TC.typeCheckCondition(guard, DC))
hadTypeError = true; hadTypeError = true;
else else
caseLabel->setGuardExpr(guard); labelItem.setGuardExpr(guard);
} }
} }

View File

@@ -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 2014-03-19
---------- ----------