From 7a614d8380297fcd2bc23986241905d97222948c Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 11 Jun 2011 17:19:42 +0000 Subject: [PATCH] Implement support for C++11 in-class initialization of non-static data members. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132878 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 60 ++++- include/clang/AST/DeclCXX.h | 18 +- include/clang/AST/DeclObjC.h | 4 +- include/clang/AST/Type.h | 1 + include/clang/Basic/DiagnosticParseKinds.td | 11 + include/clang/Basic/DiagnosticSemaKinds.td | 6 +- .../clang/Basic/ExceptionSpecificationType.h | 13 +- include/clang/Parse/Parser.h | 42 +++- include/clang/Sema/DeclSpec.h | 27 ++- include/clang/Sema/Scope.h | 7 +- include/clang/Sema/Sema.h | 35 ++- lib/AST/ASTContext.cpp | 18 +- lib/AST/ASTImporter.cpp | 5 +- lib/AST/Decl.cpp | 14 +- lib/AST/DeclCXX.cpp | 26 ++ lib/AST/DeclPrinter.cpp | 115 +++++---- lib/AST/Expr.cpp | 28 ++- lib/AST/Mangle.cpp | 5 + lib/CodeGen/CGBlocks.cpp | 6 +- lib/CodeGen/CGClass.cpp | 1 + lib/CodeGen/CGObjCMac.cpp | 8 +- lib/Parse/ParseCXXInlineMethods.cpp | 113 +++++++++ lib/Parse/ParseDeclCXX.cpp | 162 +++++++++---- lib/Parse/Parser.cpp | 16 ++ lib/Rewrite/RewriteObjC.cpp | 15 +- lib/Sema/SemaDecl.cpp | 42 ++-- lib/Sema/SemaDeclCXX.cpp | 229 +++++++++++++++--- lib/Sema/SemaExceptionSpec.cpp | 10 +- lib/Sema/SemaExpr.cpp | 36 ++- lib/Sema/SemaExprCXX.cpp | 73 +++--- lib/Sema/SemaTemplateInstantiate.cpp | 48 +++- lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 + lib/Sema/SemaType.cpp | 2 + lib/Sema/TreeTransform.h | 8 +- lib/Serialization/ASTReaderDecl.cpp | 7 +- lib/Serialization/ASTWriterDecl.cpp | 5 +- test/CXX/class/class.mem/p5-0x.cpp | 9 + .../dcl.spec/dcl.type/dcl.spec.auto/p4.cpp | 25 +- .../dcl.spec/dcl.type/dcl.spec.auto/p5.cpp | 8 +- test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp | 4 +- .../dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp | 63 +++++ test/CXX/dcl.decl/dcl.init/p14-0x.cpp | 44 ++++ test/CXX/except/except.spec/p14.cpp | 14 +- test/CXX/expr/expr.prim/p12-0x.cpp | 12 + test/CXX/expr/expr.prim/p4-0x.cpp | 10 + test/CXX/special/class.ctor/p5-0x.cpp | 173 +++++++++++++ .../class.init/class.base.init/p8-0x.cpp | 23 ++ .../class.init/class.base.init/p9-0x.cpp | 36 +++ test/CodeGenCXX/member-init-ctor.cpp | 14 ++ test/CodeGenObjCXX/blocks.mm | 18 +- test/PCH/cxx-member-init.cpp | 22 ++ test/Parser/cxx0x-member-initializers.cpp | 15 ++ test/SemaCXX/PR9572.cpp | 8 +- test/SemaCXX/class.cpp | 2 +- test/SemaCXX/implicit-exception-spec.cpp | 63 +++++ test/SemaCXX/member-init.cpp | 50 ++++ test/SemaCXX/type-traits.cpp | 15 +- 57 files changed, 1563 insertions(+), 282 deletions(-) create mode 100644 test/CXX/class/class.mem/p5-0x.cpp create mode 100644 test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp create mode 100644 test/CXX/dcl.decl/dcl.init/p14-0x.cpp create mode 100644 test/CXX/expr/expr.prim/p12-0x.cpp create mode 100644 test/CXX/expr/expr.prim/p4-0x.cpp create mode 100644 test/CXX/special/class.ctor/p5-0x.cpp create mode 100644 test/CXX/special/class.init/class.base.init/p8-0x.cpp create mode 100644 test/CXX/special/class.init/class.base.init/p9-0x.cpp create mode 100644 test/CodeGenCXX/member-init-ctor.cpp create mode 100644 test/PCH/cxx-member-init.cpp create mode 100644 test/Parser/cxx0x-member-initializers.cpp create mode 100644 test/SemaCXX/implicit-exception-spec.cpp create mode 100644 test/SemaCXX/member-init.cpp diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index e1dad644c9..d993d345ef 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1953,20 +1953,33 @@ class FieldDecl : public DeclaratorDecl { bool Mutable : 1; mutable unsigned CachedFieldIndex : 31; - Expr *BitWidth; + /// \brief A pointer to either the in-class initializer for this field (if + /// the boolean value is false), or the bit width expression for this bit + /// field (if the boolean value is true). + /// + /// We can safely combine these two because in-class initializers are not + /// permitted for bit-fields. + /// + /// If the boolean is false and the initializer is null, then this field has + /// an in-class initializer which has not yet been parsed and attached. + llvm::PointerIntPair InitializerOrBitWidth; protected: FieldDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, - QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable) + QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable, + bool HasInit) : DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc), - Mutable(Mutable), CachedFieldIndex(0), BitWidth(BW) { + Mutable(Mutable), CachedFieldIndex(0), + InitializerOrBitWidth(BW, !HasInit) { + assert(!(BW && HasInit) && "got initializer for bitfield"); } public: static FieldDecl *Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, QualType T, - TypeSourceInfo *TInfo, Expr *BW, bool Mutable); + TypeSourceInfo *TInfo, Expr *BW, bool Mutable, + bool HasInit); /// getFieldIndex - Returns the index of this field within its record, /// as appropriate for passing to ASTRecordLayout::getFieldOffset. @@ -1979,10 +1992,12 @@ public: void setMutable(bool M) { Mutable = M; } /// isBitfield - Determines whether this field is a bitfield. - bool isBitField() const { return BitWidth != NULL; } + bool isBitField() const { + return InitializerOrBitWidth.getInt() && InitializerOrBitWidth.getPointer(); + } /// @brief Determines whether this is an unnamed bitfield. - bool isUnnamedBitfield() const { return BitWidth != NULL && !getDeclName(); } + bool isUnnamedBitfield() const { return isBitField() && !getDeclName(); } /// isAnonymousStructOrUnion - Determines whether this field is a /// representative for an anonymous struct or union. Such fields are @@ -1990,8 +2005,37 @@ public: /// store the data for the anonymous union or struct. bool isAnonymousStructOrUnion() const; - Expr *getBitWidth() const { return BitWidth; } - void setBitWidth(Expr *BW) { BitWidth = BW; } + Expr *getBitWidth() const { + return isBitField() ? InitializerOrBitWidth.getPointer() : 0; + } + void setBitWidth(Expr *BW) { + assert(!InitializerOrBitWidth.getPointer() && + "bit width or initializer already set"); + InitializerOrBitWidth.setPointer(BW); + InitializerOrBitWidth.setInt(1); + } + + /// hasInClassInitializer - Determine whether this member has a C++0x in-class + /// initializer. + bool hasInClassInitializer() const { + return !InitializerOrBitWidth.getInt(); + } + /// getInClassInitializer - Get the C++0x in-class initializer for this + /// member, or null if one has not been set. If a valid declaration has an + /// in-class initializer, but this returns null, then we have not parsed and + /// attached it yet. + Expr *getInClassInitializer() const { + return hasInClassInitializer() ? InitializerOrBitWidth.getPointer() : 0; + } + /// setInClassInitializer - Set the C++0x in-class initializer for this member. + void setInClassInitializer(Expr *Init); + /// removeInClassInitializer - Remove the C++0x in-class initializer from this + /// member. + void removeInClassInitializer() { + assert(!InitializerOrBitWidth.getInt() && "no initializer to remove"); + InitializerOrBitWidth.setPointer(0); + InitializerOrBitWidth.setInt(1); + } /// getParent - Returns the parent of this field declaration, which /// is the struct in which this method is defined. diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 096ace7128..42a12eb5a3 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1378,6 +1378,8 @@ class CXXCtorInitializer { /// \brief The argument used to initialize the base or member, which may /// end up constructing an object (when multiple arguments are involved). + /// If 0, this is a field initializer, and the in-class member initializer + /// will be used. Stmt *Init; /// LParenLoc - Location of the left paren of the ctor-initializer. @@ -1452,6 +1454,13 @@ public: return Initializee.is(); } + /// isInClassMemberInitializer - Returns true when this initializer is an + /// implicit ctor initializer generated for a field with an initializer + /// defined on the member declaration. + bool isInClassMemberInitializer() const { + return !Init; + } + /// isDelegatingInitializer - Returns true when this initializer is creating /// a delegating constructor. bool isDelegatingInitializer() const { @@ -1580,7 +1589,14 @@ public: reinterpret_cast(this + 1)[I] = Index; } - Expr *getInit() const { return static_cast(Init); } + /// \brief Get the initializer. This is 0 if this is an in-class initializer + /// for a non-static data member which has not yet been parsed. + Expr *getInit() const { + if (!Init) + return getAnyMember()->getInClassInitializer(); + + return static_cast(Init); + } }; /// CXXConstructorDecl - Represents a C++ constructor within a diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 0e698607db..74ceb43c71 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -724,7 +724,7 @@ private: QualType T, TypeSourceInfo *TInfo, AccessControl ac, Expr *BW, bool synthesized) : FieldDecl(ObjCIvar, DC, StartLoc, IdLoc, Id, T, TInfo, BW, - /*Mutable=*/false), + /*Mutable=*/false, /*HasInit=*/false), NextIvar(0), DeclAccess(ac), Synthesized(synthesized) {} public: @@ -779,7 +779,7 @@ private: QualType T, Expr *BW) : FieldDecl(ObjCAtDefsField, DC, StartLoc, IdLoc, Id, T, /*TInfo=*/0, // FIXME: Do ObjCAtDefs have declarators ? - BW, /*Mutable=*/false) {} + BW, /*Mutable=*/false, /*HasInit=*/false) {} public: static ObjCAtDefsFieldDecl *Create(ASTContext &C, DeclContext *DC, diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 7dc672a41a..77633831ff 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2604,6 +2604,7 @@ public: } bool isNothrow(ASTContext &Ctx) const { ExceptionSpecificationType EST = getExceptionSpecType(); + assert(EST != EST_Delayed); if (EST == EST_DynamicNone || EST == EST_BasicNoexcept) return true; if (EST != EST_ComputedNoexcept) diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index efac2b7d77..fb1c909508 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -193,6 +193,8 @@ def ext_ref_qualifier : ExtWarn< "reference qualifiers on functions are a C++0x extension">, InGroup; def ext_inline_namespace : ExtWarn< "inline namespaces are a C++0x feature">, InGroup; +def err_generalized_initializer_lists : Error< + "generalized initializer lists are a C++0x extension unsupported in Clang">; def ext_generalized_initializer_lists : ExtWarn< "generalized initializer lists are a C++0x extension unsupported in Clang">, InGroup; @@ -444,6 +446,15 @@ def warn_defaulted_function_accepted_as_extension: ExtWarn< "defaulted function definition accepted as a C++0x extension">, InGroup; +// C++0x in-class member initialization +def warn_nonstatic_member_init_accepted_as_extension: ExtWarn< + "in-class initialization of non-static data member accepted as a C++0x extension">, + InGroup; +def err_bitfield_member_init: Error< + "bitfield member cannot have an in-class initializer">; +def err_incomplete_array_member_init: Error< + "array bound cannot be deduced from an in-class initializer">; + // C++0x alias-declaration def ext_alias_declaration : ExtWarn< "alias declarations accepted as a C++0x extension">, InGroup; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7890aefc50..5cfa61b397 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -589,6 +589,8 @@ def warn_missing_exception_specification : Warning< "%0 is missing exception specification '%1'">; def err_noexcept_needs_constant_expression : Error< "argument to noexcept specifier must be a constant expression">; +def err_exception_spec_unknown : Error< + "exception specification is not available until end of class definition">; // C++ access checking def err_class_redeclared_with_different_access : Error< @@ -973,8 +975,8 @@ def err_illegal_decl_array_of_auto : Error< def err_new_array_of_auto : Error< "cannot allocate array of 'auto'">; def err_auto_not_allowed : Error< - "'auto' not allowed %select{in function prototype|in struct member" - "|in union member|in class member|in exception declaration" + "'auto' not allowed %select{in function prototype|in non-static struct member" + "|in non-static union member|in non-static class member|in exception declaration" "|in template parameter|in block literal|in template argument" "|in typedef|in type alias|in function return type|here}0">; def err_auto_var_requires_init : Error< diff --git a/include/clang/Basic/ExceptionSpecificationType.h b/include/clang/Basic/ExceptionSpecificationType.h index aecf6eb269..98cfd297ad 100644 --- a/include/clang/Basic/ExceptionSpecificationType.h +++ b/include/clang/Basic/ExceptionSpecificationType.h @@ -18,12 +18,13 @@ namespace clang { /// \brief The various types of exception specifications that exist in C++0x. enum ExceptionSpecificationType { - EST_None, ///< no exception specification - EST_DynamicNone, ///< throw() - EST_Dynamic, ///< throw(T1, T2) - EST_MSAny, ///< Microsoft throw(...) extension - EST_BasicNoexcept, ///< noexcept - EST_ComputedNoexcept ///< noexcept(expression) + EST_None, ///< no exception specification + EST_DynamicNone, ///< throw() + EST_Dynamic, ///< throw(T1, T2) + EST_MSAny, ///< Microsoft throw(...) extension + EST_BasicNoexcept, ///< noexcept + EST_ComputedNoexcept, ///< noexcept(expression) + EST_Delayed ///< not known yet }; inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) { diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 680b788dce..e4cdc27c43 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -580,6 +580,18 @@ private: /// ExitScope - Pop a scope off the scope stack. void ExitScope(); + /// \brief RAII object used to modify the scope flags for the current scope. + class ParseScopeFlags { + Scope *CurScope; + unsigned OldFlags; + ParseScopeFlags(const ParseScopeFlags &); // do not implement + void operator=(const ParseScopeFlags &); // do not implement + + public: + ParseScopeFlags(Parser *Self, unsigned ScopeFlags, bool ManageFlags = true); + ~ParseScopeFlags(); + }; + //===--------------------------------------------------------------------===// // Diagnostic Emission and Error recovery. @@ -621,8 +633,8 @@ private: /// - function bodies /// - default arguments /// - exception-specifications (TODO: C++0x) - /// - and brace-or-equal-initializers (TODO: C++0x) - /// for non-static data members (including such things in nested classes)." + /// - and brace-or-equal-initializers for non-static data members + /// (including such things in nested classes)." /// LateParsedDeclarations build the tree of those elements so they can /// be parsed after parsing the top-level class. class LateParsedDeclaration { @@ -630,6 +642,7 @@ private: virtual ~LateParsedDeclaration(); virtual void ParseLexedMethodDeclarations(); + virtual void ParseLexedMemberInitializers(); virtual void ParseLexedMethodDefs(); }; @@ -641,6 +654,7 @@ private: virtual ~LateParsedClass(); virtual void ParseLexedMethodDeclarations(); + virtual void ParseLexedMemberInitializers(); virtual void ParseLexedMethodDefs(); private: @@ -714,6 +728,25 @@ private: llvm::SmallVector DefaultArgs; }; + /// LateParsedMemberInitializer - An initializer for a non-static class data + /// member whose parsing must to be delayed until the class is completely + /// defined (C++11 [class.mem]p2). + struct LateParsedMemberInitializer : public LateParsedDeclaration { + LateParsedMemberInitializer(Parser *P, Decl *FD) + : Self(P), Field(FD) { } + + virtual void ParseLexedMemberInitializers(); + + Parser *Self; + + /// Field - The field declaration. + Decl *Field; + + /// CachedTokens - The sequence of tokens that comprises the initializer, + /// including any leading '='. + CachedTokens Toks; + }; + /// LateParsedDeclarationsContainer - During parsing of a top (non-nested) /// C++ class, its method declarations that contain parts that won't be /// parsed until after the definition is completed (C++ [class.mem]p2), @@ -974,10 +1007,13 @@ private: Decl *ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D, const ParsedTemplateInfo &TemplateInfo, const VirtSpecifiers& VS, ExprResult& Init); + void ParseCXXNonStaticMemberInitializer(Decl *VarD); void ParseLexedMethodDeclarations(ParsingClass &Class); void ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM); void ParseLexedMethodDefs(ParsingClass &Class); void ParseLexedMethodDef(LexedMethod &LM); + void ParseLexedMemberInitializers(ParsingClass &Class); + void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI); bool ConsumeAndStoreUntil(tok::TokenKind T1, CachedTokens &Toks, bool StopAtSemi = true, @@ -1755,6 +1791,8 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl &Names, bool SuppressDeclarations = false); void ParseCXXMemberSpecification(SourceLocation StartLoc, unsigned TagType, Decl *TagDecl); + ExprResult ParseCXXMemberInitializer(bool IsFunction, + SourceLocation &EqualLoc); void ParseCXXClassMemberDeclaration(AccessSpecifier AS, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), ParsingDeclRAIIObject *DiagsFromTParams = 0); diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index be1f5f8c79..7ce4e00943 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -1078,8 +1078,8 @@ struct DeclaratorChunk { /// If this is an invalid location, there is no ref-qualifier. unsigned RefQualifierLoc; - /// \brief When ExceptionSpecType isn't EST_None, the location of the - /// keyword introducing the spec. + /// \brief When ExceptionSpecType isn't EST_None or EST_Delayed, the + /// location of the keyword introducing the spec. unsigned ExceptionSpecLoc; /// ArgInfo - This is a pointer to a new[]'d array of ParamInfo objects that @@ -1616,6 +1616,29 @@ public: DeclTypeInfo.erase(DeclTypeInfo.begin()); } + /// isArrayOfUnknownBound - This method returns true if the declarator + /// is a declarator for an array of unknown bound (looking through + /// parentheses). + bool isArrayOfUnknownBound() const { + for (unsigned i = 0, i_end = DeclTypeInfo.size(); i < i_end; ++i) { + switch (DeclTypeInfo[i].Kind) { + case DeclaratorChunk::Paren: + continue; + case DeclaratorChunk::Function: + case DeclaratorChunk::Pointer: + case DeclaratorChunk::Reference: + case DeclaratorChunk::BlockPointer: + case DeclaratorChunk::MemberPointer: + return false; + case DeclaratorChunk::Array: + return !DeclTypeInfo[i].Arr.NumElts; + } + llvm_unreachable("Invalid type chunk"); + return false; + } + return false; + } + /// isFunctionDeclarator - This method returns true if the declarator /// is a function declarator (looking through parentheses). /// If true is returned, then the reference type parameter idx is diff --git a/include/clang/Sema/Scope.h b/include/clang/Sema/Scope.h index 6588a1d92d..7514633c0e 100644 --- a/include/clang/Sema/Scope.h +++ b/include/clang/Sema/Scope.h @@ -79,7 +79,12 @@ public: ObjCMethodScope = 0x400, /// SwitchScope - This is a scope that corresponds to a switch statement. - SwitchScope = 0x800 + SwitchScope = 0x800, + + /// ThisScope - This is the scope of a struct/union/class definition, + /// outside of any member function definition, where 'this' is nonetheless + /// usable. + ThisScope = 0x1000 }; private: /// The parent scope for this scope. This is null for the translation-unit diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index bb3ec2dbed..6dd0d3ceab 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1144,13 +1144,13 @@ public: Declarator &D, Expr *BitfieldWidth); FieldDecl *HandleField(Scope *S, RecordDecl *TagD, SourceLocation DeclStart, - Declarator &D, Expr *BitfieldWidth, + Declarator &D, Expr *BitfieldWidth, bool HasInit, AccessSpecifier AS); FieldDecl *CheckFieldDecl(DeclarationName Name, QualType T, TypeSourceInfo *TInfo, RecordDecl *Record, SourceLocation Loc, - bool Mutable, Expr *BitfieldWidth, + bool Mutable, Expr *BitfieldWidth, bool HasInit, SourceLocation TSSL, AccessSpecifier AS, NamedDecl *PrevDecl, Declarator *D = 0); @@ -2612,6 +2612,13 @@ public: // Then a throw(collected exceptions) // Finally no specification. // throw(...) is used instead if any called function uses it. + // + // If this exception specification cannot be known yet (for instance, + // because this is the exception specification for a defaulted default + // constructor and we haven't finished parsing the deferred parts of the + // class yet), the C++0x standard does not specify how to behave. We + // record this as an 'unknown' exception specification, which overrules + // any other specification (even 'none', to keep this rule simple). ExceptionSpecificationType ComputedEST; llvm::SmallPtrSet ExceptionsSeen; llvm::SmallVector Exceptions; @@ -2644,6 +2651,15 @@ public: /// \brief Integrate another called method into the collected data. void CalledDecl(CXXMethodDecl *Method); + /// \brief Integrate an invoked expression into the collected data. + void CalledExpr(Expr *E); + + /// \brief Specify that the exception specification can't be detemined yet. + void SetDelayed() { + ClearExceptions(); + ComputedEST = EST_Delayed; + } + FunctionProtoType::ExtProtoInfo getEPI() const { FunctionProtoType::ExtProtoInfo EPI; EPI.ExceptionSpecType = getExceptionSpecType(); @@ -2836,10 +2852,9 @@ public: //// ActOnCXXThis - Parse 'this' pointer. ExprResult ActOnCXXThis(SourceLocation loc); - /// tryCaptureCXXThis - Try to capture a 'this' pointer. Returns a - /// pointer to an instance method whose 'this' pointer is - /// capturable, or null if this is not possible. - CXXMethodDecl *tryCaptureCXXThis(); + /// getAndCaptureCurrentThisType - Try to capture a 'this' pointer. Returns + /// the type of the 'this' pointer, or a null type if this is not possible. + QualType getAndCaptureCurrentThisType(); /// ActOnCXXBoolLiteral - Parse {true,false} literals. ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind); @@ -3238,7 +3253,10 @@ public: Declarator &D, MultiTemplateParamsArg TemplateParameterLists, Expr *BitfieldWidth, const VirtSpecifiers &VS, - Expr *Init, bool IsDefinition); + Expr *Init, bool HasDeferredInit, + bool IsDefinition); + void ActOnCXXInClassMemberInitializer(Decl *VarDecl, SourceLocation EqualLoc, + Expr *Init); MemInitResult ActOnMemInitializer(Decl *ConstructorD, Scope *S, @@ -3342,8 +3360,9 @@ public: void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record); void ActOnStartDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnDelayedCXXMethodParameter(Scope *S, Decl *Param); - void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnFinishDelayedMemberDeclarations(Scope *S, Decl *Record); + void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method); + void ActOnFinishDelayedMemberInitializers(Decl *Record); void MarkAsLateParsedTemplate(FunctionDecl *FD, bool Flag = true); bool IsInsideALocalClassWithinATemplateFunction(); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 17b7c9d34b..9094abad2e 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3536,7 +3536,8 @@ QualType ASTContext::getCFConstantStringType() const { SourceLocation(), 0, FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, - /*Mutable=*/false); + /*Mutable=*/false, + /*HasInit=*/false); Field->setAccess(AS_public); CFConstantStringTypeDecl->addDecl(Field); } @@ -3577,7 +3578,8 @@ QualType ASTContext::getNSConstantStringType() const { SourceLocation(), 0, FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, - /*Mutable=*/false); + /*Mutable=*/false, + /*HasInit=*/false); Field->setAccess(AS_public); NSConstantStringTypeDecl->addDecl(Field); } @@ -3616,7 +3618,8 @@ QualType ASTContext::getObjCFastEnumerationStateType() const { SourceLocation(), 0, FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, - /*Mutable=*/false); + /*Mutable=*/false, + /*HasInit=*/false); Field->setAccess(AS_public); ObjCFastEnumerationStateTypeDecl->addDecl(Field); } @@ -3653,7 +3656,8 @@ QualType ASTContext::getBlockDescriptorType() const { &Idents.get(FieldNames[i]), FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, - /*Mutable=*/false); + /*Mutable=*/false, + /*HasInit=*/false); Field->setAccess(AS_public); T->addDecl(Field); } @@ -3701,7 +3705,8 @@ QualType ASTContext::getBlockDescriptorExtendedType() const { &Idents.get(FieldNames[i]), FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, - /*Mutable=*/false); + /*Mutable=*/false, + /*HasInit=*/false); Field->setAccess(AS_public); T->addDecl(Field); } @@ -3786,7 +3791,8 @@ ASTContext::BuildByRefType(llvm::StringRef DeclName, QualType Ty) const { SourceLocation(), &Idents.get(FieldNames[i]), FieldTypes[i], /*TInfo=*/0, - /*BitWidth=*/0, /*Mutable=*/false); + /*BitWidth=*/0, /*Mutable=*/false, + /*HasInit=*/false); Field->setAccess(AS_public); T->addDecl(Field); } diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index fcca95cefd..100e604d1c 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -2512,9 +2512,12 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { FieldDecl *ToField = FieldDecl::Create(Importer.getToContext(), DC, Importer.Import(D->getInnerLocStart()), Loc, Name.getAsIdentifierInfo(), - T, TInfo, BitWidth, D->isMutable()); + T, TInfo, BitWidth, D->isMutable(), + D->hasInClassInitializer()); ToField->setAccess(D->getAccess()); ToField->setLexicalDeclContext(LexicalDC); + if (ToField->hasInClassInitializer()) + ToField->setInClassInitializer(D->getInClassInitializer()); Importer.Imported(D, ToField); LexicalDC->addDecl(ToField); return ToField; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 1cad64e055..12357c07a7 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2077,9 +2077,10 @@ SourceRange FunctionDecl::getSourceRange() const { FieldDecl *FieldDecl::Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, QualType T, - TypeSourceInfo *TInfo, Expr *BW, bool Mutable) { + TypeSourceInfo *TInfo, Expr *BW, bool Mutable, + bool HasInit) { return new (C) FieldDecl(Decl::Field, DC, StartLoc, IdLoc, Id, T, TInfo, - BW, Mutable); + BW, Mutable, HasInit); } bool FieldDecl::isAnonymousStructOrUnion() const { @@ -2124,10 +2125,17 @@ unsigned FieldDecl::getFieldIndex() const { SourceRange FieldDecl::getSourceRange() const { if (isBitField()) - return SourceRange(getInnerLocStart(), BitWidth->getLocEnd()); + return SourceRange(getInnerLocStart(), getBitWidth()->getLocEnd()); return DeclaratorDecl::getSourceRange(); } +void FieldDecl::setInClassInitializer(Expr *Init) { + assert(!InitializerOrBitWidth.getPointer() && + "bit width or initializer already set"); + InitializerOrBitWidth.setPointer(Init); + InitializerOrBitWidth.setInt(0); +} + //===----------------------------------------------------------------------===// // TagDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 9e82a1a84c..08ac2a5be4 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -715,6 +715,22 @@ NotASpecialMember:; if (!hasNonLiteralTypeFieldsOrBases() && !T->isLiteralType()) data().HasNonLiteralTypeFieldsOrBases = true; + if (Field->hasInClassInitializer()) { + // C++0x [class]p5: + // A default constructor is trivial if [...] no non-static data member + // of its class has a brace-or-equal-initializer. + data().HasTrivialDefaultConstructor = false; + + // C++0x [dcl.init.aggr]p1: + // An aggregate is a [...] class with [...] no + // brace-or-equal-initializers for non-static data members. + data().Aggregate = false; + + // C++0x [class]p10: + // A POD struct is [...] a trivial class. + data().PlainOldData = false; + } + if (const RecordType *RecordTy = T->getAs()) { CXXRecordDecl* FieldRec = cast(RecordTy->getDecl()); if (FieldRec->getDefinition()) { @@ -1345,11 +1361,21 @@ const Type *CXXCtorInitializer::getBaseClass() const { SourceLocation CXXCtorInitializer::getSourceLocation() const { if (isAnyMemberInitializer() || isDelegatingInitializer()) return getMemberLocation(); + + if (isInClassMemberInitializer()) + return getAnyMember()->getLocation(); return getBaseClassLoc().getLocalSourceRange().getBegin(); } SourceRange CXXCtorInitializer::getSourceRange() const { + if (isInClassMemberInitializer()) { + FieldDecl *D = getAnyMember(); + if (Expr *I = D->getInClassInitializer()) + return I->getSourceRange(); + return SourceRange(); + } + return SourceRange(getSourceLocation(), getRParenLoc()); } diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 49f27234c0..de1b6108ba 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -450,62 +450,67 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (D->hasAttr()) Proto += " __attribute((noreturn))"; if (CXXConstructorDecl *CDecl = dyn_cast(D)) { - if (CDecl->getNumCtorInitializers() > 0) { - Proto += " : "; - Out << Proto; - Proto.clear(); - for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(), - E = CDecl->init_end(); - B != E; ++B) { - CXXCtorInitializer * BMInitializer = (*B); - if (B != CDecl->init_begin()) - Out << ", "; - if (BMInitializer->isAnyMemberInitializer()) { - FieldDecl *FD = BMInitializer->getAnyMember(); - Out << FD; - } else { - Out << QualType(BMInitializer->getBaseClass(), - 0).getAsString(Policy); - } + bool HasInitializerList = false; + for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(), + E = CDecl->init_end(); + B != E; ++B) { + CXXCtorInitializer * BMInitializer = (*B); + if (BMInitializer->isInClassMemberInitializer()) + continue; + + if (!HasInitializerList) { + Proto += " : "; + Out << Proto; + Proto.clear(); + HasInitializerList = true; + } else + Out << ", "; + + if (BMInitializer->isAnyMemberInitializer()) { + FieldDecl *FD = BMInitializer->getAnyMember(); + Out << FD; + } else { + Out << QualType(BMInitializer->getBaseClass(), + 0).getAsString(Policy); + } + + Out << "("; + if (!BMInitializer->getInit()) { + // Nothing to print + } else { + Expr *Init = BMInitializer->getInit(); + if (ExprWithCleanups *Tmp = dyn_cast(Init)) + Init = Tmp->getSubExpr(); + + Init = Init->IgnoreParens(); - Out << "("; - if (!BMInitializer->getInit()) { - // Nothing to print - } else { - Expr *Init = BMInitializer->getInit(); - if (ExprWithCleanups *Tmp = dyn_cast(Init)) - Init = Tmp->getSubExpr(); - - Init = Init->IgnoreParens(); - - Expr *SimpleInit = 0; - Expr **Args = 0; - unsigned NumArgs = 0; - if (ParenListExpr *ParenList = dyn_cast(Init)) { - Args = ParenList->getExprs(); - NumArgs = ParenList->getNumExprs(); - } else if (CXXConstructExpr *Construct - = dyn_cast(Init)) { - Args = Construct->getArgs(); - NumArgs = Construct->getNumArgs(); - } else - SimpleInit = Init; - - if (SimpleInit) - SimpleInit->printPretty(Out, Context, 0, Policy, Indentation); - else { - for (unsigned I = 0; I != NumArgs; ++I) { - if (isa(Args[I])) - break; - - if (I) - Out << ", "; - Args[I]->printPretty(Out, Context, 0, Policy, Indentation); - } + Expr *SimpleInit = 0; + Expr **Args = 0; + unsigned NumArgs = 0; + if (ParenListExpr *ParenList = dyn_cast(Init)) { + Args = ParenList->getExprs(); + NumArgs = ParenList->getNumExprs(); + } else if (CXXConstructExpr *Construct + = dyn_cast(Init)) { + Args = Construct->getArgs(); + NumArgs = Construct->getNumArgs(); + } else + SimpleInit = Init; + + if (SimpleInit) + SimpleInit->printPretty(Out, Context, 0, Policy, Indentation); + else { + for (unsigned I = 0; I != NumArgs; ++I) { + if (isa(Args[I])) + break; + + if (I) + Out << ", "; + Args[I]->printPretty(Out, Context, 0, Policy, Indentation); } } - Out << ")"; } + Out << ")"; } } else @@ -553,6 +558,12 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { Out << " : "; D->getBitWidth()->printPretty(Out, Context, 0, Policy, Indentation); } + + Expr *Init = D->getInClassInitializer(); + if (!Policy.SuppressInitializers && Init) { + Out << " = "; + Init->printPretty(Out, Context, 0, Policy, Indentation); + } } void DeclPrinter::VisitLabelDecl(LabelDecl *D) { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 012701d08a..987213907e 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -22,6 +22,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Lex/Lexer.h" +#include "clang/Sema/SemaDiagnostic.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -1653,7 +1654,8 @@ static Expr::CanThrowResult CanSubExprsThrow(ASTContext &C, const Expr *CE) { return R; } -static Expr::CanThrowResult CanCalleeThrow(ASTContext &Ctx, const Decl *D, +static Expr::CanThrowResult CanCalleeThrow(ASTContext &Ctx, const Expr *E, + const Decl *D, bool NullThrows = true) { if (!D) return NullThrows ? Expr::CT_Can : Expr::CT_Cannot; @@ -1683,6 +1685,15 @@ static Expr::CanThrowResult CanCalleeThrow(ASTContext &Ctx, const Decl *D, if (!FT) return Expr::CT_Can; + if (FT->getExceptionSpecType() == EST_Delayed) { + assert(isa(D) && + "only constructor exception specs can be unknown"); + Ctx.getDiagnostics().Report(E->getLocStart(), + diag::err_exception_spec_unknown) + << E->getSourceRange(); + return Expr::CT_Can; + } + return FT->isNothrow(Ctx) ? Expr::CT_Cannot : Expr::CT_Can; } @@ -1757,7 +1768,7 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { else if (isa(CE->getCallee()->IgnoreParens())) CT = CT_Cannot; else - CT = CanCalleeThrow(C, CE->getCalleeDecl()); + CT = CanCalleeThrow(C, this, CE->getCalleeDecl()); if (CT == CT_Can) return CT; return MergeCanThrow(CT, CanSubExprsThrow(C, this)); @@ -1765,7 +1776,7 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { case CXXConstructExprClass: case CXXTemporaryObjectExprClass: { - CanThrowResult CT = CanCalleeThrow(C, + CanThrowResult CT = CanCalleeThrow(C, this, cast(this)->getConstructor()); if (CT == CT_Can) return CT; @@ -1778,8 +1789,8 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { CT = CT_Dependent; else CT = MergeCanThrow( - CanCalleeThrow(C, cast(this)->getOperatorNew()), - CanCalleeThrow(C, cast(this)->getConstructor(), + CanCalleeThrow(C, this, cast(this)->getOperatorNew()), + CanCalleeThrow(C, this, cast(this)->getConstructor(), /*NullThrows*/false)); if (CT == CT_Can) return CT; @@ -1792,10 +1803,11 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { if (DTy.isNull() || DTy->isDependentType()) { CT = CT_Dependent; } else { - CT = CanCalleeThrow(C, cast(this)->getOperatorDelete()); + CT = CanCalleeThrow(C, this, + cast(this)->getOperatorDelete()); if (const RecordType *RT = DTy->getAs()) { const CXXRecordDecl *RD = cast(RT->getDecl()); - CT = MergeCanThrow(CT, CanCalleeThrow(C, RD->getDestructor())); + CT = MergeCanThrow(CT, CanCalleeThrow(C, this, RD->getDestructor())); } if (CT == CT_Can) return CT; @@ -1805,7 +1817,7 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { case CXXBindTemporaryExprClass: { // The bound temporary has to be destroyed again, which might throw. - CanThrowResult CT = CanCalleeThrow(C, + CanThrowResult CT = CanCalleeThrow(C, this, cast(this)->getTemporary()->getDestructor()); if (CT == CT_Can) return CT; diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index 3a0b909430..c3f3b11cac 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -48,6 +48,11 @@ static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) { const DeclContext *ExpectedDC = BD->getDeclContext(); while (isa(ExpectedDC) || isa(ExpectedDC)) ExpectedDC = ExpectedDC->getParent(); + // In-class initializers for non-static data members are lexically defined + // within the class, but are mangled as if they were specified as constructor + // member initializers. + if (isa(ExpectedDC) && DC != ExpectedDC) + DC = DC->getParent(); assert(DC == ExpectedDC && "Given decl context did not match expected!"); #endif } diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index f11d528e13..e5da703a61 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -304,7 +304,11 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) { const DeclContext *DC = block->getDeclContext(); for (; isa(DC); DC = cast(DC)->getDeclContext()) ; - QualType thisType = cast(DC)->getThisType(C); + QualType thisType; + if (const CXXRecordDecl *RD = dyn_cast(DC)) + thisType = C.getPointerType(C.getRecordType(RD)); + else + thisType = cast(DC)->getThisType(C); const llvm::Type *llvmType = CGM.getTypes().ConvertType(thisType); std::pair tinfo diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 0d2500894e..5725d80b7d 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -520,6 +520,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, FunctionArgList &Args) { assert(MemberInit->isAnyMemberInitializer() && "Must have member initializer!"); + assert(MemberInit->getInit() && "Must have initializer!"); // non-static data member initializers. FieldDecl *Field = MemberInit->getAnyMember(); diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 520d94e1bb..8c3e9a36e3 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -4098,9 +4098,9 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm) SourceLocation(), SourceLocation(), &Ctx.Idents.get("_objc_super")); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.getObjCIdType(), 0, 0, false)); + Ctx.getObjCIdType(), 0, 0, false, false)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.getObjCClassType(), 0, 0, false)); + Ctx.getObjCClassType(), 0, 0, false, false)); RD->completeDefinition(); SuperCTy = Ctx.getTagDeclType(RD); @@ -4559,9 +4559,9 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul SourceLocation(), SourceLocation(), &Ctx.Idents.get("_message_ref_t")); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.VoidPtrTy, 0, 0, false)); + Ctx.VoidPtrTy, 0, 0, false, false)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.getObjCSelType(), 0, 0, false)); + Ctx.getObjCSelType(), 0, 0, false, false)); RD->completeDefinition(); MessageRefCTy = Ctx.getTagDeclType(RD); diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index dee00275e7..f5c69981ca 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -42,6 +42,7 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D, FnD = Actions.ActOnCXXMemberDeclarator(getCurScope(), AS, D, move(TemplateParams), 0, VS, Init.release(), + /*HasInit=*/false, /*IsDefinition*/true); } @@ -166,8 +167,50 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D, return FnD; } +/// ParseCXXNonStaticMemberInitializer - We parsed and verified that the +/// specified Declarator is a well formed C++ non-static data member +/// declaration. Now lex its initializer and store its tokens for parsing +/// after the class is complete. +void Parser::ParseCXXNonStaticMemberInitializer(Decl *VarD) { + assert((Tok.is(tok::l_brace) || Tok.is(tok::equal)) && + "Current token not a '{' or '='!"); + + LateParsedMemberInitializer *MI = + new LateParsedMemberInitializer(this, VarD); + getCurrentClass().LateParsedDeclarations.push_back(MI); + CachedTokens &Toks = MI->Toks; + + tok::TokenKind kind = Tok.getKind(); + if (kind == tok::equal) { + Toks.push_back(Tok); + ConsumeAnyToken(); + } + + if (kind == tok::l_brace) { + // Begin by storing the '{' token. + Toks.push_back(Tok); + ConsumeBrace(); + + // Consume everything up to (and including) the matching right brace. + ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/true); + } else { + // Consume everything up to (but excluding) the comma or semicolon. + ConsumeAndStoreUntil(tok::comma, Toks, /*StopAtSemi=*/true, + /*ConsumeFinalToken=*/false); + } + + // Store an artificial EOF token to ensure that we don't run off the end of + // the initializer when we come to parse it. + Token Eof; + Eof.startToken(); + Eof.setKind(tok::eof); + Eof.setLocation(Tok.getLocation()); + Toks.push_back(Eof); +} + Parser::LateParsedDeclaration::~LateParsedDeclaration() {} void Parser::LateParsedDeclaration::ParseLexedMethodDeclarations() {} +void Parser::LateParsedDeclaration::ParseLexedMemberInitializers() {} void Parser::LateParsedDeclaration::ParseLexedMethodDefs() {} Parser::LateParsedClass::LateParsedClass(Parser *P, ParsingClass *C) @@ -181,6 +224,10 @@ void Parser::LateParsedClass::ParseLexedMethodDeclarations() { Self->ParseLexedMethodDeclarations(*Class); } +void Parser::LateParsedClass::ParseLexedMemberInitializers() { + Self->ParseLexedMemberInitializers(*Class); +} + void Parser::LateParsedClass::ParseLexedMethodDefs() { Self->ParseLexedMethodDefs(*Class); } @@ -193,6 +240,10 @@ void Parser::LexedMethod::ParseLexedMethodDefs() { Self->ParseLexedMethodDef(*this); } +void Parser::LateParsedMemberInitializer::ParseLexedMemberInitializers() { + Self->ParseLexedMemberInitializer(*this); +} + /// ParseLexedMethodDeclarations - We finished parsing the member /// specification of a top (non-nested) C++ class. Now go over the /// stack of method declarations with some parts for which parsing was @@ -364,8 +415,70 @@ void Parser::ParseLexedMethodDef(LexedMethod &LM) { origLoc)) while (Tok.getLocation() != origLoc && Tok.isNot(tok::eof)) ConsumeAnyToken(); + } +} + +/// ParseLexedMemberInitializers - We finished parsing the member specification +/// of a top (non-nested) C++ class. Now go over the stack of lexed data member +/// initializers that were collected during its parsing and parse them all. +void Parser::ParseLexedMemberInitializers(ParsingClass &Class) { + bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; + ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, + HasTemplateScope); + if (HasTemplateScope) + Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); + + // Set or update the scope flags to include Scope::ThisScope. + bool AlreadyHasClassScope = Class.TopLevelClass; + unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope|Scope::ThisScope; + ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope); + ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope); + if (!AlreadyHasClassScope) + Actions.ActOnStartDelayedMemberDeclarations(getCurScope(), + Class.TagOrTemplate); + + for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) { + Class.LateParsedDeclarations[i]->ParseLexedMemberInitializers(); } + + if (!AlreadyHasClassScope) + Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(), + Class.TagOrTemplate); + + Actions.ActOnFinishDelayedMemberInitializers(Class.TagOrTemplate); +} + +void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) { + if (MI.Field->isInvalidDecl()) + return; + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + MI.Toks.push_back(Tok); + PP.EnterTokenStream(MI.Toks.data(), MI.Toks.size(), true, false); + + // Consume the previously pushed token. + ConsumeAnyToken(); + + SourceLocation EqualLoc; + ExprResult Init = ParseCXXMemberInitializer(/*IsFunction=*/false, EqualLoc); + + Actions.ActOnCXXInClassMemberInitializer(MI.Field, EqualLoc, Init.release()); + + // The next token should be our artificial terminating EOF token. + if (Tok.isNot(tok::eof)) { + SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation); + if (!EndLoc.isValid()) + EndLoc = Tok.getLocation(); + // No fixit; we can't recover as if there were a semicolon here. + Diag(EndLoc, diag::err_expected_semi_decl_list); + + // Consume tokens until we hit the artificial EOF. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + } + ConsumeAnyToken(); } /// ConsumeAndStoreUntil - Consume and store the token at the passed token diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index dca7f903d2..51aa01091e 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1553,6 +1553,7 @@ bool Parser::isCXX0XFinalKeyword() const { /// member-declarator: /// declarator virt-specifier-seq[opt] pure-specifier[opt] /// declarator constant-initializer[opt] +/// [C++11] declarator brace-or-equal-initializer[opt] /// identifier[opt] ':' constant-expression /// /// virt-specifier-seq: @@ -1731,10 +1732,14 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, bool IsDefinition = false; // function-definition: - if (Tok.is(tok::l_brace)) { + // + // In C++11, a non-function declarator followed by an open brace is a + // braced-init-list for an in-class member initialization, not an + // erroneous function definition. + if (Tok.is(tok::l_brace) && !getLang().CPlusPlus0x) { IsDefinition = true; } else if (DeclaratorInfo.isFunctionDeclarator()) { - if (Tok.is(tok::colon) || Tok.is(tok::kw_try)) { + if (Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) { IsDefinition = true; } else if (Tok.is(tok::equal)) { const Token &KW = NextToken(); @@ -1790,7 +1795,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, while (1) { // member-declarator: // declarator pure-specifier[opt] - // declarator constant-initializer[opt] + // declarator brace-or-equal-initializer[opt] // identifier[opt] ':' constant-expression if (Tok.is(tok::colon)) { ConsumeToken(); @@ -1799,38 +1804,6 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, SkipUntil(tok::comma, true, true); } - ParseOptionalCXX0XVirtSpecifierSeq(VS); - - // pure-specifier: - // '= 0' - // - // constant-initializer: - // '=' constant-expression - // - // defaulted/deleted function-definition: - // '=' 'default' [TODO] - // '=' 'delete' - if (Tok.is(tok::equal)) { - ConsumeToken(); - if (Tok.is(tok::kw_delete)) { - if (DeclaratorInfo.isFunctionDeclarator()) - Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration) - << 1 /* delete */; - else - Diag(ConsumeToken(), diag::err_deleted_non_function); - } else if (Tok.is(tok::kw_default)) { - if (DeclaratorInfo.isFunctionDeclarator()) - Diag(Tok, diag::err_default_delete_in_multiple_declaration) - << 1 /* delete */; - else - Diag(ConsumeToken(), diag::err_default_special_members); - } else { - Init = ParseInitializer(); - if (Init.isInvalid()) - SkipUntil(tok::comma, true, true); - } - } - // If a simple-asm-expr is present, parse it. if (Tok.is(tok::kw_asm)) { SourceLocation Loc; @@ -1845,6 +1818,30 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // If attributes exist after the declarator, parse them. MaybeParseGNUAttributes(DeclaratorInfo); + // FIXME: When g++ adds support for this, we'll need to check whether it + // goes before or after the GNU attributes and __asm__. + ParseOptionalCXX0XVirtSpecifierSeq(VS); + + bool HasDeferredInitializer = false; + if (Tok.is(tok::equal) || Tok.is(tok::l_brace)) { + if (BitfieldSize.get()) { + Diag(Tok, diag::err_bitfield_member_init); + SkipUntil(tok::comma, true, true); + } else { + HasDeferredInitializer = !DeclaratorInfo.isFunctionDeclarator() && + DeclaratorInfo.getDeclSpec().getStorageClassSpec() + != DeclSpec::SCS_static; + + if (!HasDeferredInitializer) { + SourceLocation EqualLoc; + Init = ParseCXXMemberInitializer( + DeclaratorInfo.isFunctionDeclarator(), EqualLoc); + if (Init.isInvalid()) + SkipUntil(tok::comma, true, true); + } + } + } + // NOTE: If Sema is the Action module and declarator is an instance field, // this call will *not* return the created decl; It will return null. // See Sema::ActOnCXXMemberDeclarator for details. @@ -1860,7 +1857,9 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DeclaratorInfo, move(TemplateParams), BitfieldSize.release(), - VS, Init.release(), false); + VS, Init.release(), + HasDeferredInitializer, + /*IsDefinition*/ false); } if (ThisDecl) DeclsInGroup.push_back(ThisDecl); @@ -1873,6 +1872,24 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DeclaratorInfo.complete(ThisDecl); + if (HasDeferredInitializer) { + if (!getLang().CPlusPlus0x) + Diag(Tok, diag::warn_nonstatic_member_init_accepted_as_extension); + + if (DeclaratorInfo.isArrayOfUnknownBound()) { + // C++0x [dcl.array]p3: An array bound may also be omitted when the + // declarator is followed by an initializer. + // + // A brace-or-equal-initializer for a member-declarator is not an + // initializer in the gramamr, so this is ill-formed. + Diag(Tok, diag::err_incomplete_array_member_init); + SkipUntil(tok::comma, true, true); + // Avoid later warnings about a class member of incomplete type. + ThisDecl->setInvalidDecl(); + } else + ParseCXXNonStaticMemberInitializer(ThisDecl); + } + // If we don't have a comma, it is either the end of the list (a ';') // or an error, bail out. if (Tok.isNot(tok::comma)) @@ -1906,6 +1923,66 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DeclsInGroup.size()); } +/// ParseCXXMemberInitializer - Parse the brace-or-equal-initializer or +/// pure-specifier. Also detect and reject any attempted defaulted/deleted +/// function definition. The location of the '=', if any, will be placed in +/// EqualLoc. +/// +/// pure-specifier: +/// '= 0' +/// +/// brace-or-equal-initializer: +/// '=' initializer-expression +/// braced-init-list [TODO] +/// +/// initializer-clause: +/// assignment-expression +/// braced-init-list [TODO] +/// +/// defaulted/deleted function-definition: +/// '=' 'default' +/// '=' 'delete' +/// +/// Prior to C++0x, the assignment-expression in an initializer-clause must +/// be a constant-expression. +ExprResult Parser::ParseCXXMemberInitializer(bool IsFunction, + SourceLocation &EqualLoc) { + assert((Tok.is(tok::equal) || Tok.is(tok::l_brace)) + && "Data member initializer not starting with '=' or '{'"); + + if (Tok.is(tok::equal)) { + EqualLoc = ConsumeToken(); + if (Tok.is(tok::kw_delete)) { + // In principle, an initializer of '= delete p;' is legal, but it will + // never type-check. It's better to diagnose it as an ill-formed expression + // than as an ill-formed deleted non-function member. + // An initializer of '= delete p, foo' will never be parsed, because + // a top-level comma always ends the initializer expression. + const Token &Next = NextToken(); + if (IsFunction || Next.is(tok::semi) || Next.is(tok::comma) || + Next.is(tok::eof)) { + if (IsFunction) + Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration) + << 1 /* delete */; + else + Diag(ConsumeToken(), diag::err_deleted_non_function); + return ExprResult(); + } + } else if (Tok.is(tok::kw_default)) { + Diag(ConsumeToken(), diag::err_default_special_members); + if (IsFunction) + Diag(Tok, diag::err_default_delete_in_multiple_declaration) + << 0 /* default */; + else + Diag(ConsumeToken(), diag::err_default_special_members); + return ExprResult(); + } + + return ParseInitializer(); + } else + return ExprError(Diag(Tok, diag::err_generalized_initializer_lists)); +} + /// ParseCXXMemberSpecification - Parse the class definition. /// /// member-specification: @@ -2057,19 +2134,20 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, LBraceLoc, RBraceLoc, attrs.getList()); - // C++ 9.2p2: Within the class member-specification, the class is regarded as - // complete within function bodies, default arguments, - // exception-specifications, and constructor ctor-initializers (including - // such things in nested classes). + // C++0x [class.mem]p2: Within the class member-specification, the class is + // regarded as complete within function bodies, default arguments, exception- + // specifications, and brace-or-equal-initializers for non-static data + // members (including such things in nested classes). // - // FIXME: Only function bodies and constructor ctor-initializers are - // parsed correctly, fix the rest. + // FIXME: Only function bodies and brace-or-equal-initializers are currently + // handled. Fix the others! if (TagDecl && NonNestedClass) { // We are not inside a nested class. This class and its nested classes // are complete and we can parse the delayed portions of method // declarations and the lexed inline method definitions. SourceLocation SavedPrevTokLocation = PrevTokLocation; ParseLexedMethodDeclarations(getCurrentClass()); + ParseLexedMemberInitializers(getCurrentClass()); ParseLexedMethodDefs(getCurrentClass()); PrevTokLocation = SavedPrevTokLocation; } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index f209723dc0..f19472ccdc 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -351,7 +351,23 @@ void Parser::ExitScope() { ScopeCache[NumCachedScopes++] = OldScope; } +/// Set the flags for the current scope to ScopeFlags. If ManageFlags is false, +/// this object does nothing. +Parser::ParseScopeFlags::ParseScopeFlags(Parser *Self, unsigned ScopeFlags, + bool ManageFlags) + : CurScope(ManageFlags ? Self->getCurScope() : 0) { + if (CurScope) { + OldFlags = CurScope->getFlags(); + CurScope->setFlags(ScopeFlags); + } +} +/// Restore the flags for the current scope to what they were before this +/// object overrode them. +Parser::ParseScopeFlags::~ParseScopeFlags() { + if (CurScope) + CurScope->setFlags(OldFlags); +} //===----------------------------------------------------------------------===// diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp index d6e34ef8fa..8cdb55a0c1 100644 --- a/lib/Rewrite/RewriteObjC.cpp +++ b/lib/Rewrite/RewriteObjC.cpp @@ -2695,7 +2695,8 @@ QualType RewriteObjC::getSuperStructType() { SourceLocation(), 0, FieldTypes[i], 0, /*BitWidth=*/0, - /*Mutable=*/false)); + /*Mutable=*/false, + /*HasInit=*/false)); } SuperStructDecl->completeDefinition(); @@ -2727,7 +2728,8 @@ QualType RewriteObjC::getConstantStringStructType() { SourceLocation(), 0, FieldTypes[i], 0, /*BitWidth=*/0, - /*Mutable=*/true)); + /*Mutable=*/true, + /*HasInit=*/false)); } ConstantStringDecl->completeDefinition(); @@ -4709,7 +4711,8 @@ Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { SourceLocation(), &Context->Idents.get("FuncPtr"), Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true); + /*BitWidth=*/0, /*Mutable=*/true, + /*HasInit=*/false); MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), FD->getType(), VK_LValue, OK_Ordinary); @@ -4763,7 +4766,8 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(Expr *DeclRefExp) { SourceLocation(), &Context->Idents.get("__forwarding"), Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true); + /*BitWidth=*/0, /*Mutable=*/true, + /*HasInit=*/false); MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, FD, SourceLocation(), FD->getType(), VK_LValue, @@ -4773,7 +4777,8 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(Expr *DeclRefExp) { FD = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(), &Context->Idents.get(Name), Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true); + /*BitWidth=*/0, /*Mutable=*/true, + /*HasInit=*/false); ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), DeclRefExp->getType(), VK_LValue, OK_Ordinary); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c2fee32aea..9446c0e8c0 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2610,7 +2610,8 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, /*IdentifierInfo=*/0, Context.getTypeDeclType(Record), TInfo, - /*BitWidth=*/0, /*Mutable=*/false); + /*BitWidth=*/0, /*Mutable=*/false, + /*HasInit=*/false); Anon->setAccess(AS); if (getLangOptions().CPlusPlus) FieldCollector->Add(cast(Anon)); @@ -2700,7 +2701,8 @@ Decl *Sema::BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS, /*IdentifierInfo=*/0, Context.getTypeDeclType(Record), TInfo, - /*BitWidth=*/0, /*Mutable=*/false); + /*BitWidth=*/0, /*Mutable=*/false, + /*HasInit=*/false); Anon->setImplicit(); // Add the anonymous struct object to the current context. @@ -7512,14 +7514,13 @@ bool Sema::VerifyBitField(SourceLocation FieldLoc, IdentifierInfo *FieldName, return false; } -/// ActOnField - Each field of a struct/union/class is passed into this in order +/// ActOnField - Each field of a C struct/union is passed into this in order /// to create a FieldDecl object for it. -Decl *Sema::ActOnField(Scope *S, Decl *TagD, - SourceLocation DeclStart, - Declarator &D, ExprTy *BitfieldWidth) { +Decl *Sema::ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart, + Declarator &D, ExprTy *BitfieldWidth) { FieldDecl *Res = HandleField(S, cast_or_null(TagD), DeclStart, D, static_cast(BitfieldWidth), - AS_public); + /*HasInit=*/false, AS_public); return Res; } @@ -7527,7 +7528,7 @@ Decl *Sema::ActOnField(Scope *S, Decl *TagD, /// FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, SourceLocation DeclStart, - Declarator &D, Expr *BitWidth, + Declarator &D, Expr *BitWidth, bool HasInit, AccessSpecifier AS) { IdentifierInfo *II = D.getIdentifier(); SourceLocation Loc = DeclStart; @@ -7576,8 +7577,8 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, = (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_mutable); SourceLocation TSSL = D.getSourceRange().getBegin(); FieldDecl *NewFD - = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, TSSL, - AS, PrevDecl, &D); + = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, HasInit, + TSSL, AS, PrevDecl, &D); if (NewFD->isInvalidDecl()) Record->setInvalidDecl(); @@ -7606,7 +7607,7 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, TypeSourceInfo *TInfo, RecordDecl *Record, SourceLocation Loc, - bool Mutable, Expr *BitWidth, + bool Mutable, Expr *BitWidth, bool HasInit, SourceLocation TSSL, AccessSpecifier AS, NamedDecl *PrevDecl, Declarator *D) { @@ -7686,7 +7687,7 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, } FieldDecl *NewFD = FieldDecl::Create(Context, Record, TSSL, Loc, II, T, TInfo, - BitWidth, Mutable); + BitWidth, Mutable, HasInit); if (InvalidDecl) NewFD->setInvalidDecl(); @@ -8289,16 +8290,17 @@ void Sema::ActOnFields(Scope* S, // Now that the record is complete, do any delayed exception spec checks // we were missing. - if (!DelayedDestructorExceptionSpecChecks.empty()) { + while (!DelayedDestructorExceptionSpecChecks.empty()) { const CXXDestructorDecl *Dtor = DelayedDestructorExceptionSpecChecks.back().first; - if (Dtor->getParent() == Record) { - assert(!Dtor->getParent()->isDependentType() && - "Should not ever add destructors of templates into the list."); - CheckOverridingFunctionExceptionSpec(Dtor, - DelayedDestructorExceptionSpecChecks.back().second); - DelayedDestructorExceptionSpecChecks.pop_back(); - } + if (Dtor->getParent() != Record) + break; + + assert(!Dtor->getParent()->isDependentType() && + "Should not ever add destructors of templates into the list."); + CheckOverridingFunctionExceptionSpec(Dtor, + DelayedDestructorExceptionSpecChecks.back().second); + DelayedDestructorExceptionSpecChecks.pop_back(); } } else { diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index a4ba5fafea..ce99efbd0b 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -113,8 +113,8 @@ namespace { void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) { assert(Context && "ImplicitExceptionSpecification without an ASTContext"); - // If we have an MSAny spec already, don't bother. - if (!Method || ComputedEST == EST_MSAny) + // If we have an MSAny or unknown spec already, don't bother. + if (!Method || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed) return; const FunctionProtoType *Proto @@ -123,12 +123,15 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) { ExceptionSpecificationType EST = Proto->getExceptionSpecType(); // If this function can throw any exceptions, make a note of that. - if (EST == EST_MSAny || EST == EST_None) { + if (EST == EST_Delayed || EST == EST_MSAny || EST == EST_None) { ClearExceptions(); ComputedEST = EST; return; } + // FIXME: If the call to this decl is using any of its default arguments, we + // need to search them for potentially-throwing calls. + // If this function has a basic noexcept, it doesn't affect the outcome. if (EST == EST_BasicNoexcept) return; @@ -175,6 +178,35 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) { Exceptions.push_back(*E); } +void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) { + if (!E || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed) + return; + + // FIXME: + // + // C++0x [except.spec]p14: + // [An] implicit exception-specification specifies the type-id T if and + // only if T is allowed by the exception-specification of a function directly + // invoked by f’s implicit definition; f shall allow all exceptions if any + // function it directly invokes allows all exceptions, and f shall allow no + // exceptions if every function it directly invokes allows no exceptions. + // + // Note in particular that if an implicit exception-specification is generated + // for a function containing a throw-expression, that specification can still + // be noexcept(true). + // + // Note also that 'directly invoked' is not defined in the standard, and there + // is no indication that we should only consider potentially-evaluated calls. + // + // Ultimately we should implement the intent of the standard: the exception + // specification should be the set of exceptions which can be thrown by the + // implicit definition. For now, we assume that any non-nothrow expression can + // throw any exception. + + if (E->CanThrow(*Context)) + ComputedEST = EST_None; +} + bool Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg, SourceLocation EqualLoc) { @@ -1029,13 +1061,15 @@ bool Sema::CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New, /// ActOnCXXMemberDeclarator - This is invoked when a C++ class member /// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the -/// bitfield width if there is one and 'InitExpr' specifies the initializer if -/// any. +/// bitfield width if there is one, 'InitExpr' specifies the initializer if +/// one has been parsed, and 'HasDeferredInit' is true if an initializer is +/// present but parsing it has been deferred. Decl * Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, MultiTemplateParamsArg TemplateParameterLists, ExprTy *BW, const VirtSpecifiers &VS, - ExprTy *InitExpr, bool IsDefinition) { + ExprTy *InitExpr, bool HasDeferredInit, + bool IsDefinition) { const DeclSpec &DS = D.getDeclSpec(); DeclarationNameInfo NameInfo = GetNameForDeclarator(D); DeclarationName Name = NameInfo.getName(); @@ -1050,6 +1084,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, assert(isa(CurContext)); assert(!DS.isFriendSpecified()); + assert(!Init || !HasDeferredInit); bool isFunc = false; if (D.isFunctionDeclarator()) @@ -1121,9 +1156,11 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, // FIXME: Check for template parameters! // FIXME: Check that the name is an identifier! Member = HandleField(S, cast(CurContext), Loc, D, BitWidth, - AS); + HasDeferredInit, AS); assert(Member && "HandleField never returns null"); } else { + assert(!HasDeferredInit); + Member = HandleDeclarator(S, D, move(TemplateParameterLists), IsDefinition); if (!Member) { return 0; @@ -1194,6 +1231,14 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, if (Init) AddInitializerToDecl(Member, Init, false, DS.getTypeSpecType() == DeclSpec::TST_auto); + else if (DS.getTypeSpecType() == DeclSpec::TST_auto && + DS.getStorageClassSpec() == DeclSpec::SCS_static) { + // C++0x [dcl.spec.auto]p4: 'auto' can only be used in the type of a static + // data member if a brace-or-equal-initializer is provided. + Diag(Loc, diag::err_auto_var_requires_init) + << Name << cast(Member)->getType(); + Member->setInvalidDecl(); + } FinalizeDeclaration(Member); @@ -1202,6 +1247,47 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, return Member; } +/// ActOnCXXInClassMemberInitializer - This is invoked after parsing an +/// in-class initializer for a non-static C++ class member. Such parsing +/// is deferred until the class is complete. +void +Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc, + Expr *InitExpr) { + FieldDecl *FD = cast(D); + + if (!InitExpr) { + FD->setInvalidDecl(); + FD->removeInClassInitializer(); + return; + } + + ExprResult Init = InitExpr; + if (!FD->getType()->isDependentType() && !InitExpr->isTypeDependent()) { + // FIXME: if there is no EqualLoc, this is list-initialization. + Init = PerformCopyInitialization( + InitializedEntity::InitializeMember(FD), EqualLoc, InitExpr); + if (Init.isInvalid()) { + FD->setInvalidDecl(); + return; + } + + CheckImplicitConversions(Init.get(), EqualLoc); + } + + // C++0x [class.base.init]p7: + // The initialization of each base and member constitutes a + // full-expression. + Init = MaybeCreateExprWithCleanups(Init); + if (Init.isInvalid()) { + FD->setInvalidDecl(); + return; + } + + InitExpr = Init.release(); + + FD->setInClassInitializer(InitExpr); +} + /// \brief Find the direct and/or virtual base specifiers that /// correspond to the given base type, for use in base initialization /// within a constructor. @@ -2073,7 +2159,7 @@ struct BaseAndFieldInfo { }; } -static bool CollectFieldInitializer(BaseAndFieldInfo &Info, +static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, FieldDecl *Top, FieldDecl *Field) { // Overwhelmingly common case: we have a direct initializer for this field. @@ -2082,6 +2168,18 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info, return false; } + // C++0x [class.base.init]p8: if the entity is a non-static data member that + // has a brace-or-equal-initializer, the entity is initialized as specified + // in [dcl.init]. + if (Field->hasInClassInitializer()) { + Info.AllToInit.push_back( + new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, + SourceLocation(), + SourceLocation(), 0, + SourceLocation())); + return false; + } + if (Info.IIK == IIK_Default && Field->isAnonymousStructOrUnion()) { const RecordType *FieldClassType = Field->getType()->getAs(); assert(FieldClassType && "anonymous struct/union without record type"); @@ -2104,7 +2202,7 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info, // field in the class is also initialized, so exit immediately. return false; } else if ((*FA)->isAnonymousStructOrUnion()) { - if (CollectFieldInitializer(Info, Top, *FA)) + if (CollectFieldInitializer(SemaRef, Info, Top, *FA)) return true; } } @@ -2119,7 +2217,7 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info, // necessary. for (RecordDecl::field_iterator FA = FieldClassDecl->field_begin(), EA = FieldClassDecl->field_end(); FA != EA; FA++) { - if (CollectFieldInitializer(Info, Top, *FA)) + if (CollectFieldInitializer(SemaRef, Info, Top, *FA)) return true; } } @@ -2128,7 +2226,7 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info, // Don't try to build an implicit initializer if there were semantic // errors in any of the initializers (and therefore we might be // missing some that the user actually wrote). - if (Info.AnyErrorsInInits) + if (Info.AnyErrorsInInits || Field->isInvalidDecl()) return false; CXXCtorInitializer *Init = 0; @@ -2260,7 +2358,7 @@ Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, "Incomplete array type is not valid"); continue; } - if (CollectFieldInitializer(Info, *Field, *Field)) + if (CollectFieldInitializer(*this, Info, *Field, *Field)) HadError = true; } @@ -2939,6 +3037,9 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { for (RecordDecl::field_iterator F = Record->field_begin(), FEnd = Record->field_end(); F != FEnd; ++F) { + if (F->hasInClassInitializer()) + continue; + if (F->getType()->isReferenceType() || (F->getType().isConstQualified() && F->getType()->isScalarType())) { if (!Complained) { @@ -3066,6 +3167,11 @@ void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) { ImplicitExceptionSpecification Spec = ComputeDefaultedDefaultCtorExceptionSpec(CD->getParent()); FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + if (EPI.ExceptionSpecType == EST_Delayed) { + // Exception specification depends on some deferred part of the class. We'll + // try again when the class's definition has been fully processed. + return; + } const FunctionProtoType *CtorType = CD->getType()->getAs(), *ExceptionType = Context.getFunctionType( Context.VoidTy, 0, 0, EPI)->getAs(); @@ -3383,12 +3489,15 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { for (CXXRecordDecl::field_iterator FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { + if (FI->isInvalidDecl()) + continue; + QualType FieldType = Context.getBaseElementType(FI->getType()); CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl(); - + // -- any non-static data member with no brace-or-equal-initializer is of // reference type - if (FieldType->isReferenceType()) + if (FieldType->isReferenceType() && !FI->hasInClassInitializer()) return true; // -- X is a union and all its variant members are of const-qualified type @@ -3413,6 +3522,7 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { // array thereof) with no brace-or-equal-initializer does not have a // user-provided default constructor if (FieldType.isConstQualified() && + !FI->hasInClassInitializer() && !FieldRecord->hasUserProvidedDefaultConstructor()) return true; @@ -3445,18 +3555,21 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { continue; } - // -- any non-static data member ... has class type M (or array thereof) - // and either M has no default constructor or overload resolution as - // applied to M's default constructor results in an ambiguity or in a - // function that is deleted or inaccessible from the defaulted default - // constructor. - CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord); - if (!FieldDefault || FieldDefault->isDeleted()) - return true; - if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(), - PDiag()) != AR_accessible) - return true; - } else if (!Union && FieldType.isConstQualified()) { + // -- any non-static data member with no brace-or-equal-initializer has + // class type M (or array thereof) and either M has no default + // constructor or overload resolution as applied to M's default + // constructor results in an ambiguity or in a function that is deleted + // or inaccessible from the defaulted default constructor. + if (!FI->hasInClassInitializer()) { + CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord); + if (!FieldDefault || FieldDefault->isDeleted()) + return true; + if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(), + PDiag()) != AR_accessible) + return true; + } + } else if (!Union && FieldType.isConstQualified() && + !FI->hasInClassInitializer()) { // -- any non-variant non-static data member of const-qualified type (or // array thereof) with no brace-or-equal-initializer does not have a // user-provided default constructor @@ -5905,7 +6018,12 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { for (RecordDecl::field_iterator F = ClassDecl->field_begin(), FEnd = ClassDecl->field_end(); F != FEnd; ++F) { - if (const RecordType *RecordTy + if (F->hasInClassInitializer()) { + if (Expr *E = F->getInClassInitializer()) + ExceptSpec.CalledExpr(E); + else if (!F->isInvalidDecl()) + ExceptSpec.SetDelayed(); + } else if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs()) { CXXRecordDecl *FieldRecDecl = cast(RecordTy->getDecl()); CXXConstructorDecl *Constructor = LookupDefaultConstructor(FieldRecDecl); @@ -6001,6 +6119,59 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, } } +/// Get any existing defaulted default constructor for the given class. Do not +/// implicitly define one if it does not exist. +static CXXConstructorDecl *getDefaultedDefaultConstructorUnsafe(Sema &Self, + CXXRecordDecl *D) { + ASTContext &Context = Self.Context; + QualType ClassType = Context.getTypeDeclType(D); + DeclarationName ConstructorName + = Context.DeclarationNames.getCXXConstructorName( + Context.getCanonicalType(ClassType.getUnqualifiedType())); + + DeclContext::lookup_const_iterator Con, ConEnd; + for (llvm::tie(Con, ConEnd) = D->lookup(ConstructorName); + Con != ConEnd; ++Con) { + // A function template cannot be defaulted. + if (isa(*Con)) + continue; + + CXXConstructorDecl *Constructor = cast(*Con); + if (Constructor->isDefaultConstructor()) + return Constructor->isDefaulted() ? Constructor : 0; + } + return 0; +} + +void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { + if (!D) return; + AdjustDeclIfTemplate(D); + + CXXRecordDecl *ClassDecl = cast(D); + CXXConstructorDecl *CtorDecl + = getDefaultedDefaultConstructorUnsafe(*this, ClassDecl); + + if (!CtorDecl) return; + + // Compute the exception specification for the default constructor. + const FunctionProtoType *CtorTy = + CtorDecl->getType()->castAs(); + if (CtorTy->getExceptionSpecType() == EST_Delayed) { + ImplicitExceptionSpecification Spec = + ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl); + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + assert(EPI.ExceptionSpecType != EST_Delayed); + + CtorDecl->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); + } + + // If the default constructor is explicitly defaulted, checking the exception + // specification is deferred until now. + if (!CtorDecl->isInvalidDecl() && CtorDecl->isExplicitlyDefaulted() && + !ClassDecl->isDependentType()) + CheckExplicitlyDefaultedDefaultConstructor(CtorDecl); +} + void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { // We start with an initial pass over the base classes to collect those that // inherit constructors from. If there are none, we can forgo all further @@ -6094,7 +6265,9 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { // Build up a function type for this particular constructor. // FIXME: The working paper does not consider that the exception spec // for the inheriting constructor might be larger than that of the - // source. This code doesn't yet, either. + // source. This code doesn't yet, either. When it does, this code will + // need to be delayed until after exception specifications and in-class + // member initializers are attached. const Type *NewCtorType; if (params == maxParams) NewCtorType = BaseCtorType; diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 0f6108c8bc..7bcec315dd 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -298,8 +298,6 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, // - both are non-throwing, regardless of their form, // - both have the form noexcept(constant-expression) and the constant- // expressions are equivalent, - // - one exception-specification is a noexcept-specification allowing all - // exceptions and the other is of the form throw(type-id-list), or // - both are dynamic-exception-specifications that have the same set of // adjusted types. // @@ -307,8 +305,6 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, // of the form throw(), noexcept, or noexcept(constant-expression) where the // constant-expression yields true. // - // CWG 1073 Proposed resolution: Strike the third bullet above. - // // C++0x [except.spec]p4: If any declaration of a function has an exception- // specifier that is not a noexcept-specification allowing all exceptions, // all declarations [...] of that function shall have a compatible @@ -320,6 +316,9 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, ExceptionSpecificationType OldEST = Old->getExceptionSpecType(); ExceptionSpecificationType NewEST = New->getExceptionSpecType(); + assert(OldEST != EST_Delayed && NewEST != EST_Delayed && + "Shouldn't see unknown exception specifications here"); + // Shortcut the case where both have no spec. if (OldEST == EST_None && NewEST == EST_None) return false; @@ -506,6 +505,9 @@ bool Sema::CheckExceptionSpecSubset( ExceptionSpecificationType SubEST = Subset->getExceptionSpecType(); + assert(SuperEST != EST_Delayed && SubEST != EST_Delayed && + "Shouldn't see unknown exception specifications here"); + // It does not. If the subset contains everything, we've failed. if (SubEST == EST_None || SubEST == EST_MSAny) { Diag(SubLoc, DiagID); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index dafd56bf34..0549e94995 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1333,8 +1333,8 @@ Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS, // We've found a member of an anonymous struct/union that is // inside a non-anonymous struct/union, so in a well-formed // program our base object expression is "this". - CXXMethodDecl *method = tryCaptureCXXThis(); - if (!method) { + QualType ThisTy = getAndCaptureCurrentThisType(); + if (ThisTy.isNull()) { Diag(loc, diag::err_invalid_member_use_in_static_method) << indirectField->getDeclName(); return ExprError(); @@ -1342,10 +1342,9 @@ Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS, // Our base object expression is "this". baseObjectExpr = - new (Context) CXXThisExpr(loc, method->getThisType(Context), - /*isImplicit=*/ true); + new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/ true); baseObjectIsPointer = true; - baseQuals = Qualifiers::fromCVRMask(method->getTypeQualifiers()); + baseQuals = ThisTy->castAs()->getPointeeType().getQualifiers(); } // Build the implicit member references to the field of the @@ -1492,14 +1491,23 @@ enum IMAKind { /// conservatively answer "yes", in which case some errors will simply /// not be caught until template-instantiation. static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, + Scope *CurScope, const LookupResult &R) { assert(!R.empty() && (*R.begin())->isCXXClassMember()); DeclContext *DC = SemaRef.getFunctionLevelDeclContext(); + bool isStaticContext = (!isa(DC) || cast(DC)->isStatic()); + // C++0x [expr.prim]p4: + // Otherwise, if a member-declarator declares a non-static data member + // of a class X, the expression this is a prvalue of type "pointer to X" + // within the optional brace-or-equal-initializer. + if (CurScope->getFlags() & Scope::ThisScope) + isStaticContext = false; + if (R.isUnresolvableResult()) return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved; @@ -1547,8 +1555,11 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, return IMA_Error_StaticContext; } - CXXRecordDecl * - contextClass = cast(DC)->getParent()->getCanonicalDecl(); + CXXRecordDecl *contextClass; + if (CXXMethodDecl *MD = dyn_cast(DC)) + contextClass = MD->getParent()->getCanonicalDecl(); + else + contextClass = cast(DC); // [class.mfct.non-static]p3: // ...is used in the body of a non-static member function of class X, @@ -2026,7 +2037,7 @@ ExprResult Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS, LookupResult &R, const TemplateArgumentListInfo *TemplateArgs) { - switch (ClassifyImplicitMemberAccess(*this, R)) { + switch (ClassifyImplicitMemberAccess(*this, CurScope, R)) { case IMA_Instance: return BuildImplicitMemberExpr(SS, R, TemplateArgs, true); @@ -2469,19 +2480,18 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS, // If this is known to be an instance access, go ahead and build an // implicit 'this' expression now. // 'this' expression now. - CXXMethodDecl *method = tryCaptureCXXThis(); - assert(method && "didn't correctly pre-flight capture of 'this'"); + QualType ThisTy = getAndCaptureCurrentThisType(); + assert(!ThisTy.isNull() && "didn't correctly pre-flight capture of 'this'"); - QualType thisType = method->getThisType(Context); Expr *baseExpr = 0; // null signifies implicit access if (IsKnownInstance) { SourceLocation Loc = R.getNameLoc(); if (SS.getRange().isValid()) Loc = SS.getRange().getBegin(); - baseExpr = new (Context) CXXThisExpr(loc, thisType, /*isImplicit=*/true); + baseExpr = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/true); } - return BuildMemberReferenceExpr(baseExpr, thisType, + return BuildMemberReferenceExpr(baseExpr, ThisTy, /*OpLoc*/ SourceLocation(), /*IsArrow*/ true, SS, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 50462abd3a..2f5a890e51 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -17,6 +17,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Scope.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" @@ -575,42 +576,54 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E) { return Owned(E); } -CXXMethodDecl *Sema::tryCaptureCXXThis() { +QualType Sema::getAndCaptureCurrentThisType() { // Ignore block scopes: we can capture through them. // Ignore nested enum scopes: we'll diagnose non-constant expressions // where they're invalid, and other uses are legitimate. // Don't ignore nested class scopes: you can't use 'this' in a local class. DeclContext *DC = CurContext; + unsigned NumBlocks = 0; while (true) { - if (isa(DC)) DC = cast(DC)->getDeclContext(); - else if (isa(DC)) DC = cast(DC)->getDeclContext(); + if (isa(DC)) { + DC = cast(DC)->getDeclContext(); + ++NumBlocks; + } else if (isa(DC)) + DC = cast(DC)->getDeclContext(); else break; } - // If we're not in an instance method, error out. - CXXMethodDecl *method = dyn_cast(DC); - if (!method || !method->isInstance()) - return 0; + QualType ThisTy; + if (CXXMethodDecl *method = dyn_cast(DC)) { + if (method && method->isInstance()) + ThisTy = method->getThisType(Context); + } else if (CXXRecordDecl *RD = dyn_cast(DC)) { + // C++0x [expr.prim]p4: + // Otherwise, if a member-declarator declares a non-static data member + // of a class X, the expression this is a prvalue of type "pointer to X" + // within the optional brace-or-equal-initializer. + Scope *S = getScopeForContext(DC); + if (!S || S->getFlags() & Scope::ThisScope) + ThisTy = Context.getPointerType(Context.getRecordType(RD)); + } - // Mark that we're closing on 'this' in all the block scopes, if applicable. - for (unsigned idx = FunctionScopes.size() - 1; - isa(FunctionScopes[idx]); - --idx) - cast(FunctionScopes[idx])->CapturesCXXThis = true; + // Mark that we're closing on 'this' in all the block scopes we ignored. + if (!ThisTy.isNull()) + for (unsigned idx = FunctionScopes.size() - 1; + NumBlocks; --idx, --NumBlocks) + cast(FunctionScopes[idx])->CapturesCXXThis = true; - return method; + return ThisTy; } -ExprResult Sema::ActOnCXXThis(SourceLocation loc) { +ExprResult Sema::ActOnCXXThis(SourceLocation Loc) { /// C++ 9.3.2: In the body of a non-static member function, the keyword this /// is a non-lvalue expression whose value is the address of the object for /// which the function is called. - CXXMethodDecl *method = tryCaptureCXXThis(); - if (!method) return Diag(loc, diag::err_invalid_this_use); + QualType ThisTy = getAndCaptureCurrentThisType(); + if (ThisTy.isNull()) return Diag(Loc, diag::err_invalid_this_use); - return Owned(new (Context) CXXThisExpr(loc, method->getThisType(Context), - /*isImplicit=*/false)); + return Owned(new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/false)); } ExprResult @@ -2663,7 +2676,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, return true; bool FoundAssign = false; - bool AllNoThrow = true; DeclarationName Name = C.DeclarationNames.getCXXOperatorName(OO_Equal); LookupResult Res(Self, DeclarationNameInfo(Name, KeyLoc), Sema::LookupOrdinaryName); @@ -2675,15 +2687,15 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, FoundAssign = true; const FunctionProtoType *CPT = Operator->getType()->getAs(); - if (!CPT->isNothrow(Self.Context)) { - AllNoThrow = false; - break; - } + if (CPT->getExceptionSpecType() == EST_Delayed) + return false; + if (!CPT->isNothrow(Self.Context)) + return false; } } } - return FoundAssign && AllNoThrow; + return FoundAssign; } return false; case UTT_HasNothrowCopy: @@ -2700,7 +2712,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, return true; bool FoundConstructor = false; - bool AllNoThrow = true; unsigned FoundTQs; DeclContext::lookup_const_iterator Con, ConEnd; for (llvm::tie(Con, ConEnd) = Self.LookupConstructors(RD); @@ -2715,16 +2726,16 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, FoundConstructor = true; const FunctionProtoType *CPT = Constructor->getType()->getAs(); + if (CPT->getExceptionSpecType() == EST_Delayed) + return false; // FIXME: check whether evaluating default arguments can throw. // For now, we'll be conservative and assume that they can throw. - if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1) { - AllNoThrow = false; - break; - } + if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1) + return false; } } - return FoundConstructor && AllNoThrow; + return FoundConstructor; } return false; case UTT_HasNothrowConstructor: @@ -2750,6 +2761,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, if (Constructor->isDefaultConstructor()) { const FunctionProtoType *CPT = Constructor->getType()->getAs(); + if (CPT->getExceptionSpecType() == EST_Delayed) + return false; // TODO: check whether evaluating default arguments can throw. // For now, we'll be conservative and assume that they can throw. return CPT->isNothrow(Self.Context) && CPT->getNumArgs() == 0; diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 444fb9a2f2..3c1964175b 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -13,6 +13,7 @@ #include "clang/Sema/SemaInternal.h" #include "TreeTransform.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" @@ -1744,6 +1745,8 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs); llvm::SmallVector Fields; + llvm::SmallVector, 4> + FieldsWithMemberInitializers; for (RecordDecl::decl_iterator Member = Pattern->decls_begin(), MemberEnd = Pattern->decls_end(); Member != MemberEnd; ++Member) { @@ -1766,9 +1769,13 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, Decl *NewMember = Instantiator.Visit(*Member); if (NewMember) { - if (FieldDecl *Field = dyn_cast(NewMember)) + if (FieldDecl *Field = dyn_cast(NewMember)) { Fields.push_back(Field); - else if (NewMember->isInvalidDecl()) + FieldDecl *OldField = cast(*Member); + if (OldField->getInClassInitializer()) + FieldsWithMemberInitializers.push_back(std::make_pair(OldField, + Field)); + } else if (NewMember->isInvalidDecl()) Invalid = true; } else { // FIXME: Eventually, a NULL return will mean that one of the @@ -1782,6 +1789,43 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, Fields.data(), Fields.size(), SourceLocation(), SourceLocation(), 0); CheckCompletedCXXClass(Instantiation); + + // Attach any in-class member initializers now the class is complete. + for (unsigned I = 0, N = FieldsWithMemberInitializers.size(); I != N; ++I) { + FieldDecl *OldField = FieldsWithMemberInitializers[I].first; + FieldDecl *NewField = FieldsWithMemberInitializers[I].second; + Expr *OldInit = OldField->getInClassInitializer(); + ExprResult NewInit = SubstExpr(OldInit, TemplateArgs); + + // If the initialization is no longer dependent, check it now. + if ((OldField->getType()->isDependentType() || OldInit->isTypeDependent()) + && !NewField->getType()->isDependentType() + && !NewInit.get()->isTypeDependent()) { + // FIXME: handle list-initialization + SourceLocation EqualLoc = NewField->getLocation(); + NewInit = PerformCopyInitialization( + InitializedEntity::InitializeMember(NewField), EqualLoc, + NewInit.release()); + + if (!NewInit.isInvalid()) { + CheckImplicitConversions(NewInit.get(), EqualLoc); + + // C++0x [class.base.init]p7: + // The initialization of each base and member constitutes a + // full-expression. + NewInit = MaybeCreateExprWithCleanups(NewInit); + } + } + + if (NewInit.isInvalid()) + NewField->setInvalidDecl(); + else + NewField->setInClassInitializer(NewInit.release()); + } + + if (!FieldsWithMemberInitializers.empty()) + ActOnFinishDelayedMemberInitializers(Instantiation); + if (Instantiation->isInvalidDecl()) Invalid = true; else { diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 22db4c8f61..e78aa2991e 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -477,6 +477,7 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) { D->getLocation(), D->isMutable(), BitWidth, + D->hasInClassInitializer(), D->getTypeSpecStartLoc(), D->getAccess(), 0); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index e7f9885698..5fd8afa6ac 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1593,6 +1593,8 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, Error = 0; // Function prototype break; case Declarator::MemberContext: + if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static) + break; switch (cast(CurContext)->getTagKind()) { case TTK_Enum: assert(0 && "unhandled tag kind"); break; case TTK_Struct: Error = 1; /* Struct member */ break; diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 245499e8a1..ff2e46a902 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -6697,8 +6697,12 @@ template ExprResult TreeTransform::TransformCXXThisExpr(CXXThisExpr *E) { DeclContext *DC = getSema().getFunctionLevelDeclContext(); - CXXMethodDecl *MD = dyn_cast(DC); - QualType T = MD->getThisType(getSema().Context); + QualType T; + if (CXXMethodDecl *MD = dyn_cast(DC)) + T = MD->getThisType(getSema().Context); + else + T = getSema().Context.getPointerType( + getSema().Context.getRecordType(cast(DC))); if (!getDerived().AlwaysRebuild() && T == E->getType()) return SemaRef.Owned(E); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 6e9d4d4820..fab2069bc5 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -677,8 +677,11 @@ void ASTDeclReader::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) { VisitDeclaratorDecl(FD); FD->setMutable(Record[Idx++]); - if (Record[Idx++]) + int BitWidthOrInitializer = Record[Idx++]; + if (BitWidthOrInitializer == 1) FD->setBitWidth(Reader.ReadExpr(F)); + else if (BitWidthOrInitializer == 2) + FD->setInClassInitializer(Reader.ReadExpr(F)); if (!FD->getDeclName()) { FieldDecl *Tmpl = cast_or_null(Reader.GetDecl(Record[Idx++])); if (Tmpl) @@ -1649,7 +1652,7 @@ Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) { break; case DECL_FIELD: D = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(), 0, - QualType(), 0, 0, false); + QualType(), 0, 0, false, false); break; case DECL_INDIRECTFIELD: D = IndirectFieldDecl::Create(*Context, 0, SourceLocation(), 0, QualType(), diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index c6b159c50b..7c24088c65 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -599,9 +599,11 @@ void ASTDeclWriter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) { VisitDeclaratorDecl(D); Record.push_back(D->isMutable()); - Record.push_back(D->getBitWidth()? 1 : 0); + Record.push_back(D->getBitWidth()? 1 : D->hasInClassInitializer() ? 2 : 0); if (D->getBitWidth()) Writer.AddStmt(D->getBitWidth()); + else if (D->hasInClassInitializer()) + Writer.AddStmt(D->getInClassInitializer()); if (!D->getDeclName()) Writer.AddDeclRef(Context.getInstantiatedFromUnnamedFieldDecl(D), Record); @@ -612,6 +614,7 @@ void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) { !D->isReferenced() && D->getPCHLevel() == 0 && !D->getBitWidth() && + !D->hasInClassInitializer() && !D->hasExtInfo() && !ObjCIvarDecl::classofKind(D->getKind()) && !ObjCAtDefsFieldDecl::classofKind(D->getKind()) && diff --git a/test/CXX/class/class.mem/p5-0x.cpp b/test/CXX/class/class.mem/p5-0x.cpp new file mode 100644 index 0000000000..78560e2d5d --- /dev/null +++ b/test/CXX/class/class.mem/p5-0x.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +int f(); + +struct S +{ + int a = f(); // ok + int b = g(); // expected-error {{use of undeclared identifier 'g'}} +}; diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp index 34a1784007..8a68e4bcd7 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp @@ -22,14 +22,21 @@ void f() { new const auto (0); new (auto) (0.0); -#if 0 - // When clang supports for-range: - for (auto i : {1,2,3}) { + int arr[] = {1, 2, 3}; + for (auto i : arr) { } - - // When clang supports inline initialization of members. - class X { - static const auto &n = 'x'; - }; -#endif } + +class X { + static const auto n = 'x'; + + auto m = 0; // expected-error {{'auto' not allowed in non-static class member}} +}; + +struct S { + static const auto a; // expected-error {{declaration of variable 'a' with type 'auto const' requires an initializer}} + static const auto b = 0; + static const int c; +}; +const int S::b; +const auto S::c = 0; diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp index 09245cfd20..fabfb5329d 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp @@ -3,13 +3,13 @@ struct S { virtual ~S(); - auto a; // expected-error{{'auto' not allowed in struct member}} - auto *b; // expected-error{{'auto' not allowed in struct member}} - const auto c; // expected-error{{'auto' not allowed in struct member}} + auto a; // expected-error{{'auto' not allowed in non-static struct member}} + auto *b; // expected-error{{'auto' not allowed in non-static struct member}} + const auto c; // expected-error{{'auto' not allowed in non-static struct member}} void f() throw (auto); // expected-error{{'auto' not allowed here}} - friend auto; // expected-error{{'auto' not allowed in struct member}} + friend auto; // expected-error{{'auto' not allowed in non-static struct member}} operator auto(); // expected-error{{'auto' not allowed here}} }; 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 index 8b278bf2a2..b93e8e35b1 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/p3-0x.cpp @@ -7,8 +7,8 @@ template using Y = struct { // expected-error {{can not be defined i class K { virtual ~K(); - // FIXME: the diagnostic here isn't very good - operator struct S {} (); // expected-error 2{{}} + // FIXME: the diagnostic here is really bad + operator struct S {} (); // expected-error 2{{}} expected-note {{}} }; void f() { diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp new file mode 100644 index 0000000000..9b92340fa4 --- /dev/null +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +// An aggregate is an array or a class... +struct Aggr { +private: + static const int n; + void f(); +protected: + struct Inner { int m; }; +public: + bool &br; +}; +bool b; +Aggr ag = { b }; + +// with no user-provided constructors, ... +struct NonAggr1a { + NonAggr1a(int, int); + int k; +}; +// In C++03, this is {{non-aggregate type 'NonAggr1a'}}. +// In C++0x, 'user-provided' is only defined for special member functions, so +// this type is considered to be an aggregate. This is probably a langauge +// defect. +NonAggr1a na1a = { 42 }; + +struct NonAggr1b { + NonAggr1b(const NonAggr1b &); + int k; +}; +NonAggr1b na1b = { 42 }; // expected-error {{non-aggregate type 'NonAggr1b'}} + +// no brace-or-equal-initializers for non-static data members, ... +struct NonAggr2 { + int m = { 123 }; +}; +NonAggr2 na2 = { 42 }; // expected-error {{non-aggregate type 'NonAggr2'}} + +// no private... +struct NonAggr3 { +private: + int n; +}; +NonAggr3 na3 = { 42 }; // expected-error {{non-aggregate type 'NonAggr3'}} + +// or protected non-static data members, ... +struct NonAggr4 { +protected: + int n; +}; +NonAggr4 na4 = { 42 }; // expected-error {{non-aggregate type 'NonAggr4'}} + +// no base classes, ... +struct NonAggr5 : Aggr { +}; +NonAggr5 na5 = { b }; // expected-error {{non-aggregate type 'NonAggr5'}} + +// and no virtual functions. +struct NonAggr6 { + virtual void f(); + int n; +}; +NonAggr6 na6 = { 42 }; // expected-error {{non-aggregate type 'NonAggr6'}} diff --git a/test/CXX/dcl.decl/dcl.init/p14-0x.cpp b/test/CXX/dcl.decl/dcl.init/p14-0x.cpp new file mode 100644 index 0000000000..e5b5889954 --- /dev/null +++ b/test/CXX/dcl.decl/dcl.init/p14-0x.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +struct NoDefault { + NoDefault() = delete; // expected-note {{here}} + NoDefault(int); +}; +struct Explicit { // expected-note {{candidate}} expected-note {{here}} + explicit Explicit(int); +}; +struct NoCopy { + NoCopy(); + NoCopy(const NoCopy &) = delete; // expected-note {{here}} +}; +struct NoMove { + NoMove(); + NoMove(NoMove &&) = delete; // expected-note {{here}} +}; +class Private { + Private(int); // expected-note {{here}} +public: + Private(); +}; +class Friend { + friend class S; + Friend(int); +}; + + +class S { + NoDefault nd1; + NoDefault nd2 = 42; + Explicit e1; // expected-note {{here}} + Explicit e2 = 42; // expected-error {{no viable conversion}} + NoCopy nc = NoCopy(); // expected-error {{call to deleted}} + NoMove nm = NoMove(); // expected-error {{call to deleted}} + Private p = 42; // expected-error {{private constructor}} + Friend f = 42; + + S() {} // expected-error {{call to deleted constructor of 'NoDefault'}} \ + expected-error {{must explicitly initialize the member 'e1' which does not have a default constructor}} + S(int) : nd1(42), e1(42) {} +}; + +// FIXME: test the other forms which use copy-initialization diff --git a/test/CXX/except/except.spec/p14.cpp b/test/CXX/except/except.spec/p14.cpp index f5e83eaac6..f42fbe907f 100644 --- a/test/CXX/except/except.spec/p14.cpp +++ b/test/CXX/except/except.spec/p14.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -verify %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -verify -std=c++0x %s struct A { }; struct B { }; struct C { }; @@ -27,3 +27,15 @@ void test_CA() { CA2 &(CA2::*captr3)(const CA2&) throw(A) = &CA2::operator=; // expected-error{{target exception specification is not superset of source}} CA2 &(CA2::*captr4)(const CA2&) throw(B) = &CA2::operator=; // expected-error{{target exception specification is not superset of source}} } + +// In-class member initializers. +struct IC0 { + int inClassInit = 0; +}; +struct IC1 { + int inClassInit = (throw B(), 0); +}; +// FIXME: the exception specification on the default constructor is wrong: +// we cannot currently compute the set of thrown types. +static_assert(noexcept(IC0()), "IC0() does not throw"); +static_assert(!noexcept(IC1()), "IC1() throws"); diff --git a/test/CXX/expr/expr.prim/p12-0x.cpp b/test/CXX/expr/expr.prim/p12-0x.cpp new file mode 100644 index 0000000000..0ff29a1882 --- /dev/null +++ b/test/CXX/expr/expr.prim/p12-0x.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +struct S { + int *j = &nonexistent; // expected-error {{use of undeclared identifier 'nonexistent'}} + int *m = &n; // ok + + int n = f(); // ok + int f(); +}; + +int i = sizeof(S::m); // ok +int j = sizeof(S::m + 42); // ok diff --git a/test/CXX/expr/expr.prim/p4-0x.cpp b/test/CXX/expr/expr.prim/p4-0x.cpp new file mode 100644 index 0000000000..13735fab4a --- /dev/null +++ b/test/CXX/expr/expr.prim/p4-0x.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +struct S { + S *p = this; // ok + decltype(this) q; // expected-error {{invalid use of 'this' outside of a nonstatic member function}} \ + expected-error {{C++ requires a type specifier for all declarations}} + + int arr[sizeof(this)]; // expected-error {{invalid use of 'this' outside of a nonstatic member function}} + int sz = sizeof(this); // ok +}; diff --git a/test/CXX/special/class.ctor/p5-0x.cpp b/test/CXX/special/class.ctor/p5-0x.cpp new file mode 100644 index 0000000000..2123d16623 --- /dev/null +++ b/test/CXX/special/class.ctor/p5-0x.cpp @@ -0,0 +1,173 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++0x + +struct DefaultedDefCtor1 {}; +struct DefaultedDefCtor2 { DefaultedDefCtor2() = default; }; +struct DeletedDefCtor { DeletedDefCtor() = delete; DeletedDefCtor(int); }; +class PrivateDefCtor { PrivateDefCtor() = default; public: PrivateDefCtor(int); }; +struct DeletedDtor { ~DeletedDtor() = delete; }; +class PrivateDtor { ~PrivateDtor() = default; }; +class Friend { + Friend() = default; ~Friend() = default; + friend struct NotDeleted6c; + friend struct NotDeleted7i; + friend struct NotDeleted7j; + friend struct NotDeleted7k; +}; +struct UserProvidedDefCtor { UserProvidedDefCtor() {} }; +int n; + + +// A defaulted default constructor for a class X is defined as deleted if: + +// - X is a union-like class that has a variant member with a non-trivial +// default constructor, +union Deleted1a { UserProvidedDefCtor u; }; // expected-note {{deleted here}} +Deleted1a d1a; // expected-error {{deleted constructor}} +// FIXME: treating this as having a deleted default constructor is probably a +// bug in the standard. +union Deleted1b { UserProvidedDefCtor u = UserProvidedDefCtor(); }; // expected-note {{deleted here}} +Deleted1b d1b; // expected-error {{deleted constructor}} +union NotDeleted1a { DefaultedDefCtor1 nu; }; +NotDeleted1a nd1a; +// FIXME: clang implements the pre-FDIS rule, under which DefaultedDefCtor2's +// default constructor is non-trivial. +union NotDeleted1b { DefaultedDefCtor2 nu; }; // unexpected-note {{deleted here}} +NotDeleted1b nd1b; // unexpected-error {{deleted constructor}} + +// - any non-static data member with no brace-or-equal-initializer is of +// reference type, +class Deleted2a { Deleted2a() = default; int &a; }; // expected-note {{deleted here}} +Deleted2a d2a; // expected-error {{deleted constructor}} +class NotDeleted2a { int &a = n; }; +NotDeleted2a nd2a; +class NotDeleted2b { int &a = error; }; // expected-error {{undeclared identifier}} +NotDeleted2b nd2b; + +// - any non-variant non-static data member of const qualified type (or array +// thereof) with no brace-or-equal-initializer does not have a user-provided +// default constructor, +class Deleted3a { const int a; }; // expected-note {{here}} \ + expected-warning {{does not declare any constructor}} \ + expected-note {{will never be initialized}} +Deleted3a d3a; // expected-error {{deleted constructor}} +class Deleted3b { const DefaultedDefCtor1 a[42]; }; // expected-note {{here}} +Deleted3b d3b; // expected-error {{deleted constructor}} +// FIXME: clang implements the pre-FDIS rule, under which DefaultedDefCtor2's +// default constructor is user-provided. +class Deleted3c { const DefaultedDefCtor2 a; }; // desired-note {{here}} +Deleted3c d3c; // desired-error {{deleted constructor}} +class NotDeleted3a { const int a = 0; }; +NotDeleted3a nd3a; +class NotDeleted3b { const DefaultedDefCtor1 a[42] = {}; }; +NotDeleted3b nd3b; +class NotDeleted3c { const DefaultedDefCtor2 a = DefaultedDefCtor2(); }; +NotDeleted3c nd3c; +union NotDeleted3d { const int a; int b; }; +NotDeleted3d nd3d; +// FIXME: this class should not have a deleted default constructor. +union NotDeleted3e { const DefaultedDefCtor1 a[42]; int b; }; // unexpected-note {{here}} +NotDeleted3e nd3e; // unexpected-error {{deleted constructor}} +// FIXME: clang implements the pre-FDIS rule, under which DefaultedDefCtor2 is +// non-trivial. +union NotDeleted3f { const DefaultedDefCtor2 a; int b; }; // unexpected-note {{here}} +NotDeleted3f nd3f; // unexpected-error {{deleted constructor}} + +// - X is a union and all of its variant members are of const-qualified type (or +// array thereof), +union Deleted4a { const int a; const int b; const UserProvidedDefCtor c; }; // expected-note {{here}} +Deleted4a d4a; // expected-error {{deleted constructor}} +union Deleted4b { const int a; int b; }; +Deleted4b d4b; + +// - X is a non-union class and all members of any anonymous union member are of +// const-qualified type (or array thereof), +struct Deleted5a { union { const int a; }; union { int b; }; }; // expected-note {{here}} +Deleted5a d5a; // expected-error {{deleted constructor}} +struct Deleted5b { union { const int a; int b; }; union { const int c; int d; }; }; +Deleted5b d5b; + +// - any direct or virtual base class, or non-static data member with no +// brace-or-equal-initializer, has class type M (or array thereof) and either +// M has no default constructor or overload resolution as applied to M's default +// constructor results in an ambiguity or in a function that is deleted or +// inaccessible from the defaulted default constructor, or +struct Deleted6a : Deleted2a {}; // expected-note {{here}} +Deleted6a d6a; // expected-error {{deleted constructor}} +struct Deleted6b : virtual Deleted2a {}; // expected-note {{here}} +Deleted6b d6b; // expected-error {{deleted constructor}} +struct Deleted6c { Deleted2a a; }; // expected-note {{here}} +Deleted6c d6c; // expected-error {{deleted constructor}} +struct Deleted6d { DeletedDefCtor a; }; // expected-note {{here}} +Deleted6d d6d; // expected-error {{deleted constructor}} +struct NotDeleted6a { DeletedDefCtor a = 0; }; +NotDeleted6a nd6a; +struct Deleted6e { PrivateDefCtor a; }; // expected-note {{here}} +Deleted6e d6e; // expected-error {{deleted constructor}} +struct NotDeleted6b { PrivateDefCtor a = 0; }; +NotDeleted6b nd6b; +struct NotDeleted6c { Friend a; }; +NotDeleted6c nd6c; + +// - any direct or virtual base class or non-static data member has a type with +// a destructor that is deleted or inaccessible from the defaulted default +// constructor. +struct Deleted7a : DeletedDtor {}; // expected-note {{here}} +Deleted7a d7a; // expected-error {{deleted constructor}} +struct Deleted7b : virtual DeletedDtor {}; // expected-note {{here}} +Deleted7b d7b; // expected-error {{deleted constructor}} +struct Deleted7c { DeletedDtor a; }; // expected-note {{here}} +Deleted7c d7c; // expected-error {{deleted constructor}} +struct Deleted7d { DeletedDtor a = {}; }; // expected-note {{here}} +Deleted7d d7d; // expected-error {{deleted constructor}} +struct Deleted7e : PrivateDtor {}; // expected-note {{here}} +Deleted7e d7e; // expected-error {{deleted constructor}} +struct Deleted7f : virtual PrivateDtor {}; // expected-note {{here}} +Deleted7f d7f; // expected-error {{deleted constructor}} +struct Deleted7g { PrivateDtor a; }; // expected-note {{here}} +Deleted7g d7g; // expected-error {{deleted constructor}} +struct Deleted7h { PrivateDtor a = {}; }; // expected-note {{here}} +Deleted7h d7h; // expected-error {{deleted constructor}} +struct NotDeleted7i : Friend {}; +NotDeleted7i d7i; +struct NotDeleted7j : virtual Friend {}; +NotDeleted7j d7j; +struct NotDeleted7k { Friend a; }; +NotDeleted7k d7k; + + +class Trivial { static const int n = 42; }; +static_assert(__has_trivial_constructor(Trivial), "Trivial is nontrivial"); + +// A default constructor is trivial if it is not user-provided and if: +class NonTrivialDefCtor1 { NonTrivialDefCtor1(); }; +static_assert(!__has_trivial_constructor(NonTrivialDefCtor1), "NonTrivialDefCtor1 is trivial"); + +// - its class has no virtual functions (10.3) and no virtual base classes (10.1), and +class NonTrivialDefCtor2 { virtual void f(); }; +static_assert(!__has_trivial_constructor(NonTrivialDefCtor2), "NonTrivialDefCtor2 is trivial"); +class NonTrivialDefCtor3 : virtual Trivial {}; +static_assert(!__has_trivial_constructor(NonTrivialDefCtor3), "NonTrivialDefCtor3 is trivial"); + +// - no non-static data member of its class has a brace-or-equal-initializer, and +class NonTrivialDefCtor4 { int m = 52; }; +static_assert(!__has_trivial_constructor(NonTrivialDefCtor4), "NonTrivialDefCtor4 is trivial"); + +// - all the direct base classes of its class have trivial default constructors, and +class NonTrivialDefCtor5 : NonTrivialDefCtor1 {}; +static_assert(!__has_trivial_constructor(NonTrivialDefCtor5), "NonTrivialDefCtor5 is trivial"); + +// - for all the non-static data members of its class that are of class type (or array thereof), each such class +// has a trivial default constructor. +class NonTrivialDefCtor6 { NonTrivialDefCtor1 t; }; +static_assert(!__has_trivial_constructor(NonTrivialDefCtor6), "NonTrivialDefCtor5 is trivial"); + +// Otherwise, the default constructor is non-trivial. +class Trivial2 { Trivial2() = delete; }; +//static_assert(__has_trivial_constructor(Trivial2), "NonTrivialDefCtor2 is trivial"); +// FIXME: clang implements the pre-FDIS rule, under which this class is non-trivial. +static_assert(!__has_trivial_constructor(Trivial2), "NonTrivialDefCtor2 is trivial"); + +class Trivial3 { Trivial3() = default; }; +//static_assert(__has_trivial_constructor(Trivial3), "NonTrivialDefCtor3 is trivial"); +// FIXME: clang implements the pre-FDIS rule, under which this class is non-trivial. +static_assert(!__has_trivial_constructor(Trivial3), "NonTrivialDefCtor3 is trivial"); diff --git a/test/CXX/special/class.init/class.base.init/p8-0x.cpp b/test/CXX/special/class.init/class.base.init/p8-0x.cpp new file mode 100644 index 0000000000..8512a9f7bb --- /dev/null +++ b/test/CXX/special/class.init/class.base.init/p8-0x.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +int n; +struct S { + int &a; // expected-note 2{{here}} + int &b = n; + + S() {} // expected-error {{constructor for 'S' must explicitly initialize the reference member 'a'}} + S(int) : a(n) {} // ok + S(char) : b(n) {} // expected-error {{constructor for 'S' must explicitly initialize the reference member 'a'}} + S(double) : a(n), b(n) {} // ok +}; + +union U { + int a = 0; + char b = 'x'; + + // FIXME: these should all be rejected + U() {} // desired-error {{at most one member of a union may be initialized}} + U(int) : a(1) {} // desired-error {{at most one member of a union may be initialized}} + U(char) : b('y') {} // desired-error {{at most one member of a union may be initialized}} + U(double) : a(1), b('y') {} // desired-error {{at most one member of a union may be initialized}} +}; diff --git a/test/CXX/special/class.init/class.base.init/p9-0x.cpp b/test/CXX/special/class.init/class.base.init/p9-0x.cpp new file mode 100644 index 0000000000..039b1c271a --- /dev/null +++ b/test/CXX/special/class.init/class.base.init/p9-0x.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++0x %s -O1 -emit-llvm -o - | FileCheck %s + +struct S { + int n = 10; + int m = 2 * n; + + S() {} + S(int a) : n(a) {} + S(int a, int b) : n(a), m(b) {} + + struct T { + T *that = this; + }; +}; + +template +struct U { + T *r = &q; + T q = 42; + U *p = this; +}; + +S a; +// CHECK: @a = {{.*}} { i32 10, i32 20 } + +S b(5); +// CHECK: @b = {{.*}} { i32 5, i32 10 } + +S c(3, 9); +// CHECK: @c = {{.*}} { i32 3, i32 9 } + +S::T d; +// CHECK: @d = {{.*}} { {{.*}} @d } + +U e; +// CHECK: @e = {{.*}} { {{.*}} { i32 42, i32 84 }, {{.*}} @e } diff --git a/test/CodeGenCXX/member-init-ctor.cpp b/test/CodeGenCXX/member-init-ctor.cpp new file mode 100644 index 0000000000..d9a6734b31 --- /dev/null +++ b/test/CodeGenCXX/member-init-ctor.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 %s -std=c++0x -emit-llvm -o - | FileCheck %s + +bool b(); +struct S { + int n = b() ? S().n + 1 : 0; +}; + +S s; + +// CHECK: define{{.*}} void @_ZN1SC2Ev( +// CHECK-NOT } +// CHECK: call {{.*}} @_Z1bv() +// CHECK-NOT } +// CHECK: call {{.*}} @_ZN1SC1Ev( diff --git a/test/CodeGenObjCXX/blocks.mm b/test/CodeGenObjCXX/blocks.mm index ffb916bf03..e220753ff7 100644 --- a/test/CodeGenObjCXX/blocks.mm +++ b/test/CodeGenObjCXX/blocks.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -x objective-c++ -fblocks -triple x86_64-apple-darwin %s +// RUN: %clang_cc1 -x objective-c++ -fblocks -triple x86_64-apple-darwin %s -verify -emit-llvm -o %t // rdar://8979379 @interface A @@ -28,3 +28,19 @@ void foo(id (^objectCreationBlock)(void)) { return bar(objectCreationBlock); } +// Test4 +struct S { + S *(^a)() = ^{ // expected-warning {{C++0x}} + return this; + }; +}; +S s; + +// Test5 +struct X { + void f() { + ^ { + struct Nested { Nested *ptr = this; }; // expected-warning {{C++0x}} + } (); + }; +}; diff --git a/test/PCH/cxx-member-init.cpp b/test/PCH/cxx-member-init.cpp new file mode 100644 index 0000000000..70392a2832 --- /dev/null +++ b/test/PCH/cxx-member-init.cpp @@ -0,0 +1,22 @@ +// Test this without pch. +// RUN: %clang_cc1 -x c++ -std=c++0x -DHEADER -DSOURCE -fsyntax-only -emit-llvm -o - %s + +// Test with pch. +// RUN: %clang_cc1 -x c++ -std=c++0x -DHEADER -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -std=c++0x -DHEADER -include-pch %t -fsyntax-only -emit-llvm -o - %s + +#ifdef HEADER +int n; +struct S { + int *p = &m; + int &m = n; + S *that = this; +}; +#endif + +#ifdef SOURCE +S s; +#elif HEADER +#undef HEADER +#define SOURCE +#endif diff --git a/test/Parser/cxx0x-member-initializers.cpp b/test/Parser/cxx0x-member-initializers.cpp new file mode 100644 index 0000000000..6c3492ef21 --- /dev/null +++ b/test/Parser/cxx0x-member-initializers.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +// Make sure we don't run off the end of the stream when parsing a deferred +// initializer. +int a; // expected-note {{previous}} +struct S { + int n = 4 + ; // expected-error {{expected expression}} +} a; // expected-error {{redefinition}} + +// Make sure we use all of the tokens. +struct T { + int a = 1 // expected-error {{expected ';' at end of declaration list}} + int b = 2; + int c = b; // expected-error {{undeclared identifier}} +}; diff --git a/test/SemaCXX/PR9572.cpp b/test/SemaCXX/PR9572.cpp index d1b70778d0..25c0c017b7 100644 --- a/test/SemaCXX/PR9572.cpp +++ b/test/SemaCXX/PR9572.cpp @@ -1,13 +1,13 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s class Base { - virtual ~Base(); + virtual ~Base(); // expected-note {{implicitly declared private here}} }; -struct Foo : public Base { - const int kBlah = 3; // expected-error{{fields can only be initialized in constructors}} +struct Foo : public Base { // expected-error {{base class 'Base' has private destructor}} + const int kBlah = 3; // expected-warning {{accepted as a C++0x extension}} Foo(); }; struct Bar : public Foo { - Bar() { } + Bar() { } // expected-note {{implicit default destructor for 'Foo' first required here}} }; struct Baz { Foo f; diff --git a/test/SemaCXX/class.cpp b/test/SemaCXX/class.cpp index 52140cb074..44fa0ce7ec 100644 --- a/test/SemaCXX/class.cpp +++ b/test/SemaCXX/class.cpp @@ -34,7 +34,7 @@ public: enum E1 { en1, en2 }; - int i = 0; // expected-error {{fields can only be initialized in constructors}} + int i = 0; // expected-warning {{in-class initialization of non-static data member accepted as a C++0x extension}} static int si = 0; // expected-error {{non-const static data member must be initialized out of line}} static const NestedC ci = 0; // expected-error {{static data member of type 'const C::NestedC' must be initialized out of line}} static const int nci = vs; // expected-error {{in-class initializer is not a constant expression}} diff --git a/test/SemaCXX/implicit-exception-spec.cpp b/test/SemaCXX/implicit-exception-spec.cpp new file mode 100644 index 0000000000..81babc043f --- /dev/null +++ b/test/SemaCXX/implicit-exception-spec.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++0x -Wall %s + +template struct ExceptionIf { static int f(); }; +template<> struct ExceptionIf { typedef int f; }; + +// The exception specification of a defaulted default constructor depends on +// the contents of in-class member initializers. However, the in-class member +// initializers can depend on the exception specification of the constructor, +// since the class is considered complete within them. We reject any such cases. +namespace InClassInitializers { + // Noexcept::Noexcept() is implicitly declared as noexcept(false), because it + // directly invokes ThrowSomething(). However... + // + // If noexcept(Noexcept()) is false, then Noexcept() is a constant expression, + // so noexcept(Noexcept()) is true. But if noexcept(Noexcept()) is true, then + // Noexcept::Noexcept is not declared constexpr, therefore noexcept(Noexcept()) + // is false. + bool ThrowSomething() noexcept(false); + struct ConstExpr { + bool b = noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{exception specification is not available until end of class definition}} + }; + // We can use it now. + bool w = noexcept(ConstExpr()); + + // Much more obviously broken: we can't parse the initializer without already + // knowing whether it produces a noexcept expression. + struct TemplateArg { + int n = ExceptionIf::f(); // expected-error {{exception specification is not available until end of class definition}} + }; + bool x = noexcept(TemplateArg()); + + // And within a nested class. + struct Nested { + struct Inner { + int n = ExceptionIf::f(); // expected-error {{exception specification is not available until end of class definition}} + } inner; + }; + bool y = noexcept(Nested()); + bool z = noexcept(Nested::Inner()); +} + +// FIXME: +// The same problem arises in delayed parsing of exception specifications, +// which clang does not yet support. +namespace ExceptionSpecification { + struct Nested { // expected-note {{not complete}} + struct T { + T() noexcept(!noexcept(Nested())); // expected-error {{incomplete type}} + } t; + }; +} + +// FIXME: +// The same problem arises in delayed parsing of default arguments, +// which clang does not yet support. +namespace DefaultArgument { + // FIXME: this diagnostic is completely wrong. + struct Default { // expected-note {{explicitly marked deleted here}} + struct T { + T(int = ExceptionIf struct T { static const int B; }; +template<> struct T<2> { template using B = int; }; +const int C = 0, D = 0; +struct S { + int as[] = { decltype(x)::B(0) }; // expected-error {{array bound cannot be deduced from an in-class initializer}} + T x; // expected-error {{requires a type specifier}} +}; + +struct ThrowCtor { ThrowCtor(int) noexcept(false); }; +struct NoThrowCtor { NoThrowCtor(int) noexcept(true); }; + +struct Throw { ThrowCtor tc = 42; }; +struct NoThrow { NoThrowCtor tc = 42; }; + +static_assert(!noexcept(Throw()), "incorrect exception specification"); +static_assert(noexcept(NoThrow()), "incorrect exception specification"); + +struct CheckExcSpec { + CheckExcSpec() noexcept(true) = default; + int n = 0; +}; +struct CheckExcSpecFail { + CheckExcSpecFail() noexcept(true) = default; // expected-error {{exception specification of explicitly defaulted default constructor does not match the calculated one}} + ThrowCtor tc = 123; +}; diff --git a/test/SemaCXX/type-traits.cpp b/test/SemaCXX/type-traits.cpp index c3470d4c16..30cc6a3f1c 100644 --- a/test/SemaCXX/type-traits.cpp +++ b/test/SemaCXX/type-traits.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=gnu++0x %s #define T(b) (b) ? 1 : -1 #define F(b) (b) ? -1 : 1 @@ -38,8 +38,7 @@ typedef Derives DerivesArNB[]; struct DerivesEmpty : Empty {}; struct HasCons { HasCons(int); }; struct HasCopyAssign { HasCopyAssign operator =(const HasCopyAssign&); }; -struct HasMoveAssign { HasMoveAssign operator =(const HasMoveAssign&&); }; // \ - // expected-warning {{rvalue references}} +struct HasMoveAssign { HasMoveAssign operator =(const HasMoveAssign&&); }; struct HasDest { ~HasDest(); }; class HasPriv { int priv; }; class HasProt { protected: int prot; }; @@ -1069,7 +1068,7 @@ struct HasCopy { }; struct HasMove { - HasMove(HasMove&& cp); // expected-warning {{rvalue references}} + HasMove(HasMove&& cp); }; struct HasTemplateCons { @@ -1253,6 +1252,9 @@ void has_nothrow_copy() { { int arr[F(__has_nothrow_copy(cvoid))]; } } +template struct assert_expr; +template<> struct assert_expr {}; + void has_nothrow_constructor() { { int arr[T(__has_nothrow_constructor(Int))]; } { int arr[T(__has_nothrow_constructor(IntAr))]; } @@ -1280,6 +1282,11 @@ void has_nothrow_constructor() { { int arr[F(__has_nothrow_constructor(void))]; } { int arr[F(__has_nothrow_constructor(cvoid))]; } { int arr[F(__has_nothrow_constructor(HasTemplateCons))]; } + + // While parsing an in-class initializer, the constructor is not known to be + // non-throwing yet. + struct HasInClassInit { int n = (assert_expr(), 0); }; + { int arr[T(__has_nothrow_constructor(HasInClassInit))]; } } void has_virtual_destructor() { -- 2.40.0