mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
----------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user