diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 62 | ||||
-rw-r--r-- | include/clang/AST/Type.h | 5 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 28 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 59 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 5 | ||||
-rw-r--r-- | lib/AST/ExprClassification.cpp | 2 | ||||
-rw-r--r-- | lib/AST/Type.cpp | 5 | ||||
-rw-r--r-- | lib/CodeGen/CGClass.cpp | 17 | ||||
-rw-r--r-- | lib/CodeGen/CGExprCXX.cpp | 9 | ||||
-rw-r--r-- | lib/CodeGen/CGExprConstant.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 1181 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 30 | ||||
-rw-r--r-- | lib/Sema/SemaLookup.cpp | 65 | ||||
-rw-r--r-- | test/CXX/dcl.decl/dcl.init/p14-0x.cpp | 2 | ||||
-rw-r--r-- | test/CXX/special/class.copy/implicit-move-def.cpp | 84 | ||||
-rw-r--r-- | test/CXX/special/class.copy/implicit-move.cpp | 164 | ||||
-rw-r--r-- | test/CXX/special/class.inhctor/p3.cpp | 4 | ||||
-rw-r--r-- | test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp | 2 | ||||
-rw-r--r-- | test/CXX/temp/temp.decls/temp.variadic/p4.cpp | 1 | ||||
-rw-r--r-- | test/SemaCXX/cast-conversion.cpp | 3 | ||||
-rw-r--r-- | test/SemaCXX/explicit.cpp | 7 |
21 files changed, 1635 insertions, 104 deletions
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<ImplicitExceptionSpecification, bool> 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<CXXConstructorDecl>(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<CXXConstructorDecl>(MD) && - cast<CXXConstructorDecl>(MD)->isCopyConstructor()) { + cast<CXXConstructorDecl>(MD)->isCopyOrMoveConstructor()) { + // Trivial move and copy ctor are the same. llvm::Value *RHS = EmitLValue(*CE->arg_begin()).getAddress(); EmitSynthesizedCXXCopyCtorCall(cast<CXXConstructorDecl>(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\<T&&>(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<MemberExpr>(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<ValueDecl>(Indirect) : cast<ValueDecl>(Field), AS_public); + MemberLookup.addDecl(Indirect ? cast<ValueDecl>(Indirect) + : cast<ValueDecl>(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<T&&>(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<VarDecl *, 4> 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<CXXConstructorDecl>(*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<FunctionProtoType>(); 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<FunctionProtoType>(), + *ExceptionType = Context.getFunctionType( + Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); + + // 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<FunctionType>()->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<FunctionProtoType>(), + *ExceptionType = Context.getFunctionType( + Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); + + 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) + <&l |