From: Douglas Gregor Date: Sun, 31 Jan 2010 09:12:51 +0000 (+0000) Subject: Rework base and member initialization in constructors, with several X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9db7dbb918ca49f4ee6c181e4917e7b6ec547353;p=clang Rework base and member initialization in constructors, with several (necessarily simultaneous) changes: - CXXBaseOrMemberInitializer now contains only a single initializer rather than a set of initialiation arguments + a constructor. The single initializer covers all aspects of initialization, including constructor calls as necessary but also cleanup of temporaries created by the initializer (which we never handled before!). - Rework + simplify code generation for CXXBaseOrMemberInitializers, since we can now just emit the initializer as an initializer. - Switched base and member initialization over to the new initialization code (InitializationSequence), so that it - Improved diagnostics for the new initialization code when initializing bases and members, to match the diagnostics produced by the previous (special-purpose) code. - Simplify the representation of type-checked constructor initializers in templates; instead of keeping the fully-type-checked AST, which is rather hard to undo at template instantiation time, throw away the type-checked AST and store the raw expressions in the AST. This simplifies instantiation, but loses a little but of information in the AST. - When type-checking implicit base or member initializers within a dependent context, don't add the generated initializers into the AST, because they'll look like they were explicit. - Record in CXXConstructExpr when the constructor call is to initialize a base class, so that CodeGen does not have to infer it from context. This ensures that we call the right kind of constructor. There are also a few "opportunity" fixes here that were needed to not regress, for example: - Diagnose default-initialization of a const-qualified class that does not have a user-declared default constructor. We had this diagnostic specifically for bases and members, but missed it for variables. That's fixed now. - When defining the implicit constructors, destructor, and copy-assignment operator, set the CurContext to that constructor when we're defining the body. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94952 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 30a4e96904..0fbdfe97cb 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -941,9 +941,9 @@ class CXXBaseOrMemberInitializer { /// \brief The source location for the field name. SourceLocation MemberLocation; - /// Args - The arguments used to initialize the base or member. - Stmt **Args; - unsigned NumArgs; + /// \brief The argument used to initialize the base or member, which may + /// end up constructing an object (when multiple arguments are involved). + Stmt *Init; /// \brief Stores either the constructor to call to initialize this base or /// member (a CXXConstructorDecl pointer), or stores the anonymous union of @@ -963,7 +963,7 @@ class CXXBaseOrMemberInitializer { /// @endcode /// In above example, BaseOrMember holds the field decl. for anonymous union /// and AnonUnionMember holds field decl for au_i1. - llvm::PointerUnion CtorOrAnonUnion; + FieldDecl *AnonUnionMember; /// LParenLoc - Location of the left paren of the ctor-initializer. SourceLocation LParenLoc; @@ -975,30 +975,22 @@ public: /// CXXBaseOrMemberInitializer - Creates a new base-class initializer. explicit CXXBaseOrMemberInitializer(ASTContext &Context, - TypeSourceInfo *TInfo, CXXConstructorDecl *C, + TypeSourceInfo *TInfo, SourceLocation L, - Expr **Args, unsigned NumArgs, + Expr *Init, SourceLocation R); /// CXXBaseOrMemberInitializer - Creates a new member initializer. explicit CXXBaseOrMemberInitializer(ASTContext &Context, FieldDecl *Member, SourceLocation MemberLoc, - CXXConstructorDecl *C, SourceLocation L, - Expr **Args, unsigned NumArgs, + SourceLocation L, + Expr *Init, SourceLocation R); /// \brief Destroy the base or member initializer. void Destroy(ASTContext &Context); - /// arg_iterator - Iterates through the member initialization - /// arguments. - typedef ExprIterator arg_iterator; - - /// arg_const_iterator - Iterates through the member initialization - /// arguments. - typedef ConstExprIterator const_arg_iterator; - /// isBaseInitializer - Returns true when this initializer is /// initializing a base class. bool isBaseInitializer() const { return BaseOrMember.is(); } @@ -1048,32 +1040,16 @@ public: SourceRange getSourceRange() const; FieldDecl *getAnonUnionMember() const { - return CtorOrAnonUnion.dyn_cast(); + return AnonUnionMember; } void setAnonUnionMember(FieldDecl *anonMember) { - CtorOrAnonUnion = anonMember; - } - - const CXXConstructorDecl *getConstructor() const { - return CtorOrAnonUnion.dyn_cast(); + AnonUnionMember = anonMember; } SourceLocation getLParenLoc() const { return LParenLoc; } SourceLocation getRParenLoc() const { return RParenLoc; } - /// arg_begin() - Retrieve an iterator to the first initializer argument. - arg_iterator arg_begin() { return Args; } - /// arg_begin() - Retrieve an iterator to the first initializer argument. - const_arg_iterator const_arg_begin() const { return Args; } - - /// arg_end() - Retrieve an iterator past the last initializer argument. - arg_iterator arg_end() { return Args + NumArgs; } - /// arg_end() - Retrieve an iterator past the last initializer argument. - const_arg_iterator const_arg_end() const { return Args + NumArgs; } - - /// getNumArgs - Determine the number of arguments used to - /// initialize the member or base. - unsigned getNumArgs() const { return NumArgs; } + Expr *getInit() { return static_cast(Init); } }; /// CXXConstructorDecl - Represents a C++ constructor within a diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 98933dbfdc..a2b9f40012 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -613,6 +613,7 @@ class CXXConstructExpr : public Expr { SourceLocation Loc; bool Elidable : 1; bool ZeroInitialization : 1; + bool BaseInitialization : 1; Stmt **Args; unsigned NumArgs; @@ -621,7 +622,8 @@ protected: SourceLocation Loc, CXXConstructorDecl *d, bool elidable, Expr **args, unsigned numargs, - bool ZeroInitialization = false); + bool ZeroInitialization = false, + bool BaseInitialization = false); ~CXXConstructExpr() { } virtual void DoDestroy(ASTContext &C); @@ -635,7 +637,8 @@ public: SourceLocation Loc, CXXConstructorDecl *D, bool Elidable, Expr **Args, unsigned NumArgs, - bool ZeroInitialization = false); + bool ZeroInitialization = false, + bool BaseInitialization = false); CXXConstructorDecl* getConstructor() const { return Constructor; } @@ -655,6 +658,11 @@ public: ZeroInitialization = ZeroInit; } + /// \brief Determines whether this constructor is actually constructing + /// a base class (rather than a complete object). + bool isBaseInitialization() const { return BaseInitialization; } + void setBaseInitialization(bool BI) { BaseInitialization = BI; } + typedef ExprIterator arg_iterator; typedef ConstExprIterator const_arg_iterator; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index e15bf20f0c..423988f47b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -588,6 +588,8 @@ def err_reference_var_requires_init : Error< "declaration of reference variable %0 requires an initializer">; def err_const_var_requires_init : Error< "declaration of const variable '%0' requires an initializer">; +def err_reference_without_init : Error< + "reference to type %0 requires an initializer">; def err_reference_has_multiple_inits : Error< "reference cannot be initialized with multiple values">; def err_init_non_aggr_init_list : Error< @@ -880,10 +882,7 @@ def err_uninitialized_member_for_assign : Error< "assignment operator">; def note_first_required_here : Note< "synthesized method is first required here">; -def err_null_intialized_reference_member : Error< - "cannot initialize the member to null in default constructor because " - "reference member %0 cannot be null-initialized">; -def err_unintialized_member_in_ctor : Error< +def err_uninitialized_member_in_ctor : Error< "%select{|implicit default }0constructor for %1 must explicitly initialize " "the %select{reference|const}2 member %3">; @@ -1949,7 +1948,8 @@ def err_new_paren_array_nonconst : Error< def err_array_size_not_integral : Error< "array size expression must have integral or enumerated type, not %0">; def err_default_init_const : Error< - "default initialization of an object of const type %0">; + "default initialization of an object of const type %0" + "%select{| requires a user-provided default constructor}1">; def err_delete_operand : Error<"cannot delete expression of type %0">; def err_ambiguous_delete_operand : Error<"ambiguous conversion of delete " "expression of type %0 to a pointer">; @@ -2252,8 +2252,6 @@ def error_multiple_base_initialization : Error < def err_mem_init_not_member_or_class : Error< "member initializer %0 does not name a non-static data member or base " "class">; -def err_mem_initializer_mismatch : Error< - "Too many arguments for member initializer %0">; def warn_field_initialized : Warning< "member '%0' will be initialized after">, diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 3d470e6244..6b398f62f6 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1654,9 +1654,12 @@ public: /// a well-formed program), ColonLoc is the location of the ':' that /// starts the constructor initializer, and MemInit/NumMemInits /// contains the individual member (and base) initializers. + /// AnyErrors will be true if there were any invalid member initializers + /// that are not represented in the list. virtual void ActOnMemInitializers(DeclPtrTy ConstructorDecl, SourceLocation ColonLoc, - MemInitTy **MemInits, unsigned NumMemInits){ + MemInitTy **MemInits, unsigned NumMemInits, + bool AnyErrors){ } virtual void ActOnDefaultCtorInitializers(DeclPtrTy CDtorDecl) {} diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 73f47d9bc0..3d690d15f2 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -673,42 +673,25 @@ bool CXXMethodDecl::hasInlineBody() const { CXXBaseOrMemberInitializer:: CXXBaseOrMemberInitializer(ASTContext &Context, - TypeSourceInfo *TInfo, CXXConstructorDecl *C, - SourceLocation L, - Expr **Args, unsigned NumArgs, - SourceLocation R) - : BaseOrMember(TInfo), Args(0), NumArgs(0), CtorOrAnonUnion(C), + TypeSourceInfo *TInfo, + SourceLocation L, Expr *Init, SourceLocation R) + : BaseOrMember(TInfo), Init(Init), AnonUnionMember(0), LParenLoc(L), RParenLoc(R) { - if (NumArgs > 0) { - this->NumArgs = NumArgs; - this->Args = new (Context) Stmt*[NumArgs]; - for (unsigned Idx = 0; Idx < NumArgs; ++Idx) - this->Args[Idx] = Args[Idx]; - } } CXXBaseOrMemberInitializer:: CXXBaseOrMemberInitializer(ASTContext &Context, FieldDecl *Member, SourceLocation MemberLoc, - CXXConstructorDecl *C, SourceLocation L, - Expr **Args, unsigned NumArgs, - SourceLocation R) - : BaseOrMember(Member), MemberLocation(MemberLoc), Args(0), NumArgs(0), - CtorOrAnonUnion(C), LParenLoc(L), RParenLoc(R) + SourceLocation L, Expr *Init, SourceLocation R) + : BaseOrMember(Member), MemberLocation(MemberLoc), Init(Init), + AnonUnionMember(0), LParenLoc(L), RParenLoc(R) { - if (NumArgs > 0) { - this->NumArgs = NumArgs; - this->Args = new (Context) Stmt*[NumArgs]; - for (unsigned Idx = 0; Idx < NumArgs; ++Idx) - this->Args[Idx] = Args[Idx]; - } } void CXXBaseOrMemberInitializer::Destroy(ASTContext &Context) { - for (unsigned I = 0; I != NumArgs; ++I) - Args[I]->Destroy(Context); - Context.Deallocate(Args); + if (Init) + Init->Destroy(Context); this->~CXXBaseOrMemberInitializer(); } diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 32ac53d85b..a625865ecd 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -403,32 +404,51 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXBaseOrMemberInitializer * BMInitializer = (*B); if (B != CDecl->init_begin()) Out << ", "; - bool hasArguments = (BMInitializer->arg_begin() != - BMInitializer->arg_end()); if (BMInitializer->isMemberInitializer()) { FieldDecl *FD = BMInitializer->getMember(); Out << FD->getNameAsString(); + } else { + Out << QualType(BMInitializer->getBaseClass(), 0).getAsString(); } - else // FIXME. skip dependent types for now. - if (const RecordType *RT = - BMInitializer->getBaseClass()->getAs()) { - const CXXRecordDecl *BaseDecl = - cast(RT->getDecl()); - Out << BaseDecl->getNameAsString(); - } - if (hasArguments) { - Out << "("; - for (CXXBaseOrMemberInitializer::const_arg_iterator BE = - BMInitializer->const_arg_begin(), - EE = BMInitializer->const_arg_end(); BE != EE; ++BE) { - if (BE != BMInitializer->const_arg_begin()) - Out<< ", "; - const Expr *Exp = (*BE); - Exp->printPretty(Out, Context, 0, Policy, Indentation); + + Out << "("; + if (!BMInitializer->getInit()) { + // Nothing to print + } else { + Expr *Init = BMInitializer->getInit(); + if (CXXExprWithTemporaries *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); + } } - Out << ")"; - } else - Out << "()"; + } + Out << ")"; } } } diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 3931bbd6da..70d0891023 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -424,22 +424,26 @@ CXXConstructExpr *CXXConstructExpr::Create(ASTContext &C, QualType T, SourceLocation Loc, CXXConstructorDecl *D, bool Elidable, Expr **Args, unsigned NumArgs, - bool ZeroInitialization) { + bool ZeroInitialization, + bool BaseInitialization) { return new (C) CXXConstructExpr(C, CXXConstructExprClass, T, Loc, D, - Elidable, Args, NumArgs, ZeroInitialization); + Elidable, Args, NumArgs, ZeroInitialization, + BaseInitialization); } CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T, SourceLocation Loc, CXXConstructorDecl *D, bool elidable, Expr **args, unsigned numargs, - bool ZeroInitialization) + bool ZeroInitialization, + bool BaseInitialization) : Expr(SC, T, T->isDependentType(), (T->isDependentType() || CallExpr::hasAnyValueDependentArguments(args, numargs))), Constructor(D), Loc(Loc), Elidable(elidable), - ZeroInitialization(ZeroInitialization), Args(0), NumArgs(numargs) + ZeroInitialization(ZeroInitialization), + BaseInitialization(BaseInitialization), Args(0), NumArgs(numargs) { if (NumArgs) { Args = new (C) Stmt*[NumArgs]; diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 1b337ebc1d..c965b34298 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -811,11 +811,7 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, llvm::Value *V = CGF.Builder.CreateBitCast(ThisPtr, Int8PtrTy); V = CGF.Builder.CreateConstInBoundsGEP1_64(V, Offset/8); V = CGF.Builder.CreateBitCast(V, BaseClassType->getPointerTo()); - - CGF.EmitCXXConstructorCall(BaseInit->getConstructor(), - Ctor_Base, V, - BaseInit->const_arg_begin(), - BaseInit->const_arg_end()); + CGF.EmitAggExpr(BaseInit->getInit(), V, false, false, true); } static void EmitMemberInitializer(CodeGenFunction &CGF, @@ -846,55 +842,34 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, // We lose the constructor for anonymous union members, so handle them // explicitly. - // FIXME: This is somwhat ugly. + // FIXME: This is somwhat ugly, and doesn't seem necessary at all. if (MemberInit->getAnonUnionMember() && FieldType->getAs()) { - if (MemberInit->getNumArgs()) - CGF.EmitAggExpr(*MemberInit->arg_begin(), LHS.getAddress(), + if (MemberInit->getInit()) + CGF.EmitAggExpr(MemberInit->getInit(), LHS.getAddress(), LHS.isVolatileQualified()); else CGF.EmitAggregateClear(LHS.getAddress(), Field->getType()); return; } - if (FieldType->getAs()) { - assert(MemberInit->getConstructor() && - "EmitCtorPrologue - no constructor to initialize member"); - if (Array) { - const llvm::Type *BasePtr = CGF.ConvertType(FieldType); - BasePtr = llvm::PointerType::getUnqual(BasePtr); - llvm::Value *BaseAddrPtr = - CGF.Builder.CreateBitCast(LHS.getAddress(), BasePtr); - CGF.EmitCXXAggrConstructorCall(MemberInit->getConstructor(), - Array, BaseAddrPtr, - MemberInit->const_arg_begin(), - MemberInit->const_arg_end()); - } - else - CGF.EmitCXXConstructorCall(MemberInit->getConstructor(), - Ctor_Complete, LHS.getAddress(), - MemberInit->const_arg_begin(), - MemberInit->const_arg_end()); - return; - } - - assert(MemberInit->getNumArgs() == 1 && "Initializer count must be 1 only"); - Expr *RhsExpr = *MemberInit->arg_begin(); + // FIXME: If there's no initializer and the CXXBaseOrMemberInitializer + // was implicitly generated, we shouldn't be zeroing memory. RValue RHS; if (FieldType->isReferenceType()) { - RHS = CGF.EmitReferenceBindingToExpr(RhsExpr, FieldType, - /*IsInitializer=*/true); + RHS = CGF.EmitReferenceBindingToExpr(MemberInit->getInit(), FieldType, + /*IsInitializer=*/true); CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); - } else if (Array) { + } else if (Array && !MemberInit->getInit()) { CGF.EmitMemSetToZero(LHS.getAddress(), Field->getType()); - } else if (!CGF.hasAggregateLLVMType(RhsExpr->getType())) { - RHS = RValue::get(CGF.EmitScalarExpr(RhsExpr, true)); + } else if (!CGF.hasAggregateLLVMType(Field->getType())) { + RHS = RValue::get(CGF.EmitScalarExpr(MemberInit->getInit(), true)); CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); - } else if (RhsExpr->getType()->isAnyComplexType()) { - CGF.EmitComplexExprIntoAddr(RhsExpr, LHS.getAddress(), + } else if (MemberInit->getInit()->getType()->isAnyComplexType()) { + CGF.EmitComplexExprIntoAddr(MemberInit->getInit(), LHS.getAddress(), LHS.isVolatileQualified()); } else { - // Handle member function pointers; other aggregates shouldn't get this far. - CGF.EmitAggExpr(RhsExpr, LHS.getAddress(), LHS.isVolatileQualified()); + CGF.EmitAggExpr(MemberInit->getInit(), LHS.getAddress(), + LHS.isVolatileQualified(), false, true); } } diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 2e8aea5e43..d7e6dbfa36 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -339,7 +339,9 @@ CodeGenFunction::EmitCXXConstructExpr(llvm::Value *Dest, } else // Call the constructor. - EmitCXXConstructorCall(CD, Ctor_Complete, Dest, + EmitCXXConstructorCall(CD, + E->isBaseInitialization()? Ctor_Base : Ctor_Complete, + Dest, E->arg_begin(), E->arg_end()); } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 04845d3b41..87bb3cb2b1 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1550,12 +1550,15 @@ void Parser::ParseConstructorInitializer(DeclPtrTy ConstructorDecl) { SourceLocation ColonLoc = ConsumeToken(); llvm::SmallVector MemInitializers; - + bool AnyErrors = false; + do { MemInitResult MemInit = ParseMemInitializer(ConstructorDecl); if (!MemInit.isInvalid()) MemInitializers.push_back(MemInit.get()); - + else + AnyErrors = true; + if (Tok.is(tok::comma)) ConsumeToken(); else if (Tok.is(tok::l_brace)) @@ -1569,7 +1572,8 @@ void Parser::ParseConstructorInitializer(DeclPtrTy ConstructorDecl) { } while (true); Actions.ActOnMemInitializers(ConstructorDecl, ColonLoc, - MemInitializers.data(), MemInitializers.size()); + MemInitializers.data(), MemInitializers.size(), + AnyErrors); } /// ParseMemInitializer - Parse a C++ member initializer, which is diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 86dbff3037..6f1ab14778 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1911,7 +1911,8 @@ public: QualType DeclInitType, CXXConstructorDecl *Constructor, MultiExprArg Exprs, - bool RequiresZeroInit = false); + bool RequiresZeroInit = false, + bool BaseInitialization = false); // FIXME: Can re remove this and have the above BuildCXXConstructExpr check if // the constructor can be elidable? @@ -1920,7 +1921,8 @@ public: CXXConstructorDecl *Constructor, bool Elidable, MultiExprArg Exprs, - bool RequiresZeroInit = false); + bool RequiresZeroInit = false, + bool BaseInitialization = false); OwningExprResult BuildCXXTemporaryObjectExpr(CXXConstructorDecl *Cons, QualType writtenTy, @@ -2282,9 +2284,10 @@ public: CXXRecordDecl *ClassDecl); bool SetBaseOrMemberInitializers(CXXConstructorDecl *Constructor, - CXXBaseOrMemberInitializer **Initializers, - unsigned NumInitializers, - bool IsImplicitConstructor); + CXXBaseOrMemberInitializer **Initializers, + unsigned NumInitializers, + bool IsImplicitConstructor, + bool AnyErrors); /// MarkBaseAndMemberDestructorsReferenced - Given a destructor decl, /// mark all its non-trivial member and base destructor declarations @@ -2316,7 +2319,8 @@ public: virtual void ActOnMemInitializers(DeclPtrTy ConstructorDecl, SourceLocation ColonLoc, - MemInitTy **MemInits, unsigned NumMemInits); + MemInitTy **MemInits, unsigned NumMemInits, + bool AnyErrors); void CheckCompletedCXXClass(CXXRecordDecl *Record); virtual void ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 130bf4dbdf..f23f702d05 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1195,10 +1195,6 @@ Sema::BuildMemberInitializer(FieldDecl *Member, Expr **Args, unsigned NumArgs, SourceLocation IdLoc, SourceLocation LParenLoc, SourceLocation RParenLoc) { - // FIXME: CXXBaseOrMemberInitializer should only contain a single - // subexpression so we can wrap it in a CXXExprWithTemporaries if necessary. - ExprTemporaries.clear(); - // Diagnose value-uses of fields to initialize themselves, e.g. // foo(foo) // where foo is not also a parameter to the constructor. @@ -1220,65 +1216,80 @@ Sema::BuildMemberInitializer(FieldDecl *Member, Expr **Args, for (unsigned i = 0; i < NumArgs; i++) HasDependentArg |= Args[i]->isTypeDependent(); - CXXConstructorDecl *C = 0; QualType FieldType = Member->getType(); if (const ArrayType *Array = Context.getAsArrayType(FieldType)) FieldType = Array->getElementType(); ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this); - if (FieldType->isDependentType()) { - // Can't check init for dependent type. - } else if (FieldType->isRecordType()) { - // Member is a record (struct/union/class), so pass the initializer - // arguments down to the record's constructor. - if (!HasDependentArg) { - C = PerformInitializationByConstructor(FieldType, - MultiExprArg(*this, - (void**)Args, - NumArgs), - IdLoc, - SourceRange(IdLoc, RParenLoc), - Member->getDeclName(), - InitializationKind::CreateDirect(IdLoc, LParenLoc, RParenLoc), - ConstructorArgs); - - if (C) { - // Take over the constructor arguments as our own. - NumArgs = ConstructorArgs.size(); - Args = (Expr **)ConstructorArgs.take(); - } - } - } else if (NumArgs != 1 && NumArgs != 0) { - // The member type is not a record type (or an array of record - // types), so it can be only be default- or copy-initialized. - return Diag(IdLoc, diag::err_mem_initializer_mismatch) - << Member->getDeclName() << SourceRange(IdLoc, RParenLoc); - } else if (!HasDependentArg) { - Expr *NewExp; - if (NumArgs == 0) { - if (FieldType->isReferenceType()) { - Diag(IdLoc, diag::err_null_intialized_reference_member) - << Member->getDeclName(); - return Diag(Member->getLocation(), diag::note_declared_at); - } - NewExp = new (Context) CXXZeroInitValueExpr(FieldType, IdLoc, RParenLoc); - NumArgs = 1; - } - else - NewExp = (Expr*)Args[0]; - if (!Member->isInvalidDecl() && - PerformCopyInitialization(NewExp, FieldType, AA_Passing)) - return true; - Args[0] = NewExp; + if (FieldType->isDependentType() || HasDependentArg) { + // Can't check initialization for a member of dependent type or when + // any of the arguments are type-dependent expressions. + OwningExprResult Init + = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, + RParenLoc)); + + // Erase any temporaries within this evaluation context; we're not + // going to track them in the AST, since we'll be rebuilding the + // ASTs during template instantiation. + ExprTemporaries.erase( + ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, + ExprTemporaries.end()); + + return new (Context) CXXBaseOrMemberInitializer(Context, Member, IdLoc, + LParenLoc, + Init.takeAs(), + RParenLoc); + } - // FIXME: CXXBaseOrMemberInitializer should only contain a single - // subexpression so we can wrap it in a CXXExprWithTemporaries if necessary. - ExprTemporaries.clear(); + if (Member->isInvalidDecl()) + return true; + + // Initialize the member. + InitializedEntity MemberEntity = + InitializedEntity::InitializeMember(Member, 0); + InitializationKind Kind = + InitializationKind::CreateDirect(IdLoc, LParenLoc, RParenLoc); + + InitializationSequence InitSeq(*this, MemberEntity, Kind, Args, NumArgs); - // FIXME: Perform direct initialization of the member. + OwningExprResult MemberInit = + InitSeq.Perform(*this, MemberEntity, Kind, + MultiExprArg(*this, (void**)Args, NumArgs), 0); + if (MemberInit.isInvalid()) + return true; + + // C++0x [class.base.init]p7: + // The initialization of each base and member constitutes a + // full-expression. + MemberInit = MaybeCreateCXXExprWithTemporaries(move(MemberInit)); + if (MemberInit.isInvalid()) + return true; + + // If we are in a dependent context, template instantiation will + // perform this type-checking again. Just save the arguments that we + // received in a ParenListExpr. + // FIXME: This isn't quite ideal, since our ASTs don't capture all + // of the information that we have about the member + // initializer. However, deconstructing the ASTs is a dicey process, + // and this approach is far more likely to get the corner cases right. + if (CurContext->isDependentContext()) { + // Bump the reference count of all of the arguments. + for (unsigned I = 0; I != NumArgs; ++I) + Args[I]->Retain(); + + OwningExprResult Init + = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, + RParenLoc)); + return new (Context) CXXBaseOrMemberInitializer(Context, Member, IdLoc, + LParenLoc, + Init.takeAs(), + RParenLoc); + } + return new (Context) CXXBaseOrMemberInitializer(Context, Member, IdLoc, - C, LParenLoc, (Expr **)Args, - NumArgs, RParenLoc); + LParenLoc, + MemberInit.takeAs(), + RParenLoc); } Sema::MemInitResult @@ -1291,76 +1302,118 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, HasDependentArg |= Args[i]->isTypeDependent(); SourceLocation BaseLoc = BaseTInfo->getTypeLoc().getSourceRange().getBegin(); - if (!BaseType->isDependentType()) { - if (!BaseType->isRecordType()) - return Diag(BaseLoc, diag::err_base_init_does_not_name_class) - << BaseType << BaseTInfo->getTypeLoc().getSourceRange(); - - // C++ [class.base.init]p2: - // [...] Unless the mem-initializer-id names a nonstatic data - // member of the constructor’s class or a direct or virtual base - // of that class, the mem-initializer is ill-formed. A - // mem-initializer-list can initialize a base class using any - // name that denotes that base class type. - - // Check for direct and virtual base classes. - const CXXBaseSpecifier *DirectBaseSpec = 0; - const CXXBaseSpecifier *VirtualBaseSpec = 0; - FindBaseInitializer(*this, ClassDecl, BaseType, DirectBaseSpec, - VirtualBaseSpec); - - // C++ [base.class.init]p2: - // If a mem-initializer-id is ambiguous because it designates both - // a direct non-virtual base class and an inherited virtual base - // class, the mem-initializer is ill-formed. - if (DirectBaseSpec && VirtualBaseSpec) - return Diag(BaseLoc, diag::err_base_init_direct_and_virtual) - << BaseType << BaseTInfo->getTypeLoc().getSourceRange(); - // C++ [base.class.init]p2: - // Unless the mem-initializer-id names a nonstatic data membeer of the - // constructor's class ot a direst or virtual base of that class, the - // mem-initializer is ill-formed. - if (!DirectBaseSpec && !VirtualBaseSpec) - return Diag(BaseLoc, diag::err_not_direct_base_or_virtual) - << BaseType << ClassDecl->getNameAsCString() - << BaseTInfo->getTypeLoc().getSourceRange(); - } - - CXXConstructorDecl *C = 0; - ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this); - if (!BaseType->isDependentType() && !HasDependentArg) { - DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( - Context.getCanonicalType(BaseType).getUnqualifiedType()); - - C = PerformInitializationByConstructor(BaseType, - MultiExprArg(*this, - (void**)Args, NumArgs), - BaseLoc, - SourceRange(BaseLoc, RParenLoc), - Name, - InitializationKind::CreateDirect(BaseLoc, LParenLoc, RParenLoc), - ConstructorArgs); - if (C) { - // Take over the constructor arguments as our own. - NumArgs = ConstructorArgs.size(); - Args = (Expr **)ConstructorArgs.take(); - } + if (BaseType->isDependentType() || HasDependentArg) { + // Can't check initialization for a base of dependent type or when + // any of the arguments are type-dependent expressions. + OwningExprResult BaseInit + = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, + RParenLoc)); + + // Erase any temporaries within this evaluation context; we're not + // going to track them in the AST, since we'll be rebuilding the + // ASTs during template instantiation. + ExprTemporaries.erase( + ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, + ExprTemporaries.end()); + + return new (Context) CXXBaseOrMemberInitializer(Context, BaseTInfo, + LParenLoc, + BaseInit.takeAs(), + RParenLoc); } + + if (!BaseType->isRecordType()) + return Diag(BaseLoc, diag::err_base_init_does_not_name_class) + << BaseType << BaseTInfo->getTypeLoc().getSourceRange(); - // FIXME: CXXBaseOrMemberInitializer should only contain a single - // subexpression so we can wrap it in a CXXExprWithTemporaries if necessary. - ExprTemporaries.clear(); + // C++ [class.base.init]p2: + // [...] Unless the mem-initializer-id names a nonstatic data + // member of the constructor’s class or a direct or virtual base + // of that class, the mem-initializer is ill-formed. A + // mem-initializer-list can initialize a base class using any + // name that denotes that base class type. + + // Check for direct and virtual base classes. + const CXXBaseSpecifier *DirectBaseSpec = 0; + const CXXBaseSpecifier *VirtualBaseSpec = 0; + FindBaseInitializer(*this, ClassDecl, BaseType, DirectBaseSpec, + VirtualBaseSpec); + + // C++ [base.class.init]p2: + // If a mem-initializer-id is ambiguous because it designates both + // a direct non-virtual base class and an inherited virtual base + // class, the mem-initializer is ill-formed. + if (DirectBaseSpec && VirtualBaseSpec) + return Diag(BaseLoc, diag::err_base_init_direct_and_virtual) + << BaseType << BaseTInfo->getTypeLoc().getSourceRange(); + // C++ [base.class.init]p2: + // Unless the mem-initializer-id names a nonstatic data membeer of the + // constructor's class ot a direst or virtual base of that class, the + // mem-initializer is ill-formed. + if (!DirectBaseSpec && !VirtualBaseSpec) + return Diag(BaseLoc, diag::err_not_direct_base_or_virtual) + << BaseType << ClassDecl->getNameAsCString() + << BaseTInfo->getTypeLoc().getSourceRange(); + + CXXBaseSpecifier *BaseSpec + = const_cast(DirectBaseSpec); + if (!BaseSpec) + BaseSpec = const_cast(VirtualBaseSpec); + + // Initialize the base. + InitializedEntity BaseEntity = + InitializedEntity::InitializeBase(Context, BaseSpec); + InitializationKind Kind = + InitializationKind::CreateDirect(BaseLoc, LParenLoc, RParenLoc); + + InitializationSequence InitSeq(*this, BaseEntity, Kind, Args, NumArgs); + + OwningExprResult BaseInit = + InitSeq.Perform(*this, BaseEntity, Kind, + MultiExprArg(*this, (void**)Args, NumArgs), 0); + if (BaseInit.isInvalid()) + return true; - return new (Context) CXXBaseOrMemberInitializer(Context, BaseTInfo, C, - LParenLoc, (Expr **)Args, - NumArgs, RParenLoc); + // C++0x [class.base.init]p7: + // The initialization of each base and member constitutes a + // full-expression. + BaseInit = MaybeCreateCXXExprWithTemporaries(move(BaseInit)); + if (BaseInit.isInvalid()) + return true; + + // If we are in a dependent context, template instantiation will + // perform this type-checking again. Just save the arguments that we + // received in a ParenListExpr. + // FIXME: This isn't quite ideal, since our ASTs don't capture all + // of the information that we have about the base + // initializer. However, deconstructing the ASTs is a dicey process, + // and this approach is far more likely to get the corner cases right. + if (CurContext->isDependentContext()) { + // Bump the reference count of all of the arguments. + for (unsigned I = 0; I != NumArgs; ++I) + Args[I]->Retain(); + + OwningExprResult Init + = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, + RParenLoc)); + return new (Context) CXXBaseOrMemberInitializer(Context, BaseTInfo, + LParenLoc, + Init.takeAs(), + RParenLoc); + } + + return new (Context) CXXBaseOrMemberInitializer(Context, BaseTInfo, + LParenLoc, + BaseInit.takeAs(), + RParenLoc); } bool Sema::SetBaseOrMemberInitializers(CXXConstructorDecl *Constructor, - CXXBaseOrMemberInitializer **Initializers, - unsigned NumInitializers, - bool IsImplicitConstructor) { + CXXBaseOrMemberInitializer **Initializers, + unsigned NumInitializers, + bool IsImplicitConstructor, + bool AnyErrors) { // We need to build the initializer AST according to order of construction // and not what user specified in the Initializers list. CXXRecordDecl *ClassDecl = cast(Constructor->getDeclContext()); @@ -1403,6 +1456,8 @@ Sema::SetBaseOrMemberInitializers(CXXConstructorDecl *Constructor, AllToInit.push_back(Member); } } else { + llvm::SmallVector BasesToDefaultInit; + // Push virtual bases before others. for (CXXRecordDecl::base_class_iterator VBase = ClassDecl->vbases_begin(), @@ -1412,44 +1467,34 @@ Sema::SetBaseOrMemberInitializers(CXXConstructorDecl *Constructor, if (CXXBaseOrMemberInitializer *Value = AllBaseFields.lookup(VBase->getType()->getAs())) { AllToInit.push_back(Value); - } - else { - CXXRecordDecl *VBaseDecl = - cast(VBase->getType()->getAs()->getDecl()); - assert(VBaseDecl && "SetBaseOrMemberInitializers - VBaseDecl null"); - CXXConstructorDecl *Ctor = VBaseDecl->getDefaultConstructor(Context); - if (!Ctor) { - Diag(Constructor->getLocation(), diag::err_missing_default_ctor) - << (int)IsImplicitConstructor << Context.getTagDeclType(ClassDecl) - << 0 << VBase->getType(); - Diag(VBaseDecl->getLocation(), diag::note_previous_decl) - << Context.getTagDeclType(VBaseDecl); + } else if (!AnyErrors) { + InitializedEntity InitEntity + = InitializedEntity::InitializeBase(Context, VBase); + InitializationKind InitKind + = InitializationKind::CreateDefault(Constructor->getLocation()); + InitializationSequence InitSeq(*this, InitEntity, InitKind, 0, 0); + OwningExprResult BaseInit = InitSeq.Perform(*this, InitEntity, InitKind, + MultiExprArg(*this, 0, 0)); + BaseInit = MaybeCreateCXXExprWithTemporaries(move(BaseInit)); + if (BaseInit.isInvalid()) { HadError = true; continue; } - ASTOwningVector<&ActionBase::DeleteExpr> CtorArgs(*this); - if (CompleteConstructorCall(Ctor, MultiExprArg(*this, 0, 0), - Constructor->getLocation(), CtorArgs)) + // Don't attach synthesized base initializers in a dependent + // context; they'll be checked again at template instantiation + // time. + if (CurContext->isDependentContext()) continue; - MarkDeclarationReferenced(Constructor->getLocation(), Ctor); - - // FIXME: CXXBaseOrMemberInitializer should only contain a single - // subexpression so we can wrap it in a CXXExprWithTemporaries if - // necessary. - // FIXME: Is there any better source-location information we can give? - ExprTemporaries.clear(); - CXXBaseOrMemberInitializer *Member = + CXXBaseOrMemberInitializer *CXXBaseInit = new (Context) CXXBaseOrMemberInitializer(Context, Context.getTrivialTypeSourceInfo(VBase->getType(), SourceLocation()), - Ctor, SourceLocation(), - CtorArgs.takeAs(), - CtorArgs.size(), + BaseInit.takeAs(), SourceLocation()); - AllToInit.push_back(Member); + AllToInit.push_back(CXXBaseInit); } } @@ -1466,43 +1511,34 @@ Sema::SetBaseOrMemberInitializers(CXXConstructorDecl *Constructor, = AllBaseFields.lookup(Base->getType()->getAs())) { AllToInit.push_back(Value); } - else { - CXXRecordDecl *BaseDecl = - cast(Base->getType()->getAs()->getDecl()); - assert(BaseDecl && "SetBaseOrMemberInitializers - BaseDecl null"); - CXXConstructorDecl *Ctor = BaseDecl->getDefaultConstructor(Context); - if (!Ctor) { - Diag(Constructor->getLocation(), diag::err_missing_default_ctor) - << (int)IsImplicitConstructor << Context.getTagDeclType(ClassDecl) - << 0 << Base->getType(); - Diag(BaseDecl->getLocation(), diag::note_previous_decl) - << Context.getTagDeclType(BaseDecl); + else if (!AnyErrors) { + InitializedEntity InitEntity + = InitializedEntity::InitializeBase(Context, Base); + InitializationKind InitKind + = InitializationKind::CreateDefault(Constructor->getLocation()); + InitializationSequence InitSeq(*this, InitEntity, InitKind, 0, 0); + OwningExprResult BaseInit = InitSeq.Perform(*this, InitEntity, InitKind, + MultiExprArg(*this, 0, 0)); + BaseInit = MaybeCreateCXXExprWithTemporaries(move(BaseInit)); + if (BaseInit.isInvalid()) { HadError = true; continue; } - - ASTOwningVector<&ActionBase::DeleteExpr> CtorArgs(*this); - if (CompleteConstructorCall(Ctor, MultiExprArg(*this, 0, 0), - Constructor->getLocation(), CtorArgs)) + + // Don't attach synthesized base initializers in a dependent + // context; they'll be regenerated at template instantiation + // time. + if (CurContext->isDependentContext()) continue; - MarkDeclarationReferenced(Constructor->getLocation(), Ctor); - - // FIXME: CXXBaseOrMemberInitializer should only contain a single - // subexpression so we can wrap it in a CXXExprWithTemporaries if - // necessary. - // FIXME: Is there any better source-location information we can give? - ExprTemporaries.clear(); - CXXBaseOrMemberInitializer *Member = + CXXBaseOrMemberInitializer *CXXBaseInit = new (Context) CXXBaseOrMemberInitializer(Context, Context.getTrivialTypeSourceInfo(Base->getType(), SourceLocation()), - Ctor, SourceLocation(), - CtorArgs.takeAs(), - CtorArgs.size(), + BaseInit.takeAs(), SourceLocation()); - AllToInit.push_back(Member); + AllToInit.push_back(CXXBaseInit); } } } @@ -1535,66 +1571,49 @@ Sema::SetBaseOrMemberInitializers(CXXConstructorDecl *Constructor, continue; } - if ((*Field)->getType()->isDependentType()) + if ((*Field)->getType()->isDependentType() || AnyErrors) continue; QualType FT = Context.getBaseElementType((*Field)->getType()); - if (const RecordType* RT = FT->getAs()) { - CXXConstructorDecl *Ctor = - cast(RT->getDecl())->getDefaultConstructor(Context); - if (!Ctor) { - Diag(Constructor->getLocation(), diag::err_missing_default_ctor) - << (int)IsImplicitConstructor << Context.getTagDeclType(ClassDecl) - << 1 << (*Field)->getDeclName(); - Diag(Field->getLocation(), diag::note_field_decl); - Diag(RT->getDecl()->getLocation(), diag::note_previous_decl) - << Context.getTagDeclType(RT->getDecl()); + if (FT->getAs()) { + InitializedEntity InitEntity + = InitializedEntity::InitializeMember(*Field); + InitializationKind InitKind + = InitializationKind::CreateDefault(Constructor->getLocation()); + + InitializationSequence InitSeq(*this, InitEntity, InitKind, 0, 0); + OwningExprResult MemberInit = InitSeq.Perform(*this, InitEntity, InitKind, + MultiExprArg(*this, 0, 0)); + MemberInit = MaybeCreateCXXExprWithTemporaries(move(MemberInit)); + if (MemberInit.isInvalid()) { HadError = true; continue; } - - if (FT.isConstQualified() && Ctor->isTrivial()) { - Diag(Constructor->getLocation(), diag::err_unintialized_member_in_ctor) - << (int)IsImplicitConstructor << Context.getTagDeclType(ClassDecl) - << 1 << (*Field)->getDeclName(); - Diag((*Field)->getLocation(), diag::note_declared_at); - HadError = true; - } - - // Don't create initializers for trivial constructors, since they don't - // actually need to be run. - if (Ctor->isTrivial()) - continue; - - ASTOwningVector<&ActionBase::DeleteExpr> CtorArgs(*this); - if (CompleteConstructorCall(Ctor, MultiExprArg(*this, 0, 0), - Constructor->getLocation(), CtorArgs)) + + // Don't attach synthesized member initializers in a dependent + // context; they'll be regenerated a template instantiation + // time. + if (CurContext->isDependentContext()) continue; - // FIXME: CXXBaseOrMemberInitializer should only contain a single - // subexpression so we can wrap it in a CXXExprWithTemporaries if necessary. - ExprTemporaries.clear(); CXXBaseOrMemberInitializer *Member = new (Context) CXXBaseOrMemberInitializer(Context, *Field, SourceLocation(), - Ctor, SourceLocation(), - CtorArgs.takeAs(), - CtorArgs.size(), + MemberInit.takeAs(), SourceLocation()); AllToInit.push_back(Member); - MarkDeclarationReferenced(Constructor->getLocation(), Ctor); } else if (FT->isReferenceType()) { - Diag(Constructor->getLocation(), diag::err_unintialized_member_in_ctor) + Diag(Constructor->getLocation(), diag::err_uninitialized_member_in_ctor) << (int)IsImplicitConstructor << Context.getTagDeclType(ClassDecl) << 0 << (*Field)->getDeclName(); Diag((*Field)->getLocation(), diag::note_declared_at); HadError = true; } else if (FT.isConstQualified()) { - Diag(Constructor->getLocation(), diag::err_unintialized_member_in_ctor) + Diag(Constructor->getLocation(), diag::err_uninitialized_member_in_ctor) << (int)IsImplicitConstructor << Context.getTagDeclType(ClassDecl) << 1 << (*Field)->getDeclName(); Diag((*Field)->getLocation(), diag::note_declared_at); @@ -1659,7 +1678,8 @@ static void *GetKeyForMember(CXXBaseOrMemberInitializer *Member, /// ActOnMemInitializers - Handle the member initializers for a constructor. void Sema::ActOnMemInitializers(DeclPtrTy ConstructorDecl, SourceLocation ColonLoc, - MemInitTy **MemInits, unsigned NumMemInits) { + MemInitTy **MemInits, unsigned NumMemInits, + bool AnyErrors) { if (!ConstructorDecl) return; @@ -1709,7 +1729,7 @@ void Sema::ActOnMemInitializers(DeclPtrTy ConstructorDecl, SetBaseOrMemberInitializers(Constructor, reinterpret_cast(MemInits), - NumMemInits, false); + NumMemInits, false, AnyErrors); if (Constructor->isDependentContext()) return; @@ -1860,7 +1880,7 @@ void Sema::ActOnDefaultCtorInitializers(DeclPtrTy CDtorDecl) { if (CXXConstructorDecl *Constructor = dyn_cast(CDtorDecl.getAs())) - SetBaseOrMemberInitializers(Constructor, 0, 0, false); + SetBaseOrMemberInitializers(Constructor, 0, 0, false, false); } namespace { @@ -3673,13 +3693,16 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, = cast(Constructor->getDeclContext()); assert(ClassDecl && "DefineImplicitDefaultConstructor - invalid constructor"); - if (SetBaseOrMemberInitializers(Constructor, 0, 0, true)) { + DeclContext *PreviousContext = CurContext; + CurContext = Constructor; + if (SetBaseOrMemberInitializers(Constructor, 0, 0, true, false)) { Diag(CurrentLocation, diag::note_member_synthesized_at) << CXXDefaultConstructor << Context.getTagDeclType(ClassDecl); Constructor->setInvalidDecl(); } else { Constructor->setUsed(); } + CurContext = PreviousContext; } void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, @@ -3688,6 +3711,10 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, "DefineImplicitDestructor - call it for implicit default dtor"); CXXRecordDecl *ClassDecl = Destructor->getParent(); assert(ClassDecl && "DefineImplicitDestructor - invalid destructor"); + + DeclContext *PreviousContext = CurContext; + CurContext = Destructor; + // C++ [class.dtor] p5 // Before the implicitly-declared default destructor for a class is // implicitly defined, all the implicitly-declared default destructors @@ -3734,8 +3761,11 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, << CXXDestructor << Context.getTagDeclType(ClassDecl); Destructor->setInvalidDecl(); + CurContext = PreviousContext; + return; } + CurContext = PreviousContext; Destructor->setUsed(); } @@ -3750,6 +3780,9 @@ void Sema::DefineImplicitOverloadedAssign(SourceLocation CurrentLocation, CXXRecordDecl *ClassDecl = cast(MethodDecl->getDeclContext()); + DeclContext *PreviousContext = CurContext; + CurContext = MethodDecl; + // C++[class.copy] p12 // Before the implicitly-declared copy assignment operator for a class is // implicitly defined, all implicitly-declared copy assignment operators @@ -3793,6 +3826,8 @@ void Sema::DefineImplicitOverloadedAssign(SourceLocation CurrentLocation, } if (!err) MethodDecl->setUsed(); + + CurContext = PreviousContext; } CXXMethodDecl * @@ -3835,6 +3870,10 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation, CXXRecordDecl *ClassDecl = cast(CopyConstructor->getDeclContext()); assert(ClassDecl && "DefineImplicitCopyConstructor - invalid constructor"); + + DeclContext *PreviousContext = CurContext; + CurContext = CopyConstructor; + // C++ [class.copy] p209 // Before the implicitly-declared copy constructor for a class is // implicitly defined, all the implicitly-declared copy constructors @@ -3863,13 +3902,16 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation, } } CopyConstructor->setUsed(); + + CurContext = PreviousContext; } Sema::OwningExprResult Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, CXXConstructorDecl *Constructor, MultiExprArg ExprArgs, - bool RequiresZeroInit) { + bool RequiresZeroInit, + bool BaseInitialization) { bool Elidable = false; // C++ [class.copy]p15: @@ -3902,7 +3944,8 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, } return BuildCXXConstructExpr(ConstructLoc, DeclInitType, Constructor, - Elidable, move(ExprArgs), RequiresZeroInit); + Elidable, move(ExprArgs), RequiresZeroInit, + BaseInitialization); } /// BuildCXXConstructExpr - Creates a complete call to a constructor, @@ -3911,14 +3954,15 @@ Sema::OwningExprResult Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, CXXConstructorDecl *Constructor, bool Elidable, MultiExprArg ExprArgs, - bool RequiresZeroInit) { + bool RequiresZeroInit, + bool BaseInitialization) { unsigned NumExprs = ExprArgs.size(); Expr **Exprs = (Expr **)ExprArgs.release(); MarkDeclarationReferenced(ConstructLoc, Constructor); return Owned(CXXConstructExpr::Create(Context, DeclInitType, ConstructLoc, Constructor, Elidable, Exprs, NumExprs, - RequiresZeroInit)); + RequiresZeroInit, BaseInitialization)); } Sema::OwningExprResult diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index b0dee9c691..5269167df2 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2562,7 +2562,18 @@ static void TryConstructorInitialization(Sema &S, Result); return; } - + + // C++0x [dcl.init]p6: + // If a program calls for the default initialization of an object + // of a const-qualified type T, T shall be a class type with a + // user-provided default constructor. + if (Kind.getKind() == InitializationKind::IK_Default && + Entity.getType().isConstQualified() && + cast(Best->Function)->isImplicit()) { + Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); + return; + } + // Add the constructor initialization step. Any cv-qualification conversion is // subsumed by the initialization. if (Kind.getKind() == InitializationKind::IK_Copy) { @@ -2635,9 +2646,6 @@ static void TryDefaultInitialization(Sema &S, // constructor for T is called (and the initialization is ill-formed if // T has no accessible default constructor); if (DestType->isRecordType()) { - // FIXME: If a program calls for the default initialization of an object of - // a const-qualified type T, T shall be a class type with a user-provided - // default constructor. return TryConstructorInitialization(S, Entity, Kind, 0, 0, DestType, Sequence); } @@ -3408,7 +3416,8 @@ InitializationSequence::Perform(Sema &S, CurInit = S.BuildCXXConstructExpr(Loc, Entity.getType(), Constructor, move_arg(ConstructorArgs), - ConstructorInitRequiresZeroInit); + ConstructorInitRequiresZeroInit, + Entity.getKind() == InitializedEntity::EK_Base); if (CurInit.isInvalid()) return S.ExprError(); @@ -3488,8 +3497,13 @@ bool InitializationSequence::Diagnose(Sema &S, QualType DestType = Entity.getType(); switch (Failure) { case FK_TooManyInitsForReference: - S.Diag(Kind.getLocation(), diag::err_reference_has_multiple_inits) - << SourceRange(Args[0]->getLocStart(), Args[NumArgs - 1]->getLocEnd()); + // FIXME: Customize for the initialized entity? + if (NumArgs == 0) + S.Diag(Kind.getLocation(), diag::err_reference_without_init) + << DestType.getNonReferenceType(); + else // FIXME: diagnostic below could be better! + S.Diag(Kind.getLocation(), diag::err_reference_has_multiple_inits) + << SourceRange(Args[0]->getLocStart(), Args[NumArgs - 1]->getLocEnd()); break; case FK_ArrayNeedsInitList: @@ -3634,6 +3648,45 @@ bool InitializationSequence::Diagnose(Sema &S, break; case OR_No_Viable_Function: + if (Kind.getKind() == InitializationKind::IK_Default && + (Entity.getKind() == InitializedEntity::EK_Base || + Entity.getKind() == InitializedEntity::EK_Member) && + isa(S.CurContext)) { + // This is implicit default initialization of a member or + // base within a constructor. If no viable function was + // found, notify the user that she needs to explicitly + // initialize this base/member. + CXXConstructorDecl *Constructor + = cast(S.CurContext); + if (Entity.getKind() == InitializedEntity::EK_Base) { + S.Diag(Kind.getLocation(), diag::err_missing_default_ctor) + << Constructor->isImplicit() + << S.Context.getTypeDeclType(Constructor->getParent()) + << /*base=*/0 + << Entity.getType(); + + RecordDecl *BaseDecl + = Entity.getBaseSpecifier()->getType()->getAs() + ->getDecl(); + S.Diag(BaseDecl->getLocation(), diag::note_previous_decl) + << S.Context.getTagDeclType(BaseDecl); + } else { + S.Diag(Kind.getLocation(), diag::err_missing_default_ctor) + << Constructor->isImplicit() + << S.Context.getTypeDeclType(Constructor->getParent()) + << /*member=*/1 + << Entity.getName(); + S.Diag(Entity.getDecl()->getLocation(), diag::note_field_decl); + + if (const RecordType *Record + = Entity.getType()->getAs()) + S.Diag(Record->getDecl()->getLocation(), + diag::note_previous_decl) + << S.Context.getTagDeclType(Record->getDecl()); + } + break; + } + S.Diag(Kind.getLocation(), diag::err_ovl_no_viable_function_in_init) << DestType << ArgsRange; S.PrintOverloadCandidates(FailedCandidateSet, Sema::OCD_AllCandidates, @@ -3664,8 +3717,23 @@ bool InitializationSequence::Diagnose(Sema &S, } case FK_DefaultInitOfConst: - S.Diag(Kind.getLocation(), diag::err_default_init_const) - << DestType; + if (Entity.getKind() == InitializedEntity::EK_Member && + isa(S.CurContext)) { + // This is implicit default-initialization of a const member in + // a constructor. Complain that it needs to be explicitly + // initialized. + CXXConstructorDecl *Constructor = cast(S.CurContext); + S.Diag(Kind.getLocation(), diag::err_uninitialized_member_in_ctor) + << Constructor->isImplicit() + << S.Context.getTypeDeclType(Constructor->getParent()) + << /*const=*/1 + << Entity.getName(); + S.Diag(Entity.getDecl()->getLocation(), diag::note_previous_decl) + << Entity.getName(); + } else { + S.Diag(Kind.getLocation(), diag::err_default_init_const) + << DestType << (bool)DestType->getAs(); + } break; } diff --git a/lib/Sema/SemaInit.h b/lib/Sema/SemaInit.h index 0b72fd4532..001ba91d54 100644 --- a/lib/Sema/SemaInit.h +++ b/lib/Sema/SemaInit.h @@ -66,7 +66,6 @@ public: /// \brief The entity being initialized is an element of a vector. /// or vector. EK_VectorElement - }; private: @@ -95,8 +94,8 @@ private: /// base class. CXXBaseSpecifier *Base; - /// \brief When Kind = EK_ArrayOrVectorElement, the index of the - /// array or vector element being initialized. + /// \brief When Kind = EK_ArrayElement or EK_VectorElement, the + /// index of the array or vector element being initialized. unsigned Index; }; @@ -201,6 +200,12 @@ public: /// initialized. DeclaratorDecl *getDecl() const; + /// \brief Retrieve the base specifier. + CXXBaseSpecifier *getBaseSpecifier() const { + assert(getKind() == EK_Base && "Not a base specifier"); + return Base; + } + /// \brief Determine the location of the 'return' keyword when initializing /// the result of a function call. SourceLocation getReturnLoc() const { diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index a2f1f63ed8..d526962323 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1830,7 +1830,8 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New, const MultiLevelTemplateArgumentList &TemplateArgs) { llvm::SmallVector NewInits; - + bool AnyErrors = false; + // Instantiate all the initializers. for (CXXConstructorDecl::init_const_iterator Inits = Tmpl->init_begin(), InitsEnd = Tmpl->init_end(); @@ -1838,26 +1839,38 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New, CXXBaseOrMemberInitializer *Init = *Inits; ASTOwningVector<&ActionBase::DeleteExpr> NewArgs(*this); + llvm::SmallVector CommaLocs; // Instantiate all the arguments. - for (ExprIterator Args = Init->arg_begin(), ArgsEnd = Init->arg_end(); - Args != ArgsEnd; ++Args) { - OwningExprResult NewArg = SubstExpr(*Args, TemplateArgs); - - if (NewArg.isInvalid()) - New->setInvalidDecl(); - else - NewArgs.push_back(NewArg.takeAs()); + Expr *InitE = Init->getInit(); + if (!InitE) { + // Nothing to instantiate; + } else if (ParenListExpr *ParenList = dyn_cast(InitE)) { + if (InstantiateInitializationArguments(*this, ParenList->getExprs(), + ParenList->getNumExprs(), + TemplateArgs, CommaLocs, + NewArgs)) { + AnyErrors = true; + continue; + } + } else { + OwningExprResult InitArg = SubstExpr(InitE, TemplateArgs); + if (InitArg.isInvalid()) { + AnyErrors = true; + continue; + } + + NewArgs.push_back(InitArg.release()); } - + MemInitResult NewInit; - if (Init->isBaseInitializer()) { TypeSourceInfo *BaseTInfo = SubstType(Init->getBaseClassInfo(), TemplateArgs, Init->getSourceLocation(), New->getDeclName()); if (!BaseTInfo) { + AnyErrors = true; New->setInvalidDecl(); continue; } @@ -1885,9 +1898,10 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New, Init->getRParenLoc()); } - if (NewInit.isInvalid()) + if (NewInit.isInvalid()) { + AnyErrors = true; New->setInvalidDecl(); - else { + } else { // FIXME: It would be nice if ASTOwningVector had a release function. NewArgs.take(); @@ -1899,7 +1913,8 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New, ActOnMemInitializers(DeclPtrTy::make(New), /*FIXME: ColonLoc */ SourceLocation(), - NewInits.data(), NewInits.size()); + NewInits.data(), NewInits.size(), + AnyErrors); } // TODO: this could be templated if the various decl types used the diff --git a/test/CXX/dcl.decl/dcl.init/p6.cpp b/test/CXX/dcl.decl/dcl.init/p6.cpp new file mode 100644 index 0000000000..370bafcfbd --- /dev/null +++ b/test/CXX/dcl.decl/dcl.init/p6.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// FIXME: Very incomplete! + +// If a program calls for the default initialization of an object of a +// const-qualified type T, T shall be a class type with a +// user-provided default constructor. +struct NoUserDefault { }; +struct HasUserDefault { HasUserDefault(); }; + +void test_const_default_init() { + const NoUserDefault x1; // expected-error{{default initialization of an object of const type 'struct NoUserDefault const' requires a user-provided default constructor}} + const HasUserDefault x2; + const int x3; // FIXME: xpected-error{{default initialization of an object of const type 'struct NoUserDefault const' requires a user-provided default constructor}} +} diff --git a/test/SemaCXX/constructor-initializer.cpp b/test/SemaCXX/constructor-initializer.cpp index 53f057ed0f..2efb7b9c21 100644 --- a/test/SemaCXX/constructor-initializer.cpp +++ b/test/SemaCXX/constructor-initializer.cpp @@ -104,8 +104,8 @@ struct M { // expected-note 2 {{candidate constructor (the implicit }; struct N : M { - N() : M(1), // expected-error {{no matching constructor for initialization of 'M'}} - m1(100) { } // expected-error {{no matching constructor for initialization of 'm1'}} + N() : M(1), // expected-error {{no matching constructor for initialization of 'struct M'}} + m1(100) { } // expected-error {{no matching constructor for initialization of 'struct M'}} M m1; }; @@ -116,8 +116,8 @@ struct P : M { }; struct Q { - Q() : f1(1,2), // expected-error {{Too many arguments for member initializer 'f1'}} - pf(0.0) { } // expected-error {{incompatible type passing 'double', expected 'float *'}} + Q() : f1(1,2), // expected-error {{excess elements in scalar initializer}} + pf(0.0) { } // expected-error {{cannot initialize a member subobject of type 'float *' with an rvalue of type 'double'}} float f1; float *pf; diff --git a/test/SemaCXX/copy-assignment.cpp b/test/SemaCXX/copy-assignment.cpp index 315e29a5b5..d7eb5cf1e6 100644 --- a/test/SemaCXX/copy-assignment.cpp +++ b/test/SemaCXX/copy-assignment.cpp @@ -47,22 +47,22 @@ struct ConvertibleToInt { void test() { A a, na; - const A constA; + const A constA = A(); ConvertibleToA convertibleToA; ConvertibleToConstA convertibleToConstA; B b, nb; - const B constB; + const B constB = B(); ConvertibleToB convertibleToB; ConvertibleToBref convertibleToBref; ConvertibleToConstB convertibleToConstB; ConvertibleToConstBref convertibleToConstBref; C c, nc; - const C constC; + const C constC = C(); D d, nd; - const D constD; + const D constD = D(); ConvertibleToInt convertibleToInt; diff --git a/test/SemaCXX/illegal-member-initialization.cpp b/test/SemaCXX/illegal-member-initialization.cpp index 1890dbc9b5..be5f91d51a 100644 --- a/test/SemaCXX/illegal-member-initialization.cpp +++ b/test/SemaCXX/illegal-member-initialization.cpp @@ -1,9 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s struct A { - A() : value(), cvalue() { } // expected-error {{cannot initialize the member to null in default constructor because reference member 'value' cannot be null-initialized}} \ - // expected-error {{constructor for 'struct A' must explicitly initialize the reference member 'value'}} - int &value; // expected-note{{declared at}} {{expected-note{{declared at}} + A() : value(), cvalue() { } // expected-error {{reference to type 'int' requires an initializer}} + int &value; const int cvalue; }; @@ -18,7 +17,7 @@ struct X { int &value; // expected-note{{declared at}} const int cvalue; // expected-note{{declared at}} B& b; // expected-note{{declared at}} - const B cb; // expected-note{{declared at}} + const B cb; // expected-note{{declared here}} }; diff --git a/test/SemaCXX/overload-member-call.cpp b/test/SemaCXX/overload-member-call.cpp index 22416f3ea4..77d9965ab7 100644 --- a/test/SemaCXX/overload-member-call.cpp +++ b/test/SemaCXX/overload-member-call.cpp @@ -89,7 +89,7 @@ namespace test1 { A a; a.foo(4, "hello"); //expected-error {{no matching member function for call to 'foo'}} - const A b; + const A b = A(); b.bar(0); //expected-error {{no matching member function for call to 'bar'}} a.baz(b); //expected-error {{no matching member function for call to 'baz'}} diff --git a/test/SemaCXX/overloaded-operator.cpp b/test/SemaCXX/overloaded-operator.cpp index 861d679c72..e07afe2020 100644 --- a/test/SemaCXX/overloaded-operator.cpp +++ b/test/SemaCXX/overloaded-operator.cpp @@ -344,7 +344,7 @@ namespace pr5900 { int operator[](unsigned); // expected-note {{candidate}} }; int test1() { - const NonConstArray x; + const NonConstArray x = NonConstArray(); return x[0]; // expected-error {{no viable overloaded operator[] for type}} } diff --git a/test/SemaTemplate/instantiate-member-initializers.cpp b/test/SemaTemplate/instantiate-member-initializers.cpp index f7b7e47e1e..eecb445ea9 100644 --- a/test/SemaTemplate/instantiate-member-initializers.cpp +++ b/test/SemaTemplate/instantiate-member-initializers.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -Wall -verify %s template struct A { - A() : a(1) { } // expected-error{{incompatible type passing 'int', expected 'void *'}} + A() : a(1) { } // expected-error{{cannot initialize a member subobject of type 'void *' with an rvalue of type 'int'}} T a; };