Author: rsmith Date: Fri Sep 28 11:44:09 2018 New Revision: 343350 URL: http://llvm.org/viewvc/llvm-project?rev=343350&view=rev Log: [cxx2a] P0614R1: Support init-statements in range-based for loops.
We don't yet support this for the case where a range-based for loop is implicitly rewritten to an ObjC for..in statement. Added: cfe/trunk/test/CodeGenCXX/cxx2a-init-statement.cpp cfe/trunk/test/PCH/cxx2a-for-init-statement.cpp cfe/trunk/test/Parser/cxx2a-init-statement.cpp Modified: cfe/trunk/include/clang/AST/RecursiveASTVisitor.h cfe/trunk/include/clang/AST/StmtCXX.h cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Parse/Parser.h cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/AST/ASTImporter.cpp cfe/trunk/lib/AST/ExprConstant.cpp cfe/trunk/lib/AST/StmtCXX.cpp cfe/trunk/lib/AST/StmtPrinter.cpp cfe/trunk/lib/Analysis/CFG.cpp cfe/trunk/lib/CodeGen/CGStmt.cpp cfe/trunk/lib/CodeGen/CodeGenPGO.cpp cfe/trunk/lib/CodeGen/CoverageMappingGen.cpp cfe/trunk/lib/Parse/ParseExprCXX.cpp cfe/trunk/lib/Parse/ParseStmt.cpp cfe/trunk/lib/Parse/ParseTentative.cpp cfe/trunk/lib/Sema/SemaStmt.cpp cfe/trunk/lib/Sema/TreeTransform.h cfe/trunk/lib/Serialization/ASTReaderStmt.cpp cfe/trunk/lib/Serialization/ASTWriterStmt.cpp cfe/trunk/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist cfe/trunk/test/Analysis/cxx-for-range.cpp cfe/trunk/test/Analysis/scopes-cfg-output.cpp cfe/trunk/test/Import/cxx-for-range/Inputs/F.cpp cfe/trunk/test/Import/cxx-for-range/test.cpp cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp cfe/trunk/test/SemaCXX/cxx17-compat.cpp cfe/trunk/test/SemaObjCXX/foreach.mm cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/AST/RecursiveASTVisitor.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecursiveASTVisitor.h?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/RecursiveASTVisitor.h (original) +++ cfe/trunk/include/clang/AST/RecursiveASTVisitor.h Fri Sep 28 11:44:09 2018 @@ -2180,6 +2180,8 @@ DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStm DEF_TRAVERSE_STMT(CXXForRangeStmt, { if (!getDerived().shouldVisitImplicitCode()) { + if (S->getInit()) + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInit()); TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getLoopVarStmt()); TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRangeInit()); TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody()); Modified: cfe/trunk/include/clang/AST/StmtCXX.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/StmtCXX.h?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/StmtCXX.h (original) +++ cfe/trunk/include/clang/AST/StmtCXX.h Fri Sep 28 11:44:09 2018 @@ -118,14 +118,15 @@ public: }; /// CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for -/// statement, represented as 'for (range-declarator : range-expression)'. +/// statement, represented as 'for (range-declarator : range-expression)' +/// or 'for (init-statement range-declarator : range-expression)'. /// /// This is stored in a partially-desugared form to allow full semantic /// analysis of the constituent components. The original syntactic components /// can be extracted using getLoopVariable and getRangeInit. class CXXForRangeStmt : public Stmt { SourceLocation ForLoc; - enum { RANGE, BEGINSTMT, ENDSTMT, COND, INC, LOOPVAR, BODY, END }; + enum { INIT, RANGE, BEGINSTMT, ENDSTMT, COND, INC, LOOPVAR, BODY, END }; // SubExprs[RANGE] is an expression or declstmt. // SubExprs[COND] and SubExprs[INC] are expressions. Stmt *SubExprs[END]; @@ -135,16 +136,17 @@ class CXXForRangeStmt : public Stmt { friend class ASTStmtReader; public: - CXXForRangeStmt(DeclStmt *Range, DeclStmt *Begin, DeclStmt *End, - Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body, - SourceLocation FL, SourceLocation CAL, SourceLocation CL, - SourceLocation RPL); + CXXForRangeStmt(Stmt *InitStmt, DeclStmt *Range, DeclStmt *Begin, + DeclStmt *End, Expr *Cond, Expr *Inc, DeclStmt *LoopVar, + Stmt *Body, SourceLocation FL, SourceLocation CAL, + SourceLocation CL, SourceLocation RPL); CXXForRangeStmt(EmptyShell Empty) : Stmt(CXXForRangeStmtClass, Empty) { } - + Stmt *getInit() { return SubExprs[INIT]; } VarDecl *getLoopVariable(); Expr *getRangeInit(); + const Stmt *getInit() const { return SubExprs[INIT]; } const VarDecl *getLoopVariable() const; const Expr *getRangeInit() const; @@ -179,6 +181,7 @@ public: } const Stmt *getBody() const { return SubExprs[BODY]; } + void setInit(Stmt *S) { SubExprs[INIT] = S; } void setRangeInit(Expr *E) { SubExprs[RANGE] = reinterpret_cast<Stmt*>(E); } void setRangeStmt(Stmt *S) { SubExprs[RANGE] = S; } void setBeginStmt(Stmt *S) { SubExprs[BEGINSTMT] = S; } Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Fri Sep 28 11:44:09 2018 @@ -540,6 +540,12 @@ def ext_init_statement : ExtWarn< def warn_cxx14_compat_init_statement : Warning< "%select{if|switch}0 initialization statements are incompatible with " "C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>; +def ext_for_range_init_stmt : ExtWarn< + "range-based for loop initialization statements are a C++2a extension">, + InGroup<CXX2a>; +def warn_cxx17_compat_for_range_init_stmt : Warning< + "range-based for loop initialization statements are incompatible with " + "C++ standards before C++2a">, DefaultIgnore, InGroup<CXXPre2aCompat>; // C++ derived classes def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">; Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Sep 28 11:44:09 2018 @@ -2299,6 +2299,9 @@ def warn_for_range_copy : Warning< "loop variable %0 of type %1 creates a copy from type %2">, InGroup<RangeLoopAnalysis>, DefaultIgnore; def note_use_reference_type : Note<"use reference type %0 to prevent copying">; +def err_objc_for_range_init_stmt : Error< + "initialization statement is not supported when iterating over Objective-C " + "collection">; // C++11 constexpr def warn_cxx98_compat_constexpr : Warning< Modified: cfe/trunk/include/clang/Parse/Parser.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/include/clang/Parse/Parser.h (original) +++ cfe/trunk/include/clang/Parse/Parser.h Fri Sep 28 11:44:09 2018 @@ -1793,10 +1793,12 @@ private: SourceLocation Start); //===--------------------------------------------------------------------===// - // C++ if/switch/while condition expression. + // C++ if/switch/while/for condition expression. + struct ForRangeInfo; Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, - Sema::ConditionKind CK); + Sema::ConditionKind CK, + ForRangeInfo *FRI = nullptr); //===--------------------------------------------------------------------===// // C++ Coroutines @@ -2045,6 +2047,9 @@ private: bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); } }; + struct ForRangeInfo : ForRangeInit { + StmtResult LoopVar; + }; DeclGroupPtrTy ParseDeclaration(DeclaratorContext Context, SourceLocation &DeclEnd, @@ -2215,13 +2220,15 @@ private: Expression, ///< Disambiguated as an expression (either kind). ConditionDecl, ///< Disambiguated as the declaration form of condition. InitStmtDecl, ///< Disambiguated as a simple-declaration init-statement. + ForRangeDecl, ///< Disambiguated as a for-range declaration. Error ///< Can't be any of the above! }; /// Disambiguates between the different kinds of things that can happen /// after 'if (' or 'switch ('. This could be one of two different kinds of /// declaration (depending on whether there is a ';' later) or an expression. ConditionOrInitStatement - isCXXConditionDeclarationOrInitStatement(bool CanBeInitStmt); + isCXXConditionDeclarationOrInitStatement(bool CanBeInitStmt, + bool CanBeForRangeDecl); bool isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous); bool isCXXTypeId(TentativeCXXTypeIdContext Context) { Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Fri Sep 28 11:44:09 2018 @@ -3777,12 +3777,14 @@ public: StmtResult ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, + Stmt *InitStmt, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, SourceLocation RParenLoc, BuildForRangeKind Kind); StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, + Stmt *InitStmt, SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End, Expr *Cond, Expr *Inc, Modified: cfe/trunk/lib/AST/ASTImporter.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTImporter.cpp (original) +++ cfe/trunk/lib/AST/ASTImporter.cpp Fri Sep 28 11:44:09 2018 @@ -5394,6 +5394,9 @@ Stmt *ASTNodeImporter::VisitCXXTryStmt(C } Stmt *ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { + auto *ToInit = dyn_cast_or_null<Stmt>(Importer.Import(S->getInit())); + if (!ToInit && S->getInit()) + return nullptr; auto *ToRange = dyn_cast_or_null<DeclStmt>(Importer.Import(S->getRangeStmt())); if (!ToRange && S->getRangeStmt()) @@ -5423,11 +5426,9 @@ Stmt *ASTNodeImporter::VisitCXXForRangeS SourceLocation ToCoawaitLoc = Importer.Import(S->getCoawaitLoc()); SourceLocation ToColonLoc = Importer.Import(S->getColonLoc()); SourceLocation ToRParenLoc = Importer.Import(S->getRParenLoc()); - return new (Importer.getToContext()) CXXForRangeStmt(ToRange, ToBegin, ToEnd, - ToCond, ToInc, - ToLoopVar, ToBody, - ToForLoc, ToCoawaitLoc, - ToColonLoc, ToRParenLoc); + return new (Importer.getToContext()) + CXXForRangeStmt(ToInit, ToRange, ToBegin, ToEnd, ToCond, ToInc, ToLoopVar, + ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc); } Stmt *ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { Modified: cfe/trunk/lib/AST/ExprConstant.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/AST/ExprConstant.cpp (original) +++ cfe/trunk/lib/AST/ExprConstant.cpp Fri Sep 28 11:44:09 2018 @@ -4217,6 +4217,13 @@ static EvalStmtResult EvaluateStmt(StmtR const CXXForRangeStmt *FS = cast<CXXForRangeStmt>(S); BlockScopeRAII Scope(Info); + // Evaluate the init-statement if present. + if (FS->getInit()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit()); + if (ESR != ESR_Succeeded) + return ESR; + } + // Initialize the __range variable. EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt()); if (ESR != ESR_Succeeded) Modified: cfe/trunk/lib/AST/StmtCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtCXX.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/AST/StmtCXX.cpp (original) +++ cfe/trunk/lib/AST/StmtCXX.cpp Fri Sep 28 11:44:09 2018 @@ -45,7 +45,7 @@ CXXTryStmt::CXXTryStmt(SourceLocation tr std::copy(handlers.begin(), handlers.end(), Stmts + 1); } -CXXForRangeStmt::CXXForRangeStmt(DeclStmt *Range, +CXXForRangeStmt::CXXForRangeStmt(Stmt *Init, DeclStmt *Range, DeclStmt *BeginStmt, DeclStmt *EndStmt, Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body, SourceLocation FL, @@ -53,6 +53,7 @@ CXXForRangeStmt::CXXForRangeStmt(DeclStm SourceLocation RPL) : Stmt(CXXForRangeStmtClass), ForLoc(FL), CoawaitLoc(CAL), ColonLoc(CL), RParenLoc(RPL) { + SubExprs[INIT] = Init; SubExprs[RANGE] = Range; SubExprs[BEGINSTMT] = BeginStmt; SubExprs[ENDSTMT] = EndStmt; Modified: cfe/trunk/lib/AST/StmtPrinter.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtPrinter.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/AST/StmtPrinter.cpp (original) +++ cfe/trunk/lib/AST/StmtPrinter.cpp Fri Sep 28 11:44:09 2018 @@ -99,6 +99,28 @@ namespace { IndentLevel -= SubIndent; } + void PrintInitStmt(Stmt *S, unsigned PrefixWidth) { + // FIXME: Cope better with odd prefix widths. + IndentLevel += (PrefixWidth + 1) / 2; + if (auto *DS = dyn_cast<DeclStmt>(S)) + PrintRawDeclStmt(DS); + else + PrintExpr(cast<Expr>(S)); + OS << "; "; + IndentLevel -= (PrefixWidth + 1) / 2; + } + + void PrintControlledStmt(Stmt *S) { + if (auto *CS = dyn_cast<CompoundStmt>(S)) { + OS << " "; + PrintRawCompoundStmt(CS); + OS << NL; + } else { + OS << NL; + PrintStmt(S); + } + } + void PrintRawCompoundStmt(CompoundStmt *S); void PrintRawDecl(Decl *D); void PrintRawDeclStmt(const DeclStmt *S); @@ -218,6 +240,8 @@ void StmtPrinter::VisitAttributedStmt(At void StmtPrinter::PrintRawIfStmt(IfStmt *If) { OS << "if ("; + if (If->getInit()) + PrintInitStmt(If->getInit(), 4); if (const DeclStmt *DS = If->getConditionVariableDeclStmt()) PrintRawDeclStmt(DS); else @@ -258,21 +282,14 @@ void StmtPrinter::VisitIfStmt(IfStmt *If void StmtPrinter::VisitSwitchStmt(SwitchStmt *Node) { Indent() << "switch ("; + if (Node->getInit()) + PrintInitStmt(Node->getInit(), 8); if (const DeclStmt *DS = Node->getConditionVariableDeclStmt()) PrintRawDeclStmt(DS); else PrintExpr(Node->getCond()); OS << ")"; - - // Pretty print compoundstmt bodies (very common). - if (auto *CS = dyn_cast<CompoundStmt>(Node->getBody())) { - OS << " "; - PrintRawCompoundStmt(CS); - OS << NL; - } else { - OS << NL; - PrintStmt(Node->getBody()); - } + PrintControlledStmt(Node->getBody()); } void StmtPrinter::VisitWhileStmt(WhileStmt *Node) { @@ -303,31 +320,19 @@ void StmtPrinter::VisitDoStmt(DoStmt *No void StmtPrinter::VisitForStmt(ForStmt *Node) { Indent() << "for ("; - if (Node->getInit()) { - if (auto *DS = dyn_cast<DeclStmt>(Node->getInit())) - PrintRawDeclStmt(DS); - else - PrintExpr(cast<Expr>(Node->getInit())); - } - OS << ";"; - if (Node->getCond()) { - OS << " "; + if (Node->getInit()) + PrintInitStmt(Node->getInit(), 5); + else + OS << (Node->getCond() ? "; " : ";"); + if (Node->getCond()) PrintExpr(Node->getCond()); - } OS << ";"; if (Node->getInc()) { OS << " "; PrintExpr(Node->getInc()); } - OS << ") "; - - if (auto *CS = dyn_cast<CompoundStmt>(Node->getBody())) { - PrintRawCompoundStmt(CS); - OS << NL; - } else { - OS << NL; - PrintStmt(Node->getBody()); - } + OS << ")"; + PrintControlledStmt(Node->getBody()); } void StmtPrinter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *Node) { @@ -338,28 +343,21 @@ void StmtPrinter::VisitObjCForCollection PrintExpr(cast<Expr>(Node->getElement())); OS << " in "; PrintExpr(Node->getCollection()); - OS << ") "; - - if (auto *CS = dyn_cast<CompoundStmt>(Node->getBody())) { - PrintRawCompoundStmt(CS); - OS << NL; - } else { - OS << NL; - PrintStmt(Node->getBody()); - } + OS << ")"; + PrintControlledStmt(Node->getBody()); } void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) { Indent() << "for ("; + if (Node->getInit()) + PrintInitStmt(Node->getInit(), 5); PrintingPolicy SubPolicy(Policy); SubPolicy.SuppressInitializers = true; Node->getLoopVariable()->print(OS, SubPolicy, IndentLevel); OS << " : "; PrintExpr(Node->getRangeInit()); - OS << ") {" << NL; - PrintStmt(Node->getBody()); - Indent() << "}"; - if (Policy.IncludeNewlines) OS << NL; + OS << ")"; + PrintControlledStmt(Node->getBody()); } void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) { Modified: cfe/trunk/lib/Analysis/CFG.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Analysis/CFG.cpp (original) +++ cfe/trunk/lib/Analysis/CFG.cpp Fri Sep 28 11:44:09 2018 @@ -4255,7 +4255,10 @@ CFGBlock *CFGBuilder::VisitCXXForRangeSt Block = createBlock(); addStmt(S->getBeginStmt()); addStmt(S->getEndStmt()); - return addStmt(S->getRangeStmt()); + CFGBlock *Head = addStmt(S->getRangeStmt()); + if (S->getInit()) + Head = addStmt(S->getInit()); + return Head; } CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E, Modified: cfe/trunk/lib/CodeGen/CGStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGStmt.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGStmt.cpp (original) +++ cfe/trunk/lib/CodeGen/CGStmt.cpp Fri Sep 28 11:44:09 2018 @@ -932,6 +932,8 @@ CodeGenFunction::EmitCXXForRangeStmt(con LexicalScope ForScope(*this, S.getSourceRange()); // Evaluate the first pieces before the loop. + if (S.getInit()) + EmitStmt(S.getInit()); EmitStmt(S.getRangeStmt()); EmitStmt(S.getBeginStmt()); EmitStmt(S.getEndStmt()); Modified: cfe/trunk/lib/CodeGen/CodeGenPGO.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenPGO.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CodeGenPGO.cpp (original) +++ cfe/trunk/lib/CodeGen/CodeGenPGO.cpp Fri Sep 28 11:44:09 2018 @@ -544,6 +544,8 @@ struct ComputeRegionCounts : public Cons void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { RecordStmtCount(S); + if (S->getInit()) + Visit(S->getInit()); Visit(S->getLoopVarStmt()); Visit(S->getRangeStmt()); Visit(S->getBeginStmt()); Modified: cfe/trunk/lib/CodeGen/CoverageMappingGen.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CoverageMappingGen.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CoverageMappingGen.cpp (original) +++ cfe/trunk/lib/CodeGen/CoverageMappingGen.cpp Fri Sep 28 11:44:09 2018 @@ -1004,6 +1004,8 @@ struct CounterCoverageMappingBuilder void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { extendRegion(S); + if (S->getInit()) + Visit(S->getInit()); Visit(S->getLoopVarStmt()); Visit(S->getRangeStmt()); Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original) +++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Fri Sep 28 11:44:09 2018 @@ -1738,10 +1738,14 @@ Parser::ParseCXXTypeConstructExpression( /// \param Loc The location of the start of the statement that requires this /// condition, e.g., the "for" in a for loop. /// +/// \param FRI If non-null, a for range declaration is permitted, and if +/// present will be parsed and stored here, and a null result will be returned. +/// /// \returns The parsed condition. Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, - Sema::ConditionKind CK) { + Sema::ConditionKind CK, + ForRangeInfo *FRI) { ParenBraceBracketBalancer BalancerRAIIObj(*this); if (Tok.is(tok::code_completion)) { @@ -1761,7 +1765,7 @@ Sema::ConditionResult Parser::ParseCXXCo }; // Determine what kind of thing we have. - switch (isCXXConditionDeclarationOrInitStatement(InitStmt)) { + switch (isCXXConditionDeclarationOrInitStatement(InitStmt, FRI)) { case ConditionOrInitStatement::Expression: { ProhibitAttributes(attrs); @@ -1799,6 +1803,15 @@ Sema::ConditionResult Parser::ParseCXXCo return ParseCXXCondition(nullptr, Loc, CK); } + case ConditionOrInitStatement::ForRangeDecl: { + assert(FRI && "should not parse a for range declaration here"); + SourceLocation DeclStart = Tok.getLocation(), DeclEnd; + DeclGroupPtrTy DG = ParseSimpleDeclaration( + DeclaratorContext::ForContext, DeclEnd, attrs, false, FRI); + FRI->LoopVar = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); + return Sema::ConditionResult(); + } + case ConditionOrInitStatement::ConditionDecl: case ConditionOrInitStatement::Error: break; Modified: cfe/trunk/lib/Parse/ParseStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseStmt.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseStmt.cpp (original) +++ cfe/trunk/lib/Parse/ParseStmt.cpp Fri Sep 28 11:44:09 2018 @@ -1570,11 +1570,11 @@ StmtResult Parser::ParseForStatement(Sou ExprResult Value; - bool ForEach = false, ForRange = false; + bool ForEach = false; StmtResult FirstPart; Sema::ConditionResult SecondPart; ExprResult Collection; - ForRangeInit ForRangeInit; + ForRangeInfo ForRangeInfo; FullExprArg ThirdPart(Actions); if (Tok.is(tok::code_completion)) { @@ -1600,20 +1600,19 @@ StmtResult Parser::ParseForStatement(Sou SourceLocation Loc = ConsumeToken(); MaybeParseCXX11Attributes(attrs); - ForRangeInit.ColonLoc = ConsumeToken(); + ForRangeInfo.ColonLoc = ConsumeToken(); if (Tok.is(tok::l_brace)) - ForRangeInit.RangeExpr = ParseBraceInitializer(); + ForRangeInfo.RangeExpr = ParseBraceInitializer(); else - ForRangeInit.RangeExpr = ParseExpression(); + ForRangeInfo.RangeExpr = ParseExpression(); Diag(Loc, diag::err_for_range_identifier) << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17) ? FixItHint::CreateInsertion(Loc, "auto &&") : FixItHint()); - FirstPart = Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, - attrs, attrs.Range.getEnd()); - ForRange = true; + ForRangeInfo.LoopVar = Actions.ActOnCXXForRangeIdentifier( + getCurScope(), Loc, Name, attrs, attrs.Range.getEnd()); } else if (isForInitDeclaration()) { // for (int X = 4; ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -1630,13 +1629,13 @@ StmtResult Parser::ParseForStatement(Sou SourceLocation DeclStart = Tok.getLocation(), DeclEnd; DeclGroupPtrTy DG = ParseSimpleDeclaration( DeclaratorContext::ForContext, DeclEnd, attrs, false, - MightBeForRangeStmt ? &ForRangeInit : nullptr); + MightBeForRangeStmt ? &ForRangeInfo : nullptr); FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); - if (ForRangeInit.ParsedForRangeDecl()) { - Diag(ForRangeInit.ColonLoc, getLangOpts().CPlusPlus11 ? + if (ForRangeInfo.ParsedForRangeDecl()) { + Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_for_range : diag::ext_for_range); - - ForRange = true; + ForRangeInfo.LoopVar = FirstPart; + FirstPart = StmtResult(); } else if (Tok.is(tok::semi)) { // for (int x = 4; ConsumeToken(); } else if ((ForEach = isTokIdentifier_in())) { @@ -1699,17 +1698,33 @@ StmtResult Parser::ParseForStatement(Sou // Parse the second part of the for specifier. getCurScope()->AddFlags(Scope::BreakScope | Scope::ContinueScope); - if (!ForEach && !ForRange && !SecondPart.isInvalid()) { + if (!ForEach && !ForRangeInfo.ParsedForRangeDecl() && + !SecondPart.isInvalid()) { // Parse the second part of the for specifier. if (Tok.is(tok::semi)) { // for (...;; // no second part. } else if (Tok.is(tok::r_paren)) { // missing both semicolons. } else { - if (getLangOpts().CPlusPlus) + if (getLangOpts().CPlusPlus) { + // C++2a: We've parsed an init-statement; we might have a + // for-range-declaration next. + bool MightBeForRangeStmt = !ForRangeInfo.ParsedForRangeDecl(); + ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt); SecondPart = - ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean); - else { + ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean, + MightBeForRangeStmt ? &ForRangeInfo : nullptr); + + if (ForRangeInfo.ParsedForRangeDecl()) { + Diag(FirstPart.get() ? FirstPart.get()->getBeginLoc() + : ForRangeInfo.ColonLoc, + getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_for_range_init_stmt + : diag::ext_for_range_init_stmt) + << (FirstPart.get() ? FirstPart.get()->getSourceRange() + : SourceRange()); + } + } else { ExprResult SecondExpr = ParseExpression(); if (SecondExpr.isInvalid()) SecondPart = Sema::ConditionError(); @@ -1719,7 +1734,10 @@ StmtResult Parser::ParseForStatement(Sou Sema::ConditionKind::Boolean); } } + } + // Parse the third part of the for statement. + if (!ForEach && !ForRangeInfo.ParsedForRangeDecl()) { if (Tok.isNot(tok::semi)) { if (!SecondPart.isInvalid()) Diag(Tok, diag::err_expected_semi_for); @@ -1732,7 +1750,6 @@ StmtResult Parser::ParseForStatement(Sou ConsumeToken(); } - // Parse the third part of the for specifier. if (Tok.isNot(tok::r_paren)) { // for (...;...;) ExprResult Third = ParseExpression(); // FIXME: The C++11 standard doesn't actually say that this is a @@ -1745,7 +1762,7 @@ StmtResult Parser::ParseForStatement(Sou // C++ Coroutines [stmt.iter]: // 'co_await' can only be used for a range-based for statement. - if (CoawaitLoc.isValid() && !ForRange) { + if (CoawaitLoc.isValid() && !ForRangeInfo.ParsedForRangeDecl()) { Diag(CoawaitLoc, diag::err_for_co_await_not_range_for); CoawaitLoc = SourceLocation(); } @@ -1756,12 +1773,12 @@ StmtResult Parser::ParseForStatement(Sou StmtResult ForRangeStmt; StmtResult ForEachStmt; - if (ForRange) { + if (ForRangeInfo.ParsedForRangeDecl()) { ExprResult CorrectedRange = - Actions.CorrectDelayedTyposInExpr(ForRangeInit.RangeExpr.get()); + Actions.CorrectDelayedTyposInExpr(ForRangeInfo.RangeExpr.get()); ForRangeStmt = Actions.ActOnCXXForRangeStmt( getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(), - ForRangeInit.ColonLoc, CorrectedRange.get(), + ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc, CorrectedRange.get(), T.getCloseLocation(), Sema::BFRK_Build); // Similarly, we need to do the semantic analysis for a for-range @@ -1816,7 +1833,7 @@ StmtResult Parser::ParseForStatement(Sou return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(), Body.get()); - if (ForRange) + if (ForRangeInfo.ParsedForRangeDecl()) return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get()); return Actions.ActOnForStmt(ForLoc, T.getOpenLocation(), FirstPart.get(), Modified: cfe/trunk/lib/Parse/ParseTentative.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTentative.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseTentative.cpp (original) +++ cfe/trunk/lib/Parse/ParseTentative.cpp Fri Sep 28 11:44:09 2018 @@ -345,22 +345,55 @@ struct Parser::ConditionDeclarationOrIni bool CanBeExpression = true; bool CanBeCondition = true; bool CanBeInitStatement; + bool CanBeForRangeDecl; - ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement) - : P(P), CanBeInitStatement(CanBeInitStatement) {} + ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement, + bool CanBeForRangeDecl) + : P(P), CanBeInitStatement(CanBeInitStatement), + CanBeForRangeDecl(CanBeForRangeDecl) {} + + bool resolved() { + return CanBeExpression + CanBeCondition + CanBeInitStatement + + CanBeForRangeDecl < 2; + } void markNotExpression() { CanBeExpression = false; - if (CanBeCondition && CanBeInitStatement) { + if (!resolved()) { // FIXME: Unify the parsing codepaths for condition variables and // simple-declarations so that we don't need to eagerly figure out which // kind we have here. (Just parse init-declarators until we reach a // semicolon or right paren.) RevertingTentativeParsingAction PA(P); - P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch); + if (CanBeForRangeDecl) { + // Skip until we hit a ')', ';', or a ':' with no matching '?'. + // The final case is a for range declaration, the rest are not. + while (true) { + unsigned QuestionColonDepth = 0; + P.SkipUntil({tok::r_paren, tok::semi, tok::question, tok::colon}, + StopBeforeMatch); + if (P.Tok.is(tok::question)) + ++QuestionColonDepth; + else if (P.Tok.is(tok::colon)) { + if (QuestionColonDepth) + --QuestionColonDepth; + else { + CanBeCondition = CanBeInitStatement = false; + return; + } + } else { + CanBeForRangeDecl = false; + break; + } + P.ConsumeToken(); + } + } else { + // Just skip until we hit a ')' or ';'. + P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch); + } if (P.Tok.isNot(tok::r_paren)) - CanBeCondition = false; + CanBeCondition = CanBeForRangeDecl = false; if (P.Tok.isNot(tok::semi)) CanBeInitStatement = false; } @@ -368,28 +401,36 @@ struct Parser::ConditionDeclarationOrIni bool markNotCondition() { CanBeCondition = false; - return !CanBeInitStatement || !CanBeExpression; + return resolved(); + } + + bool markNotForRangeDecl() { + CanBeForRangeDecl = false; + return resolved(); } bool update(TPResult IsDecl) { switch (IsDecl) { case TPResult::True: markNotExpression(); - return true; + assert(resolved() && "can't continue after tentative parsing bails out"); + break; case TPResult::False: - CanBeCondition = CanBeInitStatement = false; - return true; + CanBeCondition = CanBeInitStatement = CanBeForRangeDecl = false; + break; case TPResult::Ambiguous: - return false; + break; case TPResult::Error: - CanBeExpression = CanBeCondition = CanBeInitStatement = false; - return true; + CanBeExpression = CanBeCondition = CanBeInitStatement = + CanBeForRangeDecl = false; + break; } - llvm_unreachable("unknown tentative parse result"); + return resolved(); } ConditionOrInitStatement result() const { - assert(CanBeExpression + CanBeCondition + CanBeInitStatement < 2 && + assert(CanBeExpression + CanBeCondition + CanBeInitStatement + + CanBeForRangeDecl < 2 && "result called but not yet resolved"); if (CanBeExpression) return ConditionOrInitStatement::Expression; @@ -397,6 +438,8 @@ struct Parser::ConditionDeclarationOrIni return ConditionOrInitStatement::ConditionDecl; if (CanBeInitStatement) return ConditionOrInitStatement::InitStmtDecl; + if (CanBeForRangeDecl) + return ConditionOrInitStatement::ForRangeDecl; return ConditionOrInitStatement::Error; } }; @@ -419,8 +462,10 @@ struct Parser::ConditionDeclarationOrIni /// to the ';' to disambiguate cases like 'int(x))' (an expression) from /// 'int(x);' (a simple-declaration in an init-statement). Parser::ConditionOrInitStatement -Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement) { - ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement); +Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement, + bool CanBeForRangeDecl) { + ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement, + CanBeForRangeDecl); if (State.update(isCXXDeclarationSpecifier())) return State.result(); @@ -447,11 +492,19 @@ Parser::isCXXConditionDeclarationOrInitS return State.result(); } + // A colon here identifies a for-range declaration. + if (State.CanBeForRangeDecl && Tok.is(tok::colon)) + return ConditionOrInitStatement::ForRangeDecl; + // At this point, it can't be a condition any more, because a condition // must have a brace-or-equal-initializer. if (State.markNotCondition()) return State.result(); + // Likewise, it can't be a for-range declaration any more. + if (State.markNotForRangeDecl()) + return State.result(); + // A parenthesized initializer could be part of an expression or a // simple-declaration. if (Tok.is(tok::l_paren)) { Modified: cfe/trunk/lib/Sema/SemaStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaStmt.cpp (original) +++ cfe/trunk/lib/Sema/SemaStmt.cpp Fri Sep 28 11:44:09 2018 @@ -1647,6 +1647,8 @@ namespace { void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { // Only visit the initialization of a for loop; the body // has a different break/continue scope. + if (const Stmt *Init = S->getInit()) + Visit(Init); if (const Stmt *Range = S->getRangeStmt()) Visit(Range); if (const Stmt *Begin = S->getBeginStmt()) @@ -2065,15 +2067,20 @@ static bool ObjCEnumerationCollection(Ex /// The body of the loop is not available yet, since it cannot be analysed until /// we have determined the type of the for-range-declaration. StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc, - SourceLocation CoawaitLoc, Stmt *First, - SourceLocation ColonLoc, Expr *Range, - SourceLocation RParenLoc, + SourceLocation CoawaitLoc, Stmt *InitStmt, + Stmt *First, SourceLocation ColonLoc, + Expr *Range, SourceLocation RParenLoc, BuildForRangeKind Kind) { if (!First) return StmtError(); - if (Range && ObjCEnumerationCollection(Range)) + if (Range && ObjCEnumerationCollection(Range)) { + // FIXME: Support init-statements in Objective-C++20 ranged for statement. + if (InitStmt) + return Diag(InitStmt->getBeginLoc(), diag::err_objc_for_range_init_stmt) + << InitStmt->getSourceRange(); return ActOnObjCForCollectionStmt(ForLoc, First, Range, RParenLoc); + } DeclStmt *DS = dyn_cast<DeclStmt>(First); assert(DS && "first part of for range not a decl stmt"); @@ -2119,10 +2126,10 @@ StmtResult Sema::ActOnCXXForRangeStmt(Sc return StmtError(); } - return BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc, RangeDecl.get(), - /*BeginStmt=*/nullptr, /*EndStmt=*/nullptr, - /*Cond=*/nullptr, /*Inc=*/nullptr, - DS, RParenLoc, Kind); + return BuildCXXForRangeStmt( + ForLoc, CoawaitLoc, InitStmt, ColonLoc, RangeDecl.get(), + /*BeginStmt=*/nullptr, /*EndStmt=*/nullptr, + /*Cond=*/nullptr, /*Inc=*/nullptr, DS, RParenLoc, Kind); } /// Create the initialization, compare, and increment steps for @@ -2270,6 +2277,7 @@ BuildNonArrayForRange(Sema &SemaRef, Exp static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, + Stmt *InitStmt, Stmt *LoopVarDecl, SourceLocation ColonLoc, Expr *Range, @@ -2286,8 +2294,8 @@ static StmtResult RebuildForRangeWithDer return StmtResult(); StmtResult SR = SemaRef.ActOnCXXForRangeStmt( - S, ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, AdjustedRange.get(), - RParenLoc, Sema::BFRK_Check); + S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc, + AdjustedRange.get(), RParenLoc, Sema::BFRK_Check); if (SR.isInvalid()) return StmtResult(); } @@ -2297,9 +2305,9 @@ static StmtResult RebuildForRangeWithDer // case there are any other (non-fatal) problems with it. SemaRef.Diag(RangeLoc, diag::err_for_range_dereference) << Range->getType() << FixItHint::CreateInsertion(RangeLoc, "*"); - return SemaRef.ActOnCXXForRangeStmt(S, ForLoc, CoawaitLoc, LoopVarDecl, - ColonLoc, AdjustedRange.get(), RParenLoc, - Sema::BFRK_Rebuild); + return SemaRef.ActOnCXXForRangeStmt( + S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc, + AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild); } namespace { @@ -2319,12 +2327,13 @@ struct InvalidateOnErrorScope { } /// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement. -StmtResult -Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, - SourceLocation ColonLoc, Stmt *RangeDecl, - Stmt *Begin, Stmt *End, Expr *Cond, - Expr *Inc, Stmt *LoopVarDecl, - SourceLocation RParenLoc, BuildForRangeKind Kind) { +StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, + SourceLocation CoawaitLoc, Stmt *InitStmt, + SourceLocation ColonLoc, Stmt *RangeDecl, + Stmt *Begin, Stmt *End, Expr *Cond, + Expr *Inc, Stmt *LoopVarDecl, + SourceLocation RParenLoc, + BuildForRangeKind Kind) { // FIXME: This should not be used during template instantiation. We should // pick up the set of unqualified lookup results for the != and + operators // in the initial parse. @@ -2519,7 +2528,7 @@ Sema::BuildCXXForRangeStmt(SourceLocatio // If building the range failed, try dereferencing the range expression // unless a diagnostic was issued or the end function is problematic. StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc, - CoawaitLoc, + CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc, Range, RangeLoc, RParenLoc); @@ -2636,7 +2645,7 @@ Sema::BuildCXXForRangeStmt(SourceLocatio return StmtResult(); return new (Context) CXXForRangeStmt( - RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()), + InitStmt, RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()), cast_or_null<DeclStmt>(EndDeclStmt.get()), NotEqExpr.get(), IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, CoawaitLoc, ColonLoc, RParenLoc); Modified: cfe/trunk/lib/Sema/TreeTransform.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Sema/TreeTransform.h (original) +++ cfe/trunk/lib/Sema/TreeTransform.h Fri Sep 28 11:44:09 2018 @@ -2021,11 +2021,10 @@ public: /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildCXXForRangeStmt(SourceLocation ForLoc, - SourceLocation CoawaitLoc, - SourceLocation ColonLoc, - Stmt *Range, Stmt *Begin, Stmt *End, - Expr *Cond, Expr *Inc, - Stmt *LoopVar, + SourceLocation CoawaitLoc, Stmt *Init, + SourceLocation ColonLoc, Stmt *Range, + Stmt *Begin, Stmt *End, Expr *Cond, + Expr *Inc, Stmt *LoopVar, SourceLocation RParenLoc) { // If we've just learned that the range is actually an Objective-C // collection, treat this as an Objective-C fast enumeration loop. @@ -2037,17 +2036,24 @@ public: Expr *RangeExpr = RangeVar->getInit(); if (!RangeExpr->isTypeDependent() && - RangeExpr->getType()->isObjCObjectPointerType()) - return getSema().ActOnObjCForCollectionStmt(ForLoc, LoopVar, RangeExpr, - RParenLoc); + RangeExpr->getType()->isObjCObjectPointerType()) { + // FIXME: Support init-statements in Objective-C++20 ranged for + // statement. + if (Init) { + return SemaRef.Diag(Init->getBeginLoc(), + diag::err_objc_for_range_init_stmt) + << Init->getSourceRange(); + } + return getSema().ActOnObjCForCollectionStmt(ForLoc, LoopVar, + RangeExpr, RParenLoc); + } } } } - return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc, - Range, Begin, End, - Cond, Inc, LoopVar, RParenLoc, - Sema::BFRK_Rebuild); + return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, Init, ColonLoc, + Range, Begin, End, Cond, Inc, LoopVar, + RParenLoc, Sema::BFRK_Rebuild); } /// Build a new C++0x range-based for statement. @@ -7412,6 +7418,11 @@ StmtResult TreeTransform<Derived>::Trans template<typename Derived> StmtResult TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) { + StmtResult Init = + S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult(); + if (Init.isInvalid()) + return StmtError(); + StmtResult Range = getDerived().TransformStmt(S->getRangeStmt()); if (Range.isInvalid()) return StmtError(); @@ -7445,6 +7456,7 @@ TreeTransform<Derived>::TransformCXXForR StmtResult NewStmt = S; if (getDerived().AlwaysRebuild() || + Init.get() != S->getInit() || Range.get() != S->getRangeStmt() || Begin.get() != S->getBeginStmt() || End.get() != S->getEndStmt() || @@ -7452,7 +7464,7 @@ TreeTransform<Derived>::TransformCXXForR Inc.get() != S->getInc() || LoopVar.get() != S->getLoopVarStmt()) { NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(), - S->getCoawaitLoc(), + S->getCoawaitLoc(), Init.get(), S->getColonLoc(), Range.get(), Begin.get(), End.get(), Cond.get(), @@ -7470,7 +7482,7 @@ TreeTransform<Derived>::TransformCXXForR // it now so we have a new statement to attach the body to. if (Body.get() != S->getBody() && NewStmt.get() == S) { NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(), - S->getCoawaitLoc(), + S->getCoawaitLoc(), Init.get(), S->getColonLoc(), Range.get(), Begin.get(), End.get(), Cond.get(), Modified: cfe/trunk/lib/Serialization/ASTReaderStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderStmt.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTReaderStmt.cpp (original) +++ cfe/trunk/lib/Serialization/ASTReaderStmt.cpp Fri Sep 28 11:44:09 2018 @@ -1270,6 +1270,7 @@ void ASTStmtReader::VisitCXXForRangeStmt S->CoawaitLoc = ReadSourceLocation(); S->ColonLoc = ReadSourceLocation(); S->RParenLoc = ReadSourceLocation(); + S->setInit(Record.readSubStmt()); S->setRangeStmt(Record.readSubStmt()); S->setBeginStmt(Record.readSubStmt()); S->setEndStmt(Record.readSubStmt()); Modified: cfe/trunk/lib/Serialization/ASTWriterStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterStmt.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTWriterStmt.cpp (original) +++ cfe/trunk/lib/Serialization/ASTWriterStmt.cpp Fri Sep 28 11:44:09 2018 @@ -1222,6 +1222,7 @@ void ASTStmtWriter::VisitCXXForRangeStmt Record.AddSourceLocation(S->getCoawaitLoc()); Record.AddSourceLocation(S->getColonLoc()); Record.AddSourceLocation(S->getRParenLoc()); + Record.AddStmt(S->getInit()); Record.AddStmt(S->getRangeStmt()); Record.AddStmt(S->getBeginStmt()); Record.AddStmt(S->getEndStmt()); Modified: cfe/trunk/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist (original) +++ cfe/trunk/test/Analysis/Inputs/expected-plists/cxx-for-range.cpp.plist Fri Sep 28 11:44:09 2018 @@ -1670,6 +1670,287 @@ </array> </dict> </dict> + <dict> + <key>path</key> + <array> + <dict> + <key>kind</key><string>control</string> + <key>edges</key> + <array> + <dict> + <key>start</key> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>3</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>5</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + <key>end</key> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>10</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </dict> + </array> + </dict> + <dict> + <key>kind</key><string>event</string> + <key>location</key> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + <key>ranges</key> + <array> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>15</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </array> + <key>depth</key><integer>0</integer> + <key>extended_message</key> + <string>Initializing to a null pointer value</string> + <key>message</key> + <string>Initializing to a null pointer value</string> + </dict> + <dict> + <key>kind</key><string>control</string> + <key>edges</key> + <array> + <dict> + <key>start</key> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>10</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + <key>end</key> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>58</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>58</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </dict> + </array> + </dict> + <dict> + <key>kind</key><string>event</string> + <key>location</key> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>58</integer> + <key>file</key><integer>0</integer> + </dict> + <key>ranges</key> + <array> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>58</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>58</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </array> + <key>depth</key><integer>0</integer> + <key>extended_message</key> + <string>Entering loop body</string> + <key>message</key> + <string>Entering loop body</string> + </dict> + <dict> + <key>kind</key><string>control</string> + <key>edges</key> + <array> + <dict> + <key>start</key> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>58</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>58</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + <key>end</key> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>51</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>53</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </dict> + </array> + </dict> + <dict> + <key>kind</key><string>event</string> + <key>location</key> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>51</integer> + <key>file</key><integer>0</integer> + </dict> + <key>ranges</key> + <array> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>51</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>56</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </array> + <key>depth</key><integer>0</integer> + <key>extended_message</key> + <string>'p' initialized to a null pointer value</string> + <key>message</key> + <string>'p' initialized to a null pointer value</string> + </dict> + <dict> + <key>kind</key><string>control</string> + <key>edges</key> + <array> + <dict> + <key>start</key> + <array> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>51</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>102</integer> + <key>col</key><integer>53</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + <key>end</key> + <array> + <dict> + <key>line</key><integer>103</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>103</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </dict> + </array> + </dict> + <dict> + <key>kind</key><string>event</string> + <key>location</key> + <dict> + <key>line</key><integer>103</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + <key>ranges</key> + <array> + <array> + <dict> + <key>line</key><integer>103</integer> + <key>col</key><integer>6</integer> + <key>file</key><integer>0</integer> + </dict> + <dict> + <key>line</key><integer>103</integer> + <key>col</key><integer>6</integer> + <key>file</key><integer>0</integer> + </dict> + </array> + </array> + <key>depth</key><integer>0</integer> + <key>extended_message</key> + <string>Dereference of null pointer (loaded from variable 'p')</string> + <key>message</key> + <string>Dereference of null pointer (loaded from variable 'p')</string> + </dict> + </array> + <key>description</key><string>Dereference of null pointer (loaded from variable 'p')</string> + <key>category</key><string>Logic error</string> + <key>type</key><string>Dereference of null pointer</string> + <key>check_name</key><string>core.NullDereference</string> + <!-- This hash is experimental and going to change! --> + <key>issue_hash_content_of_line_in_context</key><string>ad377f8d4510dfd77d6485c402d57a2d</string> + <key>issue_context_kind</key><string>function</string> + <key>issue_context</key><string>testForRangeInit</string> + <key>issue_hash_function_offset</key><string>2</string> + <key>location</key> + <dict> + <key>line</key><integer>103</integer> + <key>col</key><integer>8</integer> + <key>file</key><integer>0</integer> + </dict> + <key>ExecutedLines</key> + <dict> + <key>0</key> + <array> + <integer>101</integer> + <integer>102</integer> + <integer>103</integer> + </array> + </dict> + </dict> </array> <key>files</key> <array> Modified: cfe/trunk/test/Analysis/cxx-for-range.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cxx-for-range.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/Analysis/cxx-for-range.cpp (original) +++ cfe/trunk/test/Analysis/cxx-for-range.cpp Fri Sep 28 11:44:09 2018 @@ -97,3 +97,8 @@ void testLoopErrorInRange() { *(volatile int *)0 = 1; // no-warning } + +void testForRangeInit() { + for (int *arr[3] = {nullptr, nullptr, nullptr}; int *p : arr) // expected-warning {{extension}} + *p = 1; // expected-warning {{Dereference of null pointer}} +} Modified: cfe/trunk/test/Analysis/scopes-cfg-output.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/scopes-cfg-output.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/Analysis/scopes-cfg-output.cpp (original) +++ cfe/trunk/test/Analysis/scopes-cfg-output.cpp Fri Sep 28 11:44:09 2018 @@ -820,10 +820,9 @@ void test_for_compound_and_break() { // CHECK-NEXT: 3: __end1 // CHECK-NEXT: 4: [B2.3] (ImplicitCastExpr, LValueToRValue, class A *) // CHECK-NEXT: 5: [B2.2] != [B2.4] -// CHECK-NEXT: T: for (auto &i : [B5.4]) { +// CHECK-NEXT: T: for (auto &i : [B5.4]) // CHECK: [B4.11]; -// CHECK-NEXT:} -// CHECK-NEXT: Preds (2): B3 B5 +// CHECK: Preds (2): B3 B5 // CHECK-NEXT: Succs (2): B4 B1 // CHECK: [B3] // CHECK-NEXT: 1: __begin1 Added: cfe/trunk/test/CodeGenCXX/cxx2a-init-statement.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx2a-init-statement.cpp?rev=343350&view=auto ============================================================================== --- cfe/trunk/test/CodeGenCXX/cxx2a-init-statement.cpp (added) +++ cfe/trunk/test/CodeGenCXX/cxx2a-init-statement.cpp Fri Sep 28 11:44:09 2018 @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -std=c++2a -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s -w | FileCheck %s + +// CHECK: @_ZZ1fvE3arr = private unnamed_addr constant [3 x i32] [i32 1, i32 2, i32 3], align 4 + +void f() { + // CHECK: %[[ARR:.*]] = alloca [3 x i32], align 4 + // CHECK: call void @llvm.memcpy{{.*}}({{.*}} @_ZZ1fvE3arr + for (int arr[3] = {1, 2, 3}; int a : arr) + ; +} Modified: cfe/trunk/test/Import/cxx-for-range/Inputs/F.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Import/cxx-for-range/Inputs/F.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/Import/cxx-for-range/Inputs/F.cpp (original) +++ cfe/trunk/test/Import/cxx-for-range/Inputs/F.cpp Fri Sep 28 11:44:09 2018 @@ -4,8 +4,7 @@ struct Container { }; void f() { - Container c; - for (int varname : c) { + for (Container c; int varname : c) { return; } } Modified: cfe/trunk/test/Import/cxx-for-range/test.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Import/cxx-for-range/test.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/Import/cxx-for-range/test.cpp (original) +++ cfe/trunk/test/Import/cxx-for-range/test.cpp Fri Sep 28 11:44:09 2018 @@ -4,6 +4,11 @@ // CHECK-NEXT: DeclStmt // CHECK-NEXT: VarDecl +// CHECK-SAME: c 'Container' +// CHECK-NEXT: CXXConstructExpr + +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl // CHECK-NEXT: DeclRefExpr // CHECK-SAME: 'c' // CHECK-SAME: Container Added: cfe/trunk/test/PCH/cxx2a-for-init-statement.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx2a-for-init-statement.cpp?rev=343350&view=auto ============================================================================== --- cfe/trunk/test/PCH/cxx2a-for-init-statement.cpp (added) +++ cfe/trunk/test/PCH/cxx2a-for-init-statement.cpp Fri Sep 28 11:44:09 2018 @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -emit-pch -std=c++2a -o %t %s +// RUN: %clang_cc1 -std=c++2a -x ast -ast-print %t | FileCheck %s + +void f() { + // CHECK: for (int arr[3]; int n : arr) { + // CHECK-NEXT: } + for (int arr[3]; int n : arr) {} +} Added: cfe/trunk/test/Parser/cxx2a-init-statement.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx2a-init-statement.cpp?rev=343350&view=auto ============================================================================== --- cfe/trunk/test/Parser/cxx2a-init-statement.cpp (added) +++ cfe/trunk/test/Parser/cxx2a-init-statement.cpp Fri Sep 28 11:44:09 2018 @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +template<int N> struct A {}; + +using F = bool(*)(int); +extern F *p; +extern int m; + +struct Convertible { template<typename T> operator T(); }; + +void f() { + int arr1[3]; + for (int n = 5; int x : arr1) {} + + int A<0>::*arr2[3]; + for (int n = 5; int A<true ? 0 : 1>::*x : arr2) {} + + F (*arr3[3])(int); + for (int n = 5; F (*p)(int n) : arr3) {} + for (int n = 5; F (*p)(int (n)) : arr3) {} + + // Here, we have a declaration rather than an expression. + for (int n = 5; F (*p)(int (n)); ++n) {} + + // We detect whether we have a for-range-declaration before parsing so that + // we can give different diagnostics for for-range-declarations versus + // conditions (even though the rules are currently identical). + Convertible arr4[3]; + for (int n = 0; struct { operator bool(); } x = {}; ++n) {} // expected-error {{cannot be defined in a condition}} + for (int n = 0; struct { operator bool(); } x : arr4) {} // expected-error {{may not be defined in a for range declaration}} + + for (int n = 0; static int m = 0; ++n) {} // expected-error {{type name does not allow storage class}} + for (int n = 0; static int m : arr1) {} // expected-error {{loop variable 'm' may not be declared 'static'}} +} Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original) +++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Fri Sep 28 11:44:09 2018 @@ -204,3 +204,10 @@ static_assert(complex_test(makeComplex<_ std::strong_equality::nonequal)); // TODO: defaulted operator <=> } // namespace ThreeWayComparison + +constexpr bool for_range_init() { + int k = 0; + for (int arr[3] = {1, 2, 3}; int n : arr) k += n; + return k == 6; +} +static_assert(for_range_init()); Modified: cfe/trunk/test/SemaCXX/cxx17-compat.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx17-compat.cpp?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/cxx17-compat.cpp (original) +++ cfe/trunk/test/SemaCXX/cxx17-compat.cpp Fri Sep 28 11:44:09 2018 @@ -54,3 +54,12 @@ struct DefaultDeleteWrongType : DefaultD // expected-warning@-4 {{explicitly defaulting this copy constructor with a type different from the implicit type is incompatible with C++ standards before C++2a}} #endif }; + +void ForRangeInit() { + for (int arr[3] = {1, 2, 3}; int n : arr) {} +#if __cplusplus <= 201703L + // expected-warning@-2 {{range-based for loop initialization statements are a C++2a extension}} +#else + // expected-warning@-4 {{range-based for loop initialization statements are incompatible with C++ standards before C++2a}} +#endif +} Modified: cfe/trunk/test/SemaObjCXX/foreach.mm URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/foreach.mm?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/test/SemaObjCXX/foreach.mm (original) +++ cfe/trunk/test/SemaObjCXX/foreach.mm Fri Sep 28 11:44:09 2018 @@ -14,6 +14,14 @@ void f(NSArray *a) { for (auto thisKey : keys) { } // expected-warning{{'auto' deduced as 'id' in declaration of 'thisKey'}} } +void for_init_stmt() { + for (id keys; id key : keys) {} // expected-warning{{extension}} expected-error{{not supported}} +} +template<typename T> void for_init_stmt_tmpl() { + for (T keys; id key : keys) {} // expected-warning{{extension}} expected-error{{not supported}} +} +template void for_init_stmt_tmpl<id>(); // expected-note {{in instantiation of}} + template<typename Collection> void ft(Collection col) { for (id x : col) { } Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=343350&r1=343349&r2=343350&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Fri Sep 28 11:44:09 2018 @@ -874,7 +874,7 @@ as the draft C++2a standard evolves. <tr> <td>Range-based for statements with initializer</td> <td><a href="http://wg21.link/p0614r1">P0614R1</a></td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <td>ADL and function templates that are not visible</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits