From: Sebastian Redl Date: Tue, 30 Aug 2011 19:58:05 +0000 (+0000) Subject: Declare and define implicit move constructor and assignment operator. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=85ea7aa961deac1d754f610af8062ae3f8b4e2a5;p=clang Declare and define implicit move constructor and assignment operator. This makes the code duplication of implicit special member handling even worse, but the cleanup will have to come later. For now, this works. Follow-up with tests for explicit defaulting and enabling the __has_feature flag to come. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@138821 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 3ac6534030..43454bec50 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -468,6 +468,14 @@ class CXXRecordDecl : public RecordDecl { /// \brief Whether we have already declared a destructor within the class. bool DeclaredDestructor : 1; + /// \brief Whether an implicit move constructor was attempted to be declared + /// but would have been deleted. + bool FailedImplicitMoveConstructor : 1; + + /// \brief Whether an implicit move assignment operator was attempted to be + /// declared but would have been deleted. + bool FailedImplicitMoveAssignment : 1; + /// NumBases - The number of base class specifiers in Bases. unsigned NumBases; @@ -780,6 +788,33 @@ public: return data().DeclaredMoveConstructor; } + /// \brief Determine whether implicit move constructor generation for this + /// class has failed before. + bool hasFailedImplicitMoveConstructor() const { + return data().FailedImplicitMoveConstructor; + } + + /// \brief Set whether implicit move constructor generation for this class + /// has failed before. + void setFailedImplicitMoveConstructor(bool Failed = true) { + data().FailedImplicitMoveConstructor = Failed; + } + + /// \brief Determine whether this class should get an implicit move + /// constructor or if any existing special member function inhibits this. + /// + /// Covers all bullets of C++0x [class.copy]p9 except the last, that the + /// constructor wouldn't be deleted, which is only looked up from a cached + /// result. + bool needsImplicitMoveConstructor() const { + return !hasFailedImplicitMoveConstructor() && + !hasDeclaredMoveConstructor() && + !hasUserDeclaredCopyConstructor() && + !hasUserDeclaredCopyAssignment() && + !hasUserDeclaredMoveAssignment() && + !hasUserDeclaredDestructor(); + } + /// hasUserDeclaredCopyAssignment - Whether this class has a /// user-declared copy assignment operator. When false, a copy /// assigment operator will be implicitly declared. @@ -807,6 +842,33 @@ public: return data().DeclaredMoveAssignment; } + /// \brief Determine whether implicit move assignment generation for this + /// class has failed before. + bool hasFailedImplicitMoveAssignment() const { + return data().FailedImplicitMoveAssignment; + } + + /// \brief Set whether implicit move assignment generation for this class + /// has failed before. + void setFailedImplicitMoveAssignment(bool Failed = true) { + data().FailedImplicitMoveAssignment = Failed; + } + + /// \brief Determine whether this class should get an implicit move + /// assignment operator or if any existing special member function inhibits + /// this. + /// + /// Covers all bullets of C++0x [class.copy]p20 except the last, that the + /// constructor wouldn't be deleted. + bool needsImplicitMoveAssignment() const { + return !hasFailedImplicitMoveAssignment() && + !hasDeclaredMoveAssignment() && + !hasUserDeclaredCopyConstructor() && + !hasUserDeclaredCopyAssignment() && + !hasUserDeclaredMoveConstructor() && + !hasUserDeclaredDestructor(); + } + /// hasUserDeclaredDestructor - Whether this class has a /// user-declared destructor. When false, a destructor will be /// implicitly declared. diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index d0d69651e2..8a826e592a 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -866,8 +866,9 @@ public: /// type other than void. bool isCForbiddenLValueType() const; - /// \brief Determine whether this type has trivial copy-assignment semantics. - bool hasTrivialCopyAssignment(ASTContext &Context) const; + /// \brief Determine whether this type has trivial copy/move-assignment + /// semantics. + bool hasTrivialAssignment(ASTContext &Context, bool Copying) const; private: // These methods are implemented in a separate translation unit; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 607bf57411..50b73984df 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4213,6 +4213,32 @@ def err_defaulted_copy_assign_const_param : Error< def err_defaulted_copy_assign_quals : Error< "an explicitly-defaulted copy assignment operator may not have 'const' " "or 'volatile' qualifiers">; +def err_defaulted_move_ctor_params : Error< + "an explicitly-defaulted move constructor must have exactly one parameter">; +def err_defaulted_move_ctor_volatile_param : Error< + "the parameter for an explicitly-defaulted move constructor may not be " + "volatile">; +def err_defaulted_move_ctor_const_param : Error< + "the parameter for an explicitly-defaulted move constructor may not be " + "const">; +def err_defaulted_move_assign_params : Error< + "an explicitly-defaulted move assignment operator must have exactly one " + "parameter">; +def err_defaulted_move_assign_return_type : Error< + "an explicitly-defaulted move assignment operator must return an unqualified " + "lvalue reference to its class type">; +def err_defaulted_move_assign_not_ref : Error< + "the parameter for an explicitly-defaulted move assignment operator must be an " + "rvalue reference type">; +def err_defaulted_move_assign_volatile_param : Error< + "the parameter for an explicitly-defaulted move assignment operator may not " + "be volatile">; +def err_defaulted_move_assign_const_param : Error< + "the parameter for an explicitly-defaulted move assignment operator may not " + "be const">; +def err_defaulted_move_assign_quals : Error< + "an explicitly-defaulted move assignment operator may not have 'const' " + "or 'volatile' qualifiers">; def err_incorrect_defaulted_exception_spec : Error< "exception specification of explicitly defaulted %select{default constructor|" "copy constructor|move constructor|copy assignment operator|move assignment " @@ -4222,8 +4248,6 @@ def err_out_of_line_default_deletes : Error< "defaulting this %select{default constructor|copy constructor|move " "constructor|copy assignment operator|move assignment operator|destructor}0 " "would delete it after its first declaration">; -def err_defaulted_move_unsupported : Error< - "defaulting move functions not yet supported">; def warn_ptr_arith_precedes_bounds : Warning< "the pointer decremented by %0 refers before the beginning of the array">, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 34c70125a7..df64349b22 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1708,6 +1708,9 @@ public: CXXMethodDecl *LookupCopyingAssignment(CXXRecordDecl *Class, unsigned Quals, bool RValueThis, unsigned ThisQuals, bool *ConstParam = 0); + CXXConstructorDecl *LookupMovingConstructor(CXXRecordDecl *Class); + CXXMethodDecl *LookupMovingAssignment(CXXRecordDecl *Class, bool RValueThis, + unsigned ThisQuals); CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); void ArgumentDependentLookup(DeclarationName Name, bool Operator, @@ -2759,6 +2762,16 @@ public: std::pair ComputeDefaultedCopyAssignmentExceptionSpecAndConst(CXXRecordDecl *ClassDecl); + /// \brief Determine what sort of exception specification a defaulted move + /// constructor of a class will have. + ImplicitExceptionSpecification + ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl); + + /// \brief Determine what sort of exception specification a defaulted move + /// assignment operator of a class will have. + ImplicitExceptionSpecification + ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl); + /// \brief Determine what sort of exception specification a defaulted /// destructor of a class will have. ImplicitExceptionSpecification @@ -2776,6 +2789,13 @@ public: /// deleted. bool ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD); + /// \brief Determine if a defaulted move constructor ought to be deleted. + bool ShouldDeleteMoveConstructor(CXXConstructorDecl *CD); + + /// \brief Determine if a defaulted move assignment operator ought to be + /// deleted. + bool ShouldDeleteMoveAssignmentOperator(CXXMethodDecl *MD); + /// \brief Determine if a defaulted destructor ought to be deleted. bool ShouldDeleteDestructor(CXXDestructorDecl *DD); @@ -2821,9 +2841,6 @@ public: /// \brief Declare the implicit copy constructor for the given class. /// - /// \param S The scope of the class, which may be NULL if this is a - /// template instantiation. - /// /// \param ClassDecl The class declaration into which the implicit /// copy constructor will be added. /// @@ -2835,21 +2852,45 @@ public: void DefineImplicitCopyConstructor(SourceLocation CurrentLocation, CXXConstructorDecl *Constructor); - /// \brief Declare the implicit copy assignment operator for the given class. + /// \brief Declare the implicit move constructor for the given class. /// - /// \param S The scope of the class, which may be NULL if this is a - /// template instantiation. + /// \param ClassDecl The Class declaration into which the implicit + /// move constructor will be added. + /// + /// \returns The implicitly-declared move constructor, or NULL if it wasn't + /// declared. + CXXConstructorDecl *DeclareImplicitMoveConstructor(CXXRecordDecl *ClassDecl); + + /// DefineImplicitMoveConstructor - Checks for feasibility of + /// defining this constructor as the move constructor. + void DefineImplicitMoveConstructor(SourceLocation CurrentLocation, + CXXConstructorDecl *Constructor); + + /// \brief Declare the implicit copy assignment operator for the given class. /// /// \param ClassDecl The class declaration into which the implicit - /// copy-assignment operator will be added. + /// copy assignment operator will be added. /// /// \returns The implicitly-declared copy assignment operator. CXXMethodDecl *DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl); - /// \brief Defined an implicitly-declared copy assignment operator. + /// \brief Defines an implicitly-declared copy assignment operator. void DefineImplicitCopyAssignment(SourceLocation CurrentLocation, CXXMethodDecl *MethodDecl); + /// \brief Declare the implicit move assignment operator for the given class. + /// + /// \param ClassDecl The Class declaration into which the implicit + /// move assignment operator will be added. + /// + /// \returns The implicitly-declared move assignment operator, or NULL if it + /// wasn't declared. + CXXMethodDecl *DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl); + + /// \brief Defines an implicitly-declared move assignment operator. + void DefineImplicitMoveAssignment(SourceLocation CurrentLocation, + CXXMethodDecl *MethodDecl); + /// \brief Force the declaration of any implicitly-declared members of this /// class. void ForceDeclarationOfImplicitMembers(CXXRecordDecl *Class); @@ -3475,6 +3516,8 @@ public: void CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *Ctor); void CheckExplicitlyDefaultedCopyConstructor(CXXConstructorDecl *Ctor); void CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *Method); + void CheckExplicitlyDefaultedMoveConstructor(CXXConstructorDecl *Ctor); + void CheckExplicitlyDefaultedMoveAssignment(CXXMethodDecl *Method); void CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *Dtor); //===--------------------------------------------------------------------===// diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 518210a3f7..be529f85a6 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -42,8 +42,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) UserProvidedDefaultConstructor(false), DeclaredDefaultConstructor(false), DeclaredCopyConstructor(false), DeclaredMoveConstructor(false), DeclaredCopyAssignment(false), DeclaredMoveAssignment(false), - DeclaredDestructor(false), NumBases(0), NumVBases(0), Bases(), VBases(), - Definition(D), FirstFriend(0) { + DeclaredDestructor(false), FailedImplicitMoveConstructor(false), + FailedImplicitMoveAssignment(false), NumBases(0), NumVBases(0), Bases(), + VBases(), Definition(D), FirstFriend(0) { } CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index e7888a6aa7..624e9d2944 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -393,7 +393,7 @@ static Cl::Kinds ClassifyUnnamed(ASTContext &Ctx, QualType T) { // C++ [expr.call]p10: A function call is an lvalue if the result type is an // lvalue reference type or an rvalue reference to function type, an xvalue - // if the result type is an rvalue refernence to object type, and a prvalue + // if the result type is an rvalue reference to object type, and a prvalue // otherwise. if (T->isLValueReferenceType()) return Cl::CL_LValue; diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a2cfe546de..16aeb45162 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2273,7 +2273,7 @@ QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) { return DK_none; } -bool QualType::hasTrivialCopyAssignment(ASTContext &Context) const { +bool QualType::hasTrivialAssignment(ASTContext &Context, bool Copying) const { switch (getObjCLifetime()) { case Qualifiers::OCL_None: break; @@ -2289,7 +2289,8 @@ bool QualType::hasTrivialCopyAssignment(ASTContext &Context) const { if (const CXXRecordDecl *Record = getTypePtr()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl()) - return Record->hasTrivialCopyAssignment(); + return Copying ? Record->hasTrivialCopyAssignment() : + Record->hasTrivialMoveAssignment(); return true; } diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 8bdfbf1231..6e0e1d418f 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -527,6 +527,12 @@ namespace { } }; } + +static bool hasTrivialCopyOrMoveConstructor(const CXXRecordDecl *Record, + bool Moving) { + return Moving ? Record->hasTrivialMoveConstructor() : + Record->hasTrivialCopyConstructor(); +} static void EmitMemberInitializer(CodeGenFunction &CGF, const CXXRecordDecl *ClassDecl, @@ -572,7 +578,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, const ConstantArrayType *Array = CGF.getContext().getAsConstantArrayType(FieldType); if (Array && Constructor->isImplicit() && - Constructor->isCopyConstructor()) { + Constructor->isCopyOrMoveConstructor()) { llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType()); @@ -595,7 +601,8 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, // constructors, perform a single aggregate copy. const CXXRecordDecl *Record = BaseElementTy->getAsCXXRecordDecl(); if (BaseElementTy.isPODType(CGF.getContext()) || - (Record && Record->hasTrivialCopyConstructor())) { + (Record && hasTrivialCopyOrMoveConstructor(Record, + Constructor->isMoveConstructor()))) { // Find the source pointer. We knows it's the last argument because // we know we're in a copy constructor. unsigned SrcArgIndex = Args.size() - 1; @@ -1201,7 +1208,8 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, } assert(ArgBeg + 1 == ArgEnd && "unexpected argcount for trivial ctor"); - assert(D->isCopyConstructor() && "trivial 1-arg ctor not a copy ctor"); + assert(D->isCopyOrMoveConstructor() && + "trivial 1-arg ctor not a copy/move ctor"); const Expr *E = (*ArgBeg); QualType Ty = E->getType(); @@ -1223,7 +1231,8 @@ CodeGenFunction::EmitSynthesizedCXXCopyCtorCall(const CXXConstructorDecl *D, CallExpr::const_arg_iterator ArgEnd) { if (D->isTrivial()) { assert(ArgBeg + 1 == ArgEnd && "unexpected argcount for trivial ctor"); - assert(D->isCopyConstructor() && "trivial 1-arg ctor not a copy ctor"); + assert(D->isCopyOrMoveConstructor() && + "trivial 1-arg ctor not a copy/move ctor"); EmitAggregateCopy(This, Src, (*ArgBeg)->getType()); return; } diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index b638e5ba0d..acc9a2b036 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -206,16 +206,17 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, cast(MD)->isDefaultConstructor()) return RValue::get(0); - if (MD->isCopyAssignmentOperator()) { - // We don't like to generate the trivial copy assignment operator when - // it isn't necessary; just produce the proper effect here. + if (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()) { + // We don't like to generate the trivial copy/move assignment operator + // when it isn't necessary; just produce the proper effect here. llvm::Value *RHS = EmitLValue(*CE->arg_begin()).getAddress(); EmitAggregateCopy(This, RHS, CE->getType()); return RValue::get(This); } if (isa(MD) && - cast(MD)->isCopyConstructor()) { + cast(MD)->isCopyOrMoveConstructor()) { + // Trivial move and copy ctor are the same. llvm::Value *RHS = EmitLValue(*CE->arg_begin()).getAddress(); EmitSynthesizedCXXCopyCtorCall(cast(MD), This, RHS, CE->arg_begin(), CE->arg_end()); diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index d2e5bc2a24..1ac0c58d2a 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -789,8 +789,8 @@ public: if (E->getNumArgs()) { assert(E->getNumArgs() == 1 && "trivial ctor with > 1 argument"); - assert(E->getConstructor()->isCopyConstructor() && - "trivial ctor has argument but isn't a copy ctor"); + assert(E->getConstructor()->isCopyOrMoveConstructor() && + "trivial ctor has argument but isn't a copy/move ctor"); Expr *Arg = E->getArg(0); assert(CGM.getContext().hasSameUnqualifiedType(Ty, Arg->getType()) && diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 428316297c..cd4023eeda 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1860,6 +1860,19 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, EllipsisLoc); } +// Create a static_cast\(expr). +static Expr *CastForMoving(Sema &SemaRef, Expr *E) { + QualType ExprType = E->getType(); + QualType TargetType = SemaRef.Context.getRValueReferenceType(ExprType); + SourceLocation ExprLoc = E->getLocStart(); + TypeSourceInfo *TargetLoc = SemaRef.Context.getTrivialTypeSourceInfo( + TargetType, ExprLoc); + + return SemaRef.BuildCXXNamedCast(ExprLoc, tok::kw_static_cast, TargetLoc, E, + SourceRange(ExprLoc, ExprLoc), + E->getSourceRange()).take(); +} + /// ImplicitInitializerKind - How an implicit base or member initializer should /// initialize its base or member. enum ImplicitInitializerKind { @@ -1890,7 +1903,9 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, break; } + case IIK_Move: case IIK_Copy: { + bool Moving = ImplicitInitKind == IIK_Move; ParmVarDecl *Param = Constructor->getParamDecl(0); QualType ParamType = Param->getType().getNonReferenceType(); @@ -1898,17 +1913,22 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), Param, Constructor->getLocation(), ParamType, VK_LValue, 0); - + // Cast to the base class to avoid ambiguities. QualType ArgTy = SemaRef.Context.getQualifiedType(BaseSpec->getType().getUnqualifiedType(), ParamType.getQualifiers()); + if (Moving) { + CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg); + } + CXXCastPath BasePath; BasePath.push_back(BaseSpec); CopyCtorArg = SemaRef.ImpCastExprToType(CopyCtorArg, ArgTy, CK_UncheckedDerivedToBase, - VK_LValue, &BasePath).take(); + Moving ? VK_RValue : VK_LValue, + &BasePath).take(); InitializationKind InitKind = InitializationKind::CreateDirect(Constructor->getLocation(), @@ -1919,9 +1939,6 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, MultiExprArg(&CopyCtorArg, 1)); break; } - - case IIK_Move: - assert(false && "Unhandled initializer kind!"); } BaseInit = SemaRef.MaybeCreateExprWithCleanups(BaseInit); @@ -1941,6 +1958,11 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, return false; } +static bool RefersToRValueRef(Expr *MemRef) { + ValueDecl *Referenced = cast(MemRef)->getMemberDecl(); + return Referenced->getType()->isRValueReferenceType(); +} + static bool BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, ImplicitInitializerKind ImplicitInitKind, @@ -1951,7 +1973,8 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, SourceLocation Loc = Constructor->getLocation(); - if (ImplicitInitKind == IIK_Copy) { + if (ImplicitInitKind == IIK_Copy || ImplicitInitKind == IIK_Move) { + bool Moving = ImplicitInitKind == IIK_Move; ParmVarDecl *Param = Constructor->getParamDecl(0); QualType ParamType = Param->getType().getNonReferenceType(); @@ -1964,11 +1987,16 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), Param, Loc, ParamType, VK_LValue, 0); + if (Moving) { + MemberExprBase = CastForMoving(SemaRef, MemberExprBase); + } + // Build a reference to this field within the parameter. CXXScopeSpec SS; LookupResult MemberLookup(SemaRef, Field->getDeclName(), Loc, Sema::LookupMemberName); - MemberLookup.addDecl(Indirect? cast(Indirect) : cast(Field), AS_public); + MemberLookup.addDecl(Indirect ? cast(Indirect) + : cast(Field), AS_public); MemberLookup.resolveKind(); ExprResult CopyCtorArg = SemaRef.BuildMemberReferenceExpr(MemberExprBase, @@ -1980,7 +2008,14 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, /*TemplateArgs=*/0); if (CopyCtorArg.isInvalid()) return true; - + + // C++11 [class.copy]p15: + // - if a member m has rvalue reference type T&&, it is direct-initialized + // with static_cast(x.m); + if (RefersToRValueRef(CopyCtorArg.get())) { + CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg.take()); + } + // When the field we are copying is an array, create index variables for // each dimension of the array. We use these index variables to subscript // the source array, and other clients (e.g., CodeGen) will perform the @@ -1988,8 +2023,10 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, SmallVector IndexVariables; QualType BaseType = Field->getType(); QualType SizeType = SemaRef.Context.getSizeType(); + bool InitializingArray = false; while (const ConstantArrayType *Array = SemaRef.Context.getAsConstantArrayType(BaseType)) { + InitializingArray = true; // Create the iteration variable for this array index. IdentifierInfo *IterationVarName = 0; { @@ -2018,10 +2055,14 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, Loc); if (CopyCtorArg.isInvalid()) return true; - + BaseType = Array->getElementType(); } - + + // The array subscript expression is an lvalue, which is wrong for moving. + if (Moving && InitializingArray) + CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg.take()); + // Construct the entity that we will be initializing. For an array, this // will be first element in the array, which may require several levels // of array-subscript entities. @@ -2154,10 +2195,11 @@ struct BaseAndFieldInfo { BaseAndFieldInfo(Sema &S, CXXConstructorDecl *Ctor, bool ErrorsInInits) : S(S), Ctor(Ctor), AnyErrorsInInits(ErrorsInInits) { - // FIXME: Handle implicit move constructors. - if ((Ctor->isImplicit() || Ctor->isDefaulted()) && - Ctor->isCopyConstructor()) + bool Generated = Ctor->isImplicit() || Ctor->isDefaulted(); + if (Generated && Ctor->isCopyConstructor()) IIK = IIK_Copy; + else if (Generated && Ctor->isMoveConstructor()) + IIK = IIK_Move; else IIK = IIK_Default; } @@ -2344,7 +2386,7 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, continue; } - // If we're not generating the implicit copy constructor, then we'll + // If we're not generating the implicit copy/move constructor, then we'll // handle anonymous struct/union fields based on their individual // indirect fields. if (F->isAnonymousStructOrUnion() && Info.IIK == IIK_Default) @@ -3158,12 +3200,14 @@ void Sema::CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record) { break; case CXXMoveConstructor: + CheckExplicitlyDefaultedMoveConstructor(cast(*MI)); + break; + case CXXMoveAssignment: - Diag(MI->getLocation(), diag::err_defaulted_move_unsupported); + CheckExplicitlyDefaultedMoveAssignment(*MI); break; - default: - // FIXME: Do moves once they exist + case CXXInvalid: llvm_unreachable("non-special member explicitly defaulted!"); } } @@ -3332,7 +3376,7 @@ void Sema::CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *MD) { Context.VoidTy, 0, 0, EPI)->getAs(); QualType ArgType = OperType->getArgType(0); - if (!ArgType->isReferenceType()) { + if (!ArgType->isLValueReferenceType()) { Diag(MD->getLocation(), diag::err_defaulted_copy_assign_not_ref); HadError = true; } else { @@ -3384,6 +3428,155 @@ void Sema::CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *MD) { } } +void Sema::CheckExplicitlyDefaultedMoveConstructor(CXXConstructorDecl *CD) { + assert(CD->isExplicitlyDefaulted() && CD->isMoveConstructor()); + + // Whether this was the first-declared instance of the constructor. + bool First = CD == CD->getCanonicalDecl(); + + bool HadError = false; + if (CD->getNumParams() != 1) { + Diag(CD->getLocation(), diag::err_defaulted_move_ctor_params) + << CD->getSourceRange(); + HadError = true; + } + + ImplicitExceptionSpecification Spec( + ComputeDefaultedMoveCtorExceptionSpec(CD->getParent())); + + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + const FunctionProtoType *CtorType = CD->getType()->getAs(), + *ExceptionType = Context.getFunctionType( + Context.VoidTy, 0, 0, EPI)->getAs(); + + // Check for parameter type matching. + // This is a move ctor so we know it's a cv-qualified rvalue reference to T. + QualType ArgType = CtorType->getArgType(0); + if (ArgType->getPointeeType().isVolatileQualified()) { + Diag(CD->getLocation(), diag::err_defaulted_move_ctor_volatile_param); + HadError = true; + } + if (ArgType->getPointeeType().isConstQualified()) { + Diag(CD->getLocation(), diag::err_defaulted_move_ctor_const_param); + HadError = true; + } + + if (CtorType->hasExceptionSpec()) { + if (CheckEquivalentExceptionSpec( + PDiag(diag::err_incorrect_defaulted_exception_spec) + << CXXMoveConstructor, + PDiag(), + ExceptionType, SourceLocation(), + CtorType, CD->getLocation())) { + HadError = true; + } + } else if (First) { + // We set the declaration to have the computed exception spec here. + // We duplicate the one parameter type. + EPI.ExtInfo = CtorType->getExtInfo(); + CD->setType(Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI)); + } + + if (HadError) { + CD->setInvalidDecl(); + return; + } + + if (ShouldDeleteMoveConstructor(CD)) { + if (First) { + CD->setDeletedAsWritten(); + } else { + Diag(CD->getLocation(), diag::err_out_of_line_default_deletes) + << CXXMoveConstructor; + CD->setInvalidDecl(); + } + } +} + +void Sema::CheckExplicitlyDefaultedMoveAssignment(CXXMethodDecl *MD) { + assert(MD->isExplicitlyDefaulted()); + + // Whether this was the first-declared instance of the operator + bool First = MD == MD->getCanonicalDecl(); + + bool HadError = false; + if (MD->getNumParams() != 1) { + Diag(MD->getLocation(), diag::err_defaulted_move_assign_params) + << MD->getSourceRange(); + HadError = true; + } + + QualType ReturnType = + MD->getType()->getAs()->getResultType(); + if (!ReturnType->isLValueReferenceType() || + !Context.hasSameType( + Context.getCanonicalType(ReturnType->getPointeeType()), + Context.getCanonicalType(Context.getTypeDeclType(MD->getParent())))) { + Diag(MD->getLocation(), diag::err_defaulted_move_assign_return_type); + HadError = true; + } + + ImplicitExceptionSpecification Spec( + ComputeDefaultedMoveCtorExceptionSpec(MD->getParent())); + + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + const FunctionProtoType *OperType = MD->getType()->getAs(), + *ExceptionType = Context.getFunctionType( + Context.VoidTy, 0, 0, EPI)->getAs(); + + QualType ArgType = OperType->getArgType(0); + if (!ArgType->isRValueReferenceType()) { + Diag(MD->getLocation(), diag::err_defaulted_move_assign_not_ref); + HadError = true; + } else { + if (ArgType->getPointeeType().isVolatileQualified()) { + Diag(MD->getLocation(), diag::err_defaulted_move_assign_volatile_param); + HadError = true; + } + if (ArgType->getPointeeType().isConstQualified()) { + Diag(MD->getLocation(), diag::err_defaulted_move_assign_const_param); + HadError = true; + } + } + + if (OperType->getTypeQuals()) { + Diag(MD->getLocation(), diag::err_defaulted_move_assign_quals); + HadError = true; + } + + if (OperType->hasExceptionSpec()) { + if (CheckEquivalentExceptionSpec( + PDiag(diag::err_incorrect_defaulted_exception_spec) + << CXXMoveAssignment, + PDiag(), + ExceptionType, SourceLocation(), + OperType, MD->getLocation())) { + HadError = true; + } + } else if (First) { + // We set the declaration to have the computed exception spec here. + // We duplicate the one parameter type. + EPI.RefQualifier = OperType->getRefQualifier(); + EPI.ExtInfo = OperType->getExtInfo(); + MD->setType(Context.getFunctionType(ReturnType, &ArgType, 1, EPI)); + } + + if (HadError) { + MD->setInvalidDecl(); + return; + } + + if (ShouldDeleteMoveAssignmentOperator(MD)) { + if (First) { + MD->setDeletedAsWritten(); + } else { + Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) + << CXXMoveAssignment; + MD->setInvalidDecl(); + } + } +} + void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) { assert(DD->isExplicitlyDefaulted()); @@ -3786,7 +3979,7 @@ bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { // FIXME: We should put some diagnostic logic right into this function. - // C++0x [class.copy]/11 + // C++0x [class.copy]/20 // A defaulted [copy] assignment operator for class X is defined as deleted // if X has: @@ -3878,35 +4071,40 @@ bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { CXXMethodDecl *CopyOper = LookupCopyingAssignment(FieldRecord, ArgQuals, false, 0); if (!CopyOper || CopyOper->isDeleted()) - return false; + return true; if (CheckDirectMemberAccess(Loc, CopyOper, PDiag()) != AR_accessible) - return false; + return true; } } return false; } -bool Sema::ShouldDeleteDestructor(CXXDestructorDecl *DD) { - CXXRecordDecl *RD = DD->getParent(); +bool Sema::ShouldDeleteMoveConstructor(CXXConstructorDecl *CD) { + CXXRecordDecl *RD = CD->getParent(); assert(!RD->isDependentType() && "do deletion after instantiation"); if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) return false; - SourceLocation Loc = DD->getLocation(); + SourceLocation Loc = CD->getLocation(); - // Do access control from the destructor - ContextRAII CtorContext(*this, DD); + // Do access control from the constructor + ContextRAII CtorContext(*this, CD); bool Union = RD->isUnion(); + assert(!CD->getParamDecl(0)->getType()->getPointeeType().isNull() && + "copy assignment arg has no pointee type"); + // We do this because we should never actually use an anonymous - // union's destructor. + // union's constructor. if (Union && RD->isAnonymousStructOrUnion()) return false; - // C++0x [class.dtor]p5 - // A defaulted destructor for a class X is defined as deleted if: + // C++0x [class.copy]/11 + // A defaulted [move] constructor for class X is defined as deleted + // if X has: + for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), BE = RD->bases_end(); BI != BE; ++BI) { @@ -3914,67 +4112,344 @@ bool Sema::ShouldDeleteDestructor(CXXDestructorDecl *DD) { if (BI->isVirtual()) continue; - CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); - CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); - assert(BaseDtor && "base has no destructor"); + QualType BaseType = BI->getType(); + CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl(); + assert(BaseDecl && "base isn't a CXXRecordDecl"); - // -- any direct or virtual base class has a deleted destructor or - // a destructor that is inaccessible from the defaulted destructor + // -- any [direct base class] of a type with a destructor that is deleted or + // inaccessible from the defaulted constructor + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); if (BaseDtor->isDeleted()) return true; if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) != AR_accessible) return true; + + // -- a [direct base class] B that cannot be [moved] because overload + // resolution, as applied to B's [move] constructor, results in an + // ambiguity or a function that is deleted or inaccessible from the + // defaulted constructor + CXXConstructorDecl *BaseCtor = LookupMovingConstructor(BaseDecl); + if (!BaseCtor || BaseCtor->isDeleted()) + return true; + if (CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(), PDiag()) != + AR_accessible) + return true; + + // -- for a move constructor, a [direct base class] with a type that + // does not have a move constructor and is not trivially copyable. + // If the field isn't a record, it's always trivially copyable. + // A moving constructor could be a copy constructor instead. + if (!BaseCtor->isMoveConstructor() && + !BaseDecl->isTriviallyCopyable()) + return true; } for (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(), BE = RD->vbases_end(); BI != BE; ++BI) { - CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); - CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); - assert(BaseDtor && "base has no destructor"); + QualType BaseType = BI->getType(); + CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl(); + assert(BaseDecl && "base isn't a CXXRecordDecl"); - // -- any direct or virtual base class has a deleted destructor or - // a destructor that is inaccessible from the defaulted destructor + // -- any [virtual base class] of a type with a destructor that is deleted + // or inaccessible from the defaulted constructor + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); if (BaseDtor->isDeleted()) return true; if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) != AR_accessible) return true; + + // -- a [virtual base class] B that cannot be [moved] because overload + // resolution, as applied to B's [move] constructor, results in an + // ambiguity or a function that is deleted or inaccessible from the + // defaulted constructor + CXXConstructorDecl *BaseCtor = LookupMovingConstructor(BaseDecl); + if (!BaseCtor || BaseCtor->isDeleted()) + return true; + if (CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(), PDiag()) != + AR_accessible) + return true; + + // -- for a move constructor, a [virtual base class] with a type that + // does not have a move constructor and is not trivially copyable. + // If the field isn't a record, it's always trivially copyable. + // A moving constructor could be a copy constructor instead. + if (!BaseCtor->isMoveConstructor() && + !BaseDecl->isTriviallyCopyable()) + return true; } for (CXXRecordDecl::field_iterator FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { QualType FieldType = Context.getBaseElementType(FI->getType()); - CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl(); - if (FieldRecord) { + + if (CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl()) { + // This is an anonymous union if (FieldRecord->isUnion() && FieldRecord->isAnonymousStructOrUnion()) { - for (CXXRecordDecl::field_iterator UI = FieldRecord->field_begin(), - UE = FieldRecord->field_end(); - UI != UE; ++UI) { - QualType UnionFieldType = Context.getBaseElementType(FI->getType()); - CXXRecordDecl *UnionFieldRecord = - UnionFieldType->getAsCXXRecordDecl(); + // Anonymous unions inside unions do not variant members create + if (!Union) { + for (CXXRecordDecl::field_iterator UI = FieldRecord->field_begin(), + UE = FieldRecord->field_end(); + UI != UE; ++UI) { + QualType UnionFieldType = Context.getBaseElementType(UI->getType()); + CXXRecordDecl *UnionFieldRecord = + UnionFieldType->getAsCXXRecordDecl(); - // -- X is a union-like class that has a variant member with a non- - // trivial destructor. - if (UnionFieldRecord && !UnionFieldRecord->hasTrivialDestructor()) - return true; - } - // Technically we are supposed to do this next check unconditionally. - // But that makes absolutely no sense. + // -- a variant member with a non-trivial [move] constructor and X + // is a union-like class + if (UnionFieldRecord && + !UnionFieldRecord->hasTrivialMoveConstructor()) + return true; + } + } + + // Don't try to initalize an anonymous union + continue; } else { - CXXDestructorDecl *FieldDtor = LookupDestructor(FieldRecord); + // -- a variant member with a non-trivial [move] constructor and X is a + // union-like class + if (Union && !FieldRecord->hasTrivialMoveConstructor()) + return true; - // -- any of the non-static data members has class type M (or array - // thereof) and M has a deleted destructor or a destructor that is - // inaccessible from the defaulted destructor + // -- any [non-static data member] of a type with a destructor that is + // deleted or inaccessible from the defaulted constructor + CXXDestructorDecl *FieldDtor = LookupDestructor(FieldRecord); if (FieldDtor->isDeleted()) return true; if (CheckDestructorAccess(Loc, FieldDtor, PDiag()) != - AR_accessible) - return true; + AR_accessible) + return true; + } + + // -- a [non-static data member of class type (or array thereof)] B that + // cannot be [moved] because overload resolution, as applied to B's + // [move] constructor, results in an ambiguity or a function that is + // deleted or inaccessible from the defaulted constructor + CXXConstructorDecl *FieldCtor = LookupMovingConstructor(FieldRecord); + if (!FieldCtor || FieldCtor->isDeleted()) + return true; + if (CheckConstructorAccess(Loc, FieldCtor, FieldCtor->getAccess(), + PDiag()) != AR_accessible) + return true; + + // -- for a move constructor, a [non-static data member] with a type that + // does not have a move constructor and is not trivially copyable. + // If the field isn't a record, it's always trivially copyable. + // A moving constructor could be a copy constructor instead. + if (!FieldCtor->isMoveConstructor() && + !FieldRecord->isTriviallyCopyable()) + return true; + } + } + + return false; +} + +bool Sema::ShouldDeleteMoveAssignmentOperator(CXXMethodDecl *MD) { + CXXRecordDecl *RD = MD->getParent(); + assert(!RD->isDependentType() && "do deletion after instantiation"); + if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) + return false; + + SourceLocation Loc = MD->getLocation(); + + // Do access control from the constructor + ContextRAII MethodContext(*this, MD); + + bool Union = RD->isUnion(); + + // We do this because we should never actually use an anonymous + // union's constructor. + if (Union && RD->isAnonymousStructOrUnion()) + return false; + + // C++0x [class.copy]/20 + // A defaulted [move] assignment operator for class X is defined as deleted + // if X has: + + // -- for the move constructor, [...] any direct or indirect virtual base + // class. + if (RD->getNumVBases() != 0) + return true; + + for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), + BE = RD->bases_end(); + BI != BE; ++BI) { + + QualType BaseType = BI->getType(); + CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl(); + assert(BaseDecl && "base isn't a CXXRecordDecl"); + + // -- a [direct base class] B that cannot be [moved] because overload + // resolution, as applied to B's [move] assignment operator, results in + // an ambiguity or a function that is deleted or inaccessible from the + // assignment operator + CXXMethodDecl *MoveOper = LookupMovingAssignment(BaseDecl, false, 0); + if (!MoveOper || MoveOper->isDeleted()) + return true; + if (CheckDirectMemberAccess(Loc, MoveOper, PDiag()) != AR_accessible) + return true; + + // -- for the move assignment operator, a [direct base class] with a type + // that does not have a move assignment operator and is not trivially + // copyable. + if (!MoveOper->isMoveAssignmentOperator() && + !BaseDecl->isTriviallyCopyable()) + return true; + } + + for (CXXRecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); + FI != FE; ++FI) { + QualType FieldType = Context.getBaseElementType(FI->getType()); + + // -- a non-static data member of reference type + if (FieldType->isReferenceType()) + return true; + + // -- a non-static data member of const non-class type (or array thereof) + if (FieldType.isConstQualified() && !FieldType->isRecordType()) + return true; + + CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl(); + + if (FieldRecord) { + // This is an anonymous union + if (FieldRecord->isUnion() && FieldRecord->isAnonymousStructOrUnion()) { + // Anonymous unions inside unions do not variant members create + if (!Union) { + for (CXXRecordDecl::field_iterator UI = FieldRecord->field_begin(), + UE = FieldRecord->field_end(); + UI != UE; ++UI) { + QualType UnionFieldType = Context.getBaseElementType(UI->getType()); + CXXRecordDecl *UnionFieldRecord = + UnionFieldType->getAsCXXRecordDecl(); + + // -- a variant member with a non-trivial [move] assignment operator + // and X is a union-like class + if (UnionFieldRecord && + !UnionFieldRecord->hasTrivialMoveAssignment()) + return true; + } + } + + // Don't try to initalize an anonymous union + continue; + // -- a variant member with a non-trivial [move] assignment operator + // and X is a union-like class + } else if (Union && !FieldRecord->hasTrivialMoveAssignment()) { + return true; + } + + CXXMethodDecl *MoveOper = LookupMovingAssignment(FieldRecord, false, 0); + if (!MoveOper || MoveOper->isDeleted()) + return true; + if (CheckDirectMemberAccess(Loc, MoveOper, PDiag()) != AR_accessible) + return true; + + // -- for the move assignment operator, a [non-static data member] with a + // type that does not have a move assignment operator and is not + // trivially copyable. + if (!MoveOper->isMoveAssignmentOperator() && + !FieldRecord->isTriviallyCopyable()) + return true; + } + } + + return false; +} + +bool Sema::ShouldDeleteDestructor(CXXDestructorDecl *DD) { + CXXRecordDecl *RD = DD->getParent(); + assert(!RD->isDependentType() && "do deletion after instantiation"); + if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) + return false; + + SourceLocation Loc = DD->getLocation(); + + // Do access control from the destructor + ContextRAII CtorContext(*this, DD); + + bool Union = RD->isUnion(); + + // We do this because we should never actually use an anonymous + // union's destructor. + if (Union && RD->isAnonymousStructOrUnion()) + return false; + + // C++0x [class.dtor]p5 + // A defaulted destructor for a class X is defined as deleted if: + for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), + BE = RD->bases_end(); + BI != BE; ++BI) { + // We'll handle this one later + if (BI->isVirtual()) + continue; + + CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); + assert(BaseDtor && "base has no destructor"); + + // -- any direct or virtual base class has a deleted destructor or + // a destructor that is inaccessible from the defaulted destructor + if (BaseDtor->isDeleted()) + return true; + if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) != + AR_accessible) + return true; + } + + for (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(), + BE = RD->vbases_end(); + BI != BE; ++BI) { + CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); + assert(BaseDtor && "base has no destructor"); + + // -- any direct or virtual base class has a deleted destructor or + // a destructor that is inaccessible from the defaulted destructor + if (BaseDtor->isDeleted()) + return true; + if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) != + AR_accessible) + return true; + } + + for (CXXRecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); + FI != FE; ++FI) { + QualType FieldType = Context.getBaseElementType(FI->getType()); + CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl(); + if (FieldRecord) { + if (FieldRecord->isUnion() && FieldRecord->isAnonymousStructOrUnion()) { + for (CXXRecordDecl::field_iterator UI = FieldRecord->field_begin(), + UE = FieldRecord->field_end(); + UI != UE; ++UI) { + QualType UnionFieldType = Context.getBaseElementType(FI->getType()); + CXXRecordDecl *UnionFieldRecord = + UnionFieldType->getAsCXXRecordDecl(); + + // -- X is a union-like class that has a variant member with a non- + // trivial destructor. + if (UnionFieldRecord && !UnionFieldRecord->hasTrivialDestructor()) + return true; + } + // Technically we are supposed to do this next check unconditionally. + // But that makes absolutely no sense. + } else { + CXXDestructorDecl *FieldDtor = LookupDestructor(FieldRecord); + + // -- any of the non-static data members has class type M (or array + // thereof) and M has a deleted destructor or a destructor that is + // inaccessible from the defaulted destructor + if (FieldDtor->isDeleted()) + return true; + if (CheckDestructorAccess(Loc, FieldDtor, PDiag()) != + AR_accessible) + return true; // -- X is a union-like class that has a variant member with a non- // trivial destructor. @@ -6452,41 +6927,45 @@ void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *classDecl, // However, we don't have a body yet, so it needs to be done somewhere else. } -/// \brief Builds a statement that copies the given entity from \p From to +/// \brief Builds a statement that copies/moves the given entity from \p From to /// \c To. /// -/// This routine is used to copy the members of a class with an -/// implicitly-declared copy assignment operator. When the entities being +/// This routine is used to copy/move the members of a class with an +/// implicitly-declared copy/move assignment operator. When the entities being /// copied are arrays, this routine builds for loops to copy them. /// /// \param S The Sema object used for type-checking. /// -/// \param Loc The location where the implicit copy is being generated. +/// \param Loc The location where the implicit copy/move is being generated. /// -/// \param T The type of the expressions being copied. Both expressions must -/// have this type. +/// \param T The type of the expressions being copied/moved. Both expressions +/// must have this type. /// -/// \param To The expression we are copying to. +/// \param To The expression we are copying/moving to. /// -/// \param From The expression we are copying from. +/// \param From The expression we are copying/moving from. /// -/// \param CopyingBaseSubobject Whether we're copying a base subobject. +/// \param CopyingBaseSubobject Whether we're copying/moving a base subobject. /// Otherwise, it's a non-static member subobject. /// +/// \param Copying Whether we're copying or moving. +/// /// \param Depth Internal parameter recording the depth of the recursion. /// /// \returns A statement or a loop that copies the expressions. static StmtResult BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, Expr *To, Expr *From, - bool CopyingBaseSubobject, unsigned Depth = 0) { - // C++0x [class.copy]p30: + bool CopyingBaseSubobject, bool Copying, + unsigned Depth = 0) { + // C++0x [class.copy]p28: // Each subobject is assigned in the manner appropriate to its type: // - // - if the subobject is of class type, the copy assignment operator - // for the class is used (as if by explicit qualification; that is, - // ignoring any possible virtual overriding functions in more derived - // classes); + // - if the subobject is of class type, as if by a call to operator= with + // the subobject as the object expression and the corresponding + // subobject of x as a single function argument (as if by explicit + // qualification; that is, ignoring any possible virtual overriding + // functions in more derived classes); if (const RecordType *RecordTy = T->getAs()) { CXXRecordDecl *ClassDecl = cast(RecordTy->getDecl()); @@ -6496,14 +6975,15 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, LookupResult OpLookup(S, Name, Loc, Sema::LookupOrdinaryName); S.LookupQualifiedName(OpLookup, ClassDecl, false); - // Filter out any result that isn't a copy-assignment operator. + // Filter out any result that isn't a copy/move-assignment operator. LookupResult::Filter F = OpLookup.makeFilter(); while (F.hasNext()) { NamedDecl *D = F.next(); if (CXXMethodDecl *Method = dyn_cast(D)) - if (Method->isCopyAssignmentOperator()) + if (Copying ? Method->isCopyAssignmentOperator() : + Method->isMoveAssignmentOperator()) continue; - + F.erase(); } F.done(); @@ -6622,11 +7102,13 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, IterationVarRef, Loc)); To = AssertSuccess(S.CreateBuiltinArraySubscriptExpr(To, Loc, IterationVarRef, Loc)); - - // Build the copy for an individual element of the array. + if (!Copying) // Cast to rvalue + From = CastForMoving(S, From); + + // Build the copy/move for an individual element of the array. StmtResult Copy = BuildSingleCopyAssign(S, Loc, ArrayTy->getElementType(), To, From, CopyingBaseSubobject, - Depth + 1); + Copying, Depth + 1); if (Copy.isInvalid()) return StmtError(); @@ -6906,7 +7388,8 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, // Build the copy. StmtResult Copy = BuildSingleCopyAssign(*this, Loc, BaseType, To.get(), From, - /*CopyingBaseSubobject=*/true); + /*CopyingBaseSubobject=*/true, + /*Copying=*/true); if (Copy.isInvalid()) { Diag(CurrentLocation, diag::note_member_synthesized_at) << CXXCopyAssignment << Context.getTagDeclType(ClassDecl); @@ -6982,7 +7465,7 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, // of scalars and arrays of class type with trivial copy-assignment // operators. if (FieldType->isArrayType() && !FieldType.isVolatileQualified() - && BaseType.hasTrivialCopyAssignment(Context)) { + && BaseType.hasTrivialAssignment(Context, /*Copying=*/true)) { // Compute the size of the memory buffer to be copied. QualType SizeType = Context.getSizeType(); llvm::APInt Size(Context.getTypeSize(SizeType), @@ -7069,8 +7552,9 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, // Build the copy of this field. StmtResult Copy = BuildSingleCopyAssign(*this, Loc, FieldType, - To.get(), From.get(), - /*CopyingBaseSubobject=*/false); + To.get(), From.get(), + /*CopyingBaseSubobject=*/false, + /*Copying=*/true); if (Copy.isInvalid()) { Diag(CurrentLocation, diag::note_member_synthesized_at) << CXXCopyAssignment << Context.getTagDeclType(ClassDecl); @@ -7115,69 +7599,491 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, } } -std::pair -Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { +Sema::ImplicitExceptionSpecification +Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { + ImplicitExceptionSpecification ExceptSpec(Context); + if (ClassDecl->isInvalidDecl()) - return std::make_pair(ImplicitExceptionSpecification(Context), false); + return ExceptSpec; - // C++ [class.copy]p5: - // The implicitly-declared copy constructor for a class X will - // have the form - // - // X::X(const X&) - // - // if - // FIXME: It ought to be possible to store this on the record. - bool HasConstCopyConstructor = true; - - // -- each direct or virtual base class B of X has a copy - // constructor whose first parameter is of type const B& or - // const volatile B&, and + // C++0x [except.spec]p14: + // An implicitly declared special member function (Clause 12) shall have an + // exception-specification. [...] + + // It is unspecified whether or not an implicit move assignment operator + // attempts to deduplicate calls to assignment operators of virtual bases are + // made. As such, this exception specification is effectively unspecified. + // Based on a similar decision made for constness in C++0x, we're erring on + // the side of assuming such calls to be made regardless of whether they + // actually happen. + // Note that a move constructor is not implicitly declared when there are + // virtual bases, but it can still be user-declared and explicitly defaulted. for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); - HasConstCopyConstructor && Base != BaseEnd; - ++Base) { - // Virtual bases are handled below. + Base != BaseEnd; ++Base) { if (Base->isVirtual()) continue; - + CXXRecordDecl *BaseClassDecl = cast(Base->getType()->getAs()->getDecl()); - LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const, - &HasConstCopyConstructor); + if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl, + false, 0)) + ExceptSpec.CalledDecl(MoveAssign); } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), BaseEnd = ClassDecl->vbases_end(); - HasConstCopyConstructor && Base != BaseEnd; - ++Base) { + Base != BaseEnd; ++Base) { CXXRecordDecl *BaseClassDecl = cast(Base->getType()->getAs()->getDecl()); - LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const, - &HasConstCopyConstructor); + if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl, + false, 0)) + ExceptSpec.CalledDecl(MoveAssign); } - - // -- for all the nonstatic data members of X that are of a - // class type M (or array thereof), each such class type - // has a copy constructor whose first parameter is of type - // const M& or const volatile M&. + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); - HasConstCopyConstructor && Field != FieldEnd; + Field != FieldEnd; ++Field) { QualType FieldType = Context.getBaseElementType((*Field)->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { - LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const, - &HasConstCopyConstructor); + if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(FieldClassDecl, + false, 0)) + ExceptSpec.CalledDecl(MoveAssign); } } - // Otherwise, the implicitly declared copy constructor will have - // the form - // - // X::X(X&) - - // C++ [except.spec]p14: - // An implicitly declared special member function (Clause 12) shall have an + + return ExceptSpec; +} + +CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { + // Note: The following rules are largely analoguous to the move + // constructor rules. + + ImplicitExceptionSpecification Spec( + ComputeDefaultedMoveAssignmentExceptionSpec(ClassDecl)); + + QualType ArgType = Context.getTypeDeclType(ClassDecl); + QualType RetType = Context.getLValueReferenceType(ArgType); + ArgType = Context.getRValueReferenceType(ArgType); + + // An implicitly-declared move assignment operator is an inline public + // member of its class. + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); + SourceLocation ClassLoc = ClassDecl->getLocation(); + DeclarationNameInfo NameInfo(Name, ClassLoc); + CXXMethodDecl *MoveAssignment + = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, + Context.getFunctionType(RetType, &ArgType, 1, EPI), + /*TInfo=*/0, /*isStatic=*/false, + /*StorageClassAsWritten=*/SC_None, + /*isInline=*/true, + /*isConstexpr=*/false, + SourceLocation()); + MoveAssignment->setAccess(AS_public); + MoveAssignment->setDefaulted(); + MoveAssignment->setImplicit(); + MoveAssignment->setTrivial(ClassDecl->hasTrivialMoveAssignment()); + + // Add the parameter to the operator. + ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveAssignment, + ClassLoc, ClassLoc, /*Id=*/0, + ArgType, /*TInfo=*/0, + SC_None, + SC_None, 0); + MoveAssignment->setParams(&FromParam, 1); + + // Note that we have added this copy-assignment operator. + ++ASTContext::NumImplicitMoveAssignmentOperatorsDeclared; + + // C++0x [class.copy]p9: + // If the definition of a class X does not explicitly declare a move + // assignment operator, one will be implicitly declared as defaulted if and + // only if: + // [...] + // - the move assignment operator would not be implicitly defined as + // deleted. + if (ShouldDeleteMoveAssignmentOperator(MoveAssignment)) { + // Cache this result so that we don't try to generate this over and over + // on every lookup, leaking memory and wasting time. + ClassDecl->setFailedImplicitMoveAssignment(); + return 0; + } + + if (Scope *S = getScopeForContext(ClassDecl)) + PushOnScopeChains(MoveAssignment, S, false); + ClassDecl->addDecl(MoveAssignment); + + AddOverriddenMethods(ClassDecl, MoveAssignment); + return MoveAssignment; +} + +void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, + CXXMethodDecl *MoveAssignOperator) { + assert((MoveAssignOperator->isDefaulted() && + MoveAssignOperator->isOverloadedOperator() && + MoveAssignOperator->getOverloadedOperator() == OO_Equal && + !MoveAssignOperator->doesThisDeclarationHaveABody()) && + "DefineImplicitMoveAssignment called for wrong function"); + + CXXRecordDecl *ClassDecl = MoveAssignOperator->getParent(); + + if (ClassDecl->isInvalidDecl() || MoveAssignOperator->isInvalidDecl()) { + MoveAssignOperator->setInvalidDecl(); + return; + } + + MoveAssignOperator->setUsed(); + + ImplicitlyDefinedFunctionScope Scope(*this, MoveAssignOperator); + DiagnosticErrorTrap Trap(Diags); + + // C++0x [class.copy]p28: + // The implicitly-defined or move assignment operator for a non-union class + // X performs memberwise move assignment of its subobjects. The direct base + // classes of X are assigned first, in the order of their declaration in the + // base-specifier-list, and then the immediate non-static data members of X + // are assigned, in the order in which they were declared in the class + // definition. + + // The statements that form the synthesized function body. + ASTOwningVector Statements(*this); + + // The parameter for the "other" object, which we are move from. + ParmVarDecl *Other = MoveAssignOperator->getParamDecl(0); + QualType OtherRefType = Other->getType()-> + getAs()->getPointeeType(); + assert(OtherRefType.getQualifiers() == 0 && + "Bad argument type of defaulted move assignment"); + + // Our location for everything implicitly-generated. + SourceLocation Loc = MoveAssignOperator->getLocation(); + + // Construct a reference to the "other" object. We'll be using this + // throughout the generated ASTs. + Expr *OtherRef = BuildDeclRefExpr(Other, OtherRefType, VK_LValue, Loc).take(); + assert(OtherRef && "Reference to parameter cannot fail!"); + // Cast to rvalue. + OtherRef = CastForMoving(*this, OtherRef); + + // Construct the "this" pointer. We'll be using this throughout the generated + // ASTs. + Expr *This = ActOnCXXThis(Loc).takeAs(); + assert(This && "Reference to this cannot fail!"); + + // Assign base classes. + bool Invalid = false; + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), + E = ClassDecl->bases_end(); Base != E; ++Base) { + // Form the assignment: + // static_cast(this)->Base::operator=(static_cast(other)); + QualType BaseType = Base->getType().getUnqualifiedType(); + if (!BaseType->isRecordType()) { + Invalid = true; + continue; + } + + CXXCastPath BasePath; + BasePath.push_back(Base); + + // Construct the "from" expression, which is an implicit cast to the + // appropriately-qualified base type. + Expr *From = OtherRef; + From = ImpCastExprToType(From, BaseType, CK_UncheckedDerivedToBase, + VK_RValue, &BasePath).take(); + + // Dereference "this". + ExprResult To = CreateBuiltinUnaryOp(Loc, UO_Deref, This); + + // Implicitly cast "this" to the appropriately-qualified base type. + To = ImpCastExprToType(To.take(), + Context.getCVRQualifiedType(BaseType, + MoveAssignOperator->getTypeQualifiers()), + CK_UncheckedDerivedToBase, + VK_LValue, &BasePath); + + // Build the move. + StmtResult Move = BuildSingleCopyAssign(*this, Loc, BaseType, + To.get(), From, + /*CopyingBaseSubobject=*/true, + /*Copying=*/false); + if (Move.isInvalid()) { + Diag(CurrentLocation, diag::note_member_synthesized_at) + << CXXMoveAssignment << Context.getTagDeclType(ClassDecl); + MoveAssignOperator->setInvalidDecl(); + return; + } + + // Success! Record the move. + Statements.push_back(Move.takeAs()); + } + + // \brief Reference to the __builtin_memcpy function. + Expr *BuiltinMemCpyRef = 0; + // \brief Reference to the __builtin_objc_memmove_collectable function. + Expr *CollectableMemCpyRef = 0; + + // Assign non-static members. + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), + FieldEnd = ClassDecl->field_end(); + Field != FieldEnd; ++Field) { + // Check for members of reference type; we can't move those. + if (Field->getType()->isReferenceType()) { + Diag(ClassDecl->getLocation(), diag::err_uninitialized_member_for_assign) + << Context.getTagDeclType(ClassDecl) << 0 << Field->getDeclName(); + Diag(Field->getLocation(), diag::note_declared_at); + Diag(CurrentLocation, diag::note_member_synthesized_at) + << CXXMoveAssignment << Context.getTagDeclType(ClassDecl); + Invalid = true; + continue; + } + + // Check for members of const-qualified, non-class type. + QualType BaseType = Context.getBaseElementType(Field->getType()); + if (!BaseType->getAs() && BaseType.isConstQualified()) { + Diag(ClassDecl->getLocation(), diag::err_uninitialized_member_for_assign) + << Context.getTagDeclType(ClassDecl) << 1 << Field->getDeclName(); + Diag(Field->getLocation(), diag::note_declared_at); + Diag(CurrentLocation, diag::note_member_synthesized_at) + << CXXMoveAssignment << Context.getTagDeclType(ClassDecl); + Invalid = true; + continue; + } + + // Suppress assigning zero-width bitfields. + if (const Expr *Width = Field->getBitWidth()) + if (Width->EvaluateAsInt(Context) == 0) + continue; + + QualType FieldType = Field->getType().getNonReferenceType(); + if (FieldType->isIncompleteArrayType()) { + assert(ClassDecl->hasFlexibleArrayMember() && + "Incomplete array type is not valid"); + continue; + } + + // Build references to the field in the object we're copying from and to. + CXXScopeSpec SS; // Intentionally empty + LookupResult MemberLookup(*this, Field->getDeclName(), Loc, + LookupMemberName); + MemberLookup.addDecl(*Field); + MemberLookup.resolveKind(); + ExprResult From = BuildMemberReferenceExpr(OtherRef, OtherRefType, + Loc, /*IsArrow=*/false, + SS, 0, MemberLookup, 0); + ExprResult To = BuildMemberReferenceExpr(This, This->getType(), + Loc, /*IsArrow=*/true, + SS, 0, MemberLookup, 0); + assert(!From.isInvalid() && "Implicit field reference cannot fail"); + assert(!To.isInvalid() && "Implicit field reference cannot fail"); + + assert(!From.get()->isLValue() && // could be xvalue or prvalue + "Member reference with rvalue base must be rvalue except for reference " + "members, which aren't allowed for move assignment."); + + // If the field should be copied with __builtin_memcpy rather than via + // explicit assignments, do so. This optimization only applies for arrays + // of scalars and arrays of class type with trivial move-assignment + // operators. + if (FieldType->isArrayType() && !FieldType.isVolatileQualified() + && BaseType.hasTrivialAssignment(Context, /*Copying=*/false)) { + // Compute the size of the memory buffer to be copied. + QualType SizeType = Context.getSizeType(); + llvm::APInt Size(Context.getTypeSize(SizeType), + Context.getTypeSizeInChars(BaseType).getQuantity()); + for (const ConstantArrayType *Array + = Context.getAsConstantArrayType(FieldType); + Array; + Array = Context.getAsConstantArrayType(Array->getElementType())) { + llvm::APInt ArraySize + = Array->getSize().zextOrTrunc(Size.getBitWidth()); + Size *= ArraySize; + } + + // Take the address of the field references for "from" and "to". + From = CreateBuiltinUnaryOp(Loc, UO_AddrOf, From.get()); + To = CreateBuiltinUnaryOp(Loc, UO_AddrOf, To.get()); + + bool NeedsCollectableMemCpy = + (BaseType->isRecordType() && + BaseType->getAs()->getDecl()->hasObjectMember()); + + if (NeedsCollectableMemCpy) { + if (!CollectableMemCpyRef) { + // Create a reference to the __builtin_objc_memmove_collectable function. + LookupResult R(*this, + &Context.Idents.get("__builtin_objc_memmove_collectable"), + Loc, LookupOrdinaryName); + LookupName(R, TUScope, true); + + FunctionDecl *CollectableMemCpy = R.getAsSingle(); + if (!CollectableMemCpy) { + // Something went horribly wrong earlier, and we will have + // complained about it. + Invalid = true; + continue; + } + + CollectableMemCpyRef = BuildDeclRefExpr(CollectableMemCpy, + CollectableMemCpy->getType(), + VK_LValue, Loc, 0).take(); + assert(CollectableMemCpyRef && "Builtin reference cannot fail"); + } + } + // Create a reference to the __builtin_memcpy builtin function. + else if (!BuiltinMemCpyRef) { + LookupResult R(*this, &Context.Idents.get("__builtin_memcpy"), Loc, + LookupOrdinaryName); + LookupName(R, TUScope, true); + + FunctionDecl *BuiltinMemCpy = R.getAsSingle(); + if (!BuiltinMemCpy) { + // Something went horribly wrong earlier, and we will have complained + // about it. + Invalid = true; + continue; + } + + BuiltinMemCpyRef = BuildDeclRefExpr(BuiltinMemCpy, + BuiltinMemCpy->getType(), + VK_LValue, Loc, 0).take(); + assert(BuiltinMemCpyRef && "Builtin reference cannot fail"); + } + + ASTOwningVector CallArgs(*this); + CallArgs.push_back(To.takeAs()); + CallArgs.push_back(From.takeAs()); + CallArgs.push_back(IntegerLiteral::Create(Context, Size, SizeType, Loc)); + ExprResult Call = ExprError(); + if (NeedsCollectableMemCpy) + Call = ActOnCallExpr(/*Scope=*/0, + CollectableMemCpyRef, + Loc, move_arg(CallArgs), + Loc); + else + Call = ActOnCallExpr(/*Scope=*/0, + BuiltinMemCpyRef, + Loc, move_arg(CallArgs), + Loc); + + assert(!Call.isInvalid() && "Call to __builtin_memcpy cannot fail!"); + Statements.push_back(Call.takeAs()); + continue; + } + + // Build the move of this field. + StmtResult Move = BuildSingleCopyAssign(*this, Loc, FieldType, + To.get(), From.get(), + /*CopyingBaseSubobject=*/false, + /*Copying=*/false); + if (Move.isInvalid()) { + Diag(CurrentLocation, diag::note_member_synthesized_at) + << CXXMoveAssignment << Context.getTagDeclType(ClassDecl); + MoveAssignOperator->setInvalidDecl(); + return; + } + + // Success! Record the copy. + Statements.push_back(Move.takeAs()); + } + + if (!Invalid) { + // Add a "return *this;" + ExprResult ThisObj = CreateBuiltinUnaryOp(Loc, UO_Deref, This); + + StmtResult Return = ActOnReturnStmt(Loc, ThisObj.get()); + if (Return.isInvalid()) + Invalid = true; + else { + Statements.push_back(Return.takeAs()); + + if (Trap.hasErrorOccurred()) { + Diag(CurrentLocation, diag::note_member_synthesized_at) + << CXXMoveAssignment << Context.getTagDeclType(ClassDecl); + Invalid = true; + } + } + } + + if (Invalid) { + MoveAssignOperator->setInvalidDecl(); + return; + } + + StmtResult Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements), + /*isStmtExpr=*/false); + assert(!Body.isInvalid() && "Compound statement creation cannot fail"); + MoveAssignOperator->setBody(Body.takeAs()); + + if (ASTMutationListener *L = getASTMutationListener()) { + L->CompletedImplicitDefinition(MoveAssignOperator); + } +} + +std::pair +Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { + if (ClassDecl->isInvalidDecl()) + return std::make_pair(ImplicitExceptionSpecification(Context), false); + + // C++ [class.copy]p5: + // The implicitly-declared copy constructor for a class X will + // have the form + // + // X::X(const X&) + // + // if + // FIXME: It ought to be possible to store this on the record. + bool HasConstCopyConstructor = true; + + // -- each direct or virtual base class B of X has a copy + // constructor whose first parameter is of type const B& or + // const volatile B&, and + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), + BaseEnd = ClassDecl->bases_end(); + HasConstCopyConstructor && Base != BaseEnd; + ++Base) { + // Virtual bases are handled below. + if (Base->isVirtual()) + continue; + + CXXRecordDecl *BaseClassDecl + = cast(Base->getType()->getAs()->getDecl()); + LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const, + &HasConstCopyConstructor); + } + + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), + BaseEnd = ClassDecl->vbases_end(); + HasConstCopyConstructor && Base != BaseEnd; + ++Base) { + CXXRecordDecl *BaseClassDecl + = cast(Base->getType()->getAs()->getDecl()); + LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const, + &HasConstCopyConstructor); + } + + // -- for all the nonstatic data members of X that are of a + // class type M (or array thereof), each such class type + // has a copy constructor whose first parameter is of type + // const M& or const volatile M&. + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), + FieldEnd = ClassDecl->field_end(); + HasConstCopyConstructor && Field != FieldEnd; + ++Field) { + QualType FieldType = Context.getBaseElementType((*Field)->getType()); + if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { + LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const, + &HasConstCopyConstructor); + } + } + // Otherwise, the implicitly declared copy constructor will have + // the form + // + // X::X(X&) + + // C++ [except.spec]p14: + // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] ImplicitExceptionSpecification ExceptSpec(Context); unsigned Quals = HasConstCopyConstructor? Qualifiers::Const : 0; @@ -7322,6 +8228,169 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation, } } +Sema::ImplicitExceptionSpecification +Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { + // C++ [except.spec]p14: + // An implicitly declared special member function (Clause 12) shall have an + // exception-specification. [...] + ImplicitExceptionSpecification ExceptSpec(Context); + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; + + // Direct base-class constructors. + for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(), + BEnd = ClassDecl->bases_end(); + B != BEnd; ++B) { + if (B->isVirtual()) // Handled below. + continue; + + if (const RecordType *BaseType = B->getType()->getAs()) { + CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); + CXXConstructorDecl *Constructor = LookupMovingConstructor(BaseClassDecl); + // If this is a deleted function, add it anyway. This might be conformant + // with the standard. This might not. I'm not sure. It might not matter. + if (Constructor) + ExceptSpec.CalledDecl(Constructor); + } + } + + // Virtual base-class constructors. + for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(), + BEnd = ClassDecl->vbases_end(); + B != BEnd; ++B) { + if (const RecordType *BaseType = B->getType()->getAs()) { + CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); + CXXConstructorDecl *Constructor = LookupMovingConstructor(BaseClassDecl); + // If this is a deleted function, add it anyway. This might be conformant + // with the standard. This might not. I'm not sure. It might not matter. + if (Constructor) + ExceptSpec.CalledDecl(Constructor); + } + } + + // Field constructors. + for (RecordDecl::field_iterator F = ClassDecl->field_begin(), + FEnd = ClassDecl->field_end(); + F != FEnd; ++F) { + 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 = LookupMovingConstructor(FieldRecDecl); + // If this is a deleted function, add it anyway. This might be conformant + // with the standard. This might not. I'm not sure. It might not matter. + // In particular, the problem is that this function never gets called. It + // might just be ill-formed because this function attempts to refer to + // a deleted function here. + if (Constructor) + ExceptSpec.CalledDecl(Constructor); + } + } + + return ExceptSpec; +} + +CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( + CXXRecordDecl *ClassDecl) { + ImplicitExceptionSpecification Spec( + ComputeDefaultedMoveCtorExceptionSpec(ClassDecl)); + + QualType ClassType = Context.getTypeDeclType(ClassDecl); + QualType ArgType = Context.getRValueReferenceType(ClassType); + + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + + DeclarationName Name + = Context.DeclarationNames.getCXXConstructorName( + Context.getCanonicalType(ClassType)); + SourceLocation ClassLoc = ClassDecl->getLocation(); + DeclarationNameInfo NameInfo(Name, ClassLoc); + + // C++0x [class.copy]p11: + // An implicitly-declared copy/move constructor is an inline public + // member of its class. + CXXConstructorDecl *MoveConstructor + = CXXConstructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, + Context.getFunctionType(Context.VoidTy, + &ArgType, 1, EPI), + /*TInfo=*/0, + /*isExplicit=*/false, + /*isInline=*/true, + /*isImplicitlyDeclared=*/true, + // FIXME: apply the rules for definitions here + /*isConstexpr=*/false); + MoveConstructor->setAccess(AS_public); + MoveConstructor->setDefaulted(); + MoveConstructor->setTrivial(ClassDecl->hasTrivialMoveConstructor()); + + // Add the parameter to the constructor. + ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveConstructor, + ClassLoc, ClassLoc, + /*IdentifierInfo=*/0, + ArgType, /*TInfo=*/0, + SC_None, + SC_None, 0); + MoveConstructor->setParams(&FromParam, 1); + + // C++0x [class.copy]p9: + // If the definition of a class X does not explicitly declare a move + // constructor, one will be implicitly declared as defaulted if and only if: + // [...] + // - the move constructor would not be implicitly defined as deleted. + if (ShouldDeleteMoveConstructor(MoveConstructor)) { + // Cache this result so that we don't try to generate this over and over + // on every lookup, leaking memory and wasting time. + ClassDecl->setFailedImplicitMoveConstructor(); + return 0; + } + + // Note that we have declared this constructor. + ++ASTContext::NumImplicitMoveConstructorsDeclared; + + if (Scope *S = getScopeForContext(ClassDecl)) + PushOnScopeChains(MoveConstructor, S, false); + ClassDecl->addDecl(MoveConstructor); + + return MoveConstructor; +} + +void Sema::DefineImplicitMoveConstructor(SourceLocation CurrentLocation, + CXXConstructorDecl *MoveConstructor) { + assert((MoveConstructor->isDefaulted() && + MoveConstructor->isMoveConstructor() && + !MoveConstructor->doesThisDeclarationHaveABody()) && + "DefineImplicitMoveConstructor - call it for implicit move ctor"); + + CXXRecordDecl *ClassDecl = MoveConstructor->getParent(); + assert(ClassDecl && "DefineImplicitMoveConstructor - invalid constructor"); + + ImplicitlyDefinedFunctionScope Scope(*this, MoveConstructor); + DiagnosticErrorTrap Trap(Diags); + + if (SetCtorInitializers(MoveConstructor, 0, 0, /*AnyErrors=*/false) || + Trap.hasErrorOccurred()) { + Diag(CurrentLocation, diag::note_member_synthesized_at) + << CXXMoveConstructor << Context.getTagDeclType(ClassDecl); + MoveConstructor->setInvalidDecl(); + } else { + MoveConstructor->setBody(ActOnCompoundStmt(MoveConstructor->getLocation(), + MoveConstructor->getLocation(), + MultiStmtArg(*this, 0, 0), + /*isStmtExpr=*/false) + .takeAs()); + } + + MoveConstructor->setUsed(); + + if (ASTMutationListener *L = getASTMutationListener()) { + L->CompletedImplicitDefinition(MoveConstructor); + } +} + ExprResult Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, CXXConstructorDecl *Constructor, @@ -8784,13 +9853,23 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { break; } - case CXXMoveConstructor: - case CXXMoveAssignment: - Diag(Dcl->getLocation(), diag::err_defaulted_move_unsupported); + case CXXMoveConstructor: { + CXXConstructorDecl *CD = cast(MD); + CheckExplicitlyDefaultedMoveConstructor(CD); + if (!CD->isInvalidDecl()) + DefineImplicitMoveConstructor(DefaultLoc, CD); + break; + } + + case CXXMoveAssignment: { + CheckExplicitlyDefaultedMoveAssignment(MD); + if (!MD->isInvalidDecl()) + DefineImplicitMoveAssignment(DefaultLoc, MD); break; + } - default: - // FIXME: Do the rest once we have move functions + case CXXInvalid: + assert(false && "Invalid special member."); break; } } else { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index a927e13cfd..d94e10b009 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9154,15 +9154,19 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { // Note that this declaration has been used. if (CXXConstructorDecl *Constructor = dyn_cast(D)) { - if (Constructor->isDefaulted() && Constructor->isDefaultConstructor()) { - if (Constructor->isTrivial()) - return; - if (!Constructor->isUsed(false)) - DefineImplicitDefaultConstructor(Loc, Constructor); - } else if (Constructor->isDefaulted() && - Constructor->isCopyConstructor()) { - if (!Constructor->isUsed(false)) - DefineImplicitCopyConstructor(Loc, Constructor); + if (Constructor->isDefaulted()) { + if (Constructor->isDefaultConstructor()) { + if (Constructor->isTrivial()) + return; + if (!Constructor->isUsed(false)) + DefineImplicitDefaultConstructor(Loc, Constructor); + } else if (Constructor->isCopyConstructor()) { + if (!Constructor->isUsed(false)) + DefineImplicitCopyConstructor(Loc, Constructor); + } else if (Constructor->isMoveConstructor()) { + if (!Constructor->isUsed(false)) + DefineImplicitMoveConstructor(Loc, Constructor); + } } MarkVTableUsed(Loc, Constructor->getParent()); @@ -9174,8 +9178,12 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { } else if (CXXMethodDecl *MethodDecl = dyn_cast(D)) { if (MethodDecl->isDefaulted() && MethodDecl->isOverloadedOperator() && MethodDecl->getOverloadedOperator() == OO_Equal) { - if (!MethodDecl->isUsed(false)) - DefineImplicitCopyAssignment(Loc, MethodDecl); + if (!MethodDecl->isUsed(false)) { + if (MethodDecl->isCopyAssignmentOperator()) + DefineImplicitCopyAssignment(Loc, MethodDecl); + else + DefineImplicitMoveAssignment(Loc, MethodDecl); + } } else if (MethodDecl->isVirtual()) MarkVTableUsed(Loc, MethodDecl->getParent()); } diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 7df049815a..5dbadf7dea 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -550,6 +550,16 @@ void Sema::ForceDeclarationOfImplicitMembers(CXXRecordDecl *Class) { if (!Class->hasDeclaredCopyAssignment()) DeclareImplicitCopyAssignment(Class); + if (getLangOptions().CPlusPlus0x) { + // If the move constructor has not yet been declared, do so now. + if (Class->needsImplicitMoveConstructor()) + DeclareImplicitMoveConstructor(Class); // might not actually do it + + // If the move assignment operator has not yet been declared, do so now. + if (Class->needsImplicitMoveAssignment()) + DeclareImplicitMoveAssignment(Class); // might not actually do it + } + // If the destructor has not yet been declared, do so now. if (!Class->hasDeclaredDestructor()) DeclareImplicitDestructor(Class); @@ -586,11 +596,14 @@ static void DeclareImplicitMemberFunctionsWithName(Sema &S, if (const CXXRecordDecl *Record = dyn_cast(DC)) if (Record->getDefinition() && CanDeclareSpecialMemberFunction(S.Context, Record)) { + CXXRecordDecl *Class = const_cast(Record); if (Record->needsImplicitDefaultConstructor()) - S.DeclareImplicitDefaultConstructor( - const_cast(Record)); + S.DeclareImplicitDefaultConstructor(Class); if (!Record->hasDeclaredCopyConstructor()) - S.DeclareImplicitCopyConstructor(const_cast(Record)); + S.DeclareImplicitCopyConstructor(Class); + if (S.getLangOptions().CPlusPlus0x && + Record->needsImplicitMoveConstructor()) + S.DeclareImplicitMoveConstructor(Class); } break; @@ -605,10 +618,17 @@ static void DeclareImplicitMemberFunctionsWithName(Sema &S, if (Name.getCXXOverloadedOperator() != OO_Equal) break; - if (const CXXRecordDecl *Record = dyn_cast(DC)) - if (Record->getDefinition() && !Record->hasDeclaredCopyAssignment() && - CanDeclareSpecialMemberFunction(S.Context, Record)) - S.DeclareImplicitCopyAssignment(const_cast(Record)); + if (const CXXRecordDecl *Record = dyn_cast(DC)) { + if (Record->getDefinition() && + CanDeclareSpecialMemberFunction(S.Context, Record)) { + CXXRecordDecl *Class = const_cast(Record); + if (!Record->hasDeclaredCopyAssignment()) + S.DeclareImplicitCopyAssignment(Class); + if (S.getLangOptions().CPlusPlus0x && + Record->needsImplicitMoveAssignment()) + S.DeclareImplicitMoveAssignment(Class); + } + } break; default: @@ -2211,12 +2231,14 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD, Name = Context.DeclarationNames.getCXXConstructorName(CanTy); if (!RD->hasDeclaredCopyConstructor()) DeclareImplicitCopyConstructor(RD); - // TODO: Move constructors + if (getLangOptions().CPlusPlus0x && RD->needsImplicitMoveConstructor()) + DeclareImplicitMoveConstructor(RD); } else { Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); if (!RD->hasDeclaredCopyAssignment()) DeclareImplicitCopyAssignment(RD); - // TODO: Move assignment + if (getLangOptions().CPlusPlus0x && RD->needsImplicitMoveAssignment()) + DeclareImplicitMoveAssignment(RD); } QualType ArgType = CanTy; @@ -2359,6 +2381,15 @@ CXXConstructorDecl *Sema::LookupCopyingConstructor(CXXRecordDecl *Class, return cast_or_null(Result->getMethod()); } +/// \brief Look up the moving constructor for the given class. +CXXConstructorDecl *Sema::LookupMovingConstructor(CXXRecordDecl *Class) { + SpecialMemberOverloadResult *Result = + LookupSpecialMember(Class, CXXMoveConstructor, false, + false, false, false, false); + + return cast_or_null(Result->getMethod()); +} + /// \brief Look up the constructors for the given class. DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) { // If the implicit constructors have not yet been declared, do so now. @@ -2367,6 +2398,8 @@ DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) { DeclareImplicitDefaultConstructor(Class); if (!Class->hasDeclaredCopyConstructor()) DeclareImplicitCopyConstructor(Class); + if (getLangOptions().CPlusPlus0x && Class->needsImplicitMoveConstructor()) + DeclareImplicitMoveConstructor(Class); } CanQualType T = Context.getCanonicalType(Context.getTypeDeclType(Class)); @@ -2395,6 +2428,20 @@ CXXMethodDecl *Sema::LookupCopyingAssignment(CXXRecordDecl *Class, return Result->getMethod(); } +/// \brief Look up the moving assignment operator for the given class. +CXXMethodDecl *Sema::LookupMovingAssignment(CXXRecordDecl *Class, + bool RValueThis, + unsigned ThisQuals) { + assert(!(ThisQuals & ~(Qualifiers::Const | Qualifiers::Volatile)) && + "non-const, non-volatile qualifiers for copy assignment this"); + SpecialMemberOverloadResult *Result = + LookupSpecialMember(Class, CXXMoveAssignment, false, false, RValueThis, + ThisQuals & Qualifiers::Const, + ThisQuals & Qualifiers::Volatile); + + return Result->getMethod(); +} + /// \brief Look for the destructor of the given class. /// /// During semantic analysis, this routine should be used in lieu of diff --git a/test/CXX/dcl.decl/dcl.init/p14-0x.cpp b/test/CXX/dcl.decl/dcl.init/p14-0x.cpp index e5b5889954..f4fad3ef14 100644 --- a/test/CXX/dcl.decl/dcl.init/p14-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/p14-0x.cpp @@ -4,7 +4,7 @@ struct NoDefault { NoDefault() = delete; // expected-note {{here}} NoDefault(int); }; -struct Explicit { // expected-note {{candidate}} expected-note {{here}} +struct Explicit { // expected-note 2 {{candidate}} expected-note {{here}} explicit Explicit(int); }; struct NoCopy { diff --git a/test/CXX/special/class.copy/implicit-move-def.cpp b/test/CXX/special/class.copy/implicit-move-def.cpp new file mode 100644 index 0000000000..84a41c4622 --- /dev/null +++ b/test/CXX/special/class.copy/implicit-move-def.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -emit-llvm -o - -std=c++0x %s | FileCheck %s + +// construct + +struct E { + E(); + E(E&&); +}; + +struct F { + F(); + F(F&&); +}; + +struct G { + E e; +}; + +struct H : G { + F l; + E m; + F ar[2]; +}; + +void f() { + H s; + // CHECK: call void @_ZN1HC1EOS_ + H t(static_cast(s)); +} + + +// assign + +struct A { + A &operator =(A&&); +}; + +struct B { + B &operator =(B&&); +}; + +struct C { + A a; +}; + +struct D : C { + A a; + B b; + A ar[2]; +}; + +void g() { + D d; + // CHECK: call {{.*}} @_ZN1DaSEOS_ + d = D(); +} + + +// move assignment ops + +// CHECK: define linkonce_odr {{.*}} @_ZN1DaSEOS_ +// CHECK: call {{.*}} @_ZN1CaSEOS_ +// CHECK: call {{.*}} @_ZN1AaSEOS_ +// CHECK: call {{.*}} @_ZN1BaSEOS_ +// array loop +// CHECK: br i1 +// CHECK: call {{.*}} @_ZN1AaSEOS_ + +// CHECK: define linkonce_odr {{.*}} @_ZN1CaSEOS_ +// CHECK: call {{.*}} @_ZN1AaSEOS_ + + +// move ctors + +// CHECK: define linkonce_odr void @_ZN1HC2EOS_ +// CHECK: call void @_ZN1GC2EOS_ +// CHECK: call void @_ZN1FC1EOS_ +// CHECK: call void @_ZN1EC1EOS_ +// array loop +// CHECK: br i1 +// CHECK: call void @_ZN1FC1EOS_ + +// CHECK: define linkonce_odr void @_ZN1GC2EOS_ +// CHECK: call void @_ZN1EC1EOS_ diff --git a/test/CXX/special/class.copy/implicit-move.cpp b/test/CXX/special/class.copy/implicit-move.cpp new file mode 100644 index 0000000000..2475f7b46f --- /dev/null +++ b/test/CXX/special/class.copy/implicit-move.cpp @@ -0,0 +1,164 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +// Tests for implicit (non-)declaration of move constructor and +// assignment: p9, p11, p20, p23. + +// This class, used as a member, allows to distinguish move from copy because +// move operations are no-throw, copy operations aren't. +struct ThrowingCopy { + ThrowingCopy() noexcept; + ThrowingCopy(ThrowingCopy &&) noexcept; + ThrowingCopy(const ThrowingCopy &) noexcept(false); + ThrowingCopy & operator =(ThrowingCopy &&) noexcept; + ThrowingCopy & operator =(const ThrowingCopy &) noexcept(false); +}; + +struct HasCopyConstructor { + ThrowingCopy tc; + HasCopyConstructor() noexcept; + HasCopyConstructor(const HasCopyConstructor &) noexcept(false); +}; + +struct HasCopyAssignment { + ThrowingCopy tc; + HasCopyAssignment() noexcept; + HasCopyAssignment & operator =(const HasCopyAssignment &) noexcept(false); +}; + +struct HasMoveConstructor { // expected-note {{implicit copy assignment}} + ThrowingCopy tc; + HasMoveConstructor() noexcept; + HasMoveConstructor(HasMoveConstructor &&) noexcept; +}; + +struct HasMoveAssignment { // expected-note {{implicit copy constructor}} + ThrowingCopy tc; + HasMoveAssignment() noexcept; + HasMoveAssignment & operator =(HasMoveAssignment &&) noexcept; +}; + +struct HasDestructor { + ThrowingCopy tc; + HasDestructor() noexcept; + ~HasDestructor() noexcept; +}; + +void test_basic_exclusion() { + static_assert(!noexcept(HasCopyConstructor((HasCopyConstructor()))), ""); + HasCopyConstructor hcc; + static_assert(!noexcept(hcc = HasCopyConstructor()), ""); + + static_assert(!noexcept(HasCopyAssignment((HasCopyAssignment()))), ""); + HasCopyAssignment hca; + static_assert(!noexcept(hca = HasCopyAssignment()), ""); + + static_assert(noexcept(HasMoveConstructor((HasMoveConstructor()))), ""); + HasMoveConstructor hmc; + hmc = HasMoveConstructor(); // expected-error {{selected deleted operator}} + + (HasMoveAssignment(HasMoveAssignment())); // expected-error {{uses deleted function}} + HasMoveAssignment hma; + static_assert(noexcept(hma = HasMoveAssignment()), ""); + + static_assert(!noexcept(HasDestructor((HasDestructor()))), ""); + HasDestructor hd; + static_assert(!noexcept(hd = HasDestructor()), ""); +} + +struct PrivateMove { + PrivateMove() noexcept; + PrivateMove(const PrivateMove &) noexcept(false); + PrivateMove & operator =(const PrivateMove &) noexcept(false); +private: + PrivateMove(PrivateMove &&) noexcept; + PrivateMove & operator =(PrivateMove &&) noexcept; +}; + +struct InheritsPrivateMove : PrivateMove {}; +struct ContainsPrivateMove { + PrivateMove pm; +}; + +struct PrivateDestructor { + PrivateDestructor() noexcept; + PrivateDestructor(const PrivateDestructor &) noexcept(false); + PrivateDestructor(PrivateDestructor &&) noexcept; +private: + ~PrivateDestructor() noexcept; +}; + +struct InheritsPrivateDestructor : PrivateDestructor {}; // expected-note {{explicitly marked deleted}} +struct ContainsPrivateDestructor { // expected-note {{explicitly marked deleted}} + PrivateDestructor pd; +}; + +struct NonTrivialCopyOnly { + NonTrivialCopyOnly() noexcept; + NonTrivialCopyOnly(const NonTrivialCopyOnly &) noexcept(false); + NonTrivialCopyOnly & operator =(const NonTrivialCopyOnly &) noexcept(false); +}; + +struct InheritsNonTrivialCopyOnly : NonTrivialCopyOnly {}; +struct ContainsNonTrivialCopyOnly { + NonTrivialCopyOnly ntco; +}; + +struct ContainsConst { + const int i; + ContainsConst() noexcept; + ContainsConst & operator =(ContainsConst &); // expected-note {{not viable}} +}; + +struct ContainsRef { + int &i; + ContainsRef() noexcept; + ContainsRef & operator =(ContainsRef &); // expected-note {{not viable}} +}; + +struct Base { + Base & operator =(Base &); +}; +struct DirectVirtualBase : virtual Base {}; // expected-note {{copy assignment operator) not viable}} +struct IndirectVirtualBase : DirectVirtualBase {}; // expected-note {{copy assignment operator) not viable}} + +void test_deletion_exclusion() { + // FIXME: How to test the union thing? + + static_assert(!noexcept(InheritsPrivateMove(InheritsPrivateMove())), ""); + static_assert(!noexcept(ContainsPrivateMove(ContainsPrivateMove())), ""); + InheritsPrivateMove ipm; + static_assert(!noexcept(ipm = InheritsPrivateMove()), ""); + ContainsPrivateMove cpm; + static_assert(!noexcept(cpm = ContainsPrivateMove()), ""); + + (InheritsPrivateDestructor(InheritsPrivateDestructor())); // expected-error {{call to deleted constructor}} + (ContainsPrivateDestructor(ContainsPrivateDestructor())); // expected-error {{call to deleted constructor}} + + static_assert(!noexcept(InheritsNonTrivialCopyOnly(InheritsNonTrivialCopyOnly())), ""); + static_assert(!noexcept(ContainsNonTrivialCopyOnly(ContainsNonTrivialCopyOnly())), ""); + InheritsNonTrivialCopyOnly intco; + static_assert(!noexcept(intco = InheritsNonTrivialCopyOnly()), ""); + ContainsNonTrivialCopyOnly cntco; + static_assert(!noexcept(cntco = ContainsNonTrivialCopyOnly()), ""); + + ContainsConst cc; + cc = ContainsConst(); // expected-error {{no viable}} + + ContainsRef cr; + cr = ContainsRef(); // expected-error {{no viable}} + + DirectVirtualBase dvb; + dvb = DirectVirtualBase(); // expected-error {{no viable}} + + IndirectVirtualBase ivb; + ivb = IndirectVirtualBase(); // expected-error {{no viable}} +} + +struct ContainsRValueRef { + int&& ri; + ContainsRValueRef() noexcept; +}; + +void test_contains_rref() { + (ContainsRValueRef(ContainsRValueRef())); +} diff --git a/test/CXX/special/class.inhctor/p3.cpp b/test/CXX/special/class.inhctor/p3.cpp index 021f701ab4..d989f7f7d1 100644 --- a/test/CXX/special/class.inhctor/p3.cpp +++ b/test/CXX/special/class.inhctor/p3.cpp @@ -14,7 +14,7 @@ D1 fd1() { return 1; } struct B2 { explicit B2(int, int = 0, int = 0); }; -struct D2 : B2 { // expected-note {{candidate constructor}} +struct D2 : B2 { // expected-note 2 {{candidate constructor}} using B2::B2; }; D2 d2a(1), d2b(1, 1), d2c(1, 1, 1); @@ -24,7 +24,7 @@ D2 fd2() { return 1; } // expected-error {{no viable conversion}} struct B3 { B3(void*); // expected-note {{inherited from here}} }; -struct D3 : B3 { // expected-note {{candidate constructor}} +struct D3 : B3 { // expected-note 2 {{candidate constructor}} using B3::B3; // expected-note {{candidate constructor (inherited)}} }; D3 fd3() { return 1; } // expected-error {{no viable conversion}} diff --git a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp index 0c92f62582..19398ae4f2 100644 --- a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp +++ b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp @@ -21,7 +21,7 @@ namespace std { using namespace inner; } -struct A { // expected-note {{candidate constructor}} +struct A { // expected-note 2 {{candidate constructor}} A(); int *begin(); // expected-note 3{{selected 'begin' function with iterator type 'int *'}} expected-note {{'begin' declared here}} int *end(); diff --git a/test/CXX/temp/temp.decls/temp.variadic/p4.cpp b/test/CXX/temp/temp.decls/temp.variadic/p4.cpp index 71839727e0..c852473ad2 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/p4.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/p4.cpp @@ -52,6 +52,7 @@ struct HasMixins : public Mixins... { }; struct A { }; // expected-note{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const A' for 1st argument}} \ +// expected-note{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'A' for 1st argument}} \ // expected-note{{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} struct B { }; struct C { }; diff --git a/test/SemaCXX/cast-conversion.cpp b/test/SemaCXX/cast-conversion.cpp index 80707d13d2..ebbacdc13f 100644 --- a/test/SemaCXX/cast-conversion.cpp +++ b/test/SemaCXX/cast-conversion.cpp @@ -8,7 +8,8 @@ struct A { A(R); }; -struct B { // expected-note 3 {{candidate constructor (the implicit copy constructor) not viable}} +struct B { // expected-note 3 {{candidate constructor (the implicit copy constructor) not viable}} \ + expected-note 3 {{candidate constructor (the implicit move constructor) not viable}} B(A); // expected-note 3 {{candidate constructor not viable}} }; diff --git a/test/SemaCXX/explicit.cpp b/test/SemaCXX/explicit.cpp index 71680d4dde..e0397470e2 100644 --- a/test/SemaCXX/explicit.cpp +++ b/test/SemaCXX/explicit.cpp @@ -41,12 +41,17 @@ namespace Conversion { { // Taken from 12.3.2p2 class Y { }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y &' for 1st argument}} \ + expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}} \ expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y &' for 1st argument}} \ + expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \ expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \ - expected-note {{andidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} \ + expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \ + expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} \ expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \ + expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \ expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} \ expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \ + expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}} \ expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} struct Z { explicit operator Y() const;