Skip to content

Commit

Permalink
P0305R1: Parsing support for init-statements in 'if' and 'switch' sta…
Browse files Browse the repository at this point in the history
…tements.

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
  • Loading branch information
zygoloid committed Jun 29, 2016
1 parent 03c38af commit c7a05a9
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 61 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
23 changes: 16 additions & 7 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,8 @@ class Parser : public CodeCompletionHandler {

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

//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -1690,7 +1691,8 @@ class Parser : public CodeCompletionHandler {
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);
Expand Down Expand Up @@ -1984,11 +1986,18 @@ class Parser : public CodeCompletionHandler {
/// 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) {
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,7 @@ class Declarator {
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.
Expand Down Expand Up @@ -1810,6 +1811,7 @@ class Declarator {
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
case ConditionContext:
return false;

Expand Down Expand Up @@ -1844,6 +1846,7 @@ class Declarator {
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
case ConditionContext:
case PrototypeContext:
case LambdaExprParameterContext:
Expand Down Expand Up @@ -1877,6 +1880,7 @@ class Declarator {
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
case ConditionContext:
case PrototypeContext:
case LambdaExprParameterContext:
Expand Down Expand Up @@ -1921,6 +1925,7 @@ class Declarator {
case FileContext:
case BlockContext:
case ForContext:
case InitStmtContext:
return true;

case ConditionContext:
Expand Down Expand Up @@ -2120,6 +2125,7 @@ class Declarator {
case MemberContext:
case BlockContext:
case ForContext:
case InitStmtContext:
return true;

case ConditionContext:
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3397,12 +3397,14 @@ class Sema {

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);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
35 changes: 30 additions & 5 deletions clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -1741,17 +1748,38 @@ 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.
ExprResult Expr = ParseExpression(); // 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);
Expand Down Expand Up @@ -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);
}
Expand Down
28 changes: 18 additions & 10 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
Expand Down
Loading

0 comments on commit c7a05a9

Please sign in to comment.