From ad762fcdc16b9e4705b12b09d92b8c026212b906 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 14 Apr 2011 22:09:26 +0000 Subject: [PATCH] Add support for C++0x's range-based for loops, as specified by the C++11 draft standard (N3291). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@129541 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 10 + include/clang/AST/Decl.h | 11 +- include/clang/AST/ExprCXX.h | 26 +- include/clang/AST/PrettyPrinter.h | 14 + include/clang/AST/RecursiveASTVisitor.h | 1 + include/clang/AST/StmtCXX.h | 82 ++++ .../clang/Analysis/Visitors/CFGStmtVisitor.h | 5 + include/clang/Basic/DiagnosticSemaKinds.td | 22 + include/clang/Basic/StmtNodes.td | 1 + include/clang/Parse/Parser.h | 18 +- include/clang/Sema/Sema.h | 18 +- include/clang/Serialization/ASTBitCodes.h | 8 +- lib/AST/ASTContext.cpp | 16 + lib/AST/DeclPrinter.cpp | 3 +- lib/AST/ExprCXX.cpp | 2 +- lib/AST/Stmt.cpp | 34 ++ lib/AST/StmtPrinter.cpp | 12 + lib/AST/StmtProfile.cpp | 4 + lib/Analysis/CFG.cpp | 120 ++++++ lib/CodeGen/CGStmt.cpp | 77 ++++ lib/CodeGen/CodeGenFunction.h | 2 + lib/Parse/ParseDecl.cpp | 72 +++- lib/Parse/ParseStmt.cpp | 54 ++- lib/Sema/SemaDecl.cpp | 41 ++ lib/Sema/SemaLookup.cpp | 5 +- lib/Sema/SemaOverload.cpp | 13 +- lib/Sema/SemaStmt.cpp | 382 ++++++++++++++++++ lib/Sema/SemaTemplateInstantiateDecl.cpp | 4 +- lib/Sema/TreeTransform.h | 77 ++++ lib/Serialization/ASTReader.cpp | 5 + lib/Serialization/ASTReaderDecl.cpp | 1 + lib/Serialization/ASTReaderStmt.cpp | 20 + lib/Serialization/ASTWriter.cpp | 2 + lib/Serialization/ASTWriterDecl.cpp | 2 + lib/Serialization/ASTWriterStmt.cpp | 17 + lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + .../basic.scope/basic.scope.local/p4-0x.cpp | 68 ++++ test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp | 44 ++ .../stmt.stmt/stmt.iter/stmt.ranged/p1.cpp | 210 ++++++++++ test/CXX/temp/temp.decls/temp.variadic/p5.cpp | 4 + test/CodeGenCXX/for-range-temporaries.cpp | 131 ++++++ test/CodeGenCXX/for-range.cpp | 128 ++++++ test/PCH/cxx-for-range.cpp | 19 + test/PCH/cxx-for-range.h | 35 ++ test/SemaCXX/for-range-examples.cpp | 150 +++++++ test/SemaCXX/for-range-no-std.cpp | 37 ++ tools/libclang/CXCursor.cpp | 1 + 47 files changed, 1961 insertions(+), 48 deletions(-) create mode 100644 test/CXX/basic/basic.scope/basic.scope.local/p4-0x.cpp create mode 100644 test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp create mode 100644 test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp create mode 100644 test/CodeGenCXX/for-range-temporaries.cpp create mode 100644 test/CodeGenCXX/for-range.cpp create mode 100644 test/PCH/cxx-for-range.cpp create mode 100644 test/PCH/cxx-for-range.h create mode 100644 test/SemaCXX/for-range-examples.cpp create mode 100644 test/SemaCXX/for-range-no-std.cpp diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index c330f4cda2..7a62af7eea 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -423,6 +423,10 @@ public: CanQualType OverloadTy, DependentTy, UnknownAnyTy; CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy; + // Types for deductions in C++0x [stmt.ranged]'s desugaring. Built on demand. + mutable QualType AutoDeductTy; // Deduction against 'auto'. + mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'. + ASTContext(const LangOptions& LOpts, SourceManager &SM, const TargetInfo &t, IdentifierTable &idents, SelectorTable &sels, Builtin::Context &builtins, @@ -745,6 +749,12 @@ public: /// getAutoType - C++0x deduced auto type. QualType getAutoType(QualType DeducedType) const; + /// getAutoDeductType - C++0x deduction pattern for 'auto' type. + QualType getAutoDeductType() const; + + /// getAutoRRefDeductType - C++0x deduction pattern for 'auto &&' type. + QualType getAutoRRefDeductType() const; + /// getTagDeclType - Return the unique reference to the type for the /// specified TagDecl (struct/union/class/enum) decl. QualType getTagDeclType(const TagDecl *Decl) const; diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 4dd3db7fad..48b7d9f1bf 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -696,6 +696,10 @@ private: /// slot of its function, enabling the named return value optimization (NRVO). bool NRVOVariable : 1; + /// \brief Whether this variable is the for-range-declaration in a C++0x + /// for-range statement. + bool CXXForRangeDecl : 1; + friend class StmtIteratorBase; friend class ASTDeclReader; @@ -706,7 +710,7 @@ protected: StorageClass SCAsWritten) : DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc), Init(), ThreadSpecified(false), HasCXXDirectInit(false), - ExceptionVar(false), NRVOVariable(false) { + ExceptionVar(false), NRVOVariable(false), CXXForRangeDecl(false) { SClass = SC; SClassAsWritten = SCAsWritten; } @@ -1051,6 +1055,11 @@ public: /// NRVO candidate. bool isNRVOVariable() const { return NRVOVariable; } void setNRVOVariable(bool NRVO) { NRVOVariable = NRVO; } + + /// \brief Determine whether this variable is the for-range-declaration in + /// a C++0x for-range statement. + bool isCXXForRangeDecl() const { return CXXForRangeDecl; } + void setCXXForRangeDecl(bool FRD) { CXXForRangeDecl = FRD; } /// \brief If this variable is an instantiated static data member of a /// class template specialization, returns the templated static data member diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 9bb1a50be9..ed4661b3a4 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1738,6 +1738,10 @@ class UnresolvedLookupExpr : public OverloadExpr { /// call. bool RequiresADL; + /// True if namespace ::std should be considered an associated namespace + /// for the purposes of argument-dependent lookup. See C++0x [stmt.ranged]p1. + bool StdIsAssociatedNamespace; + /// True if these lookup results are overloaded. This is pretty /// trivially rederivable if we urgently need to kill this field. bool Overloaded; @@ -1755,15 +1759,19 @@ class UnresolvedLookupExpr : public OverloadExpr { const DeclarationNameInfo &NameInfo, bool RequiresADL, bool Overloaded, const TemplateArgumentListInfo *TemplateArgs, - UnresolvedSetIterator Begin, UnresolvedSetIterator End) + UnresolvedSetIterator Begin, UnresolvedSetIterator End, + bool StdIsAssociatedNamespace) : OverloadExpr(UnresolvedLookupExprClass, C, QualifierLoc, NameInfo, TemplateArgs, Begin, End), - RequiresADL(RequiresADL), Overloaded(Overloaded), NamingClass(NamingClass) + RequiresADL(RequiresADL), + StdIsAssociatedNamespace(StdIsAssociatedNamespace), + Overloaded(Overloaded), NamingClass(NamingClass) {} UnresolvedLookupExpr(EmptyShell Empty) : OverloadExpr(UnresolvedLookupExprClass, Empty), - RequiresADL(false), Overloaded(false), NamingClass(0) + RequiresADL(false), StdIsAssociatedNamespace(false), Overloaded(false), + NamingClass(0) {} friend class ASTStmtReader; @@ -1775,9 +1783,13 @@ public: const DeclarationNameInfo &NameInfo, bool ADL, bool Overloaded, UnresolvedSetIterator Begin, - UnresolvedSetIterator End) { + UnresolvedSetIterator End, + bool StdIsAssociatedNamespace = false) { + assert((ADL || !StdIsAssociatedNamespace) && + "std considered associated namespace when not performing ADL"); return new(C) UnresolvedLookupExpr(C, NamingClass, QualifierLoc, NameInfo, - ADL, Overloaded, 0, Begin, End); + ADL, Overloaded, 0, Begin, End, + StdIsAssociatedNamespace); } static UnresolvedLookupExpr *Create(ASTContext &C, @@ -1797,6 +1809,10 @@ public: /// argument-dependent lookup. bool requiresADL() const { return RequiresADL; } + /// True if namespace ::std should be artificially added to the set of + /// associated namespaecs for argument-dependent lookup purposes. + bool isStdAssociatedNamespace() const { return StdIsAssociatedNamespace; } + /// True if this lookup is overloaded. bool isOverloaded() const { return Overloaded; } diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h index 5d52cde4b1..cf5fadbd18 100644 --- a/include/clang/AST/PrettyPrinter.h +++ b/include/clang/AST/PrettyPrinter.h @@ -39,6 +39,7 @@ struct PrintingPolicy { PrintingPolicy(const LangOptions &LO) : Indentation(2), LangOpts(LO), SuppressSpecifiers(false), SuppressTagKeyword(false), SuppressTag(false), SuppressScope(false), + SuppressInitializers(false), Dump(false), ConstantArraySizeAsWritten(false), AnonymousTagLocations(true) { } @@ -87,6 +88,19 @@ struct PrintingPolicy { /// \brief Suppresses printing of scope specifiers. bool SuppressScope : 1; + /// \brief Suppress printing of variable initializers. + /// + /// This flag is used when printing the loop variable in a for-range + /// statement. For example, given: + /// + /// \code + /// for (auto x : coll) + /// \endcode + /// + /// SuppressInitializers will be true when printing "auto x", so that the + /// internal initializer constructed for x will not be printed. + bool SuppressInitializers : 1; + /// \brief True when we are "dumping" rather than "pretty-printing", /// where dumping involves printing the internal details of the AST /// and pretty-printing involves printing something similar to diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index ccc327b5b6..2a4348feac 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1696,6 +1696,7 @@ DEF_TRAVERSE_STMT(ObjCAtSynchronizedStmt, { }) DEF_TRAVERSE_STMT(ObjCAtThrowStmt, { }) DEF_TRAVERSE_STMT(ObjCAtTryStmt, { }) DEF_TRAVERSE_STMT(ObjCForCollectionStmt, { }) +DEF_TRAVERSE_STMT(CXXForRangeStmt, { }) DEF_TRAVERSE_STMT(ReturnStmt, { }) DEF_TRAVERSE_STMT(SwitchStmt, { }) DEF_TRAVERSE_STMT(WhileStmt, { }) diff --git a/include/clang/AST/StmtCXX.h b/include/clang/AST/StmtCXX.h index f08815fd56..42dcf2bb7b 100644 --- a/include/clang/AST/StmtCXX.h +++ b/include/clang/AST/StmtCXX.h @@ -119,6 +119,88 @@ public: friend class ASTStmtReader; }; +/// CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for +/// statement, represented as 'for (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 { + enum { RANGE, BEGINEND, COND, INC, LOOPVAR, BODY, END }; + // SubExprs[RANGE] is an expression or declstmt. + // SubExprs[COND] and SubExprs[INC] are expressions. + Stmt *SubExprs[END]; + SourceLocation ForLoc; + SourceLocation ColonLoc; + SourceLocation RParenLoc; +public: + CXXForRangeStmt(DeclStmt *Range, DeclStmt *BeginEnd, + Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body, + SourceLocation FL, SourceLocation CL, SourceLocation RPL); + CXXForRangeStmt(EmptyShell Empty) : Stmt(CXXForRangeStmtClass, Empty) { } + + + VarDecl *getLoopVariable(); + Expr *getRangeInit(); + + const VarDecl *getLoopVariable() const; + const Expr *getRangeInit() const; + + + DeclStmt *getRangeStmt() { return cast(SubExprs[RANGE]); } + DeclStmt *getBeginEndStmt() { return cast_or_null(SubExprs[BEGINEND]); } + Expr *getCond() { return cast_or_null(SubExprs[COND]); } + Expr *getInc() { return cast_or_null(SubExprs[INC]); } + DeclStmt *getLoopVarStmt() { return cast(SubExprs[LOOPVAR]); } + Stmt *getBody() { return SubExprs[BODY]; } + + const DeclStmt *getRangeStmt() const { + return cast(SubExprs[RANGE]); + } + const DeclStmt *getBeginEndStmt() const { + return cast_or_null(SubExprs[BEGINEND]); + } + const Expr *getCond() const { + return cast_or_null(SubExprs[COND]); + } + const Expr *getInc() const { + return cast_or_null(SubExprs[INC]); + } + const DeclStmt *getLoopVarStmt() const { + return cast(SubExprs[LOOPVAR]); + } + const Stmt *getBody() const { return SubExprs[BODY]; } + + void setRangeInit(Expr *E) { SubExprs[RANGE] = reinterpret_cast(E); } + void setRangeStmt(Stmt *S) { SubExprs[RANGE] = S; } + void setBeginEndStmt(Stmt *S) { SubExprs[BEGINEND] = S; } + void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast(E); } + void setInc(Expr *E) { SubExprs[INC] = reinterpret_cast(E); } + void setLoopVarStmt(Stmt *S) { SubExprs[LOOPVAR] = S; } + void setBody(Stmt *S) { SubExprs[BODY] = S; } + + + SourceLocation getForLoc() const { return ForLoc; } + void setForLoc(SourceLocation Loc) { ForLoc = Loc; } + SourceLocation getColonLoc() const { return ColonLoc; } + void setColonLoc(SourceLocation Loc) { ColonLoc = Loc; } + SourceLocation getRParenLoc() const { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceRange getSourceRange() const { + return SourceRange(ForLoc, SubExprs[BODY]->getLocEnd()); + } + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXForRangeStmtClass; + } + static bool classof(const CXXForRangeStmt *) { return true; } + + // Iterators + child_range children() { + return child_range(&SubExprs[0], &SubExprs[END]); + } +}; + } // end namespace clang diff --git a/include/clang/Analysis/Visitors/CFGStmtVisitor.h b/include/clang/Analysis/Visitors/CFGStmtVisitor.h index d197e69bab..7fb4ab3eba 100644 --- a/include/clang/Analysis/Visitors/CFGStmtVisitor.h +++ b/include/clang/Analysis/Visitors/CFGStmtVisitor.h @@ -82,6 +82,7 @@ public: DISPATCH_CASE(ConditionalOperator) DISPATCH_CASE(BinaryConditionalOperator) DISPATCH_CASE(ObjCForCollectionStmt) + DISPATCH_CASE(CXXForRangeStmt) case Stmt::BinaryOperatorClass: { BinaryOperator* B = cast(S); @@ -109,6 +110,10 @@ public: return static_cast(this)->BlockStmt_VisitStmt(S); } + RetTy BlockStmt_VisitCXXForRangeStmt(CXXForRangeStmt* S) { + return static_cast(this)->BlockStmt_VisitStmt(S); + } + RetTy BlockStmt_VisitImplicitControlFlowExpr(Expr* E) { return static_cast(this)->BlockStmt_VisitExpr(E); } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 90945d7bd4..b96c461c6c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -995,6 +995,28 @@ def err_delegation_unimplemented : Error< "delegating constructors are not fully implemented">; def err_delegating_initializer_alone : Error< "an initializer for a delegating constructor must appear alone">; + +// C++0x range-based for loop +def err_for_range_decl_must_be_var : Error< + "for range declaration must declare a variable">; +def err_for_range_storage_class : Error< + "loop variable %0 may not be declared %select{'extern'|'static'|" + "'__private_extern__'|'auto'|'register'|'constexpr'}1">; +def err_type_defined_in_for_range : Error< + "types may not be defined in a for range declaration">; +def err_for_range_deduction_failure : Error< + "cannot use type %0 as a range">; +def err_for_range_incomplete_type : Error< + "cannot use incomplete type %0 as a range">; +def err_for_range_iter_deduction_failure : Error< + "cannot use type %0 as an iterator">; +def err_for_range_member_begin_end_mismatch : Error< + "range type %0 has '%select{begin|end}1' member but no '%select{end|begin}1' member">; +def err_for_range_begin_end_types_differ : Error< + "'begin' and 'end' must return the same type (got %0 and %1)">; +def note_for_range_type : Note<"range has type %0">; +def note_for_range_begin_end : Note< + "selected '%select{begin|end}0' %select{function|template }1%2 with iterator type %3">; // Objective-C++ def err_objc_decls_may_only_appear_in_global_scope : Error< diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 44f6f53939..a25af44790 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -41,6 +41,7 @@ def ObjCForCollectionStmt : Stmt; // C++ statments def CXXCatchStmt : Stmt; def CXXTryStmt : Stmt; +def CXXForRangeStmt : Stmt; // Expressions def Expr : Stmt<1>; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 4c422cc4b6..f10003fcd2 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1288,6 +1288,15 @@ private: DSC_top_level // top-level/namespace declaration context }; + /// Information on a C++0x for-range-initializer found while parsing a + /// declaration which turns out to be a for-range-declaration. + struct ForRangeInit { + SourceLocation ColonLoc; + ExprResult RangeExpr; + + bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); } + }; + DeclGroupPtrTy ParseDeclaration(StmtVector &Stmts, unsigned Context, SourceLocation &DeclEnd, ParsedAttributesWithRange &attrs); @@ -1295,12 +1304,17 @@ private: unsigned Context, SourceLocation &DeclEnd, ParsedAttributes &attrs, - bool RequireSemi); + bool RequireSemi, + ForRangeInit *FRI = 0); DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, unsigned Context, bool AllowFunctionDefinitions, - SourceLocation *DeclEnd = 0); + SourceLocation *DeclEnd = 0, + ForRangeInit *FRI = 0); Decl *ParseDeclarationAfterDeclarator(Declarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); + bool ParseAttributesAfterDeclarator(Declarator &D); + Decl *ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, + const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); Decl *ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope); Decl *ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 1169b50f85..f03bfe49b4 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -864,6 +864,7 @@ public: bool TypeMayContainAuto); void ActOnUninitializedDecl(Decl *dcl, bool TypeMayContainAuto); void ActOnInitializerError(Decl *Dcl); + void ActOnCXXForRangeDecl(Decl *D); void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc); void FinalizeDeclaration(Decl *D); DeclGroupPtrTy FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, @@ -1268,7 +1269,8 @@ public: Expr **Args, unsigned NumArgs, TemplateArgumentListInfo *ExplicitTemplateArgs, OverloadCandidateSet& CandidateSet, - bool PartialOverloading = false); + bool PartialOverloading = false, + bool StdNamespaceIsAssociated = false); // Emit as a 'note' the specific overload candidate void NoteOverloadCandidate(FunctionDecl *Fn); @@ -1473,7 +1475,8 @@ public: void ArgumentDependentLookup(DeclarationName Name, bool Operator, Expr **Args, unsigned NumArgs, - ADLResult &Functions); + ADLResult &Functions, + bool StdNamespaceIsAssociated = false); void LookupVisibleDecls(Scope *S, LookupNameKind Kind, VisibleDeclConsumer &Consumer, @@ -1804,6 +1807,17 @@ public: SourceLocation LParenLoc, Stmt *First, Expr *Second, SourceLocation RParenLoc, Stmt *Body); + StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, + SourceLocation LParenLoc, Stmt *LoopVar, + SourceLocation ColonLoc, Expr *Collection, + SourceLocation RParenLoc); + StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc, + SourceLocation ColonLoc, + Stmt *RangeDecl, Stmt *BeginEndDecl, + Expr *Cond, Expr *Inc, + Stmt *LoopVarDecl, + SourceLocation RParenLoc); + StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body); StmtResult ActOnGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc, diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 17cb9efcea..35d6fe7f3b 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -624,7 +624,11 @@ namespace clang { /// \brief NSConstantString type SPECIAL_TYPE_NS_CONSTANT_STRING = 15, /// \brief Whether __[u]int128_t identifier is installed. - SPECIAL_TYPE_INT128_INSTALLED = 16 + SPECIAL_TYPE_INT128_INSTALLED = 16, + /// \brief Cached "auto" deduction type. + SPECIAL_TYPE_AUTO_DEDUCT = 17, + /// \brief Cached "auto &&" deduction type. + SPECIAL_TYPE_AUTO_RREF_DEDUCT = 18 }; /// \brief Record codes for each kind of declaration. @@ -915,6 +919,8 @@ namespace clang { STMT_CXX_CATCH, /// \brief A CXXTryStmt record. STMT_CXX_TRY, + /// \brief A CXXForRangeStmt record. + STMT_CXX_FOR_RANGE, /// \brief A CXXOperatorCallExpr record. EXPR_CXX_OPERATOR_CALL, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 7317928d97..eea5394cfa 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2754,6 +2754,22 @@ QualType ASTContext::getAutoType(QualType DeducedType) const { return QualType(AT, 0); } +/// getAutoDeductType - Get type pattern for deducing against 'auto'. +QualType ASTContext::getAutoDeductType() const { + if (AutoDeductTy.isNull()) + AutoDeductTy = getAutoType(QualType()); + assert(!AutoDeductTy.isNull() && "can't build 'auto' pattern"); + return AutoDeductTy; +} + +/// getAutoRRefDeductType - Get type pattern for deducing against 'auto &&'. +QualType ASTContext::getAutoRRefDeductType() const { + if (AutoRRefDeductTy.isNull()) + AutoRRefDeductTy = getRValueReferenceType(getAutoDeductType()); + assert(!AutoRRefDeductTy.isNull() && "can't build 'auto &&' pattern"); + return AutoRRefDeductTy; +} + /// getTagDeclType - Return the unique reference to the type for the /// specified TagDecl (struct/union/class/enum) decl. QualType ASTContext::getTagDeclType(const TagDecl *Decl) const { diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 446b4d4646..37cd2af1b1 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -567,7 +567,8 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { T = Parm->getOriginalType(); T.getAsStringInternal(Name, Policy); Out << Name; - if (Expr *Init = D->getInit()) { + Expr *Init = D->getInit(); + if (!Policy.SuppressInitializers && Init) { if (D->hasCXXDirectInitializer()) Out << "("; else { diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 692c2c3760..127431ba7d 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -189,7 +189,7 @@ UnresolvedLookupExpr::Create(ASTContext &C, ExplicitTemplateArgumentList::sizeFor(Args)); return new (Mem) UnresolvedLookupExpr(C, NamingClass, QualifierLoc, NameInfo, ADL, /*Overload*/ true, &Args, - Begin, End); + Begin, End, /*StdIsAssociated=*/false); } UnresolvedLookupExpr * diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 6c35319278..5b0db8621a 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -540,6 +540,40 @@ CXXTryStmt::CXXTryStmt(SourceLocation tryLoc, Stmt *tryBlock, std::copy(handlers, handlers + NumHandlers, Stmts + 1); } +CXXForRangeStmt::CXXForRangeStmt(DeclStmt *Range, DeclStmt *BeginEndStmt, + Expr *Cond, Expr *Inc, DeclStmt *LoopVar, + Stmt *Body, SourceLocation FL, + SourceLocation CL, SourceLocation RPL) + : Stmt(CXXForRangeStmtClass), ForLoc(FL), ColonLoc(CL), RParenLoc(RPL) { + SubExprs[RANGE] = Range; + SubExprs[BEGINEND] = BeginEndStmt; + SubExprs[COND] = reinterpret_cast(Cond); + SubExprs[INC] = reinterpret_cast(Inc); + SubExprs[LOOPVAR] = LoopVar; + SubExprs[BODY] = Body; +} + +Expr *CXXForRangeStmt::getRangeInit() { + DeclStmt *RangeStmt = getRangeStmt(); + VarDecl *RangeDecl = dyn_cast_or_null(RangeStmt->getSingleDecl()); + assert(RangeDecl &&& "for-range should have a single var decl"); + return RangeDecl->getInit(); +} + +const Expr *CXXForRangeStmt::getRangeInit() const { + return const_cast(this)->getRangeInit(); +} + +VarDecl *CXXForRangeStmt::getLoopVariable() { + Decl *LV = cast(getLoopVarStmt())->getSingleDecl(); + assert(LV && "No loop variable in CXXForRangeStmt"); + return cast(LV); +} + +const VarDecl *CXXForRangeStmt::getLoopVariable() const { + return const_cast(this)->getLoopVariable(); +} + IfStmt::IfStmt(ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond, Stmt *then, SourceLocation EL, Stmt *elsev) : Stmt(IfStmtClass), IfLoc(IL), ElseLoc(EL) diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 4f50044116..37010a0358 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -291,6 +291,18 @@ void StmtPrinter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *Node) { } } +void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) { + Indent() << "for ("; + PrintingPolicy SubPolicy(Policy); + SubPolicy.SuppressInitializers = true; + Node->getLoopVariable()->print(OS, SubPolicy, IndentLevel); + OS << " : "; + PrintExpr(Node->getRangeInit()); + OS << ") {\n"; + PrintStmt(Node->getBody()); + Indent() << "}\n"; +} + void StmtPrinter::VisitGotoStmt(GotoStmt *Node) { Indent() << "goto " << Node->getLabel()->getName() << ";\n"; } diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index a506d055b3..92292cea71 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -177,6 +177,10 @@ void StmtProfiler::VisitCXXTryStmt(CXXTryStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitCXXForRangeStmt(CXXForRangeStmt *S) { + VisitStmt(S); +} + void StmtProfiler::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { VisitStmt(S); } diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 3dcc763eda..149607404c 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -312,6 +312,7 @@ private: AddStmtChoice asc); CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T); CFGBlock *VisitCXXTryStmt(CXXTryStmt *S); + CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S); CFGBlock *VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, AddStmtChoice asc); CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc); @@ -911,6 +912,9 @@ tryAgain: case Stmt::CXXTryStmtClass: return VisitCXXTryStmt(cast(S)); + case Stmt::CXXForRangeStmtClass: + return VisitCXXForRangeStmt(cast(S)); + case Stmt::DeclStmtClass: return VisitDeclStmt(cast(S)); @@ -2513,6 +2517,122 @@ CFGBlock* CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt* CS) { return CatchBlock; } +CFGBlock* CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt* S) { + // C++0x for-range statements are specified as [stmt.ranged]: + // + // { + // auto && __range = range-init; + // for ( auto __begin = begin-expr, + // __end = end-expr; + // __begin != __end; + // ++__begin ) { + // for-range-declaration = *__begin; + // statement + // } + // } + + // Save local scope position before the addition of the implicit variables. + SaveAndRestore save_scope_pos(ScopePos); + + // Create local scopes and destructors for range, begin and end variables. + if (Stmt *Range = S->getRangeStmt()) + addLocalScopeForStmt(Range); + if (Stmt *BeginEnd = S->getBeginEndStmt()) + addLocalScopeForStmt(BeginEnd); + addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S); + + LocalScope::const_iterator ContinueScopePos = ScopePos; + + // "for" is a control-flow statement. Thus we stop processing the current + // block. + CFGBlock* LoopSuccessor = NULL; + if (Block) { + if (badCFG) + return 0; + LoopSuccessor = Block; + } else + LoopSuccessor = Succ; + + // Save the current value for the break targets. + // All breaks should go to the code following the loop. + SaveAndRestore save_break(BreakJumpTarget); + BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); + + // The block for the __begin != __end expression. + CFGBlock* ConditionBlock = createBlock(false); + ConditionBlock->setTerminator(S); + + // Now add the actual condition to the condition block. + if (Expr *C = S->getCond()) { + Block = ConditionBlock; + CFGBlock *BeginConditionBlock = addStmt(C); + if (badCFG) + return 0; + assert(BeginConditionBlock == ConditionBlock && + "condition block in for-range was unexpectedly complex"); + (void)BeginConditionBlock; + } + + // The condition block is the implicit successor for the loop body as well as + // any code above the loop. + Succ = ConditionBlock; + + // See if this is a known constant. + TryResult KnownVal(true); + + if (S->getCond()) + KnownVal = tryEvaluateBool(S->getCond()); + + // Now create the loop body. + { + assert(S->getBody()); + + // Save the current values for Block, Succ, and continue targets. + SaveAndRestore save_Block(Block), save_Succ(Succ); + SaveAndRestore save_continue(ContinueJumpTarget); + + // Generate increment code in its own basic block. This is the target of + // continue statements. + Block = 0; + Succ = addStmt(S->getInc()); + ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); + + // The starting block for the loop increment is the block that should + // represent the 'loop target' for looping back to the start of the loop. + ContinueJumpTarget.block->setLoopTarget(S); + + // Finish up the increment block and prepare to start the loop body. + assert(Block); + if (badCFG) + return 0; + Block = 0; + + + // Add implicit scope and dtors for loop variable. + addLocalScopeAndDtors(S->getLoopVarStmt()); + + // Populate a new block to contain the loop body and loop variable. + Block = addStmt(S->getBody()); + if (badCFG) + return 0; + Block = addStmt(S->getLoopVarStmt()); + if (badCFG) + return 0; + + // This new body block is a successor to our condition block. + addSuccessor(ConditionBlock, KnownVal.isFalse() ? 0 : Block); + } + + // Link up the condition block with the code that follows the loop (the + // false branch). + addSuccessor(ConditionBlock, KnownVal.isTrue() ? 0 : LoopSuccessor); + + // Add the initialization statements. + Block = createBlock(); + addStmt(S->getRangeStmt()); + return addStmt(S->getBeginEndStmt()); +} + CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc) { if (BuildOpts.AddImplicitDtors) { diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index e72d5c73f5..7a1ce19b07 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -153,6 +153,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::CXXTryStmtClass: EmitCXXTryStmt(cast(*S)); break; + case Stmt::CXXForRangeStmtClass: + EmitCXXForRangeStmt(cast(*S)); + break; } } @@ -636,6 +639,80 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S) { EmitBlock(LoopExit.getBlock(), true); } +void CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S) { + JumpDest LoopExit = getJumpDestInCurrentScope("for.end"); + + RunCleanupsScope ForScope(*this); + + CGDebugInfo *DI = getDebugInfo(); + if (DI) { + DI->setLocation(S.getSourceRange().getBegin()); + DI->EmitRegionStart(Builder); + } + + // Evaluate the first pieces before the loop. + EmitStmt(S.getRangeStmt()); + EmitStmt(S.getBeginEndStmt()); + + // Start the loop with a block that tests the condition. + // If there's an increment, the continue scope will be overwritten + // later. + llvm::BasicBlock *CondBlock = createBasicBlock("for.cond"); + EmitBlock(CondBlock); + + // If there are any cleanups between here and the loop-exit scope, + // create a block to stage a loop exit along. + llvm::BasicBlock *ExitBlock = LoopExit.getBlock(); + if (ForScope.requiresCleanups()) + ExitBlock = createBasicBlock("for.cond.cleanup"); + + // The loop body, consisting of the specified body and the loop variable. + llvm::BasicBlock *ForBody = createBasicBlock("for.body"); + + // The body is executed if the expression, contextually converted + // to bool, is true. + llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + Builder.CreateCondBr(BoolCondVal, ForBody, ExitBlock); + + if (ExitBlock != LoopExit.getBlock()) { + EmitBlock(ExitBlock); + EmitBranchThroughCleanup(LoopExit); + } + + EmitBlock(ForBody); + + // Create a block for the increment. In case of a 'continue', we jump there. + JumpDest Continue = getJumpDestInCurrentScope("for.inc"); + + // Store the blocks to use for break and continue. + BreakContinueStack.push_back(BreakContinue(LoopExit, Continue)); + + { + // Create a separate cleanup scope for the loop variable and body. + RunCleanupsScope BodyScope(*this); + EmitStmt(S.getLoopVarStmt()); + EmitStmt(S.getBody()); + } + + // If there is an increment, emit it next. + EmitBlock(Continue.getBlock()); + EmitStmt(S.getInc()); + + BreakContinueStack.pop_back(); + + EmitBranch(CondBlock); + + ForScope.ForceCleanup(); + + if (DI) { + DI->setLocation(S.getSourceRange().getEnd()); + DI->EmitRegionEnd(Builder); + } + + // Emit the fall-through block. + EmitBlock(LoopExit.getBlock(), true); +} + void CodeGenFunction::EmitReturnOfRValue(RValue RV, QualType Ty) { if (RV.isScalar()) { Builder.CreateStore(RV.getScalarVal(), ReturnValue); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index f3b00e3370..d53da9f341 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -42,6 +42,7 @@ namespace clang { class APValue; class ASTContext; class CXXDestructorDecl; + class CXXForRangeStmt; class CXXTryStmt; class Decl; class LabelDecl; @@ -1694,6 +1695,7 @@ public: void ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false); void EmitCXXTryStmt(const CXXTryStmt &S); + void EmitCXXForRangeStmt(const CXXForRangeStmt &S); //===--------------------------------------------------------------------===// // LValue Expression Emission diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 8674485d5f..5e6c51c1ee 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -705,13 +705,21 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, ///[C90/C++]init-declarator-list ';' [TODO] /// [OMP] threadprivate-directive [TODO] /// +/// for-range-declaration: [C++0x 6.5p1: stmt.ranged] +/// attribute-specifier-seq[opt] type-specifier-seq declarator +/// /// If RequireSemi is false, this does not check for a ';' at the end of the /// declaration. If it is true, it checks for and eats it. +/// +/// If FRI is non-null, we might be parsing a for-range-declaration instead +/// of a simple-declaration. If we find that we are, we also parse the +/// for-range-initializer, and place it here. Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts, unsigned Context, SourceLocation &DeclEnd, ParsedAttributes &attrs, - bool RequireSemi) { + bool RequireSemi, + ForRangeInit *FRI) { // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this); DS.takeAttributesFrom(attrs); @@ -731,7 +739,7 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts, return Actions.ConvertDeclToDeclGroup(TheDecl); } - return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd); + return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI); } /// ParseDeclGroup - Having concluded that this is either a function @@ -740,7 +748,8 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts, Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, unsigned Context, bool AllowFunctionDefinitions, - SourceLocation *DeclEnd) { + SourceLocation *DeclEnd, + ForRangeInit *FRI) { // Parse the first declarator. ParsingDeclarator D(*this, DS, static_cast(Context)); ParseDeclarator(D); @@ -786,8 +795,24 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, } } + if (ParseAttributesAfterDeclarator(D)) + return DeclGroupPtrTy(); + + // C++0x [stmt.iter]p1: Check if we have a for-range-declarator. If so, we + // must parse and analyze the for-range-initializer before the declaration is + // analyzed. + if (FRI && Tok.is(tok::colon)) { + FRI->ColonLoc = ConsumeToken(); + // FIXME: handle braced-init-list here. + FRI->RangeExpr = ParseExpression(); + Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D); + Actions.ActOnCXXForRangeDecl(ThisDecl); + Actions.FinalizeDeclaration(ThisDecl); + return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, &ThisDecl, 1); + } + llvm::SmallVector DeclsInGroup; - Decl *FirstDecl = ParseDeclarationAfterDeclarator(D); + Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(D); D.complete(FirstDecl); if (FirstDecl) DeclsInGroup.push_back(FirstDecl); @@ -841,6 +866,26 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclsInGroup.size()); } +/// Parse an optional simple-asm-expr and attributes, and attach them to a +/// declarator. Returns true on an error. +bool Parser::ParseAttributesAfterDeclarator(Declarator &D) { + // If a simple-asm-expr is present, parse it. + if (Tok.is(tok::kw_asm)) { + SourceLocation Loc; + ExprResult AsmLabel(ParseSimpleAsm(&Loc)); + if (AsmLabel.isInvalid()) { + SkipUntil(tok::semi, true, true); + return true; + } + + D.setAsmLabel(AsmLabel.release()); + D.SetRangeEnd(Loc); + } + + MaybeParseGNUAttributes(D); + return false; +} + /// \brief Parse 'declaration' after parsing 'declaration-specifiers /// declarator'. This method parses the remainder of the declaration /// (including any attributes or initializer, among other things) and @@ -864,21 +909,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, /// Decl *Parser::ParseDeclarationAfterDeclarator(Declarator &D, const ParsedTemplateInfo &TemplateInfo) { - // If a simple-asm-expr is present, parse it. - if (Tok.is(tok::kw_asm)) { - SourceLocation Loc; - ExprResult AsmLabel(ParseSimpleAsm(&Loc)); - if (AsmLabel.isInvalid()) { - SkipUntil(tok::semi, true, true); - return 0; - } + if (ParseAttributesAfterDeclarator(D)) + return 0; - D.setAsmLabel(AsmLabel.release()); - D.SetRangeEnd(Loc); - } - - MaybeParseGNUAttributes(D); + return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo); +} +Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, + const ParsedTemplateInfo &TemplateInfo) { // Inform the current actions module that we just parsed this declarator. Decl *ThisDecl = 0; switch (TemplateInfo.Kind) { diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 8ffd6e921b..d70a745314 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -977,6 +977,7 @@ StmtResult Parser::ParseDoStatement(ParsedAttributes &attrs) { /// 'for' '(' declaration expr[opt] ';' expr[opt] ')' statement /// [C++] 'for' '(' for-init-statement condition[opt] ';' expression[opt] ')' /// [C++] statement +/// [C++0x] 'for' '(' for-range-declaration : for-range-initializer ) statement /// [OBJC2] 'for' '(' declaration 'in' expr ')' statement /// [OBJC2] 'for' '(' expr 'in' expr ')' statement /// @@ -984,6 +985,11 @@ StmtResult Parser::ParseDoStatement(ParsedAttributes &attrs) { /// [C++] expression-statement /// [C++] simple-declaration /// +/// [C++0x] for-range-declaration: +/// [C++0x] attribute-specifier-seq[opt] type-specifier-seq declarator +/// [C++0x] for-range-initializer: +/// [C++0x] expression +/// [C++0x] braced-init-list [TODO] StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { // FIXME: Use attributes? @@ -1025,11 +1031,12 @@ StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { SourceLocation LParenLoc = ConsumeParen(); ExprResult Value; - bool ForEach = false; + bool ForEach = false, ForRange = false; StmtResult FirstPart; bool SecondPartIsInvalid = false; FullExprArg SecondPart(Actions); ExprResult Collection; + ForRangeInit ForRangeInit; FullExprArg ThirdPart(Actions); Decl *SecondVar = 0; @@ -1052,13 +1059,21 @@ StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { ParsedAttributesWithRange attrs(AttrFactory); MaybeParseCXX0XAttributes(attrs); + // In C++0x, "for (T NS:a" might not be a typo for :: + bool MightBeForRangeStmt = getLang().CPlusPlus; + ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt); + SourceLocation DeclStart = Tok.getLocation(), DeclEnd; StmtVector Stmts(Actions); DeclGroupPtrTy DG = ParseSimpleDeclaration(Stmts, Declarator::ForContext, - DeclEnd, attrs, false); + DeclEnd, attrs, false, + MightBeForRangeStmt ? + &ForRangeInit : 0); FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); - if (Tok.is(tok::semi)) { // for (int x = 4; + if (ForRangeInit.ParsedForRangeDecl()) { + ForRange = true; + } else if (Tok.is(tok::semi)) { // for (int x = 4; ConsumeToken(); } else if ((ForEach = isTokIdentifier_in())) { Actions.ActOnForEachDeclStmt(DG); @@ -1107,7 +1122,7 @@ StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { } } } - if (!ForEach) { + if (!ForEach && !ForRange) { assert(!SecondPart.get() && "Shouldn't have a second expression yet."); // Parse the second part of the for specifier. if (Tok.is(tok::semi)) { // for (...;; @@ -1149,6 +1164,17 @@ StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { // Match the ')'. SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + // We need to perform most of the semantic analysis for a C++0x for-range + // statememt before parsing the body, in order to be able to deduce the type + // of an auto-typed loop variable. + StmtResult ForRangeStmt; + if (ForRange) + ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, LParenLoc, + FirstPart.take(), + ForRangeInit.ColonLoc, + ForRangeInit.RangeExpr.get(), + RParenLoc); + // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. @@ -1175,15 +1201,19 @@ StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { if (Body.isInvalid()) return StmtError(); - if (!ForEach) - return Actions.ActOnForStmt(ForLoc, LParenLoc, FirstPart.take(), SecondPart, - SecondVar, ThirdPart, RParenLoc, Body.take()); + if (ForEach) + // FIXME: It isn't clear how to communicate the late destruction of + // C++ temporaries used to create the collection. + return Actions.ActOnObjCForCollectionStmt(ForLoc, LParenLoc, + FirstPart.take(), + Collection.take(), RParenLoc, + Body.take()); + + if (ForRange) + return Actions.FinishCXXForRangeStmt(ForRangeStmt.take(), Body.take()); - // FIXME: It isn't clear how to communicate the late destruction of - // C++ temporaries used to create the collection. - return Actions.ActOnObjCForCollectionStmt(ForLoc, LParenLoc, FirstPart.take(), - Collection.take(), RParenLoc, - Body.take()); + return Actions.ActOnForStmt(ForLoc, LParenLoc, FirstPart.take(), SecondPart, + SecondVar, ThirdPart, RParenLoc, Body.take()); } /// ParseGotoStatement diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 2917424581..7c32a381df 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5247,6 +5247,47 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl, } } +void Sema::ActOnCXXForRangeDecl(Decl *D) { + VarDecl *VD = dyn_cast(D); + if (!VD) { + Diag(D->getLocation(), diag::err_for_range_decl_must_be_var); + D->setInvalidDecl(); + return; + } + + VD->setCXXForRangeDecl(true); + + // for-range-declaration cannot be given a storage class specifier. + int Error = -1; + switch (VD->getStorageClassAsWritten()) { + case SC_None: + break; + case SC_Extern: + Error = 0; + break; + case SC_Static: + Error = 1; + break; + case SC_PrivateExtern: + Error = 2; + break; + case SC_Auto: + Error = 3; + break; + case SC_Register: + Error = 4; + break; + } + // FIXME: constexpr isn't allowed here. + //if (DS.isConstexprSpecified()) + // Error = 5; + if (Error != -1) { + Diag(VD->getOuterLocStart(), diag::err_for_range_storage_class) + << VD->getDeclName() << Error; + D->setInvalidDecl(); + } +} + void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (var->isInvalidDecl()) return; diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index fe94224a6e..437898775a 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -2205,7 +2205,8 @@ void ADLResult::insert(NamedDecl *New) { void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator, Expr **Args, unsigned NumArgs, - ADLResult &Result) { + ADLResult &Result, + bool StdNamespaceIsAssociated) { // Find all of the associated namespaces and classes based on the // arguments we have. AssociatedNamespaceSet AssociatedNamespaces; @@ -2213,6 +2214,8 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator, FindAssociatedClassesAndNamespaces(Args, NumArgs, AssociatedNamespaces, AssociatedClasses); + if (StdNamespaceIsAssociated && StdNamespace) + AssociatedNamespaces.insert(getStdNamespace()); QualType T1, T2; if (Operator) { diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 25e25a2139..57338f255c 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -6152,7 +6152,8 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name, Expr **Args, unsigned NumArgs, TemplateArgumentListInfo *ExplicitTemplateArgs, OverloadCandidateSet& CandidateSet, - bool PartialOverloading) { + bool PartialOverloading, + bool StdNamespaceIsAssociated) { ADLResult Fns; // FIXME: This approach for uniquing ADL results (and removing @@ -6163,7 +6164,8 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name, // we supposed to consider on ADL candidates, anyway? // FIXME: Pass in the explicit template arguments? - ArgumentDependentLookup(Name, Operator, Args, NumArgs, Fns); + ArgumentDependentLookup(Name, Operator, Args, NumArgs, Fns, + StdNamespaceIsAssociated); // Erase all of the candidates we already knew about. for (OverloadCandidateSet::iterator Cand = CandidateSet.begin(), @@ -7693,7 +7695,8 @@ void Sema::AddOverloadedCallCandidates(UnresolvedLookupExpr *ULE, Args, NumArgs, ExplicitTemplateArgs, CandidateSet, - PartialOverloading); + PartialOverloading, + ULE->isStdAssociatedNamespace()); } /// Attempts to recover from a call where no functions were found. @@ -7772,7 +7775,9 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, // We don't perform ADL in C. assert(getLangOptions().CPlusPlus && "ADL enabled in C"); - } + } else + assert(!ULE->isStdAssociatedNamespace() && + "std is associated namespace but not doing ADL"); #endif OverloadCandidateSet CandidateSet(Fn->getExprLoc()); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index e957a4b93f..407618219f 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -15,6 +15,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Initialization.h" +#include "clang/Sema/Lookup.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" @@ -1013,6 +1014,387 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, ForLoc, RParenLoc)); } +namespace { + +enum BeginEndFunction { + BEF_begin, + BEF_end +}; + +/// Build a variable declaration for a for-range statement. +static VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, + QualType Type, const char *Name) { + DeclContext *DC = SemaRef.CurContext; + IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name); + TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc); + VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type, + TInfo, SC_Auto, SC_None); + return Decl; +} + +/// Finish building a variable declaration for a for-range statement. +/// \return true if an error occurs. +static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, + SourceLocation Loc, int diag) { + // Deduce the type for the iterator variable now rather than leaving it to + // AddInitializerToDecl, so we can produce a more suitable diagnostic. + TypeSourceInfo *InitTSI = 0; + if (Init->getType()->isVoidType() || + !SemaRef.DeduceAutoType(Decl->getTypeSourceInfo(), Init, InitTSI)) + SemaRef.Diag(Loc, diag) << Init->getType(); + if (!InitTSI) { + Decl->setInvalidDecl(); + return true; + } + Decl->setTypeSourceInfo(InitTSI); + Decl->setType(InitTSI->getType()); + + SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false, + /*TypeMayContainAuto=*/false); + SemaRef.FinalizeDeclaration(Decl); + return false; +} + +/// Produce a note indicating which begin/end function was implicitly called +/// by a C++0x for-range statement. This is often not obvious from the code, +/// nor from the diagnostics produced when analysing the implicit expressions +/// required in a for-range statement. +void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E, + BeginEndFunction BEF) { + CallExpr *CE = dyn_cast(E); + if (!CE) + return; + FunctionDecl *D = dyn_cast(CE->getCalleeDecl()); + if (!D) + return; + SourceLocation Loc = D->getLocation(); + + std::string Description; + bool IsTemplate = false; + if (FunctionTemplateDecl *FunTmpl = D->getPrimaryTemplate()) { + Description = SemaRef.getTemplateArgumentBindingsText( + FunTmpl->getTemplateParameters(), *D->getTemplateSpecializationArgs()); + IsTemplate = true; + } + + SemaRef.Diag(Loc, diag::note_for_range_begin_end) + << BEF << IsTemplate << Description << E->getType(); +} + +/// Build a call to 'begin' or 'end' for a C++0x for-range statement. If the +/// given LookupResult is non-empty, it is assumed to describe a member which +/// will be invoked. Otherwise, the function will be found via argument +/// dependent lookup. +static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S, + SourceLocation Loc, + VarDecl *Decl, + BeginEndFunction BEF, + const DeclarationNameInfo &NameInfo, + LookupResult &MemberLookup, + Expr *Range) { + ExprResult CallExpr; + if (!MemberLookup.empty()) { + ExprResult MemberRef = + SemaRef.BuildMemberReferenceExpr(Range, Range->getType(), Loc, + /*IsPtr=*/false, CXXScopeSpec(), + /*Qualifier=*/0, MemberLookup, + /*TemplateArgs=*/0); + if (MemberRef.isInvalid()) + return ExprError(); + CallExpr = SemaRef.ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(), + Loc, 0); + if (CallExpr.isInvalid()) + return ExprError(); + } else { + UnresolvedSet<0> FoundNames; + // C++0x [stmt.ranged]p1: For the purposes of this name lookup, namespace + // std is an associated namespace. + UnresolvedLookupExpr *Fn = + UnresolvedLookupExpr::Create(SemaRef.Context, /*NamingClass=*/0, + NestedNameSpecifierLoc(), NameInfo, + /*NeedsADL=*/true, /*Overloaded=*/false, + FoundNames.begin(), FoundNames.end(), + /*LookInStdNamespace=*/true); + CallExpr = SemaRef.BuildOverloadedCallExpr(S, Fn, Fn, Loc, &Range, 1, Loc, + 0); + if (CallExpr.isInvalid()) { + SemaRef.Diag(Range->getLocStart(), diag::note_for_range_type) + << Range->getType(); + return ExprError(); + } + } + if (FinishForRangeVarDecl(SemaRef, Decl, CallExpr.get(), Loc, + diag::err_for_range_iter_deduction_failure)) { + NoteForRangeBeginEndFunction(SemaRef, CallExpr.get(), BEF); + return ExprError(); + } + return CallExpr; +} + +} + +/// ActOnCXXForRangeStmt - Check and build a C++0x for-range statement. +/// +/// C++0x [stmt.ranged]: +/// A range-based for statement is equivalent to +/// +/// { +/// auto && __range = range-init; +/// for ( auto __begin = begin-expr, +/// __end = end-expr; +/// __begin != __end; +/// ++__begin ) { +/// for-range-declaration = *__begin; +/// statement +/// } +/// } +/// +/// 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(SourceLocation ForLoc, SourceLocation LParenLoc, + Stmt *First, SourceLocation ColonLoc, Expr *Range, + SourceLocation RParenLoc) { + if (!First || !Range) + return StmtError(); + + DeclStmt *DS = dyn_cast(First); + assert(DS && "first part of for range not a decl stmt"); + + if (!DS->isSingleDecl()) { + Diag(DS->getStartLoc(), diag::err_type_defined_in_for_range); + return StmtError(); + } + if (DS->getSingleDecl()->isInvalidDecl()) + return StmtError(); + + if (DiagnoseUnexpandedParameterPack(Range, UPPC_Expression)) + return StmtError(); + + // Build auto && __range = range-init + SourceLocation RangeLoc = Range->getLocStart(); + VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc, + Context.getAutoRRefDeductType(), + "__range"); + if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc, + diag::err_for_range_deduction_failure)) + return StmtError(); + + // Claim the type doesn't contain auto: we've already done the checking. + DeclGroupPtrTy RangeGroup = + BuildDeclaratorGroup((Decl**)&RangeVar, 1, /*TypeMayContainAuto=*/false); + StmtResult RangeDecl = ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc); + if (RangeDecl.isInvalid()) + return StmtError(); + + return BuildCXXForRangeStmt(ForLoc, ColonLoc, RangeDecl.get(), + /*BeginEndDecl=*/0, /*Cond=*/0, /*Inc=*/0, DS, + RParenLoc); +} + +/// BuildCXXForRangeStmt - Build or instantiate a C++0x for-range statement. +StmtResult +Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, + Stmt *RangeDecl, Stmt *BeginEnd, Expr *Cond, + Expr *Inc, Stmt *LoopVarDecl, + SourceLocation RParenLoc) { + Scope *S = getCurScope(); + + DeclStmt *RangeDS = cast(RangeDecl); + VarDecl *RangeVar = cast(RangeDS->getSingleDecl()); + QualType RangeVarType = RangeVar->getType(); + + DeclStmt *LoopVarDS = cast(LoopVarDecl); + VarDecl *LoopVar = cast(LoopVarDS->getSingleDecl()); + + StmtResult BeginEndDecl = BeginEnd; + ExprResult NotEqExpr = Cond, IncrExpr = Inc; + + if (!BeginEndDecl.get() && !RangeVarType->isDependentType()) { + SourceLocation RangeLoc = RangeVar->getLocation(); + + ExprResult RangeRef = BuildDeclRefExpr(RangeVar, + RangeVarType.getNonReferenceType(), + VK_LValue, ColonLoc); + if (RangeRef.isInvalid()) + return StmtError(); + + QualType AutoType = Context.getAutoDeductType(); + Expr *Range = RangeVar->getInit(); + if (!Range) + return StmtError(); + QualType RangeType = Range->getType(); + + if (RequireCompleteType(RangeLoc, RangeType, + PDiag(diag::err_for_range_incomplete_type))) + return StmtError(); + + // Build auto __begin = begin-expr, __end = end-expr. + VarDecl *BeginVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType, + "__begin"); + VarDecl *EndVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType, + "__end"); + + // Build begin-expr and end-expr and attach to __begin and __end variables. + ExprResult BeginExpr, EndExpr; + if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) { + // - if _RangeT is an array type, begin-expr and end-expr are __range and + // __range + __bound, respectively, where __bound is the array bound. If + // _RangeT is an array of unknown size or an array of incomplete type, + // the program is ill-formed; + + // begin-expr is __range. + BeginExpr = RangeRef; + if (FinishForRangeVarDecl(*this, BeginVar, RangeRef.get(), ColonLoc, + diag::err_for_range_iter_deduction_failure)) { + NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + return StmtError(); + } + + // Find the array bound. + ExprResult BoundExpr; + if (const ConstantArrayType *CAT = dyn_cast(UnqAT)) + BoundExpr = Owned(IntegerLiteral::Create(Context, CAT->getSize(), + Context.IntTy, RangeLoc)); + else if (const VariableArrayType *VAT = + dyn_cast(UnqAT)) + BoundExpr = VAT->getSizeExpr(); + else { + // Can't be a DependentSizedArrayType or an IncompleteArrayType since + // UnqAT is not incomplete and Range is not type-dependent. + assert(0 && "Unexpected array type in for-range"); + return StmtError(); + } + + // end-expr is __range + __bound. + EndExpr = ActOnBinOp(S, ColonLoc, tok::plus, RangeRef.get(), + BoundExpr.get()); + if (EndExpr.isInvalid()) + return StmtError(); + if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc, + diag::err_for_range_iter_deduction_failure)) { + NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end); + return StmtError(); + } + } else { + DeclarationNameInfo BeginNameInfo(&PP.getIdentifierTable().get("begin"), + ColonLoc); + DeclarationNameInfo EndNameInfo(&PP.getIdentifierTable().get("end"), + ColonLoc); + + LookupResult BeginMemberLookup(*this, BeginNameInfo, LookupMemberName); + LookupResult EndMemberLookup(*this, EndNameInfo, LookupMemberName); + + if (CXXRecordDecl *D = RangeType->getAsCXXRecordDecl()) { + // - if _RangeT is a class type, the unqualified-ids begin and end are + // looked up in the scope of class _RangeT as if by class member access + // lookup (3.4.5), and if either (or both) finds at least one + // declaration, begin-expr and end-expr are __range.begin() and + // __range.end(), respectively; + LookupQualifiedName(BeginMemberLookup, D); + LookupQualifiedName(EndMemberLookup, D); + + if (BeginMemberLookup.empty() != EndMemberLookup.empty()) { + Diag(ColonLoc, diag::err_for_range_member_begin_end_mismatch) + << RangeType << BeginMemberLookup.empty(); + return StmtError(); + } + } else { + // - otherwise, begin-expr and end-expr are begin(__range) and + // end(__range), respectively, where begin and end are looked up with + // argument-dependent lookup (3.4.2). For the purposes of this name + // lookup, namespace std is an associated namespace. + } + + BeginExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, BeginVar, + BEF_begin, BeginNameInfo, + BeginMemberLookup, RangeRef.get()); + if (BeginExpr.isInvalid()) + return StmtError(); + + EndExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, EndVar, + BEF_end, EndNameInfo, + EndMemberLookup, RangeRef.get()); + if (EndExpr.isInvalid()) + return StmtError(); + } + + // C++0x [decl.spec.auto]p6: BeginType and EndType must be the same. + QualType BeginType = BeginVar->getType(), EndType = EndVar->getType(); + if (!Context.hasSameType(BeginType, EndType)) { + Diag(RangeLoc, diag::err_for_range_begin_end_types_differ) + << BeginType << EndType; + NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end); + } + + Decl *BeginEndDecls[] = { BeginVar, EndVar }; + // Claim the type doesn't contain auto: we've already done the checking. + DeclGroupPtrTy BeginEndGroup = + BuildDeclaratorGroup(BeginEndDecls, 2, /*TypeMayContainAuto=*/false); + BeginEndDecl = ActOnDeclStmt(BeginEndGroup, ColonLoc, ColonLoc); + + ExprResult BeginRef = BuildDeclRefExpr(BeginVar, + BeginType.getNonReferenceType(), + VK_LValue, ColonLoc); + ExprResult EndRef = BuildDeclRefExpr(EndVar, EndType.getNonReferenceType(), + VK_LValue, ColonLoc); + + // Build and check __begin != __end expression. + NotEqExpr = ActOnBinOp(S, ColonLoc, tok::exclaimequal, + BeginRef.get(), EndRef.get()); + NotEqExpr = ActOnBooleanCondition(S, ColonLoc, NotEqExpr.get()); + NotEqExpr = ActOnFinishFullExpr(NotEqExpr.get()); + if (NotEqExpr.isInvalid()) { + NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + if (!Context.hasSameType(BeginType, EndType)) + NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end); + return StmtError(); + } + + // Build and check ++__begin expression. + IncrExpr = ActOnUnaryOp(S, ColonLoc, tok::plusplus, BeginRef.get()); + IncrExpr = ActOnFinishFullExpr(IncrExpr.get()); + if (IncrExpr.isInvalid()) { + NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + return StmtError(); + } + + // Build and check *__begin expression. + ExprResult DerefExpr = ActOnUnaryOp(S, ColonLoc, tok::star, BeginRef.get()); + if (DerefExpr.isInvalid()) { + NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + return StmtError(); + } + + // Attach *__begin as initializer for VD. + if (!LoopVar->isInvalidDecl()) { + AddInitializerToDecl(LoopVar, DerefExpr.get(), /*DirectInit=*/false, + /*TypeMayContainAuto=*/true); + if (LoopVar->isInvalidDecl()) + NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + } + } + + return Owned(new (Context) CXXForRangeStmt(RangeDS, + cast_or_null(BeginEndDecl.get()), + NotEqExpr.take(), IncrExpr.take(), + LoopVarDS, /*Body=*/0, ForLoc, + ColonLoc, RParenLoc)); +} + +/// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement. +/// This is a separate step from ActOnCXXForRangeStmt because analysis of the +/// body cannot be performed until after the type of the range variable is +/// determined. +StmtResult Sema::FinishCXXForRangeStmt(Stmt *S, Stmt *B) { + if (!S || !B) + return StmtError(); + + cast(S)->setBody(B); + return S; +} + StmtResult Sema::ActOnGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc, LabelDecl *TheDecl) { diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index a2d1f871a2..11b98e0af2 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -271,6 +271,7 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { D->getStorageClassAsWritten()); Var->setThreadSpecified(D->isThreadSpecified()); Var->setCXXDirectInitializer(D->hasCXXDirectInitializer()); + Var->setCXXForRangeDecl(D->isCXXForRangeDecl()); // Substitute the nested name specifier, if any. if (SubstQualifier(D, Var)) @@ -350,7 +351,8 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { } SemaRef.PopExpressionEvaluationContext(); - } else if (!Var->isStaticDataMember() || Var->isOutOfLine()) + } else if ((!Var->isStaticDataMember() || Var->isOutOfLine()) && + !Var->isCXXForRangeDecl()) SemaRef.ActOnUninitializedDecl(Var, false); // Diagnose unused local variables. diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index a7dc920f84..a639f5f303 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1228,6 +1228,28 @@ public: return getSema().ActOnCXXTryBlock(TryLoc, TryBlock, move(Handlers)); } + /// \brief Build a new C++0x range-based for statement. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult RebuildCXXForRangeStmt(SourceLocation ForLoc, + SourceLocation ColonLoc, + Stmt *Range, Stmt *BeginEnd, + Expr *Cond, Expr *Inc, + Stmt *LoopVar, + SourceLocation RParenLoc) { + return getSema().BuildCXXForRangeStmt(ForLoc, ColonLoc, Range, BeginEnd, + Cond, Inc, LoopVar, RParenLoc); + } + + /// \brief Attach body to a C++0x range-based for statement. + /// + /// By default, performs semantic analysis to finish the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body) { + return getSema().FinishCXXForRangeStmt(ForRange, Body); + } + /// \brief Build a new expression that references a declaration. /// /// By default, performs semantic analysis to build the new expression. @@ -5352,6 +5374,61 @@ TreeTransform::TransformCXXTryStmt(CXXTryStmt *S) { move_arg(Handlers)); } +template +StmtResult +TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { + StmtResult Range = getDerived().TransformStmt(S->getRangeStmt()); + if (Range.isInvalid()) + return StmtError(); + + StmtResult BeginEnd = getDerived().TransformStmt(S->getBeginEndStmt()); + if (BeginEnd.isInvalid()) + return StmtError(); + + ExprResult Cond = getDerived().TransformExpr(S->getCond()); + if (Cond.isInvalid()) + return StmtError(); + + ExprResult Inc = getDerived().TransformExpr(S->getInc()); + if (Inc.isInvalid()) + return StmtError(); + + StmtResult LoopVar = getDerived().TransformStmt(S->getLoopVarStmt()); + if (LoopVar.isInvalid()) + return StmtError(); + + StmtResult NewStmt = S; + if (getDerived().AlwaysRebuild() || + Range.get() != S->getRangeStmt() || + BeginEnd.get() != S->getBeginEndStmt() || + Cond.get() != S->getCond() || + Inc.get() != S->getInc() || + LoopVar.get() != S->getLoopVarStmt()) + NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(), + S->getColonLoc(), Range.get(), + BeginEnd.get(), Cond.get(), + Inc.get(), LoopVar.get(), + S->getRParenLoc()); + + StmtResult Body = getDerived().TransformStmt(S->getBody()); + if (Body.isInvalid()) + return StmtError(); + + // Body has changed but we didn't rebuild the for-range statement. Rebuild + // 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->getColonLoc(), Range.get(), + BeginEnd.get(), Cond.get(), + Inc.get(), LoopVar.get(), + S->getRParenLoc()); + + if (NewStmt.get() == S) + return SemaRef.Owned(S); + + return FinishCXXForRangeStmt(NewStmt.get(), Body.get()); +} + //===----------------------------------------------------------------------===// // Expression transformation //===----------------------------------------------------------------------===// diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 8e8a1bd2dc..a7c3fea7d6 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2676,6 +2676,11 @@ void ASTReader::InitializeContext(ASTContext &Ctx) { if (SpecialTypes[SPECIAL_TYPE_INT128_INSTALLED]) Context->setInt128Installed(); + if (unsigned AutoDeduct = SpecialTypes[SPECIAL_TYPE_AUTO_DEDUCT]) + Context->AutoDeductTy = GetType(AutoDeduct); + if (unsigned AutoRRefDeduct = SpecialTypes[SPECIAL_TYPE_AUTO_RREF_DEDUCT]) + Context->AutoRRefDeductTy = GetType(AutoRRefDeduct); + ReadPragmaDiagnosticMappings(Context->getDiagnostics()); // If there were any CUDA special declarations, deserialize them. diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 60fcc14d61..92b387e21a 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -684,6 +684,7 @@ void ASTDeclReader::VisitVarDecl(VarDecl *VD) { VD->setCXXDirectInitializer(Record[Idx++]); VD->setExceptionVariable(Record[Idx++]); VD->setNRVOVariable(Record[Idx++]); + VD->setCXXForRangeDecl(Record[Idx++]); if (Record[Idx++]) VD->setInit(Reader.ReadExpr(F)); diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 557cb38195..68591bec3f 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -141,6 +141,7 @@ namespace clang { // C++ Statements void VisitCXXCatchStmt(CXXCatchStmt *S); void VisitCXXTryStmt(CXXTryStmt *S); + void VisitCXXForRangeStmt(CXXForRangeStmt *); void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E); void VisitCXXConstructExpr(CXXConstructExpr *E); @@ -999,6 +1000,19 @@ void ASTStmtReader::VisitCXXTryStmt(CXXTryStmt *S) { S->getStmts()[i + 1] = Reader.ReadSubStmt(); } +void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) { + VisitStmt(S); + S->setForLoc(ReadSourceLocation(Record, Idx)); + S->setColonLoc(ReadSourceLocation(Record, Idx)); + S->setRParenLoc(ReadSourceLocation(Record, Idx)); + S->setRangeStmt(Reader.ReadSubStmt()); + S->setBeginEndStmt(Reader.ReadSubStmt()); + S->setCond(Reader.ReadSubExpr()); + S->setInc(Reader.ReadSubExpr()); + S->setLoopVarStmt(Reader.ReadSubStmt()); + S->setBody(Reader.ReadSubStmt()); +} + void ASTStmtReader::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); E->setOperator((OverloadedOperatorKind)Record[Idx++]); @@ -1269,6 +1283,8 @@ void ASTStmtReader::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { void ASTStmtReader::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) { VisitOverloadExpr(E); E->RequiresADL = Record[Idx++]; + if (E->RequiresADL) + E->StdIsAssociatedNamespace = Record[Idx++]; E->Overloaded = Record[Idx++]; E->NamingClass = cast_or_null(Reader.GetDecl(Record[Idx++])); } @@ -1738,6 +1754,10 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) { /*NumHandlers=*/Record[ASTStmtReader::NumStmtFields]); break; + case STMT_CXX_FOR_RANGE: + S = new (Context) CXXForRangeStmt(Empty); + break; + case EXPR_CXX_OPERATOR_CALL: S = new (Context) CXXOperatorCallExpr(*Context, Empty); break; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 0e925a2691..85b5326da9 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -2825,6 +2825,8 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, AddTypeRef(Context.ObjCSelRedefinitionType, Record); AddTypeRef(Context.getRawNSConstantStringType(), Record); Record.push_back(Context.isInt128Installed()); + AddTypeRef(Context.AutoDeductTy, Record); + AddTypeRef(Context.AutoRRefDeductTy, Record); Stream.EmitRecord(SPECIAL_TYPES, Record); // Keep writing types and declarations until all types and diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 95b0c3c4db..c74fe176f3 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -553,6 +553,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(D->hasCXXDirectInitializer()); Record.push_back(D->isExceptionVariable()); Record.push_back(D->isNRVOVariable()); + Record.push_back(D->isCXXForRangeDecl()); Record.push_back(D->getInit() ? 1 : 0); if (D->getInit()) Writer.AddStmt(D->getInit()); @@ -1145,6 +1146,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // hasCXXDirectInitializer Abv->Add(BitCodeAbbrevOp(0)); // isExceptionVariable Abv->Add(BitCodeAbbrevOp(0)); // isNRVOVariable + Abv->Add(BitCodeAbbrevOp(0)); // isCXXForRangeDecl Abv->Add(BitCodeAbbrevOp(0)); // HasInit Abv->Add(BitCodeAbbrevOp(0)); // HasMemberSpecializationInfo // ParmVarDecl diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 875d662929..9d54bb9b21 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -115,6 +115,7 @@ namespace clang { // C++ Statements void VisitCXXCatchStmt(CXXCatchStmt *S); void VisitCXXTryStmt(CXXTryStmt *S); + void VisitCXXForRangeStmt(CXXForRangeStmt *); void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E); void VisitCXXMemberCallExpr(CXXMemberCallExpr *E); @@ -963,6 +964,20 @@ void ASTStmtWriter::VisitCXXTryStmt(CXXTryStmt *S) { Code = serialization::STMT_CXX_TRY; } +void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { + VisitStmt(S); + Writer.AddSourceLocation(S->getForLoc(), Record); + Writer.AddSourceLocation(S->getColonLoc(), Record); + Writer.AddSourceLocation(S->getRParenLoc(), Record); + Writer.AddStmt(S->getRangeStmt()); + Writer.AddStmt(S->getBeginEndStmt()); + Writer.AddStmt(S->getCond()); + Writer.AddStmt(S->getInc()); + Writer.AddStmt(S->getLoopVarStmt()); + Writer.AddStmt(S->getBody()); + Code = serialization::STMT_CXX_FOR_RANGE; +} + void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); Record.push_back(E->getOperator()); @@ -1267,6 +1282,8 @@ void ASTStmtWriter::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { void ASTStmtWriter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) { VisitOverloadExpr(E); Record.push_back(E->requiresADL()); + if (E->requiresADL()) + Record.push_back(E->isStdAssociatedNamespace()); Record.push_back(E->isOverloaded()); Writer.AddDeclRef(E->getNamingClass(), Record); Code = serialization::EXPR_CXX_UNRESOLVED_LOOKUP; diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 9102f23f15..477292f6ff 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -423,6 +423,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::CXXForRangeStmtClass: case Stmt::CXXNullPtrLiteralExprClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::CXXTemporaryObjectExprClass: diff --git a/test/CXX/basic/basic.scope/basic.scope.local/p4-0x.cpp b/test/CXX/basic/basic.scope/basic.scope.local/p4-0x.cpp new file mode 100644 index 0000000000..d930f97ce7 --- /dev/null +++ b/test/CXX/basic/basic.scope/basic.scope.local/p4-0x.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +void f() { + int b; + int arr[] = {1, 2, 3}; + + if (bool b = true) // expected-note 2{{previous definition}} + bool b; // expected-error {{redefinition}} + else + int b; // expected-error {{redefinition}} + while (bool b = true) // expected-note {{previous definition}} + int b; // expected-error {{redefinition}} + for (int c; // expected-note 2{{previous definition}} + bool c = true;) // expected-error {{redefinition}} + double c; // expected-error {{redefinition}} + switch (int n = 37 + 5) // expected-note {{previous definition}} + int n; // expected-error {{redefinition}} + for (int a : arr) // expected-note {{previous definition}} + int a = 0; // expected-error {{redefinition}} + + if (bool b = true) { // expected-note 2{{previous definition}} + int b; // expected-error {{redefinition}} + } else { + int b; // expected-error {{redefinition}} + } + while (bool b = true) { // expected-note {{previous definition}} + int b; // expected-error {{redefinition}} + } + for (int c; // expected-note 2{{previous definition}} + bool c = true;) { // expected-error {{redefinition}} + double c; // expected-error {{redefinition}} + } + switch (int n = 37 + 5) { // expected-note {{previous definition}} + int n; // expected-error {{redefinition}} + } + for (int &a : arr) { // expected-note {{previous definition}} + int a = 0; // expected-error {{redefinition}} + } + + if (bool b = true) {{ // expected-note {{previous definition}} + bool b; + }} else { + int b; // expected-error {{redefinition}} + } + if (bool b = true) { // expected-note {{previous definition}} + bool b; // expected-error {{redefinition}} + } else {{ + int b; + }} + if (bool b = true) {{ + bool b; + }} else {{ + int b; + }} + while (bool b = true) {{ + int b; + }} + for (int c; // expected-note {{previous definition}} + bool c = true; ) {{ // expected-error {{redefinition}} + double c; + }} + switch (int n = 37 + 5) {{ + int n; + }} + for (int &a : arr) {{ + int a = 0; + }} +} diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp new file mode 100644 index 0000000000..10184a058f --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +// FIXME: when clang supports alias-declarations. +#if 0 +using X = struct { // ok +}; +#endif + +class K { + virtual ~K(); + // FIXME: the diagnostic here isn't very good + operator struct S {} (); // expected-error 2{{}} +}; + +void f() { + int arr[3] = {1,2,3}; + + for (struct S { S(int) {} } s : arr) { // expected-error {{types may not be defined in a for range declaration}} + } + + new struct T {}; // expected-error {{allocation of incomplete type}} expected-note {{forward declaration}} + + // FIXME: the diagnostic here isn't very good + try {} catch (struct U {}); // expected-error 3{{}} expected-note 2{{}} + + (void)(struct V { V(int); })0; // expected-error {{'V' can not be defined in a type specifier}} + + (void)dynamic_cast((K*)0); // expected-error {{'W' can not be defined in a type specifier}} + (void)static_cast(0); // expected-error {{'X' can not be defined in a type specifier}} + (void)reinterpret_cast(0); // expected-error {{'Y' can not be defined in a type specifier}} + (void)const_cast((const Z*)0); // expected-error {{'Z' can not be defined in a type specifier}} +} + +void g() throw (struct Ex {}) { // expected-error {{'Ex' can not be defined in a type specifier}} +} + +// FIXME: this currently gives a strange error because alignas is not recognised as a keyword yet. +int alignas(struct Aa {}) x; // expected-error {{'Aa' can not be defined in a parameter type}} expected-error {{expected function body}} + +int a = sizeof(struct So {}); // expected-error {{'So' can not be defined in a type specifier}} +int b = alignof(struct Ao {}); // expected-error {{'Ao' can not be defined in a type specifier}} + +namespace std { struct type_info; } +const std::type_info &ti = typeid(struct Ti {}); // expected-error {{'Ti' can not be defined in a type specifier}} diff --git a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp new file mode 100644 index 0000000000..12acde143c --- /dev/null +++ b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp @@ -0,0 +1,210 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +namespace std { + template + auto begin(T &&t) -> decltype(t.begin()) { return t.begin(); } // expected-note 4{{ignored: substitution failure}} + template + auto end(T &&t) -> decltype(t.end()) { return t.end(); } // expected-note {{candidate template ignored: substitution failure [with T = }} + + template + auto begin(T &&t) -> decltype(t.alt_begin()) { return t.alt_begin(); } // expected-note {{selected 'begin' template [with T = }} \ + expected-note 4{{candidate template ignored: substitution failure [with T = }} + template + auto end(T &&t) -> decltype(t.alt_end()) { return t.alt_end(); } // expected-note {{candidate template ignored: substitution failure [with T = }} + + namespace inner { + // These should never be considered. + int begin(int); + int end(int); + } + + using namespace inner; +} + +struct A { // expected-note {{candidate constructor}} + A(); + int *begin(); // expected-note 3{{selected 'begin' function with iterator type 'int *'}} expected-note {{'begin' declared here}} + int *end(); +}; + +struct B { + B(); + int *alt_begin(); + int *alt_end(); +}; + +void f(); // expected-note {{candidate}} +void f(int); // expected-note {{candidate}} + +void g() { + for (int a : A()) + A __begin; + for (char *a : A()) { // expected-error {{cannot initialize a variable of type 'char *' with an lvalue of type 'int'}} + } + for (char *a : B()) { // expected-error {{cannot initialize a variable of type 'char *' with an lvalue of type 'int'}} + } + // FIXME: Terrible diagnostic here. auto deduction should fail, but does not! + for (double a : f) { // expected-error {{address of overloaded function 'f' does not match required type ''}} + } + for (auto a : A()) { + } + for (auto a : B()) { + } + for (auto *a : A()) { // expected-error {{variable 'a' with type 'auto *' has incompatible initializer of type 'int'}} + } + // : is not a typo for :: here. + for (A NS:A()) { // expected-error {{no viable conversion from 'int' to 'A'}} + } + for (auto not_in_scope : not_in_scope) { // expected-error {{use of undeclared identifier 'not_in_scope'}} + } + + for (auto a : A()) + for (auto b : A()) { + __range.begin(); // expected-error {{use of undeclared identifier '__range'}} + ++__begin; // expected-error {{use of undeclared identifier '__begin'}} + --__end; // expected-error {{use of undeclared identifier '__end'}} + } + + for (char c : "test") + ; + for (auto a : f()) // expected-error {{cannot use type 'void' as a range}} + ; + + extern int incomplete[]; + for (auto a : incomplete) // expected-error {{cannot use incomplete type 'int []' as a range}} + ; + extern struct Incomplete also_incomplete[2]; // expected-note {{forward declaration}} + for (auto &a : also_incomplete) // expected-error {{cannot use incomplete type 'struct Incomplete [2]' as a range}} + ; + + struct VoidBegin { + void begin(); // expected-note {{selected 'begin' function with iterator type 'void'}} + void end(); + }; + for (auto a : VoidBegin()) // expected-error {{cannot use type 'void' as an iterator}} + ; + + struct null_t { + operator int*(); + }; + struct Differ { + int *begin(); // expected-note {{selected 'begin' function with iterator type 'int *'}} + null_t end(); // expected-note {{selected 'end' function with iterator type 'null_t'}} + }; + for (auto a : Differ()) // expected-error {{'begin' and 'end' must return the same type (got 'int *' and 'null_t')}} + ; + + for (void f() : "error") // expected-error {{for range declaration must declare a variable}} + ; + + for (extern int a : A()) {} // expected-error {{loop variable 'a' may not be declared 'extern'}} + for (static int a : A()) {} // expected-error {{loop variable 'a' may not be declared 'static'}} + for (register int a : A()) {} // expected-error {{loop variable 'a' may not be declared 'register'}} + // FIXME: when clang supports constexpr, this should be rejected. + for (constexpr int a : A()) {} // desired-error {{loop variable 'a' may not be declared 'constexpr'}} + + struct NoBeginADL { + null_t alt_end(); + }; + struct NoEndADL { + null_t alt_begin(); + }; + for (auto u : NoBeginADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type 'NoBeginADL'}} + } + for (auto u : NoEndADL()) { // expected-error {{no matching function for call to 'end'}} expected-note {{range has type 'NoEndADL'}} + } + + struct NoBegin { + null_t end(); + }; + struct NoEnd { + null_t begin(); + }; + for (auto u : NoBegin()) { // expected-error {{range type 'NoBegin' has 'end' member but no 'begin' member}} + } + for (auto u : NoEnd()) { // expected-error {{range type 'NoEnd' has 'begin' member but no 'end' member}} + } + + struct NoIncr { + void *begin(); // expected-note {{selected 'begin' function with iterator type 'void *'}} + void *end(); + }; + for (auto u : NoIncr()) { // expected-error {{arithmetic on pointer to void type}} + } + + struct NoNotEq { + NoNotEq begin(); // expected-note {{selected 'begin' function with iterator type 'NoNotEq'}} + NoNotEq end(); + void operator++(); + }; + for (auto u : NoNotEq()) { // expected-error {{invalid operands to binary expression}} + } + + struct NoCopy { + NoCopy(); + NoCopy(const NoCopy &) = delete; + int *begin(); + int *end(); + }; + for (int n : NoCopy()) { // ok + } + + for (int n : 42) { // expected-error {{no matching function for call to 'begin'}} \ + expected-note {{range has type 'int'}} + } + + for (auto a : *also_incomplete) { // expected-error {{cannot use incomplete type 'struct Incomplete' as a range}} + } +} + +template +void h(T t) { + for (U u : t) { // expected-error {{no viable conversion from 'A' to 'int'}} + } + for (auto u : t) { + } +} + +template void h(A); +template void h(A(&)[4]); +template void h(A(&)[13]); +template void h(A(&)[13]); // expected-note {{requested here}} + +template +void i(T t) { + for (auto u : t) { // expected-error {{no matching function for call to 'begin'}} \ + expected-error {{member function 'begin' not viable}} \ + expected-note {{range has type}} + } +} +template void i(A*); // expected-note {{requested here}} +template void i(const A); // expected-note {{requested here}} + +namespace NS { + class ADL {}; + int *begin(ADL); // expected-note {{no known conversion from 'NS::NoADL' to 'NS::ADL'}} + int *end(ADL); + + class NoADL {}; +} +int *begin(NS::NoADL); +int *end(NS::NoADL); + +struct VoidBeginADL {}; +void begin(VoidBeginADL); // expected-note {{selected 'begin' function with iterator type 'void'}} +void end(VoidBeginADL); + +void j() { + for (auto u : NS::ADL()) { + } + for (auto u : NS::NoADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}} + } + for (auto a : VoidBeginADL()) { // expected-error {{cannot use type 'void' as an iterator}} + } +} + +void example() { + int array[5] = { 1, 2, 3, 4, 5 }; + for (int &x : array) + x *= 2; +} diff --git a/test/CXX/temp/temp.decls/temp.variadic/p5.cpp b/test/CXX/temp/temp.decls/temp.variadic/p5.cpp index a0b17adad6..25338e3bae 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/p5.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/p5.cpp @@ -205,6 +205,8 @@ struct TestUnexpandedDecls : T{ Types t; // expected-error{{declaration type contains unexpanded parameter pack 'Types'}} for (Types *t = 0; ; ) { } // expected-error{{declaration type contains unexpanded parameter pack 'Types'}} for (; Types *t = 0; ) { } // expected-error{{declaration type contains unexpanded parameter pack 'Types'}} + T a[] = { T(), T(), T() }; + for (Types t : a) { } // expected-error{{declaration type contains unexpanded parameter pack 'Types'}} switch(Types *t = 0) { } // expected-error{{declaration type contains unexpanded parameter pack 'Types'}} while(Types *t = 0) { } // expected-error{{declaration type contains unexpanded parameter pack 'Types'}} if (Types *t = 0) { } // expected-error{{declaration type contains unexpanded parameter pack 'Types'}} @@ -341,6 +343,8 @@ void test_unexpanded_exprs(Types ...values) { // SizeOfPackExpr is uninteresting // FIXME: Objective-C expressions will need to go elsewhere + + for (auto t : values) { } // expected-error{{expression contains unexpanded parameter pack 'values'}} } // Test unexpanded parameter packs in partial specializations. diff --git a/test/CodeGenCXX/for-range-temporaries.cpp b/test/CodeGenCXX/for-range-temporaries.cpp new file mode 100644 index 0000000000..285862f51f --- /dev/null +++ b/test/CodeGenCXX/for-range-temporaries.cpp @@ -0,0 +1,131 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -std=c++0x -emit-llvm -o - -UDESUGAR %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -std=c++0x -emit-llvm -o - -DDESUGAR %s | FileCheck %s + +struct A { + A(); + A(const A &); + ~A(); +}; + +struct B { + B(); + B(const B &); + ~B(); +}; + +struct C { + C(const B &); + C(const C &); + ~C(); +}; + +struct E; +struct D { + D(const C &); + D(const D &); + ~D(); +}; +E begin(D); +E end(D); + +struct F; +struct G; +struct H; +struct E { + E(const E &); + ~E(); + F operator*(); + G operator++(); + H operator!=(const E &o); +}; + +struct I; +struct F { + F(const F &); + ~F(); + operator I(); +}; + +struct G { + G(const G &); + ~G(); + operator bool(); +}; + +struct H { + H(const H &); + ~H(); + operator bool(); +}; + +struct I { + I(const I &); + ~I(); +}; + +void body(const I &); + +void for_temps() { + A a; +#ifdef DESUGAR + { + auto && __range = D(B()); + for (auto __begin = begin(__range), __end = end(__range); + __begin != __end; ++__begin) { + I i = *__begin; + body(i); + } + } +#else + for (I i : D(B())) { + body(i); + } +#endif +} + +// CHECK: define void @_Z9for_tempsv() +// CHECK: call void @_ZN1AC1Ev( +// CHECK: call void @_ZN1BC1Ev( +// CHECK: call void @_ZN1CC1ERK1B( +// CHECK: call void @_ZN1DC1ERK1C( +// CHECK: call void @_ZN1CD1Ev( +// CHECK: call void @_ZN1BD1Ev( +// CHECK: call void @_ZN1DC1ERKS_( +// CHECK: call void @_Z5begin1D( +// CHECK: call void @_ZN1DD1Ev( +// CHECK: call void @_ZN1DC1ERKS_( +// CHECK: call void @_Z3end1D( +// CHECK: call void @_ZN1DD1Ev( +// CHECK: br label %[[COND:.*]] + +// CHECK: [[COND]]: +// CHECK: call void @_ZN1EneERKS_( +// CHECK: %[[CMP:.*]] = call zeroext i1 @_ZN1HcvbEv( +// CHECK: call void @_ZN1HD1Ev( +// CHECK: br i1 %[[CMP]], label %[[BODY:.*]], label %[[CLEANUP:.*]] + +// CHECK: [[CLEANUP]]: +// CHECK: call void @_ZN1ED1Ev( +// CHECK: call void @_ZN1ED1Ev( +// In for-range: +// call void @_ZN1DD1Ev( +// CHECK: br label %[[END:.*]] + +// CHECK: [[BODY]]: +// CHECK: call void @_ZN1EdeEv( +// CHECK: call void @_ZN1Fcv1IEv( +// CHECK: call void @_ZN1FD1Ev( +// CHECK: call void @_Z4bodyRK1I( +// CHECK: call void @_ZN1ID1Ev( +// CHECK: br label %[[INC:.*]] + +// CHECK: [[INC]]: +// CHECK: call void @_ZN1EppEv( +// CHECK: call void @_ZN1GD1Ev( +// CHECK: br label %[[COND]] + +// CHECK: [[END]]: +// In desugared version: +// call void @_ZN1DD1Ev( +// CHECK: call void @_ZN1AD1Ev( +// CHECK: ret void diff --git a/test/CodeGenCXX/for-range.cpp b/test/CodeGenCXX/for-range.cpp new file mode 100644 index 0000000000..94b614f726 --- /dev/null +++ b/test/CodeGenCXX/for-range.cpp @@ -0,0 +1,128 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -std=c++0x -emit-llvm -o - %s | FileCheck %s + +struct A { + A(); + A(const A&); + ~A(); +}; + +struct B { + B(); + B(const B&); + ~B(); +}; + +struct C { + C(); + C(const C&); + ~C(); +}; + +struct D { + D(); + D(const D&); + ~D(); + + B *begin(); + B *end(); +}; + +namespace std { + B *begin(C&); + B *end(C&); +} + +extern B array[5]; + +// CHECK: define void @_Z9for_arrayv( +void for_array() { + // CHECK: call void @_ZN1AC1Ev(%struct.A* [[A:.*]]) + A a; + for (B b : array) { + // CHECK-NOT: 5begin + // CHECK-NOT: 3end + // CHECK: getelementptr {{.*}}, i32 0 + // CHECK: getelementptr {{.*}}, i64 5 + // CHECK: br label %[[COND:.*]] + + // CHECK: [[COND]]: + // CHECK: %[[CMP:.*]] = icmp ne + // CHECK: br i1 %[[CMP]], label %[[BODY:.*]], label %[[END:.*]] + + // CHECK: [[BODY]]: + // CHECK: call void @_ZN1BC1ERKS_( + // CHECK: call void @_ZN1BD1Ev( + // CHECK: br label %[[INC:.*]] + + // CHECK: [[INC]]: + // CHECK: getelementptr {{.*}} i32 1 + // CHECK: br label %[[COND]] + } + // CHECK: [[END]]: + // CHECK: call void @_ZN1AD1Ev(%struct.A* [[A]]) + // CHECK: ret void +} + +// CHECK: define void @_Z9for_rangev( +void for_range() { + // CHECK: call void @_ZN1AC1Ev(%struct.A* [[A:.*]]) + A a; + for (B b : C()) { + // CHECK: call void @_ZN1CC1Ev( + // CHECK: = call %struct.A* @_ZSt5beginR1C( + // CHECK: = call %struct.A* @_ZSt3endR1C( + // CHECK: br label %[[COND:.*]] + + // CHECK: [[COND]]: + // CHECK: %[[CMP:.*]] = icmp ne + // CHECK: br i1 %[[CMP]], label %[[BODY:.*]], label %[[CLEANUP:.*]] + + // CHECK: [[CLEANUP]]: + // CHECK: call void @_ZN1CD1Ev( + // CHECK: br label %[[END:.*]] + + // CHECK: [[BODY]]: + // CHECK: call void @_ZN1BC1ERKS_( + // CHECK: call void @_ZN1BD1Ev( + // CHECK: br label %[[INC:.*]] + + // CHECK: [[INC]]: + // CHECK: getelementptr {{.*}} i32 1 + // CHECK: br label %[[COND]] + } + // CHECK: [[END]]: + // CHECK: call void @_ZN1AD1Ev(%struct.A* [[A]]) + // CHECK: ret void +} + +// CHECK: define void @_Z16for_member_rangev( +void for_member_range() { + // CHECK: call void @_ZN1AC1Ev(%struct.A* [[A:.*]]) + A a; + for (B b : D()) { + // CHECK: call void @_ZN1DC1Ev( + // CHECK: = call %struct.A* @_ZN1D5beginEv( + // CHECK: = call %struct.A* @_ZN1D3endEv( + // CHECK: br label %[[COND:.*]] + + // CHECK: [[COND]]: + // CHECK: %[[CMP:.*]] = icmp ne + // CHECK: br i1 %[[CMP]], label %[[BODY:.*]], label %[[CLEANUP:.*]] + + // CHECK: [[CLEANUP]]: + // CHECK: call void @_ZN1DD1Ev( + // CHECK: br label %[[END:.*]] + + // CHECK: [[BODY]]: + // CHECK: call void @_ZN1BC1ERKS_( + // CHECK: call void @_ZN1BD1Ev( + // CHECK: br label %[[INC:.*]] + + // CHECK: [[INC]]: + // CHECK: getelementptr {{.*}} i32 1 + // CHECK: br label %[[COND]] + } + // CHECK: [[END]]: + // CHECK: call void @_ZN1AD1Ev(%struct.A* [[A]]) + // CHECK: ret void +} diff --git a/test/PCH/cxx-for-range.cpp b/test/PCH/cxx-for-range.cpp new file mode 100644 index 0000000000..5854917da5 --- /dev/null +++ b/test/PCH/cxx-for-range.cpp @@ -0,0 +1,19 @@ +// Test this without pch. +// RUN: %clang_cc1 -std=c++0x -include %S/cxx-for-range.h -fsyntax-only -emit-llvm -o - %s + +// Test with pch. +// RUN: %clang_cc1 -std=c++0x -emit-pch -o %t %S/cxx-for-range.h +// RUN: %clang_cc1 -std=c++0x -include-pch %t -fsyntax-only -emit-llvm -o - %s + +void h() { + f(); + + g(); + + char a[3] = { 0, 1, 2 }; + for (auto w : a) + for (auto x : S()) + for (auto y : T()) + for (auto z : U()) + ; +} diff --git a/test/PCH/cxx-for-range.h b/test/PCH/cxx-for-range.h new file mode 100644 index 0000000000..f15c7e73df --- /dev/null +++ b/test/PCH/cxx-for-range.h @@ -0,0 +1,35 @@ +// Header for PCH test cxx-for-range.cpp + +struct S { + int *begin(); + int *end(); +}; + +struct T { }; +char *begin(T); +char *end(T); + +struct U { }; +namespace std { + char *begin(U); + char *end(U); +} + +void f() { + char a[3] = { 0, 1, 2 }; + for (auto w : a) + for (auto x : S()) + for (auto y : T()) + for (auto z : U()) + ; +} + +template +void g() { + A a[3] = { 0, 1, 2 }; + for (auto &v : a) + for (auto x : S()) + for (auto y : T()) + for (auto z : U()) + ; +} diff --git a/test/SemaCXX/for-range-examples.cpp b/test/SemaCXX/for-range-examples.cpp new file mode 100644 index 0000000000..810f1de441 --- /dev/null +++ b/test/SemaCXX/for-range-examples.cpp @@ -0,0 +1,150 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++0x + +namespace value_range_detail { + template + class value_range_iter { + T t; + public: + value_range_iter(const T &t) : t(t) {} + T operator*() const { return t; } + bool operator!=(const value_range_iter &o) const { return t != o.t; } + value_range_iter &operator++() { ++t; return *this; } + }; + + template + struct value_range { + value_range(const T &a, const T &b) : begin_(a), end_(b) {} + value_range_iter begin_, end_; + }; + + template + value_range_iter begin(const value_range &r) { return r.begin_; } + template + value_range_iter end(const value_range &r) { return r.end_; } + + + struct end_t {}; + + template + class value_range_step_iter { + T it, step; + public: + value_range_step_iter(const T &it, const T &step) : it(it), step(step) {} + T operator*() const { return it; } + bool operator!=(value_range_step_iter end) const { return it != end.it; } + value_range_step_iter &operator++() { it += step; return *this; } + }; + + template + class value_range_step { + T it, step, end_; + public: + value_range_step(const T &it, const T &end, const T &step) : + it(it), end_(end), step(step) {} + typedef value_range_step_iter iterator; + iterator begin() const { return iterator(it, step); } + iterator end() const { return iterator(end_, step); } + }; +} + +template +value_range_detail::value_range range(const T &a, const T &b) { return value_range_detail::value_range(a, b); } + +template +value_range_detail::value_range_step range(const T &a, const T &b, const T &step) { return value_range_detail::value_range_step(a, b, step); } + + +namespace map_range { + template + class vector { + T storage[100]; + decltype(sizeof(char)) size; + public: + vector() : size() {} + void push_back(T t) { storage[size++] = t; } + T *begin() { return storage; } + T *end() { return storage + size; } + }; + + template struct tuple_elem { + T t; + tuple_elem() {} + tuple_elem(T t) : t(t) {} + }; + template + struct tuple : tuple_elem... { + tuple() : tuple_elem()... {} + tuple(A... a) : tuple_elem(a)... {} + template B &get() { return tuple_elem::t; } + }; + + template + class map_iter { + F f; + I i; + public: + map_iter(F f, I i) : f(f), i(i) {} + auto operator*() const -> decltype(f(*i)) { return f(*i); } + bool operator!=(const map_iter &o) const { return i != o.i; } + map_iter &operator++() { ++i; return *this; } + }; + + template + struct iter_pair { + T begin_, end_; + iter_pair(T begin, T end) : begin_(begin), end_(end) {} + }; + template T begin(iter_pair p) { return p.begin_; } + template T end(iter_pair p) { return p.end_; } + + template class mem_fun_impl; + template + class mem_fun_impl { + typedef R (T::*F)(A...); + F f; + public: + mem_fun_impl(F f) : f(f) {} + R operator()(T &t, A &&...a) const { return (t.*f)(static_cast(a)...); } + }; + template mem_fun_impl mem_fun(F f) { return mem_fun_impl(f); } + + template + auto map(const F &f, T &t) -> iter_pair> { + typedef map_iter iter; + return iter_pair(iter(f, t.begin()), iter(f, t.end())); + } +} + +#define assert(b) if (!b) { return 1; } +int main() { + int total = 0; + + for (auto n : range(1, 5)) { + total += n; + } + assert(total == 10); + + for (auto n : range(10, 100, 10)) { + total += n; + } + assert(total == 460); + + map_range::vector chars; + chars.push_back('a'); + chars.push_back('b'); + chars.push_back('c'); + for (char c : chars) { + ++total; + } + assert(total == 463); + + typedef map_range::tuple T; + map_range::vector pairs; + pairs.push_back(T(42, 12.9)); + pairs.push_back(T(6, 4.2)); + pairs.push_back(T(9, 1.1)); + for (auto a : map(map_range::mem_fun(&T::get), pairs)) { + total += a; + } + assert(total == 500); +} diff --git a/test/SemaCXX/for-range-no-std.cpp b/test/SemaCXX/for-range-no-std.cpp new file mode 100644 index 0000000000..8cc71e5111 --- /dev/null +++ b/test/SemaCXX/for-range-no-std.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++0x + +struct S { + int *begin(); + int *end(); +}; + +struct T { +}; + +struct Range {}; +int begin(Range); // expected-note {{not viable}} +int end(Range); + +namespace NS { + struct ADL {}; + struct iter { + int operator*(); + bool operator!=(iter); + void operator++(); + }; + iter begin(ADL); // expected-note {{not viable}} + iter end(ADL); + + struct NoADL {}; +} +NS::iter begin(NS::NoADL); // expected-note {{not viable}} +NS::iter end(NS::NoADL); + +void f() { + int a[] = {1, 2, 3}; + for (auto b : S()) {} // ok + for (auto b : T()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}} + for (auto b : a) {} // ok + for (int b : NS::ADL()) {} // ok + for (int b : NS::NoADL()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}} +} diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index 7ed5d2051c..89800598ed 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -95,6 +95,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, case Stmt::ObjCForCollectionStmtClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXTryStmtClass: + case Stmt::CXXForRangeStmtClass: K = CXCursor_UnexposedStmt; break; -- 2.40.0