Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -879,7 +879,7 @@ /// 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 @@ 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 @@ 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 @@ /// 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 @@ 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 @@ 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(); } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7503,9 +7503,6 @@ 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< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3401,6 +3401,7 @@ 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, Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -4961,6 +4961,9 @@ 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,6 +4983,7 @@ return nullptr; return new (Importer.getToContext()) IfStmt(Importer.getToContext(), ToIfLoc, S->isConstexpr(), + ToInit, ToConditionVariable, ToCondition, ToThenStmt, ToElseLoc, ToElseStmt); @@ -4986,6 +4990,9 @@ } 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 @@ 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; Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -3485,6 +3485,11 @@ 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 @@ // 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; Index: lib/AST/Stmt.cpp =================================================================== --- lib/AST/Stmt.cpp +++ lib/AST/Stmt.cpp @@ -764,11 +764,12 @@ } IfStmt::IfStmt(const ASTContext &C, SourceLocation IL, bool IsConstexpr, - VarDecl *var, Expr *cond, Stmt *then, SourceLocation EL, + 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,10 @@ 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; } Index: lib/Analysis/BodyFarm.cpp =================================================================== --- lib/Analysis/BodyFarm.cpp +++ lib/Analysis/BodyFarm.cpp @@ -239,7 +239,7 @@ 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; } @@ -343,7 +343,7 @@ /// Construct the If. Stmt *If = - new (C) IfStmt(C, SourceLocation(), false, nullptr, Comparison, Body, + new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr, Comparison, Body, SourceLocation(), Else); return If; Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -2168,6 +2168,13 @@ // won't be restored when traversing AST. SaveAndRestore save_scope_pos(ScopePos); + // Create local scope for C++17 if-init stmt if 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,7 +2275,7 @@ // 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(); @@ -2275,6 +2282,12 @@ 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 +3072,13 @@ // won't be restored when traversing AST. SaveAndRestore save_scope_pos(ScopePos); + // Create local scope for C++17 switch-init stmt if 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 +3158,7 @@ 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 +3168,12 @@ } } + // 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; } Index: lib/CodeGen/CGStmt.cpp =================================================================== --- lib/CodeGen/CGStmt.cpp +++ lib/CodeGen/CGStmt.cpp @@ -568,6 +568,9 @@ // 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()); @@ -1472,6 +1475,9 @@ 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()) @@ -1499,6 +1505,10 @@ 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()); Index: lib/Sema/JumpDiagnostics.cpp =================================================================== --- lib/Sema/JumpDiagnostics.cpp +++ lib/Sema/JumpDiagnostics.cpp @@ -279,7 +279,7 @@ 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 @@ 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 @@ } 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. Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -508,9 +508,6 @@ 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,10 +525,10 @@ 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, +StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr, Stmt *InitStmt, ConditionResult Cond, Stmt *thenStmt, SourceLocation ElseLoc, Stmt *elseStmt) { if (Cond.isInvalid()) @@ -543,7 +540,7 @@ DiagnoseUnusedExprResult(thenStmt); DiagnoseUnusedExprResult(elseStmt); - return new (Context) IfStmt(Context, IfLoc, IsConstexpr, Cond.get().first, + return new (Context) IfStmt(Context, IfLoc, IsConstexpr, InitStmt, Cond.get().first, Cond.get().second, thenStmt, ElseLoc, elseStmt); } @@ -668,13 +665,10 @@ 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); + new (Context) SwitchStmt(Context, InitStmt, Cond.get().first, Cond.get().second); getCurFunction()->SwitchStack.push_back(SS); return SS; } Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -1174,9 +1174,9 @@ /// 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); } @@ -1185,8 +1185,9 @@ /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. 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. @@ -6242,6 +6243,11 @@ 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(), @@ -6274,6 +6280,7 @@ } if (!getDerived().AlwaysRebuild() && + Init.get() == S->getInit() && Cond.get() == std::make_pair(S->getConditionVariable(), S->getCond()) && Then.get() == S->getThen() && Else.get() == S->getElse()) @@ -6280,12 +6287,17 @@ 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(), @@ -6295,7 +6307,8 @@ // Rebuild the switch statement. StmtResult Switch - = getDerived().RebuildSwitchStmtStart(S->getSwitchLoc(), Cond); + = getDerived().RebuildSwitchStmtStart(S->getSwitchLoc(), + S->getInit(), Cond); if (Switch.isInvalid()) return StmtError(); Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -185,6 +185,7 @@ 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::VisitSwitchStmt(SwitchStmt *S) { VisitStmt(S); + S->setInit(Reader.ReadSubStmt()); S->setConditionVariable(Reader.getContext(), ReadDeclAs(Record, Idx)); S->setCond(Reader.ReadSubExpr()); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -129,6 +129,7 @@ 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::VisitSwitchStmt(SwitchStmt *S) { VisitStmt(S); + Record.AddStmt(S->getInit()); Record.AddDeclRef(S->getConditionVariable()); Record.AddStmt(S->getCond()); Record.AddStmt(S->getBody()); Index: test/CodeGenCXX/cxx1z-init-statement.cpp =================================================================== --- test/CodeGenCXX/cxx1z-init-statement.cpp +++ 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; +} Index: test/PCH/cxx1z-init-statement.h =================================================================== --- test/PCH/cxx1z-init-statement.h +++ 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; +} Index: test/PCH/cxx1z-init-statement.cpp =================================================================== --- test/PCH/cxx1z-init-statement.cpp +++ 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, ""); +} Index: test/Parser/cxx1z-init-statement.cpp =================================================================== --- test/Parser/cxx1z-init-statement.cpp +++ test/Parser/cxx1z-init-statement.cpp @@ -4,18 +4,18 @@ 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 @@ 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 b; // expected-error {{redefinition}} int c; // expected-error {{redefinition}} } + + return 0; } Index: test/SemaCXX/cxx1z-init-statement-warn-unused.cpp =================================================================== --- test/SemaCXX/cxx1z-init-statement-warn-unused.cpp +++ 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; + } +} Index: test/SemaCXX/cxx1z-init-statement.cpp =================================================================== --- test/SemaCXX/cxx1z-init-statement.cpp +++ 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, ""); +}