diff options
-rw-r--r-- | Driver/PrintParserCallbacks.cpp | 3 | ||||
-rw-r--r-- | include/clang/AST/Stmt.h | 6 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 12 | ||||
-rw-r--r-- | lib/AST/Stmt.cpp | 9 | ||||
-rw-r--r-- | lib/AST/StmtSerialization.cpp | 2 | ||||
-rw-r--r-- | lib/Parse/ParseStmt.cpp | 128 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 24 |
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) { |