diff options
| author | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-06-29 21:17:59 +0000 |
|---|---|---|
| committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-06-29 21:17:59 +0000 |
| commit | c7a05a9f4d589f562c4fd043fb8e605e536b0dec (patch) | |
| tree | 582b215d2a5518247b9a0fe1125579511d89f645 /clang/lib | |
| parent | 03c38af1097a4da22f39cec4cb16d0fcbd935dd6 (diff) | |
| download | bcm5719-llvm-c7a05a9f4d589f562c4fd043fb8e605e536b0dec.tar.gz bcm5719-llvm-c7a05a9f4d589f562c4fd043fb8e605e536b0dec.zip | |
P0305R1: Parsing support for init-statements in 'if' and 'switch' statements.
No semantic analysis yet.
This is a pain to disambiguate correctly, because the parsing rules for the
declaration form of a condition and of an init-statement are quite different --
for a token sequence that looks like a declaration, we frequently need to
disambiguate all the way to the ')' or ';'.
We could do better here in some cases by stopping disambiguation once we've
decided whether we've got an expression or not (rather than keeping going until
we know whether it's an init-statement declaration or a condition declaration),
by unifying our parsing code for the two types of declaration and moving the
syntactic checks into Sema; if this has a measurable impact on parsing
performance, I'll look into that.
llvm-svn: 274169
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 3 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 35 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseStmt.cpp | 28 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 145 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaStmt.cpp | 13 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaType.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/Sema/TreeTransform.h | 5 |
7 files changed, 180 insertions, 53 deletions
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index b7a253156f2..65d7a9b8fd7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2075,7 +2075,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( if (Init.isInvalid()) { SmallVector<tok::TokenKind, 2> StopTokens; StopTokens.push_back(tok::comma); - if (D.getContext() == Declarator::ForContext) + if (D.getContext() == Declarator::ForContext || + D.getContext() == Declarator::InitStmtContext) StopTokens.push_back(tok::r_paren); SkipUntil(StopTokens, StopAtSemi | StopBeforeMatch); Actions.ActOnInitializerError(ThisDecl); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 8a6626b9f17..b09d7dbc12f 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1726,11 +1726,18 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) { /// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt] /// '=' assignment-expression /// +/// In C++1z, a condition may in some contexts be preceded by an +/// optional init-statement. This function will parse that too. +/// +/// \param InitStmt If non-null, an init-statement is permitted, and if present +/// will be parsed and stored here. +/// /// \param Loc The location of the start of the statement that requires this /// condition, e.g., the "for" in a for loop. /// /// \returns The parsed condition. -Sema::ConditionResult Parser::ParseCXXCondition(SourceLocation Loc, +Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt, + SourceLocation Loc, Sema::ConditionKind CK) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Condition); @@ -1741,7 +1748,9 @@ Sema::ConditionResult Parser::ParseCXXCondition(SourceLocation Loc, ParsedAttributesWithRange attrs(AttrFactory); MaybeParseCXX11Attributes(attrs); - if (!isCXXConditionDeclaration()) { + // Determine what kind of thing we have. + switch (isCXXConditionDeclarationOrInitStatement(InitStmt)) { + case ConditionOrInitStatement::Expression: { ProhibitAttributes(attrs); // Parse the expression. @@ -1749,9 +1758,28 @@ Sema::ConditionResult Parser::ParseCXXCondition(SourceLocation Loc, if (Expr.isInvalid()) return Sema::ConditionError(); + if (InitStmt && Tok.is(tok::semi)) { + *InitStmt = Actions.ActOnExprStmt(Expr.get()); + ConsumeToken(); + return ParseCXXCondition(nullptr, Loc, CK); + } + return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK); } + case ConditionOrInitStatement::InitStmtDecl: { + SourceLocation DeclStart = Tok.getLocation(), DeclEnd; + DeclGroupPtrTy DG = ParseSimpleDeclaration( + Declarator::InitStmtContext, DeclEnd, attrs, /*RequireSemi=*/true); + *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd); + return ParseCXXCondition(nullptr, Loc, CK); + } + + case ConditionOrInitStatement::ConditionDecl: + case ConditionOrInitStatement::Error: + break; + } + // type-specifier-seq DeclSpec DS(AttrFactory); DS.takeAttributesFrom(attrs); @@ -1814,9 +1842,6 @@ Sema::ConditionResult Parser::ParseCXXCondition(SourceLocation Loc, else Actions.ActOnInitializerError(DeclOut); - // FIXME: Build a reference to this declaration? Convert it to bool? - // (This is currently handled by Sema). - Actions.FinalizeDeclaration(DeclOut); return Actions.ActOnConditionVariable(DeclOut, Loc, CK); } diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 5ca99339008..fa8eb12044b 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1043,7 +1043,8 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { /// ParseParenExprOrCondition: /// [C ] '(' expression ')' -/// [C++] '(' condition ')' [not allowed if OnlyAllowCondition=true] +/// [C++] '(' condition ')' +/// [C++1z] '(' init-statement[opt] condition ')' /// /// This function parses and performs error recovery on the specified condition /// or expression (depending on whether we're in C++ or C mode). This function @@ -1052,14 +1053,15 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { /// should try to recover harder. It returns false if the condition is /// successfully parsed. Note that a successful parse can still have semantic /// errors in the condition. -bool Parser::ParseParenExprOrCondition(Sema::ConditionResult &Cond, +bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt, + Sema::ConditionResult &Cond, SourceLocation Loc, Sema::ConditionKind CK) { BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); if (getLangOpts().CPlusPlus) - Cond = ParseCXXCondition(Loc, CK); + Cond = ParseCXXCondition(InitStmt, Loc, CK); else { ExprResult CondExpr = ParseExpression(); @@ -1139,8 +1141,9 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { ParseScope IfScope(this, Scope::DeclScope | Scope::ControlScope, C99orCXX); // Parse the condition. + StmtResult InitStmt; Sema::ConditionResult Cond; - if (ParseParenExprOrCondition(Cond, IfLoc, + if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc, IsConstexpr ? Sema::ConditionKind::ConstexprIf : Sema::ConditionKind::Boolean)) return StmtError(); @@ -1241,8 +1244,8 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { if (ElseStmt.isInvalid()) ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc); - return Actions.ActOnIfStmt(IfLoc, IsConstexpr, Cond, ThenStmt.get(), ElseLoc, - ElseStmt.get()); + return Actions.ActOnIfStmt(IfLoc, IsConstexpr, InitStmt.get(), Cond, + ThenStmt.get(), ElseLoc, ElseStmt.get()); } /// ParseSwitchStatement @@ -1279,11 +1282,14 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) { ParseScope SwitchScope(this, ScopeFlags); // Parse the condition. + StmtResult InitStmt; Sema::ConditionResult Cond; - if (ParseParenExprOrCondition(Cond, SwitchLoc, Sema::ConditionKind::Switch)) + if (ParseParenExprOrCondition(&InitStmt, Cond, SwitchLoc, + Sema::ConditionKind::Switch)) return StmtError(); - StmtResult Switch = Actions.ActOnStartOfSwitchStmt(SwitchLoc, Cond); + StmtResult Switch = + Actions.ActOnStartOfSwitchStmt(SwitchLoc, InitStmt.get(), Cond); if (Switch.isInvalid()) { // Skip the switch body. @@ -1366,7 +1372,8 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) { // Parse the condition. Sema::ConditionResult Cond; - if (ParseParenExprOrCondition(Cond, WhileLoc, Sema::ConditionKind::Boolean)) + if (ParseParenExprOrCondition(nullptr, Cond, WhileLoc, + Sema::ConditionKind::Boolean)) return StmtError(); // C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if @@ -1681,7 +1688,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { // missing both semicolons. } else { if (getLangOpts().CPlusPlus) - SecondPart = ParseCXXCondition(ForLoc, Sema::ConditionKind::Boolean); + SecondPart = + ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean); else { ExprResult SecondExpr = ParseExpression(); if (SecondExpr.isInvalid()) diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index c79208a0019..7703c33b878 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -330,10 +330,70 @@ Parser::TPResult Parser::TryParseInitDeclaratorList() { return TPResult::Ambiguous; } -/// isCXXConditionDeclaration - Disambiguates between a declaration or an -/// expression for a condition of a if/switch/while/for statement. -/// If during the disambiguation process a parsing error is encountered, -/// the function returns true to let the declaration parsing code handle it. +struct Parser::ConditionDeclarationOrInitStatementState { + Parser &P; + bool CanBeExpression = true; + bool CanBeCondition = true; + bool CanBeInitStatement; + + ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement) + : P(P), CanBeInitStatement(CanBeInitStatement) {} + + void markNotExpression() { + CanBeExpression = false; + + if (CanBeCondition && CanBeInitStatement) { + // FIXME: Unify the parsing codepaths for condition variables and + // simple-declarations so that we don't need to eagerly figure out which + // kind we have here. (Just parse init-declarators until we reach a + // semicolon or right paren.) + RevertingTentativeParsingAction PA(P); + P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch); + if (P.Tok.isNot(tok::r_paren)) + CanBeCondition = false; + if (P.Tok.isNot(tok::semi)) + CanBeInitStatement = false; + } + } + + bool markNotCondition() { + CanBeCondition = false; + return !CanBeInitStatement || !CanBeExpression; + } + + bool update(TPResult IsDecl) { + switch (IsDecl) { + case TPResult::True: + markNotExpression(); + return true; + case TPResult::False: + CanBeCondition = CanBeInitStatement = false; + return true; + case TPResult::Ambiguous: + return false; + case TPResult::Error: + CanBeExpression = CanBeCondition = CanBeInitStatement = false; + return true; + } + llvm_unreachable("unknown tentative parse result"); + } + + ConditionOrInitStatement result() const { + assert(CanBeExpression + CanBeCondition + CanBeInitStatement < 2 && + "result called but not yet resolved"); + if (CanBeExpression) + return ConditionOrInitStatement::Expression; + if (CanBeCondition) + return ConditionOrInitStatement::ConditionDecl; + if (CanBeInitStatement) + return ConditionOrInitStatement::InitStmtDecl; + return ConditionOrInitStatement::Error; + } +}; + +/// \brief Disambiguates between a declaration in a condition, a +/// simple-declaration in an init-statement, and an expression for +/// a condition of a if/switch statement. /// /// condition: /// expression @@ -342,45 +402,64 @@ Parser::TPResult Parser::TryParseInitDeclaratorList() { /// [C++11] type-specifier-seq declarator braced-init-list /// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt] /// '=' assignment-expression +/// simple-declaration: +/// decl-specifier-seq init-declarator-list[opt] ';' /// -bool Parser::isCXXConditionDeclaration() { - TPResult TPR = isCXXDeclarationSpecifier(); - if (TPR != TPResult::Ambiguous) - return TPR != TPResult::False; // Returns true for TPResult::True or - // TPResult::Error. +/// Note that, unlike isCXXSimpleDeclaration, we must disambiguate all the way +/// to the ';' to disambiguate cases like 'int(x))' (an expression) from +/// 'int(x);' (a simple-declaration in an init-statement). +Parser::ConditionOrInitStatement +Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement) { + ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement); - // FIXME: Add statistics about the number of ambiguous statements encountered - // and how they were resolved (number of declarations+number of expressions). - - // Ok, we have a simple-type-specifier/typename-specifier followed by a '('. - // We need tentative parsing... + if (State.update(isCXXDeclarationSpecifier())) + return State.result(); + // It might be a declaration; we need tentative parsing. RevertingTentativeParsingAction PA(*this); - // type-specifier-seq - TryConsumeDeclarationSpecifier(); + // FIXME: A tag definition unambiguously tells us this is an init-statement. + if (State.update(TryConsumeDeclarationSpecifier())) + return State.result(); assert(Tok.is(tok::l_paren) && "Expected '('"); - // declarator - TPR = TryParseDeclarator(false/*mayBeAbstract*/); + while (true) { + // Consume a declarator. + if (State.update(TryParseDeclarator(false/*mayBeAbstract*/))) + return State.result(); + + // Attributes, asm label, or an initializer imply this is not an expression. + // FIXME: Disambiguate properly after an = instead of assuming that it's a + // valid declaration. + if (Tok.isOneOf(tok::equal, tok::kw_asm, tok::kw___attribute) || + (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace))) { + State.markNotExpression(); + return State.result(); + } - // In case of an error, let the declaration parsing code handle it. - if (TPR == TPResult::Error) - TPR = TPResult::True; + // At this point, it can't be a condition any more, because a condition + // must have a brace-or-equal-initializer. + if (State.markNotCondition()) + return State.result(); - if (TPR == TPResult::Ambiguous) { - // '=' - // [GNU] simple-asm-expr[opt] attributes[opt] - if (Tok.isOneOf(tok::equal, tok::kw_asm, tok::kw___attribute)) - TPR = TPResult::True; - else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) - TPR = TPResult::True; - else - TPR = TPResult::False; + // A parenthesized initializer could be part of an expression or a + // simple-declaration. + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + SkipUntil(tok::r_paren, StopAtSemi); + } + + if (!TryConsumeToken(tok::comma)) + break; } - assert(TPR == TPResult::True || TPR == TPResult::False); - return TPR == TPResult::True; + // We reached the end. If it can now be some kind of decl, then it is. + if (State.CanBeCondition && Tok.is(tok::r_paren)) + return ConditionOrInitStatement::ConditionDecl; + else if (State.CanBeInitStatement && Tok.is(tok::semi)) + return ConditionOrInitStatement::InitStmtDecl; + else + return ConditionOrInitStatement::Expression; } /// \brief Determine whether the next set of tokens contains a type-id. @@ -1329,6 +1408,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, *HasMissingTypename = true; return TPResult::Ambiguous; } + + // FIXME: Fails to either revert or commit the tentative parse! } else { // Try to resolve the name. If it doesn't exist, assume it was // intended to name a type and keep disambiguating. diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 5432a709444..61ef8f3a81c 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -504,9 +504,13 @@ public: } StmtResult -Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, ConditionResult Cond, +Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt, + ConditionResult Cond, Stmt *thenStmt, SourceLocation ElseLoc, Stmt *elseStmt) { + if (InitStmt) + Diag(InitStmt->getLocStart(), diag::err_init_stmt_not_supported); + if (Cond.isInvalid()) Cond = ConditionResult( *this, nullptr, @@ -659,11 +663,14 @@ ExprResult Sema::CheckSwitchCondition(SourceLocation SwitchLoc, Expr *Cond) { return UsualUnaryConversions(CondResult.get()); } -StmtResult -Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, ConditionResult Cond) { +StmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, + Stmt *InitStmt, ConditionResult Cond) { if (Cond.isInvalid()) return StmtError(); + if (InitStmt) + Diag(InitStmt->getLocStart(), diag::err_init_stmt_not_supported); + getCurFunction()->setHasBranchIntoScope(); SwitchStmt *SS = diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 84ebb9bc866..d58de8a3822 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2883,6 +2883,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case Declarator::FileContext: case Declarator::BlockContext: case Declarator::ForContext: + case Declarator::InitStmtContext: case Declarator::ConditionContext: break; case Declarator::CXXNewContext: @@ -2968,6 +2969,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case Declarator::MemberContext: case Declarator::BlockContext: case Declarator::ForContext: + case Declarator::InitStmtContext: case Declarator::BlockLiteralContext: case Declarator::LambdaExprContext: // C++11 [dcl.type]p3: @@ -3711,6 +3713,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case Declarator::CXXCatchContext: case Declarator::CXXNewContext: case Declarator::ForContext: + case Declarator::InitStmtContext: case Declarator::LambdaExprContext: case Declarator::LambdaExprParameterContext: case Declarator::ObjCCatchContext: @@ -4523,6 +4526,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case Declarator::MemberContext: case Declarator::BlockContext: case Declarator::ForContext: + case Declarator::InitStmtContext: case Declarator::ConditionContext: case Declarator::CXXCatchContext: case Declarator::ObjCCatchContext: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index fbfc078e988..c32827fa25c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1176,7 +1176,8 @@ public: StmtResult RebuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, Sema::ConditionResult Cond, Stmt *Then, SourceLocation ElseLoc, Stmt *Else) { - return getSema().ActOnIfStmt(IfLoc, IsConstexpr, Cond, Then, ElseLoc, Else); + return getSema().ActOnIfStmt(IfLoc, IsConstexpr, nullptr, Cond, Then, + ElseLoc, Else); } /// \brief Start building a new switch statement. @@ -1185,7 +1186,7 @@ public: /// Subclasses may override this routine to provide different behavior. StmtResult RebuildSwitchStmtStart(SourceLocation SwitchLoc, Sema::ConditionResult Cond) { - return getSema().ActOnStartOfSwitchStmt(SwitchLoc, Cond); + return getSema().ActOnStartOfSwitchStmt(SwitchLoc, nullptr, Cond); } /// \brief Attach the body to the switch statement. |

