Skip to content

Commit c7a05a9

Browse files
committed
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
1 parent 03c38af commit c7a05a9

13 files changed

+254
-61
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -7499,6 +7499,9 @@ def warn_empty_switch_body : Warning<
74997499
def note_empty_body_on_separate_line : Note<
75007500
"put the semicolon on a separate line to silence this warning">;
75017501

7502+
def err_init_stmt_not_supported : Error<
7503+
"C++1z init-statement not yet supported">;
7504+
75027505
def err_va_start_used_in_non_variadic_function : Error<
75037506
"'va_start' used in function with fixed args">;
75047507
def err_va_start_used_in_wrong_abi_function : Error<

clang/include/clang/Parse/Parser.h

+16-7
Original file line numberDiff line numberDiff line change
@@ -1598,7 +1598,8 @@ class Parser : public CodeCompletionHandler {
15981598

15991599
//===--------------------------------------------------------------------===//
16001600
// C++ if/switch/while condition expression.
1601-
Sema::ConditionResult ParseCXXCondition(SourceLocation Loc,
1601+
Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
1602+
SourceLocation Loc,
16021603
Sema::ConditionKind CK);
16031604

16041605
//===--------------------------------------------------------------------===//
@@ -1690,7 +1691,8 @@ class Parser : public CodeCompletionHandler {
16901691
unsigned ScopeFlags);
16911692
void ParseCompoundStatementLeadingPragmas();
16921693
StmtResult ParseCompoundStatementBody(bool isStmtExpr = false);
1693-
bool ParseParenExprOrCondition(Sema::ConditionResult &CondResult,
1694+
bool ParseParenExprOrCondition(StmtResult *InitStmt,
1695+
Sema::ConditionResult &CondResult,
16941696
SourceLocation Loc,
16951697
Sema::ConditionKind CK);
16961698
StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc);
@@ -1984,11 +1986,18 @@ class Parser : public CodeCompletionHandler {
19841986
/// the function returns true to let the declaration parsing code handle it.
19851987
bool isCXXFunctionDeclarator(bool *IsAmbiguous = nullptr);
19861988

1987-
/// isCXXConditionDeclaration - Disambiguates between a declaration or an
1988-
/// expression for a condition of a if/switch/while/for statement.
1989-
/// If during the disambiguation process a parsing error is encountered,
1990-
/// the function returns true to let the declaration parsing code handle it.
1991-
bool isCXXConditionDeclaration();
1989+
struct ConditionDeclarationOrInitStatementState;
1990+
enum class ConditionOrInitStatement {
1991+
Expression, ///< Disambiguated as an expression (either kind).
1992+
ConditionDecl, ///< Disambiguated as the declaration form of condition.
1993+
InitStmtDecl, ///< Disambiguated as a simple-declaration init-statement.
1994+
Error ///< Can't be any of the above!
1995+
};
1996+
/// \brief Disambiguates between the different kinds of things that can happen
1997+
/// after 'if (' or 'switch ('. This could be one of two different kinds of
1998+
/// declaration (depending on whether there is a ';' later) or an expression.
1999+
ConditionOrInitStatement
2000+
isCXXConditionDeclarationOrInitStatement(bool CanBeInitStmt);
19922001

19932002
bool isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous);
19942003
bool isCXXTypeId(TentativeCXXTypeIdContext Context) {

clang/include/clang/Sema/DeclSpec.h

+6
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,7 @@ class Declarator {
16321632
MemberContext, // Struct/Union field.
16331633
BlockContext, // Declaration within a block in a function.
16341634
ForContext, // Declaration within first part of a for loop.
1635+
InitStmtContext, // Declaration within optional init stmt of if/switch.
16351636
ConditionContext, // Condition declaration in a C++ if/switch/while/for.
16361637
TemplateParamContext,// Within a template parameter list.
16371638
CXXNewContext, // C++ new-expression.
@@ -1810,6 +1811,7 @@ class Declarator {
18101811
case MemberContext:
18111812
case BlockContext:
18121813
case ForContext:
1814+
case InitStmtContext:
18131815
case ConditionContext:
18141816
return false;
18151817

@@ -1844,6 +1846,7 @@ class Declarator {
18441846
case MemberContext:
18451847
case BlockContext:
18461848
case ForContext:
1849+
case InitStmtContext:
18471850
case ConditionContext:
18481851
case PrototypeContext:
18491852
case LambdaExprParameterContext:
@@ -1877,6 +1880,7 @@ class Declarator {
18771880
case MemberContext:
18781881
case BlockContext:
18791882
case ForContext:
1883+
case InitStmtContext:
18801884
case ConditionContext:
18811885
case PrototypeContext:
18821886
case LambdaExprParameterContext:
@@ -1921,6 +1925,7 @@ class Declarator {
19211925
case FileContext:
19221926
case BlockContext:
19231927
case ForContext:
1928+
case InitStmtContext:
19241929
return true;
19251930

19261931
case ConditionContext:
@@ -2120,6 +2125,7 @@ class Declarator {
21202125
case MemberContext:
21212126
case BlockContext:
21222127
case ForContext:
2128+
case InitStmtContext:
21232129
return true;
21242130

21252131
case ConditionContext:

clang/include/clang/Sema/Sema.h

+2
Original file line numberDiff line numberDiff line change
@@ -3397,12 +3397,14 @@ class Sema {
33973397

33983398
class ConditionResult;
33993399
StmtResult ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
3400+
Stmt *InitStmt,
34003401
ConditionResult Cond, Stmt *ThenVal,
34013402
SourceLocation ElseLoc, Stmt *ElseVal);
34023403
StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
34033404
ConditionResult Cond, Stmt *ThenVal,
34043405
SourceLocation ElseLoc, Stmt *ElseVal);
34053406
StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
3407+
Stmt *InitStmt,
34063408
ConditionResult Cond);
34073409
StmtResult ActOnFinishSwitchStmt(SourceLocation SwitchLoc,
34083410
Stmt *Switch, Stmt *Body);

clang/lib/Parse/ParseDecl.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -2075,7 +2075,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
20752075
if (Init.isInvalid()) {
20762076
SmallVector<tok::TokenKind, 2> StopTokens;
20772077
StopTokens.push_back(tok::comma);
2078-
if (D.getContext() == Declarator::ForContext)
2078+
if (D.getContext() == Declarator::ForContext ||
2079+
D.getContext() == Declarator::InitStmtContext)
20792080
StopTokens.push_back(tok::r_paren);
20802081
SkipUntil(StopTokens, StopAtSemi | StopBeforeMatch);
20812082
Actions.ActOnInitializerError(ThisDecl);

clang/lib/Parse/ParseExprCXX.cpp

+30-5
Original file line numberDiff line numberDiff line change
@@ -1726,11 +1726,18 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
17261726
/// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt]
17271727
/// '=' assignment-expression
17281728
///
1729+
/// In C++1z, a condition may in some contexts be preceded by an
1730+
/// optional init-statement. This function will parse that too.
1731+
///
1732+
/// \param InitStmt If non-null, an init-statement is permitted, and if present
1733+
/// will be parsed and stored here.
1734+
///
17291735
/// \param Loc The location of the start of the statement that requires this
17301736
/// condition, e.g., the "for" in a for loop.
17311737
///
17321738
/// \returns The parsed condition.
1733-
Sema::ConditionResult Parser::ParseCXXCondition(SourceLocation Loc,
1739+
Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
1740+
SourceLocation Loc,
17341741
Sema::ConditionKind CK) {
17351742
if (Tok.is(tok::code_completion)) {
17361743
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Condition);
@@ -1741,17 +1748,38 @@ Sema::ConditionResult Parser::ParseCXXCondition(SourceLocation Loc,
17411748
ParsedAttributesWithRange attrs(AttrFactory);
17421749
MaybeParseCXX11Attributes(attrs);
17431750

1744-
if (!isCXXConditionDeclaration()) {
1751+
// Determine what kind of thing we have.
1752+
switch (isCXXConditionDeclarationOrInitStatement(InitStmt)) {
1753+
case ConditionOrInitStatement::Expression: {
17451754
ProhibitAttributes(attrs);
17461755

17471756
// Parse the expression.
17481757
ExprResult Expr = ParseExpression(); // expression
17491758
if (Expr.isInvalid())
17501759
return Sema::ConditionError();
17511760

1761+
if (InitStmt && Tok.is(tok::semi)) {
1762+
*InitStmt = Actions.ActOnExprStmt(Expr.get());
1763+
ConsumeToken();
1764+
return ParseCXXCondition(nullptr, Loc, CK);
1765+
}
1766+
17521767
return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK);
17531768
}
17541769

1770+
case ConditionOrInitStatement::InitStmtDecl: {
1771+
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
1772+
DeclGroupPtrTy DG = ParseSimpleDeclaration(
1773+
Declarator::InitStmtContext, DeclEnd, attrs, /*RequireSemi=*/true);
1774+
*InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
1775+
return ParseCXXCondition(nullptr, Loc, CK);
1776+
}
1777+
1778+
case ConditionOrInitStatement::ConditionDecl:
1779+
case ConditionOrInitStatement::Error:
1780+
break;
1781+
}
1782+
17551783
// type-specifier-seq
17561784
DeclSpec DS(AttrFactory);
17571785
DS.takeAttributesFrom(attrs);
@@ -1814,9 +1842,6 @@ Sema::ConditionResult Parser::ParseCXXCondition(SourceLocation Loc,
18141842
else
18151843
Actions.ActOnInitializerError(DeclOut);
18161844

1817-
// FIXME: Build a reference to this declaration? Convert it to bool?
1818-
// (This is currently handled by Sema).
1819-
18201845
Actions.FinalizeDeclaration(DeclOut);
18211846
return Actions.ActOnConditionVariable(DeclOut, Loc, CK);
18221847
}

clang/lib/Parse/ParseStmt.cpp

+18-10
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,8 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
10431043

10441044
/// ParseParenExprOrCondition:
10451045
/// [C ] '(' expression ')'
1046-
/// [C++] '(' condition ')' [not allowed if OnlyAllowCondition=true]
1046+
/// [C++] '(' condition ')'
1047+
/// [C++1z] '(' init-statement[opt] condition ')'
10471048
///
10481049
/// This function parses and performs error recovery on the specified condition
10491050
/// or expression (depending on whether we're in C++ or C mode). This function
@@ -1052,14 +1053,15 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
10521053
/// should try to recover harder. It returns false if the condition is
10531054
/// successfully parsed. Note that a successful parse can still have semantic
10541055
/// errors in the condition.
1055-
bool Parser::ParseParenExprOrCondition(Sema::ConditionResult &Cond,
1056+
bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
1057+
Sema::ConditionResult &Cond,
10561058
SourceLocation Loc,
10571059
Sema::ConditionKind CK) {
10581060
BalancedDelimiterTracker T(*this, tok::l_paren);
10591061
T.consumeOpen();
10601062

10611063
if (getLangOpts().CPlusPlus)
1062-
Cond = ParseCXXCondition(Loc, CK);
1064+
Cond = ParseCXXCondition(InitStmt, Loc, CK);
10631065
else {
10641066
ExprResult CondExpr = ParseExpression();
10651067

@@ -1139,8 +1141,9 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
11391141
ParseScope IfScope(this, Scope::DeclScope | Scope::ControlScope, C99orCXX);
11401142

11411143
// Parse the condition.
1144+
StmtResult InitStmt;
11421145
Sema::ConditionResult Cond;
1143-
if (ParseParenExprOrCondition(Cond, IfLoc,
1146+
if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc,
11441147
IsConstexpr ? Sema::ConditionKind::ConstexprIf
11451148
: Sema::ConditionKind::Boolean))
11461149
return StmtError();
@@ -1241,8 +1244,8 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
12411244
if (ElseStmt.isInvalid())
12421245
ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc);
12431246

1244-
return Actions.ActOnIfStmt(IfLoc, IsConstexpr, Cond, ThenStmt.get(), ElseLoc,
1245-
ElseStmt.get());
1247+
return Actions.ActOnIfStmt(IfLoc, IsConstexpr, InitStmt.get(), Cond,
1248+
ThenStmt.get(), ElseLoc, ElseStmt.get());
12461249
}
12471250

12481251
/// ParseSwitchStatement
@@ -1279,11 +1282,14 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) {
12791282
ParseScope SwitchScope(this, ScopeFlags);
12801283

12811284
// Parse the condition.
1285+
StmtResult InitStmt;
12821286
Sema::ConditionResult Cond;
1283-
if (ParseParenExprOrCondition(Cond, SwitchLoc, Sema::ConditionKind::Switch))
1287+
if (ParseParenExprOrCondition(&InitStmt, Cond, SwitchLoc,
1288+
Sema::ConditionKind::Switch))
12841289
return StmtError();
12851290

1286-
StmtResult Switch = Actions.ActOnStartOfSwitchStmt(SwitchLoc, Cond);
1291+
StmtResult Switch =
1292+
Actions.ActOnStartOfSwitchStmt(SwitchLoc, InitStmt.get(), Cond);
12871293

12881294
if (Switch.isInvalid()) {
12891295
// Skip the switch body.
@@ -1366,7 +1372,8 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
13661372

13671373
// Parse the condition.
13681374
Sema::ConditionResult Cond;
1369-
if (ParseParenExprOrCondition(Cond, WhileLoc, Sema::ConditionKind::Boolean))
1375+
if (ParseParenExprOrCondition(nullptr, Cond, WhileLoc,
1376+
Sema::ConditionKind::Boolean))
13701377
return StmtError();
13711378

13721379
// 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) {
16811688
// missing both semicolons.
16821689
} else {
16831690
if (getLangOpts().CPlusPlus)
1684-
SecondPart = ParseCXXCondition(ForLoc, Sema::ConditionKind::Boolean);
1691+
SecondPart =
1692+
ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean);
16851693
else {
16861694
ExprResult SecondExpr = ParseExpression();
16871695
if (SecondExpr.isInvalid())

0 commit comments

Comments
 (0)