From 9530e8bab6f84fd9b0da961423a395f5bfcb1e2a Mon Sep 17 00:00:00 2001
From: Richard Smith
Date: Thu, 14 Jul 2016 00:11:03 +0000
Subject: [PATCH] P0305R0: Semantic analysis and code generation for C++17
init-statement for 'if' and 'switch':
if (stmt; condition) { ... }
Patch by Anton Bikineev! Some minor formatting and comment tweets by me.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@275350 91177308-0d34-0410-b5e6-96231b3b80d8
---
include/clang/AST/Stmt.h | 14 ++-
include/clang/Basic/DiagnosticSemaKinds.td | 3 -
include/clang/Sema/Sema.h | 1 +
lib/AST/ASTImporter.cpp | 11 ++-
lib/AST/ExprConstant.cpp | 10 ++
lib/AST/Stmt.cpp | 9 +-
lib/Analysis/BodyFarm.cpp | 8 +-
lib/Analysis/CFG.cpp | 35 ++++++-
lib/CodeGen/CGStmt.cpp | 10 ++
lib/Sema/JumpDiagnostics.cpp | 20 ++--
lib/Sema/SemaStmt.cpp | 23 ++---
lib/Sema/TreeTransform.h | 25 +++--
lib/Serialization/ASTReaderStmt.cpp | 2 +
lib/Serialization/ASTWriterStmt.cpp | 2 +
test/CodeGenCXX/cxx1z-init-statement.cpp | 70 ++++++++++++++
test/Misc/ast-dump-invalid.cpp | 2 +-
test/PCH/cxx1z-init-statement.cpp | 17 ++++
test/PCH/cxx1z-init-statement.h | 22 +++++
test/Parser/cxx1z-init-statement.cpp | 28 +++---
.../cxx1z-init-statement-warn-unused.cpp | 26 ++++++
test/SemaCXX/cxx1z-init-statement.cpp | 91 +++++++++++++++++++
www/cxx_status.html | 2 +-
22 files changed, 369 insertions(+), 62 deletions(-)
create mode 100644 test/CodeGenCXX/cxx1z-init-statement.cpp
create mode 100644 test/PCH/cxx1z-init-statement.cpp
create mode 100644 test/PCH/cxx1z-init-statement.h
create mode 100644 test/SemaCXX/cxx1z-init-statement-warn-unused.cpp
create mode 100644 test/SemaCXX/cxx1z-init-statement.cpp
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index 30c237ef36..96847cf88f 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -879,7 +879,7 @@ public:
/// IfStmt - This represents an if/then/else.
///
class IfStmt : public Stmt {
- enum { VAR, COND, THEN, ELSE, END_EXPR };
+ enum { INIT, VAR, COND, THEN, ELSE, END_EXPR };
Stmt* SubExprs[END_EXPR];
SourceLocation IfLoc;
@@ -887,7 +887,7 @@ class IfStmt : public Stmt {
public:
IfStmt(const ASTContext &C, SourceLocation IL,
- bool IsConstexpr, VarDecl *var, Expr *cond,
+ bool IsConstexpr, Stmt *init, VarDecl *var, Expr *cond,
Stmt *then, SourceLocation EL = SourceLocation(),
Stmt *elsev = nullptr);
@@ -911,6 +911,9 @@ public:
return reinterpret_cast(SubExprs[VAR]);
}
+ Stmt *getInit() { return SubExprs[INIT]; }
+ const Stmt *getInit() const { return SubExprs[INIT]; }
+ void setInit(Stmt *S) { SubExprs[INIT] = S; }
const Expr *getCond() const { return reinterpret_cast(SubExprs[COND]);}
void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast(E); }
const Stmt *getThen() const { return SubExprs[THEN]; }
@@ -953,7 +956,7 @@ public:
///
class SwitchStmt : public Stmt {
SourceLocation SwitchLoc;
- enum { VAR, COND, BODY, END_EXPR };
+ enum { INIT, VAR, COND, BODY, END_EXPR };
Stmt* SubExprs[END_EXPR];
// This points to a linked list of case and default statements and, if the
// SwitchStmt is a switch on an enum value, records whether all the enum
@@ -962,7 +965,7 @@ class SwitchStmt : public Stmt {
llvm::PointerIntPair FirstCase;
public:
- SwitchStmt(const ASTContext &C, VarDecl *Var, Expr *cond);
+ SwitchStmt(const ASTContext &C, Stmt *Init, VarDecl *Var, Expr *cond);
/// \brief Build a empty switch statement.
explicit SwitchStmt(EmptyShell Empty) : Stmt(SwitchStmtClass, Empty) { }
@@ -985,6 +988,9 @@ public:
return reinterpret_cast(SubExprs[VAR]);
}
+ Stmt *getInit() { return SubExprs[INIT]; }
+ const Stmt *getInit() const { return SubExprs[INIT]; }
+ void setInit(Stmt *S) { SubExprs[INIT] = S; }
const Expr *getCond() const { return reinterpret_cast(SubExprs[COND]);}
const Stmt *getBody() const { return SubExprs[BODY]; }
const SwitchCase *getSwitchCaseList() const { return FirstCase.getPointer(); }
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index b071c64946..664853baeb 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7503,9 +7503,6 @@ 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<
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 00a4349590..a44de30828 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -3401,6 +3401,7 @@ public:
ConditionResult Cond, Stmt *ThenVal,
SourceLocation ElseLoc, Stmt *ElseVal);
StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
+ Stmt *InitStmt,
ConditionResult Cond, Stmt *ThenVal,
SourceLocation ElseLoc, Stmt *ElseVal);
StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index f4698ee229..2be4b9e938 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -4961,6 +4961,9 @@ Stmt *ASTNodeImporter::VisitAttributedStmt(AttributedStmt *S) {
Stmt *ASTNodeImporter::VisitIfStmt(IfStmt *S) {
SourceLocation ToIfLoc = Importer.Import(S->getIfLoc());
+ Stmt *ToInit = Importer.Import(S->getInit());
+ if (!ToInit && S->getInit())
+ return nullptr;
VarDecl *ToConditionVariable = nullptr;
if (VarDecl *FromConditionVariable = S->getConditionVariable()) {
ToConditionVariable =
@@ -4980,12 +4983,16 @@ Stmt *ASTNodeImporter::VisitIfStmt(IfStmt *S) {
return nullptr;
return new (Importer.getToContext()) IfStmt(Importer.getToContext(),
ToIfLoc, S->isConstexpr(),
+ ToInit,
ToConditionVariable,
ToCondition, ToThenStmt,
ToElseLoc, ToElseStmt);
}
Stmt *ASTNodeImporter::VisitSwitchStmt(SwitchStmt *S) {
+ Stmt *ToInit = Importer.Import(S->getInit());
+ if (!ToInit && S->getInit())
+ return nullptr;
VarDecl *ToConditionVariable = nullptr;
if (VarDecl *FromConditionVariable = S->getConditionVariable()) {
ToConditionVariable =
@@ -4997,8 +5004,8 @@ Stmt *ASTNodeImporter::VisitSwitchStmt(SwitchStmt *S) {
if (!ToCondition && S->getCond())
return nullptr;
SwitchStmt *ToStmt = new (Importer.getToContext()) SwitchStmt(
- Importer.getToContext(), ToConditionVariable,
- ToCondition);
+ Importer.getToContext(), ToInit,
+ ToConditionVariable, ToCondition);
Stmt *ToBody = Importer.Import(S->getBody());
if (!ToBody && S->getBody())
return nullptr;
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index b21577e1a0..28bdf6da3a 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -3485,6 +3485,11 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
APSInt Value;
{
FullExpressionRAII Scope(Info);
+ if (const Stmt *Init = SS->getInit()) {
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
+ if (ESR != ESR_Succeeded)
+ return ESR;
+ }
if (SS->getConditionVariable() &&
!EvaluateDecl(Info, SS->getConditionVariable()))
return ESR_Failed;
@@ -3667,6 +3672,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// Evaluate the condition, as either a var decl or as an expression.
BlockScopeRAII Scope(Info);
+ if (const Stmt *Init = IS->getInit()) {
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
+ if (ESR != ESR_Succeeded)
+ return ESR;
+ }
bool Cond;
if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
return ESR_Failed;
diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp
index 5f1adf7a20..75c0763995 100644
--- a/lib/AST/Stmt.cpp
+++ b/lib/AST/Stmt.cpp
@@ -764,11 +764,12 @@ void MSAsmStmt::initialize(const ASTContext &C, StringRef asmstr,
}
IfStmt::IfStmt(const ASTContext &C, SourceLocation IL, bool IsConstexpr,
- VarDecl *var, Expr *cond, Stmt *then, SourceLocation EL,
- Stmt *elsev)
+ Stmt *init, VarDecl *var, Expr *cond, Stmt *then,
+ SourceLocation EL, Stmt *elsev)
: Stmt(IfStmtClass), IfLoc(IL), ElseLoc(EL) {
setConstexpr(IsConstexpr);
setConditionVariable(C, var);
+ SubExprs[INIT] = init;
SubExprs[COND] = cond;
SubExprs[THEN] = then;
SubExprs[ELSE] = elsev;
@@ -824,9 +825,11 @@ void ForStmt::setConditionVariable(const ASTContext &C, VarDecl *V) {
VarRange.getEnd());
}
-SwitchStmt::SwitchStmt(const ASTContext &C, VarDecl *Var, Expr *cond)
+SwitchStmt::SwitchStmt(const ASTContext &C, Stmt *init, VarDecl *Var,
+ Expr *cond)
: Stmt(SwitchStmtClass), FirstCase(nullptr, false) {
setConditionVariable(C, Var);
+ SubExprs[INIT] = init;
SubExprs[COND] = cond;
SubExprs[BODY] = nullptr;
}
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index 07167014d6..d202a04064 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/lib/Analysis/BodyFarm.cpp
@@ -239,7 +239,8 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
SourceLocation());
// (5) Create the 'if' statement.
- IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, UO, CS);
+ IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr,
+ UO, CS);
return If;
}
@@ -342,9 +343,8 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
Stmt *Else = M.makeReturn(RetVal);
/// Construct the If.
- Stmt *If =
- new (C) IfStmt(C, SourceLocation(), false, nullptr, Comparison, Body,
- SourceLocation(), Else);
+ Stmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr,
+ Comparison, Body, SourceLocation(), Else);
return If;
}
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 2c502cdcf4..d7a9bdb3d8 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -1,4 +1,4 @@
- //===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===//
+//===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -1945,7 +1945,8 @@ CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
addLocalScopeForStmt(C);
}
if (!C->body_empty() && !isa(*C->body_rbegin())) {
- // If the body ends with a ReturnStmt, the dtors will be added in VisitReturnStmt
+ // If the body ends with a ReturnStmt, the dtors will be added in
+ // VisitReturnStmt.
addAutomaticObjDtors(ScopePos, scopeBeginPos, C);
}
@@ -2168,6 +2169,13 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
// won't be restored when traversing AST.
SaveAndRestore save_scope_pos(ScopePos);
+ // Create local scope for C++17 if init-stmt if one exists.
+ if (Stmt *Init = I->getInit()) {
+ LocalScope::const_iterator BeginScopePos = ScopePos;
+ addLocalScopeForStmt(Init);
+ addAutomaticObjDtors(ScopePos, BeginScopePos, I);
+ }
+
// Create local scope for possible condition variable.
// Store scope position. Add implicit destructor.
if (VarDecl *VD = I->getConditionVariable()) {
@@ -2268,13 +2276,19 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
// blocks will be pointed to be "Block".
CFGBlock *LastBlock = addStmt(I->getCond());
- // Finally, if the IfStmt contains a condition variable, add it and its
+ // If the IfStmt contains a condition variable, add it and its
// initializer to the CFG.
if (const DeclStmt* DS = I->getConditionVariableDeclStmt()) {
autoCreateBlock();
LastBlock = addStmt(const_cast(DS));
}
+ // Finally, if the IfStmt contains a C++17 init-stmt, add it to the CFG.
+ if (Stmt *Init = I->getInit()) {
+ autoCreateBlock();
+ LastBlock = addStmt(Init);
+ }
+
return LastBlock;
}
@@ -3059,6 +3073,13 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
// won't be restored when traversing AST.
SaveAndRestore save_scope_pos(ScopePos);
+ // Create local scope for C++17 switch init-stmt if one exists.
+ if (Stmt *Init = Terminator->getInit()) {
+ LocalScope::const_iterator BeginScopePos = ScopePos;
+ addLocalScopeForStmt(Init);
+ addAutomaticObjDtors(ScopePos, BeginScopePos, Terminator);
+ }
+
// Create local scope for possible condition variable.
// Store scope position. Add implicit destructor.
if (VarDecl *VD = Terminator->getConditionVariable()) {
@@ -3138,7 +3159,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
Block = SwitchTerminatedBlock;
CFGBlock *LastBlock = addStmt(Terminator->getCond());
- // Finally, if the SwitchStmt contains a condition variable, add both the
+ // If the SwitchStmt contains a condition variable, add both the
// SwitchStmt and the condition variable initialization to the CFG.
if (VarDecl *VD = Terminator->getConditionVariable()) {
if (Expr *Init = VD->getInit()) {
@@ -3148,6 +3169,12 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
}
}
+ // Finally, if the SwitchStmt contains a C++17 init-stmt, add it to the CFG.
+ if (Stmt *Init = Terminator->getInit()) {
+ autoCreateBlock();
+ LastBlock = addStmt(Init);
+ }
+
return LastBlock;
}
diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp
index ee6e3ebb35..6b4452cdb6 100644
--- a/lib/CodeGen/CGStmt.cpp
+++ b/lib/CodeGen/CGStmt.cpp
@@ -568,6 +568,9 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
// unequal to 0. The condition must be a scalar type.
LexicalScope ConditionScope(*this, S.getCond()->getSourceRange());
+ if (S.getInit())
+ EmitStmt(S.getInit());
+
if (S.getConditionVariable())
EmitAutoVarDecl(*S.getConditionVariable());
@@ -1484,6 +1487,9 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
incrementProfileCounter(Case);
RunCleanupsScope ExecutedScope(*this);
+ if (S.getInit())
+ EmitStmt(S.getInit());
+
// Emit the condition variable if needed inside the entire cleanup scope
// used by this special case for constant folded switches.
if (S.getConditionVariable())
@@ -1511,6 +1517,10 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
JumpDest SwitchExit = getJumpDestInCurrentScope("sw.epilog");
RunCleanupsScope ConditionScope(*this);
+
+ if (S.getInit())
+ EmitStmt(S.getInit());
+
if (S.getConditionVariable())
EmitAutoVarDecl(*S.getConditionVariable());
llvm::Value *CondV = EmitScalarExpr(S.getCond());
diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp
index bd541397e1..bdbe06c496 100644
--- a/lib/Sema/JumpDiagnostics.cpp
+++ b/lib/Sema/JumpDiagnostics.cpp
@@ -279,7 +279,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
unsigned &ParentScope = ((isa(S) && !isa(S))
? origParentScope : independentParentScope);
- bool SkipFirstSubStmt = false;
+ unsigned StmtsToSkip = 0u;
// If we found a label, remember that it is in ParentScope scope.
switch (S->getStmtClass()) {
@@ -304,11 +304,15 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
break;
case Stmt::SwitchStmtClass:
- // Evaluate the condition variable before entering the scope of the switch
- // statement.
+ // Evaluate the C++17 init stmt and condition variable
+ // before entering the scope of the switch statement.
+ if (Stmt *Init = cast(S)->getInit()) {
+ BuildScopeInformation(Init, ParentScope);
+ ++StmtsToSkip;
+ }
if (VarDecl *Var = cast(S)->getConditionVariable()) {
BuildScopeInformation(Var, ParentScope);
- SkipFirstSubStmt = true;
+ ++StmtsToSkip;
}
// Fall through
@@ -537,13 +541,13 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
}
for (Stmt *SubStmt : S->children()) {
- if (SkipFirstSubStmt) {
- SkipFirstSubStmt = false;
+ if (!SubStmt)
+ continue;
+ if (StmtsToSkip) {
+ --StmtsToSkip;
continue;
}
- if (!SubStmt) continue;
-
// Cases, labels, and defaults aren't "scope parents". It's also
// important to handle these iteratively instead of recursively in
// order to avoid blowing out the stack.
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index ed80b08a49..8e8104e581 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -508,9 +508,6 @@ 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,
@@ -528,12 +525,14 @@ Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt,
DiagnoseEmptyStmtBody(CondExpr->getLocEnd(), thenStmt,
diag::warn_empty_if_body);
- return BuildIfStmt(IfLoc, IsConstexpr, Cond, thenStmt, ElseLoc, elseStmt);
+ return BuildIfStmt(IfLoc, IsConstexpr, InitStmt, Cond, thenStmt, ElseLoc,
+ elseStmt);
}
StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
- ConditionResult Cond, Stmt *thenStmt,
- SourceLocation ElseLoc, Stmt *elseStmt) {
+ Stmt *InitStmt, ConditionResult Cond,
+ Stmt *thenStmt, SourceLocation ElseLoc,
+ Stmt *elseStmt) {
if (Cond.isInvalid())
return StmtError();
@@ -543,8 +542,9 @@ StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
DiagnoseUnusedExprResult(thenStmt);
DiagnoseUnusedExprResult(elseStmt);
- return new (Context) IfStmt(Context, IfLoc, IsConstexpr, Cond.get().first,
- Cond.get().second, thenStmt, ElseLoc, elseStmt);
+ return new (Context)
+ IfStmt(Context, IfLoc, IsConstexpr, InitStmt, Cond.get().first,
+ Cond.get().second, thenStmt, ElseLoc, elseStmt);
}
namespace {
@@ -668,13 +668,10 @@ StmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
if (Cond.isInvalid())
return StmtError();
- if (InitStmt)
- Diag(InitStmt->getLocStart(), diag::err_init_stmt_not_supported);
-
getCurFunction()->setHasBranchIntoScope();
- SwitchStmt *SS =
- new (Context) SwitchStmt(Context, Cond.get().first, Cond.get().second);
+ SwitchStmt *SS = new (Context)
+ SwitchStmt(Context, InitStmt, Cond.get().first, Cond.get().second);
getCurFunction()->SwitchStack.push_back(SS);
return SS;
}
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 825a2008d4..d9ad79f75d 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -1174,9 +1174,9 @@ public:
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
StmtResult RebuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
- Sema::ConditionResult Cond, Stmt *Then,
+ Sema::ConditionResult Cond, Stmt *Init, Stmt *Then,
SourceLocation ElseLoc, Stmt *Else) {
- return getSema().ActOnIfStmt(IfLoc, IsConstexpr, nullptr, Cond, Then,
+ return getSema().ActOnIfStmt(IfLoc, IsConstexpr, Init, Cond, Then,
ElseLoc, Else);
}
@@ -1184,9 +1184,9 @@ public:
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
- StmtResult RebuildSwitchStmtStart(SourceLocation SwitchLoc,
+ StmtResult RebuildSwitchStmtStart(SourceLocation SwitchLoc, Stmt *Init,
Sema::ConditionResult Cond) {
- return getSema().ActOnStartOfSwitchStmt(SwitchLoc, nullptr, Cond);
+ return getSema().ActOnStartOfSwitchStmt(SwitchLoc, Init, Cond);
}
/// \brief Attach the body to the switch statement.
@@ -6266,6 +6266,11 @@ StmtResult TreeTransform::TransformAttributedStmt(AttributedStmt *S) {
template
StmtResult
TreeTransform::TransformIfStmt(IfStmt *S) {
+ // Transform the initialization statement
+ StmtResult Init = getDerived().TransformStmt(S->getInit());
+ if (Init.isInvalid())
+ return StmtError();
+
// Transform the condition
Sema::ConditionResult Cond = getDerived().TransformCondition(
S->getIfLoc(), S->getConditionVariable(), S->getCond(),
@@ -6298,18 +6303,25 @@ TreeTransform::TransformIfStmt(IfStmt *S) {
}
if (!getDerived().AlwaysRebuild() &&
+ Init.get() == S->getInit() &&
Cond.get() == std::make_pair(S->getConditionVariable(), S->getCond()) &&
Then.get() == S->getThen() &&
Else.get() == S->getElse())
return S;
return getDerived().RebuildIfStmt(S->getIfLoc(), S->isConstexpr(), Cond,
- Then.get(), S->getElseLoc(), Else.get());
+ Init.get(), Then.get(), S->getElseLoc(),
+ Else.get());
}
template
StmtResult
TreeTransform::TransformSwitchStmt(SwitchStmt *S) {
+ // Transform the initialization statement
+ StmtResult Init = getDerived().TransformStmt(S->getInit());
+ if (Init.isInvalid())
+ return StmtError();
+
// Transform the condition.
Sema::ConditionResult Cond = getDerived().TransformCondition(
S->getSwitchLoc(), S->getConditionVariable(), S->getCond(),
@@ -6319,7 +6331,8 @@ TreeTransform::TransformSwitchStmt(SwitchStmt *S) {
// Rebuild the switch statement.
StmtResult Switch
- = getDerived().RebuildSwitchStmtStart(S->getSwitchLoc(), Cond);
+ = getDerived().RebuildSwitchStmtStart(S->getSwitchLoc(),
+ S->getInit(), Cond);
if (Switch.isInvalid())
return StmtError();
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 4bf7d90a5d..c6c62a71b3 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -185,6 +185,7 @@ void ASTStmtReader::VisitAttributedStmt(AttributedStmt *S) {
void ASTStmtReader::VisitIfStmt(IfStmt *S) {
VisitStmt(S);
S->setConstexpr(Record[Idx++]);
+ S->setInit(Reader.ReadSubStmt());
S->setConditionVariable(Reader.getContext(),
ReadDeclAs(Record, Idx));
S->setCond(Reader.ReadSubExpr());
@@ -196,6 +197,7 @@ void ASTStmtReader::VisitIfStmt(IfStmt *S) {
void ASTStmtReader::VisitSwitchStmt(SwitchStmt *S) {
VisitStmt(S);
+ S->setInit(Reader.ReadSubStmt());
S->setConditionVariable(Reader.getContext(),
ReadDeclAs(Record, Idx));
S->setCond(Reader.ReadSubExpr());
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index ecabc04e6b..00f889ff82 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -129,6 +129,7 @@ void ASTStmtWriter::VisitAttributedStmt(AttributedStmt *S) {
void ASTStmtWriter::VisitIfStmt(IfStmt *S) {
VisitStmt(S);
Record.push_back(S->isConstexpr());
+ Record.AddStmt(S->getInit());
Record.AddDeclRef(S->getConditionVariable());
Record.AddStmt(S->getCond());
Record.AddStmt(S->getThen());
@@ -140,6 +141,7 @@ void ASTStmtWriter::VisitIfStmt(IfStmt *S) {
void ASTStmtWriter::VisitSwitchStmt(SwitchStmt *S) {
VisitStmt(S);
+ Record.AddStmt(S->getInit());
Record.AddDeclRef(S->getConditionVariable());
Record.AddStmt(S->getCond());
Record.AddStmt(S->getBody());
diff --git a/test/CodeGenCXX/cxx1z-init-statement.cpp b/test/CodeGenCXX/cxx1z-init-statement.cpp
new file mode 100644
index 0000000000..5c05212c72
--- /dev/null
+++ b/test/CodeGenCXX/cxx1z-init-statement.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -std=c++1z -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s -w | FileCheck %s
+
+typedef int T;
+void f() {
+ // CHECK: %[[A:.*]] = alloca i32, align 4
+ // CHECK-NEXT: store i32 5, i32* %[[A]], align 4
+ // CHECK-NEXT: %[[B:.*]] = load i32, i32* %[[A]], align 4
+ // CHECK-NEXT %[[C:.*]] = icmp slt i32 %[[B]], 8
+ if (int a = 5; a < 8)
+ ;
+}
+
+void f1() {
+ // CHECK: %[[A:.*]] = alloca i32, align 4
+ // CHECK-NEXT: %[[B:.*]] = alloca i32, align 4
+ // CHECK-NEXT: %[[C:.*]] = alloca i32, align 4
+ // CHECK-NEXT: store i32 5, i32* %[[B]], align 4
+ // CHECK-NEXT: store i32 7, i32* %[[C]], align 4
+ if (int a, b = 5; int c = 7)
+ ;
+}
+
+int f2() {
+ // CHECK: %[[A:.*]] = alloca i32, align 4
+ // CHECK-NEXT: %[[B:.*]] = call i32 @_Z2f2v()
+ // CHECK-NEXT: store i32 7, i32* %[[A]], align 4
+ // CHECK-NEXT: %[[C:.*]] = load i32, i32* %[[A]], align 4
+ // CHECK-NEXT: %[[D:.*]] = icmp ne i32 %[[C]], 0
+ if (T{f2()}; int c = 7)
+ ;
+ return 2;
+}
+
+void g() {
+ // CHECK: %[[A:.*]] = alloca i32, align 4
+ // CHECK-NEXT: store i32 5, i32* %[[A]], align 4
+ // CHECK-NEXT: %[[B:.*]] = load i32, i32* %[[A]], align 4
+ // CHECK-NEXT: switch i32 %[[B]], label %[[C:.*]] [
+ switch (int a = 5; a) {
+ case 0:
+ break;
+ }
+}
+
+void g1() {
+ // CHECK: %[[A:.*]] = alloca i32, align 4
+ // CHECK-NEXT: %[[B:.*]] = alloca i32, align 4
+ // CHECK-NEXT: %[[C:.*]] = alloca i32, align 4
+ // CHECK-NEXT: store i32 5, i32* %[[B]], align 4
+ // CHECK-NEXT: store i32 7, i32* %[[C]], align 4
+ // CHECK-NEXT: %[[D:.*]] = load i32, i32* %[[C]], align 4
+ // CHECK-NEXT: switch i32 %[[D]], label %[[E:.*]] [
+ switch (int a, b = 5; int c = 7) {
+ case 0:
+ break;
+ }
+}
+
+int g2() {
+ // CHECK: %[[A:.*]] = alloca i32, align 4
+ // CHECK-NEXT: %[[B:.*]] = call i32 @_Z2f2v()
+ // CHECK-NEXT: store i32 7, i32* %[[A]], align 4
+ // CHECK-NEXT: %[[C:.*]] = load i32, i32* %[[A]], align 4
+ // CHECK-NEXT: switch i32 %[[C]], label %[[E:.*]] [
+ switch (T{f2()}; int c = 7) {
+ case 0:
+ break;
+ }
+ return 2;
+}
diff --git a/test/Misc/ast-dump-invalid.cpp b/test/Misc/ast-dump-invalid.cpp
index bf260c040c..aa6cd52692 100644
--- a/test/Misc/ast-dump-invalid.cpp
+++ b/test/Misc/ast-dump-invalid.cpp
@@ -34,6 +34,7 @@ int g(int i) {
// CHECK-NEXT: `-CompoundStmt
// CHECK-NEXT: `-IfStmt {{.*}}
// CHECK-NEXT: |-<<>>
+// CHECK-NEXT: |-<<>>
// CHECK-NEXT: |-OpaqueValueExpr {{.*}} <> '_Bool'
// CHECK-NEXT: |-ReturnStmt {{.*}}
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 4
@@ -41,7 +42,6 @@ int g(int i) {
// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int'
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'i' 'int'
-
namespace TestInvalidFunctionDecl {
struct Str {
double foo1(double, invalid_type);
diff --git a/test/PCH/cxx1z-init-statement.cpp b/test/PCH/cxx1z-init-statement.cpp
new file mode 100644
index 0000000000..d08fb7c56b
--- /dev/null
+++ b/test/PCH/cxx1z-init-statement.cpp
@@ -0,0 +1,17 @@
+// Test this without pch.
+// RUN: %clang_cc1 -std=c++1z -include %S/cxx1z-init-statement.h -fsyntax-only -emit-llvm -o - %s
+
+// Test with pch.
+// RUN: %clang_cc1 -x c++ -std=c++1z -emit-pch -o %t %S/cxx1z-init-statement.h
+// RUN: %clang_cc1 -std=c++1z -include-pch %t -fsyntax-only -emit-llvm -o - %s
+
+void g0(void) {
+ static_assert(test_if(-1) == -1, "");
+ static_assert(test_if(0) == 0, "");
+}
+
+void g1(void) {
+ static_assert(test_switch(-1) == -1, "");
+ static_assert(test_switch(0) == 0, "");
+ static_assert(test_switch(1) == 1, "");
+}
diff --git a/test/PCH/cxx1z-init-statement.h b/test/PCH/cxx1z-init-statement.h
new file mode 100644
index 0000000000..16bd569c84
--- /dev/null
+++ b/test/PCH/cxx1z-init-statement.h
@@ -0,0 +1,22 @@
+// Header for PCH test cxx1z-init-statement.cpp
+
+constexpr int test_if(int x) {
+ if (int a = ++x; a == 0) {
+ return -1;
+ } else if (++a; a == 2) {
+ return 0;
+ }
+ return 2;
+}
+
+constexpr int test_switch(int x) {
+ switch (int a = ++x; a) {
+ case 0:
+ return -1;
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ }
+ return 2;
+}
diff --git a/test/Parser/cxx1z-init-statement.cpp b/test/Parser/cxx1z-init-statement.cpp
index c118522e8c..3d119ef8e7 100644
--- a/test/Parser/cxx1z-init-statement.cpp
+++ b/test/Parser/cxx1z-init-statement.cpp
@@ -4,18 +4,18 @@ 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}}
- if (T(n) = 0; n) {} // expected-error {{not yet supported}}
+ if (T n = 0; n != 0) {}
+ if (T f(); f()) {}
+ if (T(f()); f()) {}
+ if (T(f()), g, h; f()) {}
+ if (T f(); f()) {}
+ if (T f(), g, h; f()) {}
+ if (T(n) = 0; n) {}
// 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}}
+ if (T{f()}; f()) {}
+ if (T{f()}, g, h; f()) {} // expected-warning 2{{unused}}
+ if (T(f()), g, h + 1; f()) {} // expected-warning 2{{unused}}
// condition declarations
if (T(n){g}) {}
@@ -34,10 +34,10 @@ int f() {
if (T(n)(int())) {} // expected-error {{undeclared identifier 'n'}}
// Likewise for 'switch'
- switch (int n; n) {} // expected-error {{not yet supported}}
- switch (g; int g = 5) {} // expected-error {{not yet supported}}
+ switch (int n; n) {}
+ switch (g; int g = 5) {}
- if (int a, b; int c = a) { // expected-error {{not yet supported}} expected-note 6{{previous}}
+ if (int a, b; int c = a) { // expected-note 6{{previous}}
int a; // expected-error {{redefinition}}
int b; // expected-error {{redefinition}}
int c; // expected-error {{redefinition}}
@@ -46,4 +46,6 @@ int f() {
int b; // expected-error {{redefinition}}
int c; // expected-error {{redefinition}}
}
+
+ return 0;
}
diff --git a/test/SemaCXX/cxx1z-init-statement-warn-unused.cpp b/test/SemaCXX/cxx1z-init-statement-warn-unused.cpp
new file mode 100644
index 0000000000..5390da4f3f
--- /dev/null
+++ b/test/SemaCXX/cxx1z-init-statement-warn-unused.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -std=c++1z -verify -Wuninitialized %s
+
+void testIf() {
+ if (bool b; b) // expected-warning {{uninitialized}} expected-note {{to silence}}
+ ;
+ if (int a, b = 2; a) // expected-warning {{uninitialized}} expected-note {{to silence}}
+ ;
+ int a;
+ if (a = 0; a) {} // OK
+}
+
+void testSwitch() {
+ switch (bool b; b) { // expected-warning {{uninitialized}} expected-warning {{boolean value}} expected-note {{to silence}}
+ case 0:
+ break;
+ }
+ switch (int a, b = 7; a) { // expected-warning {{uninitialized}} expected-note {{to silence}}
+ case 0:
+ break;
+ }
+ int c;
+ switch (c = 0; c) { // OK
+ case 0:
+ break;
+ }
+}
diff --git a/test/SemaCXX/cxx1z-init-statement.cpp b/test/SemaCXX/cxx1z-init-statement.cpp
new file mode 100644
index 0000000000..4afe0402d1
--- /dev/null
+++ b/test/SemaCXX/cxx1z-init-statement.cpp
@@ -0,0 +1,91 @@
+// RUN: %clang_cc1 -std=c++1z -verify %s
+
+void testIf() {
+ int x = 0;
+ if (x; x) ++x;
+ if (int t = 0; t) ++t; else --t;
+
+ if (int x, y = 0; y) // expected-note 2 {{previous definition is here}}
+ int x = 0; // expected-error {{redefinition of 'x'}}
+ else
+ int x = 0; // expected-error {{redefinition of 'x'}}
+
+ if (x; int a = 0) ++a;
+ if (x, +x; int a = 0) // expected-note 2 {{previous definition is here}} expected-warning {{unused}}
+ int a = 0; // expected-error {{redefinition of 'a'}}
+ else
+ int a = 0; // expected-error {{redefinition of 'a'}}
+
+ if (int b = 0; b)
+ ;
+ b = 2; // expected-error {{use of undeclared identifier}}
+}
+
+void testSwitch() {
+ int x = 0;
+ switch (x; x) {
+ case 1:
+ ++x;
+ }
+
+ switch (int x, y = 0; y) {
+ case 1:
+ ++x;
+ default:
+ ++y;
+ }
+
+ switch (int x, y = 0; y) { // expected-note 2 {{previous definition is here}}
+ case 0:
+ int x = 0; // expected-error {{redefinition of 'x'}}
+ case 1:
+ int y = 0; // expected-error {{redefinition of 'y'}}
+ };
+
+ switch (x; int a = 0) {
+ case 0:
+ ++a;
+ }
+
+ switch (x, +x; int a = 0) { // expected-note {{previous definition is here}} expected-warning {{unused}}
+ case 0:
+ int a = 0; // expected-error {{redefinition of 'a'}} // expected-note {{previous definition is here}}
+ case 1:
+ int a = 0; // expected-error {{redefinition of 'a'}}
+ }
+
+ switch (int b = 0; b) {
+ case 0:
+ break;
+ }
+ b = 2; // expected-error {{use of undeclared identifier}}
+}
+
+constexpr bool constexpr_if_init(int n) {
+ if (int a = n; ++a > 0)
+ return true;
+ else
+ return false;
+}
+
+constexpr int constexpr_switch_init(int n) {
+ switch (int p = n + 2; p) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ default:
+ return -1;
+ }
+}
+
+void test_constexpr_init_stmt() {
+ constexpr bool a = constexpr_if_init(-2);
+ static_assert(!a, "");
+ static_assert(constexpr_if_init(1), "");
+
+ constexpr int b = constexpr_switch_init(-1);
+ static_assert(b == 1, "");
+ static_assert(constexpr_switch_init(-2) == 0, "");
+ static_assert(constexpr_switch_init(-5) == -1, "");
+}
diff --git a/www/cxx_status.html b/www/cxx_status.html
index 0246c14ef2..da9d2c618a 100644
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -725,7 +725,7 @@ as the draft C++1z standard evolves.
Separate variable and condition for if and switch |
P0305R1 |
- No |
+ SVN |
--
2.40.0