From 71b914b999d9c4b6df11068fc5a3291ac4770492 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Sep 2008 20:38:47 +0000 Subject: [PATCH] Implement parser support for the 'condition' part of C++ selection-statements and iteration-statements (if/switch/while/for). Add new 'ActOnCXXConditionDeclarationExpr' action, called when the 'condition' is a declaration instead of an expression. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56007 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 2 + include/clang/Parse/Action.h | 11 +++ include/clang/Parse/DeclSpec.h | 3 +- include/clang/Parse/Parser.h | 4 ++ include/clang/Parse/Scope.h | 7 +- lib/Parse/ParseExprCXX.cpp | 49 ++++++++++++++ lib/Parse/ParseStmt.cpp | 90 ++++++++++++++++++------- test/Parser/cxx-condition.cpp | 11 +++ 8 files changed, 151 insertions(+), 26 deletions(-) create mode 100644 test/Parser/cxx-condition.cpp diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index b5caa6b77b..dffbfad620 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -537,6 +537,8 @@ DIAG(err_func_def_no_params, ERROR, "function definition does not declare parameters") DIAG(err_expected_lparen_after_type, ERROR, "expected '(' for function-style cast or type construction") +DIAG(err_expected_equal_after_declarator, ERROR, + "expected '=' after declarator") //===----------------------------------------------------------------------===// // Semantic Analysis diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index d402ed212c..8bf4afefbb 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -618,6 +618,17 @@ public: return 0; } + /// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a + /// C++ if/switch/while/for statement. + /// e.g: "if (int x = f()) {...}" + virtual ExprResult ActOnCXXConditionDeclarationExpr(Scope *S, + SourceLocation StartLoc, + Declarator &D, + SourceLocation EqualLoc, + ExprTy *AssignExprVal) { + return 0; + } + //===---------------------------- C++ Classes ---------------------------===// /// ActOnBaseSpecifier - Parsed a base specifier virtual void ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index 4f6c2fbb5b..bac88bf9c2 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -580,7 +580,8 @@ public: TypeNameContext, // Abstract declarator for types. MemberContext, // Struct/Union field. BlockContext, // Declaration within a block in a function. - ForContext // Declaration within first part of a for loop. + ForContext, // Declaration within first part of a for loop. + ConditionContext // Condition declaration in a C++ if/switch/while/for. }; private: /// Context - Where we are parsing this declarator. diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 2d9f903be9..c27954f2a0 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -436,6 +436,10 @@ private: /// simple-type-specifier. void ParseCXXSimpleTypeSpecifier(DeclSpec &DS); + //===--------------------------------------------------------------------===// + // C++ if/switch/while/for condition expression. + ExprResult ParseCXXCondition(); + //===--------------------------------------------------------------------===// // C99 6.7.8: Initialization. ExprResult ParseInitializer(); diff --git a/include/clang/Parse/Scope.h b/include/clang/Parse/Scope.h index 30e70f8276..d0626ffc90 100644 --- a/include/clang/Parse/Scope.h +++ b/include/clang/Parse/Scope.h @@ -44,14 +44,17 @@ public: /// just contain loop constructs but don't contain decls. DeclScope = 0x08, + /// ControlScope - The controlling scope in a if/switch/while/for statement. + ControlScope = 0x10, + /// CXXClassScope - The scope of a C++ struct/union/class definition. - CXXClassScope = 0x10, + CXXClassScope = 0x20, /// BlockScope - This is a scope that corresponds to a block object. /// Blocks serve as top-level scopes for some objects like labels, they /// also prevent things like break and continue. BlockScopes have the /// other flags set as well. - BlockScope = 0x20 + BlockScope = 0x40 }; private: /// The parent scope for this scope. This is null for the translation-unit diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index bd8e7d510e..28071b6e29 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -153,6 +153,55 @@ Parser::ExprResult Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) { &CommaLocs[0], RParenLoc); } +/// ParseCXXCondition - if/switch/while/for condition expression. +/// +/// condition: +/// expression +/// type-specifier-seq declarator '=' assignment-expression +/// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt] +/// '=' assignment-expression +/// +Parser::ExprResult Parser::ParseCXXCondition() { + if (!isDeclarationSpecifier()) + return ParseExpression(); // expression + + SourceLocation StartLoc = Tok.getLocation(); + + // type-specifier-seq + DeclSpec DS; + ParseSpecifierQualifierList(DS); + + // declarator + Declarator DeclaratorInfo(DS, Declarator::ConditionContext); + ParseDeclarator(DeclaratorInfo); + + // simple-asm-expr[opt] + if (Tok.is(tok::kw_asm)) { + ExprResult AsmLabel = ParseSimpleAsm(); + if (AsmLabel.isInvalid) { + SkipUntil(tok::semi); + return true; + } + DeclaratorInfo.setAsmLabel(AsmLabel.Val); + } + + // If attributes are present, parse them. + if (Tok.is(tok::kw___attribute)) + DeclaratorInfo.AddAttributes(ParseAttributes()); + + // '=' assignment-expression + if (Tok.isNot(tok::equal)) + return Diag(Tok, diag::err_expected_equal_after_declarator); + SourceLocation EqualLoc = ConsumeToken(); + ExprResult AssignExpr = ParseAssignmentExpression(); + if (AssignExpr.isInvalid) + return true; + + return Actions.ActOnCXXConditionDeclarationExpr(CurScope, StartLoc, + DeclaratorInfo, + EqualLoc, AssignExpr.Val); +} + /// ParseCXXSimpleTypeSpecifier - [C++ 7.1.5.2] Simple type specifiers. /// This should only be called when the current token is known to be part of /// simple-type-specifier. diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 5b2d3a3e0f..83ec302fc4 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -417,6 +417,8 @@ Parser::StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { /// if-statement: [C99 6.8.4.1] /// 'if' '(' expression ')' statement /// 'if' '(' expression ')' statement 'else' statement +/// [C++] 'if' '(' condition ')' statement +/// [C++] 'if' '(' condition ')' statement 'else' statement /// Parser::StmtResult Parser::ParseIfStatement() { assert(Tok.is(tok::kw_if) && "Not an if stmt!"); @@ -427,17 +429,27 @@ Parser::StmtResult Parser::ParseIfStatement() { SkipUntil(tok::semi); return true; } - + + bool C99orCXX = getLang().C99 || getLang().CPlusPlus; + // C99 6.8.4p3 - In C99, the if statement is a block. This is not // the case for C90. - if (getLang().C99) - EnterScope(Scope::DeclScope); + if (C99orCXX) + EnterScope(Scope::DeclScope | Scope::ControlScope); // Parse the condition. - ExprResult CondExp = ParseSimpleParenExpression(); + ExprResult CondExp; + if (getLang().CPlusPlus) { + SourceLocation LParenLoc = ConsumeParen(); + CondExp = ParseCXXCondition(); + MatchRHSPunctuation(tok::r_paren, LParenLoc); + } else { + CondExp = ParseSimpleParenExpression(); + } + if (CondExp.isInvalid) { SkipUntil(tok::semi); - if (getLang().C99) + if (C99orCXX) ExitScope(); return true; } @@ -445,9 +457,9 @@ Parser::StmtResult Parser::ParseIfStatement() { // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. - bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + bool NeedsInnerScope = C99orCXX && Tok.isNot(tok::l_brace); if (NeedsInnerScope) EnterScope(Scope::DeclScope); - + // Read the 'then' stmt. SourceLocation ThenStmtLoc = Tok.getLocation(); StmtResult ThenStmt = ParseStatement(); @@ -467,7 +479,7 @@ Parser::StmtResult Parser::ParseIfStatement() { // there is no compound stmt. C90 does not have this clause. We only do // this if the body isn't a compound statement to avoid push/pop in common // cases. - NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + NeedsInnerScope = C99orCXX && Tok.isNot(tok::l_brace); if (NeedsInnerScope) EnterScope(Scope::DeclScope); ElseStmtLoc = Tok.getLocation(); @@ -477,7 +489,7 @@ Parser::StmtResult Parser::ParseIfStatement() { if (NeedsInnerScope) ExitScope(); } - if (getLang().C99) + if (C99orCXX) ExitScope(); // If the then or else stmt is invalid and the other is valid (and present), @@ -505,6 +517,7 @@ Parser::StmtResult Parser::ParseIfStatement() { /// ParseSwitchStatement /// switch-statement: /// 'switch' '(' expression ')' statement +/// [C++] 'switch' '(' condition ')' statement Parser::StmtResult Parser::ParseSwitchStatement() { assert(Tok.is(tok::kw_switch) && "Not a switch stmt!"); SourceLocation SwitchLoc = ConsumeToken(); // eat the 'switch'. @@ -515,15 +528,24 @@ Parser::StmtResult Parser::ParseSwitchStatement() { return true; } + bool C99orCXX = getLang().C99 || getLang().CPlusPlus; + // C99 6.8.4p3 - In C99, the switch statement is a block. This is // not the case for C90. Start the switch scope. - if (getLang().C99) - EnterScope(Scope::BreakScope|Scope::DeclScope); + if (C99orCXX) + EnterScope(Scope::BreakScope | Scope::DeclScope | Scope::ControlScope); else EnterScope(Scope::BreakScope); // Parse the condition. - ExprResult Cond = ParseSimpleParenExpression(); + ExprResult Cond; + if (getLang().CPlusPlus) { + SourceLocation LParenLoc = ConsumeParen(); + Cond = ParseCXXCondition(); + MatchRHSPunctuation(tok::r_paren, LParenLoc); + } else { + Cond = ParseSimpleParenExpression(); + } if (Cond.isInvalid) { ExitScope(); @@ -535,7 +557,7 @@ Parser::StmtResult Parser::ParseSwitchStatement() { // C99 6.8.4p3 - In C99, the body of the switch statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. - bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + bool NeedsInnerScope = C99orCXX && Tok.isNot(tok::l_brace); if (NeedsInnerScope) EnterScope(Scope::DeclScope); // Read the body statement. @@ -557,6 +579,7 @@ Parser::StmtResult Parser::ParseSwitchStatement() { /// ParseWhileStatement /// while-statement: [C99 6.8.5.1] /// 'while' '(' expression ')' statement +/// [C++] 'while' '(' condition ')' statement Parser::StmtResult Parser::ParseWhileStatement() { assert(Tok.is(tok::kw_while) && "Not a while stmt!"); SourceLocation WhileLoc = Tok.getLocation(); @@ -568,20 +591,30 @@ Parser::StmtResult Parser::ParseWhileStatement() { return true; } + bool C99orCXX = getLang().C99 || getLang().CPlusPlus; + // C99 6.8.5p5 - In C99, the while statement is a block. This is not // the case for C90. Start the loop scope. - if (getLang().C99) - EnterScope(Scope::BreakScope | Scope::ContinueScope | Scope::DeclScope); + if (C99orCXX) + EnterScope(Scope::BreakScope | Scope::ContinueScope | + Scope::DeclScope | Scope::ControlScope); else EnterScope(Scope::BreakScope | Scope::ContinueScope); // Parse the condition. - ExprResult Cond = ParseSimpleParenExpression(); + ExprResult Cond; + if (getLang().CPlusPlus) { + SourceLocation LParenLoc = ConsumeParen(); + Cond = ParseCXXCondition(); + MatchRHSPunctuation(tok::r_paren, LParenLoc); + } else { + Cond = ParseSimpleParenExpression(); + } // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. - bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + bool NeedsInnerScope = C99orCXX && Tok.isNot(tok::l_brace); if (NeedsInnerScope) EnterScope(Scope::DeclScope); // Read the body statement. @@ -654,8 +687,15 @@ Parser::StmtResult Parser::ParseDoStatement() { /// for-statement: [C99 6.8.5.3] /// 'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement /// 'for' '(' declaration expr[opt] ';' expr[opt] ')' statement +/// [C++] 'for' '(' for-init-statement condition[opt] ';' expression[opt] ')' +/// [C++] statement /// [OBJC2] 'for' '(' declaration 'in' expr ')' statement /// [OBJC2] 'for' '(' expr 'in' expr ')' statement +/// +/// [C++] for-init-statement: +/// [C++] expression-statement +/// [C++] simple-declaration +/// Parser::StmtResult Parser::ParseForStatement() { assert(Tok.is(tok::kw_for) && "Not a for stmt!"); SourceLocation ForLoc = ConsumeToken(); // eat the 'for'. @@ -666,10 +706,13 @@ Parser::StmtResult Parser::ParseForStatement() { return true; } + bool C99orCXX = getLang().C99 || getLang().CPlusPlus; + // C99 6.8.5p5 - In C99, the for statement is a block. This is not // the case for C90. Start the loop scope. - if (getLang().C99) - EnterScope(Scope::BreakScope | Scope::ContinueScope | Scope::DeclScope); + if (C99orCXX) + EnterScope(Scope::BreakScope | Scope::ContinueScope | + Scope::DeclScope | Scope::ControlScope); else EnterScope(Scope::BreakScope | Scope::ContinueScope); @@ -687,11 +730,11 @@ Parser::StmtResult Parser::ParseForStatement() { ConsumeToken(); } else if (isDeclarationSpecifier()) { // for (int X = 4; // Parse declaration, which eats the ';'. - if (!getLang().C99) // Use of C99-style for loops in C90 mode? + if (!C99orCXX) // Use of C99-style for loops in C90 mode? Diag(Tok, diag::ext_c99_variable_decl_in_for_loop); SourceLocation DeclStart = Tok.getLocation(); - DeclTy *aBlockVarDecl = ParseDeclaration(Declarator::ForContext); + DeclTy *aBlockVarDecl = ParseSimpleDeclaration(Declarator::ForContext); // FIXME: Pass in the right location for the end of the declstmt. StmtResult stmtResult = Actions.ActOnDeclStmt(aBlockVarDecl, DeclStart, DeclStart); @@ -732,7 +775,8 @@ Parser::StmtResult Parser::ParseForStatement() { // no second part. Value = ExprResult(); } else { - Value = ParseExpression(); + Value = getLang().CPlusPlus ? ParseCXXCondition() + : ParseExpression(); if (!Value.isInvalid) SecondPart = Value.Val; } @@ -764,7 +808,7 @@ Parser::StmtResult Parser::ParseForStatement() { // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. - bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + bool NeedsInnerScope = C99orCXX && Tok.isNot(tok::l_brace); if (NeedsInnerScope) EnterScope(Scope::DeclScope); // Read the body statement. diff --git a/test/Parser/cxx-condition.cpp b/test/Parser/cxx-condition.cpp new file mode 100644 index 0000000000..ccb7767173 --- /dev/null +++ b/test/Parser/cxx-condition.cpp @@ -0,0 +1,11 @@ +// RUN: clang -parse-noop %s -verify + +void f() { + int a; + while (a) ; + while (int x) ; // expected-error {{expected '=' after declarator}} + while (float x = 0) ; + if (const int x = a) ; + switch (int x = a+10) {} + for (; int x = ++a; ) ; +} -- 2.40.0