def note_empty_body_on_separate_line : Note<
"put the semicolon on a separate line to silence this warning">;
+def err_init_stmt_not_supported : Error<
+ "C++1z init-statement not yet supported">;
+
def err_va_start_used_in_non_variadic_function : Error<
"'va_start' used in function with fixed args">;
def err_va_start_used_in_wrong_abi_function : Error<
//===--------------------------------------------------------------------===//
// C++ if/switch/while condition expression.
- Sema::ConditionResult ParseCXXCondition(SourceLocation Loc,
+ Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
+ SourceLocation Loc,
Sema::ConditionKind CK);
//===--------------------------------------------------------------------===//
unsigned ScopeFlags);
void ParseCompoundStatementLeadingPragmas();
StmtResult ParseCompoundStatementBody(bool isStmtExpr = false);
- bool ParseParenExprOrCondition(Sema::ConditionResult &CondResult,
+ bool ParseParenExprOrCondition(StmtResult *InitStmt,
+ Sema::ConditionResult &CondResult,
SourceLocation Loc,
Sema::ConditionKind CK);
StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc);
/// the function returns true to let the declaration parsing code handle it.
bool isCXXFunctionDeclarator(bool *IsAmbiguous = nullptr);
- /// 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.
- bool isCXXConditionDeclaration();
+ struct ConditionDeclarationOrInitStatementState;
+ enum class ConditionOrInitStatement {
+ Expression, ///< Disambiguated as an expression (either kind).
+ ConditionDecl, ///< Disambiguated as the declaration form of condition.
+ InitStmtDecl, ///< Disambiguated as a simple-declaration init-statement.
+ Error ///< Can't be any of the above!
+ };
+ /// \brief Disambiguates between the different kinds of things that can happen
+ /// after 'if (' or 'switch ('. This could be one of two different kinds of
+ /// declaration (depending on whether there is a ';' later) or an expression.
+ ConditionOrInitStatement
+ isCXXConditionDeclarationOrInitStatement(bool CanBeInitStmt);
bool isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous);
bool isCXXTypeId(TentativeCXXTypeIdContext Context) {
MemberContext, // Struct/Union field.
BlockContext, // Declaration within a block in a function.
ForContext, // Declaration within first part of a for loop.
+ InitStmtContext, // Declaration within optional init stmt of if/switch.
ConditionContext, // Condition declaration in a C++ if/switch/while/for.
TemplateParamContext,// Within a template parameter list.
CXXNewContext, // C++ new-expression.
case MemberContext:
case BlockContext:
case ForContext:
+ case InitStmtContext:
case ConditionContext:
return false;
case MemberContext:
case BlockContext:
case ForContext:
+ case InitStmtContext:
case ConditionContext:
case PrototypeContext:
case LambdaExprParameterContext:
case MemberContext:
case BlockContext:
case ForContext:
+ case InitStmtContext:
case ConditionContext:
case PrototypeContext:
case LambdaExprParameterContext:
case FileContext:
case BlockContext:
case ForContext:
+ case InitStmtContext:
return true;
case ConditionContext:
case MemberContext:
case BlockContext:
case ForContext:
+ case InitStmtContext:
return true;
case ConditionContext:
class ConditionResult;
StmtResult ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
+ Stmt *InitStmt,
ConditionResult Cond, Stmt *ThenVal,
SourceLocation ElseLoc, Stmt *ElseVal);
StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
ConditionResult Cond, Stmt *ThenVal,
SourceLocation ElseLoc, Stmt *ElseVal);
StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
+ Stmt *InitStmt,
ConditionResult Cond);
StmtResult ActOnFinishSwitchStmt(SourceLocation SwitchLoc,
Stmt *Switch, Stmt *Body);
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);
/// [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);
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.
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);
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);
}
/// 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
/// 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();
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();
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
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.
// 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
// 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())
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
/// [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.
*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.
}
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,
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 =
case Declarator::FileContext:
case Declarator::BlockContext:
case Declarator::ForContext:
+ case Declarator::InitStmtContext:
case Declarator::ConditionContext:
break;
case Declarator::CXXNewContext:
case Declarator::MemberContext:
case Declarator::BlockContext:
case Declarator::ForContext:
+ case Declarator::InitStmtContext:
case Declarator::BlockLiteralContext:
case Declarator::LambdaExprContext:
// C++11 [dcl.type]p3:
case Declarator::CXXCatchContext:
case Declarator::CXXNewContext:
case Declarator::ForContext:
+ case Declarator::InitStmtContext:
case Declarator::LambdaExprContext:
case Declarator::LambdaExprParameterContext:
case Declarator::ObjCCatchContext:
case Declarator::MemberContext:
case Declarator::BlockContext:
case Declarator::ForContext:
+ case Declarator::InitStmtContext:
case Declarator::ConditionContext:
case Declarator::CXXCatchContext:
case Declarator::ObjCCatchContext:
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.
/// 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.
--- /dev/null
+// RUN: %clang_cc1 -std=c++1z -verify %s -Wno-vexing-parse
+
+int g, h;
+typedef int T;
+int f() {
+ // init-statement declarations
+ if (T n = 0; n != 0) {} // expected-error {{not yet supported}}
+ if (T f(); f()) {} // expected-error {{not yet supported}}
+ if (T(f()); f()) {} // expected-error {{not yet supported}}
+ if (T(f()), g, h; f()) {} // expected-error {{not yet supported}}
+ if (T f(); f()) {} // expected-error {{not yet supported}}
+ if (T f(), g, h; f()) {} // expected-error {{not yet supported}}
+
+ // init-statement expressions
+ if (T{f()}; f()) {} // expected-error {{not yet supported}}
+ if (T{f()}, g, h; f()) {} // expected-error {{not yet supported}} expected-warning 2{{unused}}
+ if (T(f()), g, h + 1; f()) {} // expected-error {{not yet supported}} expected-warning 2{{unused}}
+
+ // condition declarations
+ if (T(n){g}) {}
+ if (T f()) {} // expected-error {{function type}}
+ if (T f(), g, h) {} // expected-error {{function type}}
+
+ // condition expressions
+ if (T(f())) {}
+ if (T{f()}) {}
+ if (T(f()), g, h) {} // expected-warning 2{{unused}}
+ if (T{f()}, g, h) {} // expected-warning 2{{unused}}
+
+ // none of the above
+ // FIXME: This causes a typo-correction crash, as does "void f() { +T(n)(g); }"
+ //if (T(n)(g)) {} // expected-err-FIXME {{not a function}}
+
+ // Likewise for 'switch'
+ switch (int n; n) {} // expected-error {{not yet supported}}
+ switch (g; int g = 5) {} // expected-error {{not yet supported}}
+
+ if (int a, b; int c = a) { // expected-error {{not yet supported}} expected-note 6{{previous}}
+ int a; // expected-error {{redefinition}}
+ int b; // expected-error {{redefinition}}
+ int c; // expected-error {{redefinition}}
+ } else {
+ int a; // expected-error {{redefinition}}
+ int b; // expected-error {{redefinition}}
+ int c; // expected-error {{redefinition}}
+ }
+}
void test1(int a;) { // expected-error{{unexpected ';' before ')'}}
while (a > 5;) {} // expected-error{{unexpected ';' before ')'}}
- if (int b = 10;) {} // expected-error{{unexpected ';' before ')'}}
for (int c = 0; c < 21; ++c;) {} // expected-error{{unexpected ';' before ')'}}
int d = int(3 + 4;); // expected-error{{unexpected ';' before ')'}}
int e[5;]; // expected-error{{unexpected ';' before ']'}}