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.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@274169 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2016-06-29 21:17:59 +00:00
parent 21ae2c7c2e
commit 24d543be36
13 changed files with 254 additions and 61 deletions

View File

@ -7499,6 +7499,9 @@ def warn_empty_switch_body : Warning<
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<

View File

@ -1598,7 +1598,8 @@ private:
//===--------------------------------------------------------------------===//
// C++ if/switch/while condition expression.
Sema::ConditionResult ParseCXXCondition(SourceLocation Loc,
Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
SourceLocation Loc,
Sema::ConditionKind CK);
//===--------------------------------------------------------------------===//
@ -1690,7 +1691,8 @@ private:
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);
@ -1984,11 +1986,18 @@ private:
/// 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) {

View File

@ -1632,6 +1632,7 @@ public:
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.
@ -1810,6 +1811,7 @@ public:
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
case ConditionContext:
return false;
@ -1844,6 +1846,7 @@ public:
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
case ConditionContext:
case PrototypeContext:
case LambdaExprParameterContext:
@ -1877,6 +1880,7 @@ public:
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
case ConditionContext:
case PrototypeContext:
case LambdaExprParameterContext:
@ -1921,6 +1925,7 @@ public:
case FileContext:
case BlockContext:
case ForContext:
case InitStmtContext:
return true;
case ConditionContext:
@ -2120,6 +2125,7 @@ public:
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
return true;
case ConditionContext:

View File

@ -3397,12 +3397,14 @@ public:
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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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())

View File

@ -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();
// In case of an error, let the declaration parsing code handle it.
if (TPR == TPResult::Error)
TPR = TPResult::True;
// 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();
}
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;
// 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();
// 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.

View File

@ -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 =

View File

@ -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:

View File

@ -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.

View File

@ -0,0 +1,47 @@
// 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}}
}
}

View File

@ -5,7 +5,6 @@
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 ']'}}