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