23 changes: 10 additions & 13 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();

Expand All @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand Down
25 changes: 19 additions & 6 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -1174,19 +1174,19 @@ class TreeTransform {
/// 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);
}

/// \brief Start building a new switch statement.
///
/// 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.
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand All @@ -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();

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTReaderStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
Expand Down
70 changes: 70 additions & 0 deletions clang/test/CodeGenCXX/cxx1z-init-statement.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 1 addition & 1 deletion clang/test/Misc/ast-dump-invalid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ 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
// CHECK-NEXT: `-ReturnStmt {{.*}} <line:28:5, col:12>
// 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);
Expand Down
17 changes: 17 additions & 0 deletions clang/test/PCH/cxx1z-init-statement.cpp
Original file line number Diff line number Diff line change
@@ -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, "");
}
22 changes: 22 additions & 0 deletions clang/test/PCH/cxx1z-init-statement.h
Original file line number Diff line number Diff line change
@@ -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;
}
28 changes: 15 additions & 13 deletions clang/test/Parser/cxx1z-init-statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}) {}
Expand All @@ -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}}
Expand All @@ -46,4 +46,6 @@ int f() {
int b; // expected-error {{redefinition}}
int c; // expected-error {{redefinition}}
}

return 0;
}
26 changes: 26 additions & 0 deletions clang/test/SemaCXX/cxx1z-init-statement-warn-unused.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
91 changes: 91 additions & 0 deletions clang/test/SemaCXX/cxx1z-init-statement.cpp
Original file line number Diff line number Diff line change
@@ -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, "");
}
2 changes: 1 addition & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ <h2 id="cxx17">C++1z implementation status</h2>
<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>

Expand Down