mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +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
|
.. 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,9 +1291,12 @@ 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)) {
|
||||||
switch point {
|
switch point {
|
||||||
case (0, 0):
|
case (0, 0):
|
||||||
println("origin")
|
println("origin")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -419,100 +419,82 @@ 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.
|
||||||
bool isDefault() const { return GuardExprAndIsDefault.getInt(); }
|
bool isDefault() const { return GuardExprAndIsDefault.getInt(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 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.
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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";
|
} else {
|
||||||
// '_' pattern is implicit and doesn't need to be printed.
|
auto PrintCaseLabelItem = [&](const CaseLabelItem &CLI) {
|
||||||
} else {
|
if (auto *P = CLI.getPattern())
|
||||||
Printer << "case ";
|
printPattern(P);
|
||||||
interleave(label->getPatterns(),
|
if (CLI.getGuardExpr()) {
|
||||||
[&](Pattern *p) { printPattern(p); },
|
Printer << " where ";
|
||||||
[&] { Printer << ", "; });
|
// FIXME: print guard expr
|
||||||
}
|
}
|
||||||
if (label->getGuardExpr()) {
|
};
|
||||||
Printer << " where ";
|
Printer << "case ";
|
||||||
// FIXME: print guard expr
|
interleave(CS->getCaseLabelItems(), PrintCaseLabelItem,
|
||||||
}
|
[&] { Printer << ", "; });
|
||||||
Printer << ":\n";
|
}
|
||||||
};
|
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 {
|
||||||
|
|||||||
@@ -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 (auto *newGuard = doIt(CLI.getGuardExpr()))
|
||||||
if (label->getGuardExpr()) {
|
CLI.setGuardExpr(newGuard);
|
||||||
if (Expr *newGuard = doIt(label->getGuardExpr()))
|
|
||||||
label->setGuardExpr(newGuard);
|
|
||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,11 +305,10 @@ 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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
alignof(CaseStmt));
|
||||||
caseLoc, patterns,
|
return ::new (Mem) CaseStmt(CaseLoc, CaseLabelItems, HasBoundDecls, ColonLoc,
|
||||||
whereLoc, guardExpr, colonLoc);
|
Body, Implicit);
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
return ::new (p) CaseStmt(Labels, HasBoundDecls, Body, implicit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchStmt *SwitchStmt::create(SourceLoc SwitchLoc,
|
SwitchStmt *SwitchStmt::create(SourceLoc SwitchLoc,
|
||||||
|
|||||||
@@ -1333,172 +1333,171 @@ ParserResult<Stmt> Parser::parseStmtSwitch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class CollectVarsAndAddToScope : public ASTWalker {
|
class CollectVarsAndAddToScope : public ASTWalker {
|
||||||
public:
|
public:
|
||||||
Parser &TheParser;
|
Parser &TheParser;
|
||||||
SmallVectorImpl<Decl*> &Decls;
|
SmallVectorImpl<Decl *> &Decls;
|
||||||
|
|
||||||
CollectVarsAndAddToScope(Parser &P, SmallVectorImpl<Decl*> &Decls)
|
CollectVarsAndAddToScope(Parser &P, SmallVectorImpl<Decl *> &Decls)
|
||||||
: TheParser(P), Decls(Decls) {}
|
: TheParser(P), Decls(Decls) {}
|
||||||
|
|
||||||
Pattern *walkToPatternPost(Pattern *P) override {
|
Pattern *walkToPatternPost(Pattern *P) override {
|
||||||
// Handle vars.
|
// Handle vars.
|
||||||
if (auto *Named = dyn_cast<NamedPattern>(P)) {
|
if (auto *Named = dyn_cast<NamedPattern>(P)) {
|
||||||
VarDecl *VD = Named->getDecl();
|
VarDecl *VD = Named->getDecl();
|
||||||
Decls.push_back(VD);
|
Decls.push_back(VD);
|
||||||
TheParser.addToScope(VD);
|
TheParser.addToScope(VD);
|
||||||
}
|
|
||||||
return P;
|
|
||||||
}
|
}
|
||||||
};
|
return P;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
ParserStatus Parser::parseStmtCaseLabels(SmallVectorImpl<CaseLabel *> &labels,
|
} // unnamed namespace
|
||||||
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();
|
||||||
// case-label ::= 'case' matching-pattern (',' matching-pattern)*
|
P.consumeToken();
|
||||||
// ('where' expr)? ':'
|
}
|
||||||
if (Tok.is(tok::kw_case)) {
|
if (P.Tok.is(tok::period) && P.peekToken().is(tok::code_complete)) {
|
||||||
SourceLoc caseLoc = consumeToken();
|
CasePattern =
|
||||||
|
makeParserErrorResult(new (P.Context) AnyPattern(SourceLoc()));
|
||||||
// Parse comma-separated patterns.
|
P.consumeToken();
|
||||||
SmallVector<Pattern *, 2> patterns;
|
P.CodeCompletion->completeCaseStmtDotPrefix();
|
||||||
do {
|
P.consumeToken();
|
||||||
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();
|
|
||||||
Status |= pattern;
|
|
||||||
if (pattern.isNonNull()) {
|
|
||||||
// Add variable bindings from the pattern to the case scope. We have
|
|
||||||
// to do this with a full AST walk, because the freshly parsed pattern
|
|
||||||
// represents tuples and var patterns as tupleexprs and
|
|
||||||
// unresolved_pattern_expr nodes, instead of as proper pattern nodes.
|
|
||||||
pattern.get()->walk(CollectVarsAndAddToScope(*this, boundDecls));
|
|
||||||
patterns.push_back(pattern.get());
|
|
||||||
}
|
|
||||||
} while (consumeIf(tok::comma));
|
|
||||||
|
|
||||||
// Parse an optional 'where' guard.
|
|
||||||
SourceLoc whereLoc;
|
|
||||||
Expr *guardExpr = nullptr;
|
|
||||||
|
|
||||||
if (Tok.is(tok::kw_where)) {
|
|
||||||
whereLoc = consumeToken();
|
|
||||||
ParserResult<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, "case");
|
|
||||||
else
|
|
||||||
colonLoc = consumeToken();
|
|
||||||
|
|
||||||
auto label = CaseLabel::create(Context, /*isDefault*/false,
|
|
||||||
caseLoc, patterns, whereLoc, guardExpr,
|
|
||||||
colonLoc);
|
|
||||||
labels.push_back(label);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// case-label ::= 'default' ':'
|
if (CasePattern.isNull())
|
||||||
|
CasePattern = P.parseMatchingPattern();
|
||||||
|
|
||||||
// 'default' should label a block by itself.
|
if (CasePattern.isNull())
|
||||||
if (!labels.empty() && !parsedOtherLabelWithDefault) {
|
CasePattern =
|
||||||
diagnose(Tok, diag::default_with_other_labels);
|
makeParserErrorResult(new (P.Context) AnyPattern(P.PreviousLoc));
|
||||||
parsedOtherLabelWithDefault = true;
|
|
||||||
|
Status |= CasePattern;
|
||||||
|
if (CasePattern.isNonNull()) {
|
||||||
|
// Add variable bindings from the pattern to the case scope. We have
|
||||||
|
// to do this with a full AST walk, because the freshly parsed pattern
|
||||||
|
// represents tuples and var patterns as tupleexprs and
|
||||||
|
// unresolved_pattern_expr nodes, instead of as proper pattern nodes.
|
||||||
|
CasePattern.get()->walk(CollectVarsAndAddToScope(P, BoundDecls));
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedDefault = true;
|
// Parse an optional 'where' guard.
|
||||||
SourceLoc defaultLoc = consumeToken(tok::kw_default);
|
SourceLoc WhereLoc;
|
||||||
|
ParserResult<Expr> Guard;
|
||||||
// We don't allow 'where' guards on a 'default' block. For recovery
|
if (P.Tok.is(tok::kw_where)) {
|
||||||
// parse one if present.
|
WhereLoc = P.consumeToken(tok::kw_where);
|
||||||
SourceLoc whereLoc;
|
Guard = P.parseExpr(diag::expected_case_where_expr);
|
||||||
Expr *guardExpr = nullptr;
|
Status |= Guard;
|
||||||
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();
|
LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false,
|
||||||
if (!Tok.is(tok::colon))
|
CasePattern.get(), WhereLoc,
|
||||||
diagnose(Tok, diag::expected_case_colon, "default");
|
Guard.getPtrOrNull()));
|
||||||
else
|
} while (P.consumeIf(tok::comma));
|
||||||
consumeToken(tok::colon);
|
|
||||||
|
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()));
|
||||||
|
|
||||||
// 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1818,10 +1818,10 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind variable bindings from the topmost pattern nodes.
|
// Bind variable bindings from the topmost pattern nodes.
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -523,34 +523,34 @@ 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 {
|
||||||
hadTypeError = true;
|
hadTypeError = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
// Coerce the pattern to the subject's type.
|
|
||||||
if (TC.coercePatternToType(pattern, DC, subjectType, None)) {
|
|
||||||
// If that failed, mark any variables binding pieces of the pattern
|
|
||||||
// as invalid to silence follow-on errors.
|
|
||||||
pattern->forEachVariable([&](VarDecl *VD) {
|
|
||||||
VD->overwriteType(ErrorType::get(TC.Context));
|
|
||||||
VD->setInvalid();
|
|
||||||
});
|
|
||||||
hadTypeError = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coerce the pattern to the subject's type.
|
||||||
|
if (TC.coercePatternToType(pattern, DC, subjectType, None)) {
|
||||||
|
// If that failed, mark any variables binding pieces of the pattern
|
||||||
|
// as invalid to silence follow-on errors.
|
||||||
|
pattern->forEachVariable([&](VarDecl *VD) {
|
||||||
|
VD->overwriteType(ErrorType::get(TC.Context));
|
||||||
|
VD->setInvalid();
|
||||||
|
});
|
||||||
|
hadTypeError = true;
|
||||||
|
}
|
||||||
|
labelItem.setPattern(pattern);
|
||||||
|
|
||||||
// Check the guard expression, if present.
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user