summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Driver/PrintParserCallbacks.cpp3
-rw-r--r--include/clang/AST/Stmt.h6
-rw-r--r--include/clang/Parse/Action.h12
-rw-r--r--lib/AST/Stmt.cpp9
-rw-r--r--lib/AST/StmtSerialization.cpp2
-rw-r--r--lib/Parse/ParseStmt.cpp128
-rw-r--r--lib/Sema/Sema.h4
-rw-r--r--lib/Sema/SemaStmt.cpp24
8 files changed, 128 insertions, 60 deletions
diff --git a/Driver/PrintParserCallbacks.cpp b/Driver/PrintParserCallbacks.cpp
index b14f2660b8..0a6d09a02e 100644
--- a/Driver/PrintParserCallbacks.cpp
+++ b/Driver/PrintParserCallbacks.cpp
@@ -272,8 +272,7 @@ namespace {
ExprArg LHSVal,
SourceLocation DotDotDotLoc,
ExprArg RHSVal,
- SourceLocation ColonLoc,
- StmtArg SubStmt) {
+ SourceLocation ColonLoc) {
llvm::cout << __FUNCTION__ << "\n";
return StmtEmpty();
}
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index 0e2daed91c..fe994f6825 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -147,7 +147,7 @@ public:
}
virtual ~Stmt() {}
- virtual void Destroy(ASTContext& Ctx);
+ virtual void Destroy(ASTContext &Ctx);
StmtClass getStmtClass() const { return sClass; }
const char *getStmtClassName() const;
@@ -440,9 +440,9 @@ class CaseStmt : public SwitchCase {
// GNU "case 1 ... 4" extension
SourceLocation CaseLoc;
public:
- CaseStmt(Expr *lhs, Expr *rhs, Stmt *substmt, SourceLocation caseLoc)
+ CaseStmt(Expr *lhs, Expr *rhs, SourceLocation caseLoc)
: SwitchCase(CaseStmtClass) {
- SubExprs[SUBSTMT] = substmt;
+ SubExprs[SUBSTMT] = 0;
SubExprs[LHS] = reinterpret_cast<Stmt*>(lhs);
SubExprs[RHS] = reinterpret_cast<Stmt*>(rhs);
CaseLoc = caseLoc;
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index e1ed5cd109..ab87eb3571 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -406,12 +406,18 @@ public:
}
/// ActOnCaseStmt - Note that this handles the GNU 'case 1 ... 4' extension,
- /// which can specify an RHS value.
+ /// which can specify an RHS value. The sub-statement of the case is
+ /// specified in a separate action.
virtual OwningStmtResult ActOnCaseStmt(SourceLocation CaseLoc, ExprArg LHSVal,
- SourceLocation DotDotDotLoc, ExprArg RHSVal,
- SourceLocation ColonLoc, StmtArg SubStmt) {
+ SourceLocation DotDotDotLoc,
+ ExprArg RHSVal,
+ SourceLocation ColonLoc) {
return StmtEmpty();
}
+
+ /// ActOnCaseStmtBody - This installs a statement as the body of a case.
+ virtual void ActOnCaseStmtBody(StmtTy *CaseStmt, StmtArg SubStmt) {}
+
virtual OwningStmtResult ActOnDefaultStmt(SourceLocation DefaultLoc,
SourceLocation ColonLoc,
StmtArg SubStmt, Scope *CurScope){
diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp
index 63d3e7f7b4..749b677d91 100644
--- a/lib/AST/Stmt.cpp
+++ b/lib/AST/Stmt.cpp
@@ -43,13 +43,12 @@ const char *Stmt::getStmtClassName() const {
return getStmtInfoTableEntry(sClass).Name;
}
-void Stmt::DestroyChildren(ASTContext& C) {
- for (child_iterator I = child_begin(), E = child_end(); I !=E; ) {
+void Stmt::DestroyChildren(ASTContext &C) {
+ for (child_iterator I = child_begin(), E = child_end(); I !=E; )
if (Stmt* Child = *I++) Child->Destroy(C);
- }
}
-void Stmt::Destroy(ASTContext& C) {
+void Stmt::Destroy(ASTContext &C) {
DestroyChildren(C);
// FIXME: Eventually all Stmts should be allocated with the allocator
// in ASTContext, just like with Decls.
@@ -57,7 +56,7 @@ void Stmt::Destroy(ASTContext& C) {
C.Deallocate((void *)this);
}
-void DeclStmt::Destroy(ASTContext& C) {
+void DeclStmt::Destroy(ASTContext &C) {
this->~DeclStmt();
C.Deallocate((void *)this);
}
diff --git a/lib/AST/StmtSerialization.cpp b/lib/AST/StmtSerialization.cpp
index 009a2dd17a..4a90edd7a1 100644
--- a/lib/AST/StmtSerialization.cpp
+++ b/lib/AST/StmtSerialization.cpp
@@ -414,7 +414,7 @@ void CaseStmt::EmitImpl(Serializer& S) const {
CaseStmt* CaseStmt::CreateImpl(Deserializer& D, ASTContext& C) {
SourceLocation CaseLoc = SourceLocation::ReadVal(D);
CaseStmt* stmt = new (C, llvm::alignof<CaseStmt>())
- CaseStmt(NULL,NULL,NULL,CaseLoc);
+ CaseStmt(NULL,NULL,CaseLoc);
D.ReadPtr(stmt->NextSwitchCase);
D.BatchReadOwnedPtrs((unsigned) END_EXPR, &stmt->SubExprs[0], C);
return stmt;
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index a8303997f7..38a3aaf13c 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -225,54 +225,112 @@ Parser::OwningStmtResult Parser::ParseLabeledStatement() {
/// 'case' constant-expression ':' statement
/// [GNU] 'case' constant-expression '...' constant-expression ':' statement
///
-/// Note that this does not parse the 'statement' at the end.
-///
Parser::OwningStmtResult Parser::ParseCaseStatement() {
assert(Tok.is(tok::kw_case) && "Not a case stmt!");
- SourceLocation CaseLoc = ConsumeToken(); // eat the 'case'.
+
+ // It is very very common for code to contain many case statements recursively
+ // nested, as in (but usually without indentation):
+ // case 1:
+ // case 2:
+ // case 3:
+ // case 4:
+ // case 5: etc.
+ //
+ // Parsing this naively works, but is both inefficient and can cause us to run
+ // out of stack space in our recursive descent parser. As a special case,
+ // flatten this recursion into an interative loop. This is complex and gross,
+ // but all the grossness is constrained to ParseCaseStatement (and some
+ // wierdness in the actions), so this is just local grossness :).
+
+ // TopLevelCase - This is the highest level we have parsed. 'case 1' in the
+ // example above.
+ OwningStmtResult TopLevelCase(Actions, true);
+
+ // DeepestParsedCaseStmt - This is the deepest statement we have parsed, which
+ // gets updated each time a new case is parsed, and whose body is unset so
+ // far. When parsing 'case 4', this is the 'case 3' node.
+ StmtTy *DeepestParsedCaseStmt = 0;
+
+ // While we have case statements, eat and stack them.
+ do {
+ SourceLocation CaseLoc = ConsumeToken(); // eat the 'case'.
+
+ OwningExprResult LHS(ParseConstantExpression());
+ if (LHS.isInvalid()) {
+ SkipUntil(tok::colon);
+ return StmtError();
+ }
- OwningExprResult LHS(ParseConstantExpression());
- if (LHS.isInvalid()) {
- SkipUntil(tok::colon);
- return StmtError();
- }
+ // GNU case range extension.
+ SourceLocation DotDotDotLoc;
+ OwningExprResult RHS(Actions);
+ if (Tok.is(tok::ellipsis)) {
+ Diag(Tok, diag::ext_gnu_case_range);
+ DotDotDotLoc = ConsumeToken();
- // GNU case range extension.
- SourceLocation DotDotDotLoc;
- OwningExprResult RHS(Actions);
- if (Tok.is(tok::ellipsis)) {
- Diag(Tok, diag::ext_gnu_case_range);
- DotDotDotLoc = ConsumeToken();
+ RHS = ParseConstantExpression();
+ if (RHS.isInvalid()) {
+ SkipUntil(tok::colon);
+ return StmtError();
+ }
+ }
- RHS = ParseConstantExpression();
- if (RHS.isInvalid()) {
+ if (Tok.isNot(tok::colon)) {
+ Diag(Tok, diag::err_expected_colon_after) << "'case'";
SkipUntil(tok::colon);
return StmtError();
}
- }
-
- if (Tok.isNot(tok::colon)) {
- Diag(Tok, diag::err_expected_colon_after) << "'case'";
- SkipUntil(tok::colon);
- return StmtError();
- }
-
- SourceLocation ColonLoc = ConsumeToken();
- // Diagnose the common error "switch (X) { case 4: }", which is not valid.
- if (Tok.is(tok::r_brace)) {
+ SourceLocation ColonLoc = ConsumeToken();
+
+ OwningStmtResult Case =
+ Actions.ActOnCaseStmt(CaseLoc, move(LHS), DotDotDotLoc,
+ move(RHS), ColonLoc);
+
+ // If we had a sema error parsing this case, then just ignore it and
+ // continue parsing the sub-stmt.
+ if (Case.isInvalid()) {
+ if (TopLevelCase.isInvalid()) // No parsed case stmts.
+ return ParseStatement();
+ // Otherwise, just don't add it as a nested case.
+ } else {
+ // If this is the first case statement we parsed, it becomes TopLevelCase.
+ // Otherwise we link it into the current chain.
+ StmtTy *NextDeepest = Case.get();
+ if (TopLevelCase.isInvalid())
+ TopLevelCase = move(Case);
+ else
+ Actions.ActOnCaseStmtBody(DeepestParsedCaseStmt, move(Case));
+ DeepestParsedCaseStmt = NextDeepest;
+ }
+
+ // Handle all case statements.
+ } while (Tok.is(tok::kw_case));
+
+ assert(!TopLevelCase.isInvalid() && "Should have parsed at least one case!");
+
+ // If we found a non-case statement, start by parsing it.
+ OwningStmtResult SubStmt(Actions);
+
+ if (Tok.isNot(tok::r_brace)) {
+ SubStmt = ParseStatement();
+ } else {
+ // Nicely diagnose the common error "switch (X) { case 4: }", which is
+ // not valid.
+ // FIXME: add insertion hint.
Diag(Tok, diag::err_label_end_of_compound_statement);
- return StmtError();
+ SubStmt = true;
}
-
- OwningStmtResult SubStmt(ParseStatement());
-
- // Broken substmt shouldn't prevent the case from being added to the AST.
+
+ // Broken sub-stmt shouldn't prevent forming the case statement properly.
if (SubStmt.isInvalid())
- SubStmt = Actions.ActOnNullStmt(ColonLoc);
+ SubStmt = Actions.ActOnNullStmt(SourceLocation());
+
+ // Install the body into the most deeply-nested case.
+ Actions.ActOnCaseStmtBody(DeepestParsedCaseStmt, move(SubStmt));
- return Actions.ActOnCaseStmt(CaseLoc, move(LHS), DotDotDotLoc,
- move(RHS), ColonLoc, move(SubStmt));
+ // Return the top level parsed statement tree.
+ return OwningStmtResult(Actions, TopLevelCase.release());
}
/// ParseDefaultStatement
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index deff504864..14b72874f7 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -995,7 +995,9 @@ public:
SourceLocation EndLoc);
virtual OwningStmtResult ActOnCaseStmt(SourceLocation CaseLoc, ExprArg LHSVal,
SourceLocation DotDotDotLoc, ExprArg RHSVal,
- SourceLocation ColonLoc, StmtArg SubStmt);
+ SourceLocation ColonLoc);
+ virtual void ActOnCaseStmtBody(StmtTy *CaseStmt, StmtArg SubStmt);
+
virtual OwningStmtResult ActOnDefaultStmt(SourceLocation DefaultLoc,
SourceLocation ColonLoc,
StmtArg SubStmt, Scope *CurScope);
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index ea9ba4c393..fcc501c45d 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -61,10 +61,9 @@ Sema::OwningStmtResult Sema::ActOnDeclStmt(DeclTy *decl,
DeclGroupRef DG(*decls.begin());
return Owned(new (Context) DeclStmt(DG, StartLoc, EndLoc));
}
- else {
- DeclGroupRef DG(DeclGroup::Create(Context, decls.size(), &decls[0]));
- return Owned(new (Context) DeclStmt(DG, StartLoc, EndLoc));
- }
+
+ DeclGroupRef DG(DeclGroup::Create(Context, decls.size(), &decls[0]));
+ return Owned(new (Context) DeclStmt(DG, StartLoc, EndLoc));
}
Action::OwningStmtResult
@@ -114,16 +113,14 @@ Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
Action::OwningStmtResult
Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprArg lhsval,
SourceLocation DotDotDotLoc, ExprArg rhsval,
- SourceLocation ColonLoc, StmtArg subStmt) {
- Stmt *SubStmt = static_cast<Stmt*>(subStmt.release());
+ SourceLocation ColonLoc) {
assert((lhsval.get() != 0) && "missing expression in case statement");
// C99 6.8.4.2p3: The expression shall be an integer constant.
// However, GCC allows any evaluatable integer expression.
-
Expr *LHSVal = static_cast<Expr*>(lhsval.get());
if (VerifyIntegerConstantExpression(LHSVal))
- return Owned(SubStmt);
+ return StmtError();
// GCC extension: The expression shall be an integer constant.
@@ -135,17 +132,24 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprArg lhsval,
if (SwitchStack.empty()) {
Diag(CaseLoc, diag::err_case_not_in_switch);
- return Owned(SubStmt);
+ return StmtError();
}
// Only now release the smart pointers.
lhsval.release();
rhsval.release();
- CaseStmt *CS = new (Context) CaseStmt(LHSVal, RHSVal, SubStmt, CaseLoc);
+ CaseStmt *CS = new (Context) CaseStmt(LHSVal, RHSVal, CaseLoc);
SwitchStack.back()->addSwitchCase(CS);
return Owned(CS);
}
+/// ActOnCaseStmtBody - This installs a statement as the body of a case.
+void Sema::ActOnCaseStmtBody(StmtTy *caseStmt, StmtArg subStmt) {
+ CaseStmt *CS = static_cast<CaseStmt*>(caseStmt);
+ Stmt *SubStmt = static_cast<Stmt*>(subStmt.release());
+ CS->setSubStmt(SubStmt);
+}
+
Action::OwningStmtResult
Sema::ActOnDefaultStmt(SourceLocation DefaultLoc, SourceLocation ColonLoc,
StmtArg subStmt, Scope *CurScope) {