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
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 <langref.pattern>`. 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 <langref.pattern.expr>`, 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
<langref.pattern.expr>`, 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,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
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)) {

View File

@@ -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

View File

@@ -419,68 +419,34 @@ public:
}
};
/// 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<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:
static CaseLabel *create(ASTContext &C,
bool isDefault,
SourceLoc caseLoc,
ArrayRef<Pattern*> patterns,
SourceLoc whereLoc,
Expr * /*nullable*/ guardExpr,
SourceLoc colonLoc);
CaseLabelItem(const CaseLabelItem &) = default;
CaseLabelItem(bool IsDefault, Pattern *CasePattern, SourceLoc WhereLoc,
Expr *GuardExpr)
: CasePattern(CasePattern), WhereLoc(WhereLoc),
GuardExprAndIsDefault(GuardExpr, IsDefault) {}
SourceLoc getLoc() const { return CaseLoc; }
SourceLoc getCaseLoc() const { return CaseLoc; }
SourceLoc getColonLoc() const { return ColonLoc; }
SourceLoc getWhereLoc() const { return WhereLoc; }
SourceRange getSourceRange() const {
return {CaseLoc, ColonLoc};
}
SourceRange getSourceRange() const;
MutableArrayRef<Pattern*> getPatterns() {
return {getPatternsBuffer(), NumPatterns};
}
ArrayRef<const Pattern *> getPatterns() const {
return {getPatternsBuffer(), NumPatterns};
}
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.
@@ -489,30 +455,46 @@ public:
/// 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.
/// 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<Stmt *, 1, bool> BodyAndHasBoundDecls;
unsigned NumCaseLabels;
unsigned NumPatterns;
CaseLabel * const *getCaseLabelsBuffer() const {
return reinterpret_cast<CaseLabel * const *>(this + 1);
const CaseLabelItem *getCaseLabelItemsBuffer() const {
return reinterpret_cast<const CaseLabelItem *>(this + 1);
}
CaseLabel **getCaseLabelsBuffer() {
return reinterpret_cast<CaseLabel **>(this + 1);
CaseLabelItem *getCaseLabelItemsBuffer() {
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);
public:
static CaseStmt *create(ASTContext &C,
ArrayRef<CaseLabel*> Labels,
bool hasBoundDecls,
Stmt *Body,
static CaseStmt *create(ASTContext &C, SourceLoc CaseLoc,
ArrayRef<CaseLabelItem> CaseLabelItems,
bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body,
Optional<bool> Implicit = {});
ArrayRef<CaseLabel *> getCaseLabels() const {
return {getCaseLabelsBuffer(), NumCaseLabels};
ArrayRef<CaseLabelItem> getCaseLabelItems() const {
return { getCaseLabelItemsBuffer(), NumPatterns };
}
MutableArrayRef<CaseLabelItem> getMutableCaseLabelItems() {
return { getCaseLabelItemsBuffer(), NumPatterns };
}
Stmt *getBody() const { return BodyAndHasBoundDecls.getPointer(); }
@@ -522,21 +504,15 @@ public:
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();
}
bool isDefault() { return getCaseLabelItems()[0].isDefault(); }
static bool classof(const Stmt *S) {
return S->getKind() == StmtKind::Case;
}
static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Case; }
};
/// Switch statement.

View File

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

View File

@@ -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<Pattern *>(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<Pattern *>(P));
}
void printRec(StmtCondition C) {
if (auto E = C.dyn_cast<Expr*>())
@@ -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<Pattern *>(P));
}
void printRec(TypeRepr *T);
raw_ostream &printCommon(Expr *E, const char *C) {

View File

@@ -1061,28 +1061,25 @@ void PrintAST::visitSwitchStmt(SwitchStmt *stmt) {
Printer << "}";
}
void PrintAST::visitCaseStmt(CaseStmt *stmt) {
auto printCaseLabel = [&](CaseLabel *label) {
if (label->isDefault()) {
void PrintAST::visitCaseStmt(CaseStmt *CS) {
if (CS->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()) {
auto PrintCaseLabelItem = [&](const CaseLabelItem &CLI) {
if (auto *P = CLI.getPattern())
printPattern(P);
if (CLI.getGuardExpr()) {
Printer << " where ";
// FIXME: print guard expr
}
Printer << ":\n";
};
Printer << "case ";
interleave(CS->getCaseLabelItems(), PrintCaseLabelItem,
[&] { Printer << ", "; });
}
Printer << ":\n";
for (auto *label : stmt->getCaseLabels())
printCaseLabel(label);
printBraceStmtElements(cast<BraceStmt>(stmt->getBody()));
printBraceStmtElements(cast<BraceStmt>(CS->getBody()));
}
void Decl::print(raw_ostream &os) const {

View File

@@ -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;
for (auto &CLI : S->getMutableCaseLabelItems()) {
if (auto *newPattern = doIt(CLI.getPattern()))
CLI.setPattern(newPattern);
else
return nullptr;
}
if (label->getGuardExpr()) {
if (Expr *newGuard = doIt(label->getGuardExpr()))
label->setGuardExpr(newGuard);
if (CLI.getGuardExpr()) {
if (auto *newGuard = doIt(CLI.getGuardExpr()))
CLI.setGuardExpr(newGuard);
else
return nullptr;
}

View File

@@ -623,9 +623,8 @@ struct FindLocalVal : public StmtVisitor<FindLocalVal> {
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());
}

View File

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

View File

@@ -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<Pattern*> patterns,
SourceLoc whereLoc, Expr *guardExpr,
SourceLoc colonLoc)
: CaseLoc(caseLoc), ColonLoc(colonLoc), WhereLoc(whereLoc),
GuardExprAndIsDefault(guardExpr, isDefault),
NumPatterns(patterns.size())
{
MutableArrayRef<Pattern*> patternBuf{getPatternsBuffer(), NumPatterns};
SourceRange CaseLabelItem::getSourceRange() const {
if (auto *E = getGuardExpr())
return { CasePattern->getStartLoc(), E->getEndLoc() };
return CasePattern->getSourceRange();
}
CaseStmt::CaseStmt(SourceLoc CaseLoc, ArrayRef<CaseLabelItem> CaseLabelItems,
bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body,
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) {
patternBuf[i] = patterns[i];
new (&Items[i]) CaseLabelItem(CaseLabelItems[i]);
}
}
CaseLabel *CaseLabel::create(ASTContext &C, bool isDefault,
SourceLoc caseLoc, ArrayRef<Pattern *> 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<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*),
CaseStmt *CaseStmt::create(ASTContext &C, SourceLoc CaseLoc,
ArrayRef<CaseLabelItem> CaseLabelItems,
bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body,
Optional<bool> Implicit) {
void *Mem = C.Allocate(sizeof(CaseStmt) +
CaseLabelItems.size() * sizeof(CaseLabelItem),
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,

View File

@@ -1351,154 +1351,153 @@ namespace {
return P;
}
};
}
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));
} // unnamed namespace
static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc,
SmallVectorImpl<CaseLabelItem> &LabelItems,
SmallVectorImpl<Decl *> &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;
ParserResult<Pattern> CasePattern;
if (P.CodeCompletion) {
if (P.Tok.is(tok::code_complete)) {
CasePattern =
makeParserErrorResult(new (P.Context) AnyPattern(SourceLoc()));
P.CodeCompletion->completeCaseStmtBeginning();
P.consumeToken();
}
// case-label ::= 'case' matching-pattern (',' matching-pattern)*
// ('where' expr)? ':'
if (Tok.is(tok::kw_case)) {
SourceLoc caseLoc = 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;
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();
}
}
ParserResult<Pattern> pattern = parseMatchingPattern();
Status |= pattern;
if (pattern.isNonNull()) {
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.
pattern.get()->walk(CollectVarsAndAddToScope(*this, boundDecls));
patterns.push_back(pattern.get());
CasePattern.get()->walk(CollectVarsAndAddToScope(P, BoundDecls));
}
} while (consumeIf(tok::comma));
// Parse an optional 'where' guard.
SourceLoc whereLoc;
Expr *guardExpr = nullptr;
if (Tok.is(tok::kw_where)) {
whereLoc = consumeToken();
ParserResult<Expr> guard = parseExpr(diag::expected_case_where_expr);
Status |= guard;
if (guard.isNonNull())
guardExpr = guard.get();
SourceLoc WhereLoc;
ParserResult<Expr> 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, "case");
else
colonLoc = consumeToken();
LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false,
CasePattern.get(), WhereLoc,
Guard.getPtrOrNull()));
} while (P.consumeIf(tok::comma));
auto label = CaseLabel::create(Context, /*isDefault*/false,
caseLoc, patterns, whereLoc, guardExpr,
colonLoc);
labels.push_back(label);
continue;
}
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<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;
}
// stmt-case ::= case-label+ brace-item*
ParserResult<CaseStmt> 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<CaseLabel*, 2> labels;
SmallVector<Decl*, 4> boundDecls;
Status |= parseStmtCaseLabels(labels, boundDecls);
SmallVector<CaseLabelItem, 2> CaseLabelItems;
SmallVector<Decl *, 4> 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<ASTNode, 8> bodyItems;
SmallVector<ASTNode, 8> BodyItems;
SourceLoc startOfBody = Tok.getLoc();
Status |= parseBraceItems(bodyItems, BraceItemListKind::Case);
BraceStmt *body;
if (bodyItems.empty()) {
body = BraceStmt::create(Context, PreviousLoc, ArrayRef<ASTNode>(),
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<ASTNode>(),
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));
}

View File

@@ -1818,9 +1818,9 @@ 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,
for (const auto &labelItem : caseBlock->getCaseLabelItems())
clauses.addRow(caseBlock, const_cast<Expr *>(labelItem.getGuardExpr()),
labelItem.getPattern(),
CleanupsDepth::invalid(), contBB);
}

View File

@@ -138,7 +138,7 @@ deriveEquatable_enum_eq(TypeChecker &tc, EnumDecl *enumDecl) {
eqDecl->setDerivedForTypeDecl(enumDecl);
SmallVector<CaseStmt*, 4> cases;
SmallVector<CaseLabel*, 4> caseLabels;
SmallVector<CaseLabelItem, 4> caseLabelItems;
for (auto elt : enumDecl->getAllElements()) {
assert(!elt->hasArgumentType()
@@ -157,31 +157,32 @@ 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,

View File

@@ -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(),

View File

@@ -523,9 +523,9 @@ 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()) {
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 {
@@ -543,14 +543,14 @@ public:
});
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);
}
}

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
----------