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.

llvm-svn: 275350
This commit is contained in:
Richard Smith 2016-07-14 00:11:03 +00:00
parent d5bbd856e2
commit a547eb27fa
22 changed files with 369 additions and 62 deletions

View File

@ -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<DeclStmt*>(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<Expr*>(SubExprs[COND]);}
void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast<Stmt *>(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<SwitchCase *, 1, bool> 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<DeclStmt*>(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<Expr*>(SubExprs[COND]);}
const Stmt *getBody() const { return SubExprs[BODY]; }
const SwitchCase *getSwitchCaseList() const { return FirstCase.getPointer(); }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<ReturnStmt>(*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<LocalScope::const_iterator> 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<DeclStmt *>(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<LocalScope::const_iterator> 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;
}

View File

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

View File

@ -279,7 +279,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
unsigned &ParentScope = ((isa<Expr>(S) && !isa<StmtExpr>(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<SwitchStmt>(S)->getInit()) {
BuildScopeInformation(Init, ParentScope);
++StmtsToSkip;
}
if (VarDecl *Var = cast<SwitchStmt>(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.

View File

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

View File

@ -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<Derived>::TransformAttributedStmt(AttributedStmt *S) {
template<typename Derived>
StmtResult
TreeTransform<Derived>::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<Derived>::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<typename Derived>
StmtResult
TreeTransform<Derived>::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<Derived>::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();

View File

@ -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<VarDecl>(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<VarDecl>(Record, Idx));
S->setCond(Reader.ReadSubExpr());

View File

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

View File

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

View File

@ -34,6 +34,7 @@ int g(int i) {
// CHECK-NEXT: `-CompoundStmt
// CHECK-NEXT: `-IfStmt {{.*}} <line:25:3, line:28:12>
// CHECK-NEXT: |-<<<NULL>>>
// CHECK-NEXT: |-<<<NULL>>>
// CHECK-NEXT: |-OpaqueValueExpr {{.*}} <<invalid sloc>> '_Bool'
// CHECK-NEXT: |-ReturnStmt {{.*}} <line:26:5, col:12>
// CHECK-NEXT: | `-IntegerLiteral {{.*}} <col:12> 'int' 4
@ -41,7 +42,6 @@ int g(int i) {
// CHECK-NEXT: `-ImplicitCastExpr {{.*}} <col:12> 'int' <LValueToRValue>
// CHECK-NEXT: `-DeclRefExpr {{.*}} <col:12> 'int' lvalue ParmVar {{.*}} 'i' 'int'
namespace TestInvalidFunctionDecl {
struct Str {
double foo1(double, invalid_type);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -725,7 +725,7 @@ as the draft C++1z standard evolves.</p>
<tr>
<td>Separate variable and condition for <tt>if</tt> and <tt>switch</tt></td>
<td><a href="http://wg21.link/p0305r1">P0305R1</a></td>
<td class="none" align="center">No</td>
<td class="svn" align="center">SVN</td>
</tr>
</table>