diff options
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. |