Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -1090,6 +1090,10 @@ ``argtypes...`` such that no non-trivial functions are called as part of that initialization. This trait is required to implement the C++11 standard library. +* ``__is_trivially_relocatable`` (Clang): Determines whether moving an object + of type ``type``, and then destroying the source object, is functionally + equivalent to copying the underlying bytes and then dropping the source object + on the floor. * ``__is_destructible`` (MSVC 2013) * ``__is_nothrow_destructible`` (MSVC 2013) * ``__is_nothrow_assignable`` (MSVC 2013, clang) Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -468,6 +468,11 @@ /// SMF_MoveConstructor, and SMF_Destructor are meaningful here. unsigned DeclaredNonTrivialSpecialMembersForCall : 6; + /// True when this class's bases and fields are all trivially relocatable + /// or references, and the class itself has a defaulted move constructor + /// and a defaulted destructor. + unsigned IsNaturallyTriviallyRelocatable : 1; + /// True when this class has a destructor with no semantic effect. unsigned HasIrrelevantDestructor : 1; @@ -1498,6 +1503,16 @@ (SMF_CopyConstructor | SMF_MoveConstructor | SMF_Destructor); } + /// Determine whether this class is trivially relocatable + bool isTriviallyRelocatable() const { + return data().IsNaturallyTriviallyRelocatable || + hasAttr(); + } + + void setIsNotNaturallyTriviallyRelocatable() { + data().IsNaturallyTriviallyRelocatable = false; + } + /// Determine whether declaring a const variable with this type is ok /// per core issue 253. bool allowConstDefaultInit() const { Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -797,6 +797,9 @@ /// Return true if this is a trivially copyable type (C++0x [basic.types]p9) bool isTriviallyCopyableType(const ASTContext &Context) const; + /// Return true if this is a trivially relocatable type + bool isTriviallyRelocatableType(const ASTContext &Context) const; + /// Returns true if it is a class and it might be dynamic. bool mayBeDynamicClass() const; Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -2129,6 +2129,13 @@ let Documentation = [Undocumented]; } +def TriviallyRelocatable : InheritableAttr { + let Spellings = [Clang<"trivially_relocatable", 0>]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [TriviallyRelocatableDocs]; + let LangOpts = [CPlusPlus]; +} + def Unused : InheritableAttr { let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">, C2x<"", "maybe_unused">]; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -2424,6 +2424,16 @@ }]; } +def TriviallyRelocatableDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``trivially_relocatable`` attribute can be applied to a C++ class, struct, or union. +It warrants to the compiler that moving an object of that type, and then destroying the +source object, is functionally equivalent to copying the underlying bytes +and then dropping the source object on the floor. + }]; +} + def MSInheritanceDocs : Documentation { let Category = DocCatType; let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance"; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2968,6 +2968,11 @@ def ext_cannot_use_trivial_abi : ExtWarn< "'trivial_abi' cannot be applied to %0">, InGroup; +def err_trivially_relocatable_class_is_not_relocatable : Error< + "'trivially_relocatable' cannot be applied to " + "%select{struct|interface|union|class|enum}0 %1 because it is " + "not %select{move-constructible|destructible}2">; + // Availability attribute def warn_availability_unknown_platform : Warning< "unknown platform %0 in availability macro">, InGroup; @@ -8282,6 +8287,11 @@ def err_block_on_vm : Error< "__block attribute not allowed on declaration with a variably modified type">; +def err_attribute_missing_on_first_decl : Error< + "type %0 declared %1 after its first declaration">; +def note_attribute_missing_first_decl : Note< + "declaration missing %0 attribute is here">; + def err_vec_builtin_non_vector : Error< "first two arguments to %0 must be vectors">; def err_vec_builtin_incompatible_vector : Error< Index: include/clang/Basic/Features.def =================================================================== --- include/clang/Basic/Features.def +++ include/clang/Basic/Features.def @@ -239,6 +239,7 @@ EXTENSION(cxx_init_captures, LangOpts.CPlusPlus11) EXTENSION(cxx_variable_templates, LangOpts.CPlusPlus) // Miscellaneous language extensions +EXTENSION(trivially_relocatable, LangOpts.CPlusPlus11) EXTENSION(overloadable_unmarked, true) #undef EXTENSION Index: include/clang/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -472,6 +472,7 @@ TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX) TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX) TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX) +TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX) TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) KEYWORD(__underlying_type , KEYCXX) Index: include/clang/Basic/TypeTraits.h =================================================================== --- include/clang/Basic/TypeTraits.h +++ include/clang/Basic/TypeTraits.h @@ -66,6 +66,7 @@ UTT_IsTrivial, UTT_IsTriviallyCopyable, UTT_IsTriviallyDestructible, + UTT_IsTriviallyRelocatable, UTT_IsUnion, UTT_IsUnsigned, UTT_IsVoid, Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -1785,6 +1785,8 @@ = FromData.DefaultedMoveAssignmentIsDeleted; ToData.DefaultedDestructorIsDeleted = FromData.DefaultedDestructorIsDeleted; ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers; + ToData.IsNaturallyTriviallyRelocatable + = FromData.IsNaturallyTriviallyRelocatable; ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor; ToData.HasConstexprNonCopyMoveConstructor = FromData.HasConstexprNonCopyMoveConstructor; Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -91,7 +91,8 @@ DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All), HasTrivialSpecialMembersForCall(SMF_All), DeclaredNonTrivialSpecialMembers(0), - DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true), + DeclaredNonTrivialSpecialMembersForCall(0), + IsNaturallyTriviallyRelocatable(true), HasIrrelevantDestructor(true), HasConstexprNonCopyMoveConstructor(false), HasDefaultedDefaultConstructor(false), DefaultedDefaultConstructorIsConstexpr(true), @@ -278,6 +279,9 @@ if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C)) data().HasNonLiteralTypeFieldsOrBases = true; + if (Base->isVirtual() || !BaseClassDecl->isTriviallyRelocatable()) + setIsNotNaturallyTriviallyRelocatable(); + // Now go through all virtual bases of this base and add them. for (const auto &VBase : BaseClassDecl->vbases()) { // Add this base if it's not already in the list. Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2231,6 +2231,19 @@ return false; } +bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const { + QualType T = Context.getBaseElementType(getCanonicalType()); + if (T->isIncompleteType()) + return false; + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { + return RD->isTriviallyRelocatable(); + } else { + // Non-class types are always both move-constructible and destructible, + // so just check whether a non-class type is trivially copyable. + return T.isTriviallyCopyableType(Context); + } +} + bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const { return !Context.getLangOpts().ObjCAutoRefCount && Context.getLangOpts().ObjCWeak && Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -15854,6 +15854,16 @@ Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); } + if (CXXRecord) { + QualType FT = FD->getType(); + if (FD->isMutable()) + CXXRecord->setIsNotNaturallyTriviallyRelocatable(); + else if (FT.isVolatileQualified()) + CXXRecord->setIsNotNaturallyTriviallyRelocatable(); + else if (!FT->isReferenceType() && !FT.isTriviallyRelocatableType(Context)) + CXXRecord->setIsNotNaturallyTriviallyRelocatable(); + } + if (Record && FD->getType().isVolatileQualified()) Record->setHasVolatileMember(true); // Keep track of the number of named members. Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -5635,6 +5635,24 @@ AL.getAttributeSpellingListIndex())); } +template +static void checkAttributeNotOnFirstDecl(Sema &S, Decl *D, const ParsedAttr &AL) { + Decl *FirstD = D->getCanonicalDecl(); + if (FirstD != D && !FirstD->hasAttr()) { + NamedDecl *ND = dyn_cast(D); + S.Diag(AL.getLoc(), diag::err_attribute_missing_on_first_decl) + << (ND ? ND->getDeclName().getAsString() : "") << AL.getName(); + S.Diag(FirstD->getLocation(), diag::note_attribute_missing_first_decl) + << AL.getName() + << FirstD->getSourceRange(); + } +} + +static void handleTriviallyRelocatableAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + checkAttributeNotOnFirstDecl(S, D, AL); + handleSimpleAttribute(S, D, AL); +} + DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex) { if (D->hasAttr()) { @@ -6536,6 +6554,9 @@ case ParsedAttr::AT_TrivialABI: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_TriviallyRelocatable: + handleTriviallyRelocatableAttr(S, D, AL); + break; case ParsedAttr::AT_MSNoVTable: handleSimpleAttribute(S, D, AL); break; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -6079,6 +6079,26 @@ Record->setTrivialForCallFlags(M); } + // A move-constructible, destructible object type T is a + // trivially relocatable type if it ... + if (CSM == CXXMoveConstructor && M->isUserProvided()) { + // - has no user-provided move constructors, + Record->setIsNotNaturallyTriviallyRelocatable(); + } else if (CSM == CXXCopyConstructor && M->isUserProvided()) { + // - either has at least one move constructor or has no user-provided copy constructors, + if (!Record->hasMoveConstructor()) + Record->setIsNotNaturallyTriviallyRelocatable(); + } else if (CSM == CXXDestructor && + (M->isUserProvided() || M->isDeleted())) { + // - has a defaulted, non-deleted destructor, + Record->setIsNotNaturallyTriviallyRelocatable(); + } else if (CSM == CXXDestructor && M->isVirtual()) { + // - either is final, or has a final destructor, + // or has a non-virtual destructor ... + if (!M->hasAttr() && !Record->hasAttr()) + Record->setIsNotNaturallyTriviallyRelocatable(); + } + if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() && M->hasAttr()) { if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && @@ -6147,6 +6167,41 @@ // is especially required for cases like vtable assumption loads. MarkVTableUsed(Record->getInnerLocStart(), Record); } + + if (getLangOpts().CPlusPlus11 && !Record->isDependentContext()) { + // Check that the destructor is non-deleted. + SpecialMemberOverloadResult SMOR = LookupSpecialMember( + Record, CXXDestructor, false, false, false, false, false); + if (SMOR.getKind() != SpecialMemberOverloadResult::Success) { + Record->setIsNotNaturallyTriviallyRelocatable(); + if (Record->hasAttr()) { + Record->dropAttr(); + if (!isTemplateInstantiation(Record->getTemplateSpecializationKind())) { + Diag(Record->getLocation(), + diag::err_trivially_relocatable_class_is_not_relocatable) + << Record->getCanonicalDecl()->getTagKind() + << Context.getRecordType(Record) << true; + } + } + } else { + // Check that the constructor used for move-construction is non-deleted. + SMOR = LookupSpecialMember(Record, CXXMoveConstructor, false, false, + false, false, false); + if (SMOR.getKind() != SpecialMemberOverloadResult::Success) { + Record->setIsNotNaturallyTriviallyRelocatable(); + if (Record->hasAttr()) { + Record->dropAttr(); + if (!isTemplateInstantiation( + Record->getTemplateSpecializationKind())) { + Diag(Record->getLocation(), + diag::err_trivially_relocatable_class_is_not_relocatable) + << Record->getCanonicalDecl()->getTagKind() + << Context.getRecordType(Record) << false; + } + } + } + } + } } /// Look up the special member function that would be called by a special Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -4424,6 +4424,7 @@ case UTT_IsDestructible: case UTT_IsNothrowDestructible: case UTT_IsTriviallyDestructible: + case UTT_IsTriviallyRelocatable: case UTT_HasUniqueObjectRepresentations: if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType()) return true; @@ -4713,7 +4714,8 @@ } } return true; - + case UTT_IsTriviallyRelocatable: + return T.isTriviallyRelocatableType(C); case UTT_HasTrivialDestructor: // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html // If __is_pod (type) is true or type is a reference type Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1674,6 +1674,7 @@ Data.HasTrivialSpecialMembersForCall = Record.readInt(); Data.DeclaredNonTrivialSpecialMembers = Record.readInt(); Data.DeclaredNonTrivialSpecialMembersForCall = Record.readInt(); + Data.IsNaturallyTriviallyRelocatable = Record.readInt(); Data.HasIrrelevantDestructor = Record.readInt(); Data.HasConstexprNonCopyMoveConstructor = Record.readInt(); Data.HasDefaultedDefaultConstructor = Record.readInt(); @@ -1815,6 +1816,7 @@ OR_FIELD(HasTrivialSpecialMembersForCall) OR_FIELD(DeclaredNonTrivialSpecialMembers) OR_FIELD(DeclaredNonTrivialSpecialMembersForCall) + MATCH_FIELD(IsNaturallyTriviallyRelocatable) MATCH_FIELD(HasIrrelevantDestructor) OR_FIELD(HasConstexprNonCopyMoveConstructor) OR_FIELD(HasDefaultedDefaultConstructor) Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -6046,6 +6046,7 @@ Record->push_back(Data.HasTrivialSpecialMembersForCall); Record->push_back(Data.DeclaredNonTrivialSpecialMembers); Record->push_back(Data.DeclaredNonTrivialSpecialMembersForCall); + Record->push_back(Data.IsNaturallyTriviallyRelocatable); Record->push_back(Data.HasIrrelevantDestructor); Record->push_back(Data.HasConstexprNonCopyMoveConstructor); Record->push_back(Data.HasDefaultedDefaultConstructor); Index: test/Lexer/has_extension_cxx.cpp =================================================================== --- test/Lexer/has_extension_cxx.cpp +++ test/Lexer/has_extension_cxx.cpp @@ -66,3 +66,9 @@ #if __has_extension(cxx_init_captures) int has_init_captures(); #endif + +// CHECK-NOT: has_trivially_relocatable +// CHECK11: has_trivially_relocatable +#if __has_extension(trivially_relocatable) +int has_trivially_relocatable(); +#endif Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 129 attributes: +// CHECK: #pragma clang attribute supports 130 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -123,6 +123,7 @@ // CHECK-NEXT: Target (SubjectMatchRule_function) // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: TrivialABI (SubjectMatchRule_record) +// CHECK-NEXT: TriviallyRelocatable (SubjectMatchRule_record) // CHECK-NEXT: VecReturn (SubjectMatchRule_record) // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function) // CHECK-NEXT: WarnUnused (SubjectMatchRule_record) Index: test/SemaCXX/trivially-relocatable.cpp =================================================================== --- /dev/null +++ test/SemaCXX/trivially-relocatable.cpp @@ -0,0 +1,642 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 +// expected-diagnostics + +static_assert(__has_extension(trivially_relocatable), ""); + +// It shall appear at most once in each attribute-list +// and no attribute-argument-clause shall be present. + +struct [[clang::trivially_relocatable, clang::trivially_relocatable]] B1 {}; +// expected-error@-1{{attribute 'trivially_relocatable' cannot appear multiple times in an attribute specifier}} + +struct [[clang::trivially_relocatable]] [[clang::trivially_relocatable]] B2 {}; // should really be an error + +struct [[clang::trivially_relocatable(42)]] B3 {}; +// expected-error@-1{{attribute 'trivially_relocatable' cannot have an argument list}} + + +// The first declaration of a type shall specify the +// trivially_relocatable attribute if any declaration of that +// type specifies the trivially_relocatable attribute. + +struct [[clang::trivially_relocatable]] A1 {}; // ok +struct [[clang::trivially_relocatable]] A1; + +struct [[clang::trivially_relocatable]] A2; // ok +struct [[clang::trivially_relocatable]] A2 {}; + +struct [[clang::trivially_relocatable]] A3 {}; // ok +struct A3; + +struct [[clang::trivially_relocatable]] A4; // ok +struct A4 {}; + +struct A5 {}; +struct [[clang::trivially_relocatable]] A5; +// expected-error@-1{{type A5 declared 'trivially_relocatable' after its first declaration}} +// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}} +// expected-warning@-3{{attribute declaration must precede definition}} +// expected-note@-5{{previous definition is here}} + +struct A6; +struct [[clang::trivially_relocatable]] A6 {}; +// expected-error@-1{{type A6 declared 'trivially_relocatable' after its first declaration}} +// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}} + + +// If a type T is declared with the trivially_relocatable attribute, and T is +// either not move-constructible or not destructible, the program is ill-formed. + +struct NonDestructible { + NonDestructible(const NonDestructible&) = default; + NonDestructible(NonDestructible&&) = default; + ~NonDestructible() = delete; +}; +struct NonCopyConstructible { + NonCopyConstructible(const NonCopyConstructible&) = delete; +}; +struct NonMoveConstructible { + NonMoveConstructible(const NonMoveConstructible&) = default; + NonMoveConstructible(NonMoveConstructible&&) = delete; +}; +static_assert(!__is_trivially_relocatable(NonDestructible), ""); +static_assert(!__is_trivially_relocatable(NonCopyConstructible), ""); +static_assert(!__is_constructible(NonCopyConstructible, NonCopyConstructible&&), ""); +static_assert(!__is_trivially_relocatable(NonMoveConstructible), ""); +static_assert(!__is_constructible(NonMoveConstructible, NonMoveConstructible&&), ""); + +struct [[clang::trivially_relocatable]] D1 { ~D1() = delete; }; +// expected-error@-1{{cannot be applied to struct 'D1' because it is not destructible}} + +struct [[clang::trivially_relocatable]] D2 : private NonDestructible { }; +// expected-error@-1{{cannot be applied to struct 'D2' because it is not destructible}} + +struct [[clang::trivially_relocatable]] D3 { D3(const D3&) = delete; }; +// expected-error@-1{{cannot be applied to struct 'D3' because it is not move-constructible}} + +struct [[clang::trivially_relocatable]] D4 { D4(const D4&) = default; D4(D4&&) = delete; }; +// expected-error@-1{{cannot be applied to struct 'D4' because it is not move-constructible}} + +struct [[clang::trivially_relocatable]] D5 : private NonCopyConstructible { }; +// expected-error@-1{{cannot be applied to struct 'D5' because it is not move-constructible}} +static_assert(!__is_constructible(D5, D5&&), ""); + +struct [[clang::trivially_relocatable]] D6 : private NonMoveConstructible { D6(D6&&) = default; }; +// expected-error@-1{{cannot be applied to struct 'D6' because it is not move-constructible}} + +template +struct [[clang::trivially_relocatable]] DT1 : private T { }; // ok + +struct D7 { + DT1 m; +}; + +class [[clang::trivially_relocatable]] D8 { + DT1 m; +}; +// expected-error@-3{{cannot be applied to class 'D8' because it is not destructible}} + + +// Now test specific types for trivial relocatability. + +static_assert(__is_trivially_relocatable(char), ""); +static_assert(__is_trivially_relocatable(int), ""); +static_assert(__is_trivially_relocatable(int*), ""); +static_assert(!__is_trivially_relocatable(int&), ""); +static_assert(__is_trivially_relocatable(float), ""); +static_assert(__is_trivially_relocatable(double), ""); +static_assert(!__is_trivially_relocatable(void), ""); +static_assert(__is_trivially_relocatable(char[1]), ""); +static_assert(__is_trivially_relocatable(char[]), ""); + +static_assert(__is_trivially_relocatable(const int), ""); +static_assert(__is_trivially_relocatable(volatile int), ""); +static_assert(!__is_trivially_relocatable(const int&), ""); +static_assert(!__is_trivially_relocatable(volatile int&), ""); + +struct C1 { int x; }; static_assert(__is_trivially_relocatable(C1), ""); +struct C2 { const int x; }; static_assert(__is_trivially_relocatable(C2), ""); +struct C3 { volatile int x; }; static_assert(!__is_trivially_relocatable(C3), "volatile member"); +struct C4 { int *x; }; static_assert(__is_trivially_relocatable(C4), ""); +struct C5 { const int *x; }; static_assert(__is_trivially_relocatable(C5), ""); +struct C6 { volatile int *x; }; static_assert(__is_trivially_relocatable(C6), ""); +struct C7 { int& x; }; static_assert(__is_trivially_relocatable(C7), ""); +struct C8 { const int& x; }; static_assert(__is_trivially_relocatable(C8), ""); +struct C9 { volatile int& x; }; static_assert(__is_trivially_relocatable(C9), ""); + +enum E { x = 1, y = 2 }; +static_assert(__is_trivially_relocatable(E), ""); +static_assert(__is_trivially_relocatable(E[1]), ""); + +struct T1 {}; +static_assert(__is_trivially_relocatable(T1), ""); + +struct T2 { int x; E y; int *z; }; +static_assert(__is_trivially_relocatable(T2), ""); + +struct T3 { int x; T3(T3&&) = default; }; +static_assert(__is_trivially_relocatable(T3), ""); + +struct T4 { int x; ~T4() = default; }; +static_assert(__is_trivially_relocatable(T4), "trivially copy-constructible, and no move constructor"); + +struct T4a { T4 a; }; +static_assert(__is_trivially_relocatable(T4a), "trivially copy-constructible, and no move constructor"); + + +struct VD { + VD(const VD&) = default; + VD(VD&&) = default; + virtual ~VD() = default; +}; +void relocate_example(VD&& src) { + VD dst(static_cast(src)); // this DEFINITELY calls the trivial move-constructor + src.~VD(); // this MAY virtually dispatch to a non-trivial destructor +} +static_assert(!__is_trivially_relocatable(VD), ""); + + +struct VD2 final { + VD2(const VD2&) = default; + VD2(VD2&&) = default; + virtual ~VD2() = default; +}; +void relocate_example(VD2&& src) { + VD2 dst(static_cast(src)); // this DEFINITELY calls the trivial move-constructor + src.~VD2(); // because "final", this CANNOT virtually dispatch to a non-trivial destructor +} +static_assert(__is_trivially_relocatable(VD2), ""); + + +struct VD3 { + VD3(const VD3&) = default; + VD3(VD3&&) = default; + virtual ~VD3() final = default; +}; +void relocate_example(VD3&& src) { + VD3 dst(static_cast(src)); // this DEFINITELY calls the trivial move-constructor + src.~VD3(); // because "final", this CANNOT virtually dispatch to a non-trivial destructor +} +static_assert(__is_trivially_relocatable(VD3), ""); + + +struct VB : public virtual T1 { + VB(const VB&) = default; + VB(VB&&) = default; + ~VB() = default; +}; +void relocate_example(VB&& src) { + VB dst(static_cast(src)); // this MAY copy the virtual bases of "src" in a way not tantamount to memcpy + src.~VB(); // this calls the trivial destructor +} +static_assert(__is_trivially_destructible(VB), ""); +static_assert(!__is_trivially_constructible(VB, VB&&), ""); +static_assert(!__is_trivially_relocatable(VB), ""); + +struct VB2 final : public virtual T1 { + VB2(const VB2&) = default; + VB2(VB2&&) = default; + ~VB2() = default; +}; +void relocate_example(VB2&& src) { + VB2 dst(static_cast(src)); // this MAY STILL copy the VBPTR of "src" in a way not tantamount to memcpy + src.~VB2(); // this calls the trivial destructor +} +static_assert(__is_trivially_destructible(VB2), ""); +static_assert(!__is_trivially_constructible(VB2, VB2&&), ""); +static_assert(!__is_trivially_relocatable(VB2), ""); + + +struct CCNMC { + CCNMC(const CCNMC&) = default; + // no move constructor at all + ~CCNMC() = default; +}; +void relocate_example(CCNMC&& src) { + CCNMC dst(static_cast(src)); // this calls the trivial copy-constructor + src.~CCNMC(); // this calls the trivial destructor +} +static_assert(__is_constructible(CCNMC, CCNMC&&), ""); +static_assert(__is_trivially_relocatable(CCNMC), ""); + + +struct CCDMC { + CCDMC(const CCDMC&) = default; + CCDMC(CCDMC&&) = delete; + ~CCDMC() = default; +}; +void relocate_example(CCDMC&& src) { + // CCDMC dst(static_cast(src)); // this is not permitted + src.~CCDMC(); // this calls the trivial destructor +} +static_assert(!__is_constructible(CCDMC, CCDMC&&), ""); +static_assert(!__is_trivially_relocatable(CCDMC), ""); + +struct DD { ~DD() = delete; }; +static_assert(__is_trivially_copyable(DD), ""); +static_assert(!__is_trivially_destructible(DD), ""); +static_assert(!__is_trivially_relocatable(DD), ""); + + +struct T5 { int x; T5(T5&&) {} }; +static_assert(!__is_trivially_relocatable(T5), ""); + +struct T6 { int x; ~T6() {} }; +static_assert(!__is_trivially_relocatable(T6), ""); + +struct T7 { int x; T7(const T7&) {} }; +static_assert(!__is_trivially_relocatable(T7), "T7 has no implicitly declared move constructor"); + +struct T8 { virtual void f() {} int x; }; +static_assert(__is_trivially_relocatable(T8), "T8 has a vptr but that's fine"); + +struct [[clang::trivially_relocatable]] T9 { int x; T9(T9&&) {} }; +static_assert(__is_trivially_relocatable(T9), "T9 isn't naturally, but it has the attribute"); + +struct [[clang::trivially_relocatable]] T10 { int x; ~T10() {} }; +static_assert(__is_trivially_relocatable(T10), "T10 isn't naturally, but it has the attribute"); + +struct T11 { + T11(); + T1 a; + T2 b; + T3 c; + T4 d; + T8 e; + T9 f; + T10 g; +}; +static_assert(__is_trivially_relocatable(T11), "all fields have trivially relocatable types"); + +struct T12 { + T1 a; + T2 b; + T3 c; + T5 d; // not trivially relocatable + T8 e; + T9 f; + T10 g; +}; +static_assert(!__is_trivially_relocatable(T12), "not all fields have trivially relocatable types"); + +struct T13 : T1, T2, T3, T4 {}; +static_assert(__is_trivially_relocatable(T13), "all bases have trivially relocatable types"); + +struct T14 : T1, T6, T3, T4 {}; +static_assert(!__is_trivially_relocatable(T14), "all bases have trivially relocatable types"); + +template +struct T15 : Ts... {}; + +static_assert(__is_trivially_relocatable(T15), "all bases have trivially relocatable types"); +static_assert(!__is_trivially_relocatable(T15), "not all bases have trivially relocatable types"); + +template +struct [[clang::trivially_relocatable]] T16 : Ts... {}; + +static_assert(__is_trivially_relocatable(T16), "all bases have trivially relocatable types"); +static_assert(__is_trivially_relocatable(T16), "not naturally, but it has the attribute"); + +struct T17 : T15 {}; // T10 is trivially relocatable +static_assert(__is_trivially_relocatable(T17), ""); +static_assert(__is_trivially_relocatable(T15), ""); +static_assert(__is_trivially_relocatable(T16), ""); + +struct T18 : T15 {}; // T12 is not trivially relocatable +static_assert(!__is_trivially_relocatable(T18), ""); +static_assert(!__is_trivially_relocatable(T15), ""); +static_assert(__is_trivially_relocatable(T16), "not naturally, but it has the attribute"); + + +// This pattern is used heavily by libc++. +struct T19 { + struct [[clang::trivially_relocatable]] Base { + Base(Base&&); + ~Base(); + }; + Base m; + T19(const T19&); + T19(T19&&) = default; +}; + +static_assert(!__is_trivially_constructible(T19, const T19&), "user-provided copy constructor"); +static_assert(!__is_trivially_constructible(T19, T19&&), "defaulted non-trivial move constructor"); +static_assert(!__is_trivially_destructible(T19), "defaulted non-trivial destructor"); +static_assert(__is_trivially_relocatable(T19), "Rule of Zero"); + + +struct T20 { + struct [[clang::trivially_relocatable]] SharedPtr { + SharedPtr(); + SharedPtr(const SharedPtr&); + SharedPtr(SharedPtr&&); + ~SharedPtr(); + }; + SharedPtr m; + T20(const T20&) = default; + ~T20() = default; + // no move constructor +}; +void relocate_example(T20&& src) { + T20 dst(static_cast(src)); // this calls the defaulted copy constructor and makes a COPY of the SharedPtr + src.~T20(); // this calls the destructor and deletes the original copy +} +static_assert(__is_trivially_relocatable(T20::SharedPtr), "because it's annotated"); +static_assert(!__is_trivially_constructible(T20, const T20&), "defaulted, non-trivial copy constructor"); +static_assert(__is_constructible(T20, T20&&), "uses the copy constructor"); +static_assert(!__is_trivially_constructible(T20, T20&&), "uses the copy constructor"); +static_assert(!__is_trivially_destructible(T20), "defaulted non-trivial destructor"); +static_assert(__is_trivially_relocatable(T20), "I'm not sure but I think copy-and-destroy should always be assumed tantamount to move-and-destroy"); + + +struct T21 { + struct [[clang::trivially_relocatable]] SharedPtr { + SharedPtr(); + SharedPtr(const SharedPtr&); + SharedPtr(SharedPtr&&); + ~SharedPtr(); + }; + SharedPtr m; + T21(const T21&); // user-provided + ~T21() = default; + // no move constructor +}; +void relocate_example(T21&& src) { + T21 dst(static_cast(src)); // this calls the user-provided copy constructor + src.~T21(); // this calls the defaulted destructor +} +static_assert(__is_trivially_relocatable(T21::SharedPtr), "because it's annotated"); +static_assert(!__is_trivially_constructible(T21, const T21&), "non-defaulted, non-trivial copy constructor"); +static_assert(__is_constructible(T21, T21&&), "uses the copy constructor"); +static_assert(!__is_trivially_constructible(T21, T21&&), "uses the copy constructor"); +static_assert(!__is_trivially_destructible(T21), "defaulted non-trivial destructor"); +static_assert(!__is_trivially_relocatable(T21), "Relocating T21 calls T21's user-provided copy constructor, which we don't know what it does"); + + +struct T22 { + struct [[clang::trivially_relocatable]] MoveOnly { MoveOnly(MoveOnly&&); }; + struct CopyOnly { ~CopyOnly() = default; }; + MoveOnly m1; + CopyOnly m2; +}; +void relocate_example(T22&& src) { + T22 dst(static_cast(src)); // this moves m1 (user-provided) and copies m2 (trivial, defaulted) + src.~T22(); // this destroys m1 (trivial, defaulted) and m2 (trivial, defaulted) +} +static_assert(!__is_constructible(T22::MoveOnly, const T22::MoveOnly&), ""); +static_assert(__is_constructible(T22::MoveOnly, T22::MoveOnly&&), ""); +static_assert(!__is_trivially_constructible(T22::MoveOnly, T22::MoveOnly&&), ""); +static_assert(__is_trivially_relocatable(T22::MoveOnly), "because it's annotated"); +static_assert(__is_constructible(T22::CopyOnly, const T22::CopyOnly&), ""); +static_assert(__is_constructible(T22::CopyOnly, T22::CopyOnly&&), ""); +static_assert(__is_trivially_constructible(T22::CopyOnly, const T22::CopyOnly&), ""); +static_assert(__is_trivially_constructible(T22::CopyOnly, T22::CopyOnly&&), ""); +static_assert(__is_trivially_relocatable(T22::CopyOnly), "because its copy constructor is defaulted and its move constructor doesn't exist"); +static_assert(!__is_constructible(T22, const T22&), "m1 is not copyable"); +static_assert(__is_constructible(T22, T22&&), ""); +static_assert(!__is_trivially_constructible(T22, T22&&), "m2 is not trivially moveable"); +static_assert(__is_trivially_destructible(T22), "both members are trivially destructible"); +static_assert(__is_trivially_relocatable(T22), "because its members are trivially relocatable"); + + +struct T23 { + struct Evil { + Evil(Evil&); + Evil(Evil&&) = default; + ~Evil() = default; + }; + mutable Evil m; +}; +void relocate_example(T23&& src) { + T23 dst(static_cast(src)); // this moves m (trivial, defaulted) + src.~T23(); // this destroys m (trivial, defaulted) +} +static_assert(__is_trivially_constructible(T23::Evil, T23::Evil&&), ""); +static_assert(__is_trivially_destructible(T23::Evil), ""); +static_assert(__is_trivially_relocatable(T23::Evil), "trivially move-constructible and trivially destructible"); +static_assert(__is_constructible(T23, T23&), ""); +static_assert(!__is_constructible(T23, const T23&), ""); +static_assert(__is_trivially_constructible(T23, T23&&), ""); +static_assert(__is_trivially_destructible(T23), ""); +static_assert(!__is_trivially_relocatable(T23), "mutable member (even though this would be safe in practice)"); + +struct T23a { + struct Evil { + Evil(Evil&); + Evil(const Evil&) = default; + ~Evil() = default; + }; + mutable Evil m; +}; +void relocate_example(T23a&& src) { + T23a dst(static_cast(src)); // this copies m using the non-defaulted copy constructor + src.~T23a(); // this destroys m (trivial, defaulted) +} +static_assert(__is_trivially_constructible(T23a::Evil, T23a::Evil&&), ""); +static_assert(__is_trivially_destructible(T23a::Evil), ""); +static_assert(!__is_trivially_relocatable(T23a::Evil), "despite being trivially move-constructible and trivially destructible, it has a user-provided copy constructor"); +static_assert(__is_trivially_constructible(T23a, T23a&&), ""); +static_assert(__is_trivially_destructible(T23a), ""); +static_assert(!__is_trivially_relocatable(T23a), "because it has a non-trivially relocatable member"); + +struct T23b { + struct Evil { + Evil(Evil&) = delete; + Evil(const Evil&) = default; + ~Evil() = default; + }; + mutable Evil m; + T23b(const T23b&) = default; // no implicit move constructor +}; +static_assert(__is_trivially_constructible(T23b::Evil, T23b::Evil&&), ""); +static_assert(__is_trivially_destructible(T23b::Evil), ""); +static_assert(__is_trivially_relocatable(T23b::Evil), "it has no user-provided copy constructors"); +static_assert(!__is_constructible(T23b, T23b&&), ""); +static_assert(!__is_trivially_relocatable(T23b), "because it is not move-constructible"); + + +// Example from D1144R0 +struct string { + char *data_; + unsigned long size_ = 0; + unsigned long capacity_ = 0; + string() = default; + string(const char *s); + string(string&& s); + ~string(); +}; +static_assert(!__is_trivially_relocatable(string), ""); + +// Example from D1144R0 +struct offset_ptr { + unsigned long value_; + offset_ptr(); + offset_ptr(void *p); + offset_ptr(const offset_ptr& rhs); + offset_ptr& operator=(const offset_ptr& rhs); + ~offset_ptr() = default; +}; +static_assert(!__is_trivially_relocatable(offset_ptr), ""); + +// Example from D1144R0 +struct registered_object { + registered_object(); + registered_object(registered_object&&) = default; + registered_object(const registered_object&) = default; + registered_object& operator=(registered_object&&) = default; + registered_object& operator=(const registered_object&) = default; + ~registered_object(); +}; +struct Widget : registered_object {}; +static_assert(!__is_trivially_relocatable(registered_object), ""); +static_assert(!__is_trivially_relocatable(Widget), ""); + +// Examples from D1144R0 draft revision 11 +namespace ND11 { + struct M { + M() = default; + M(M&); + M(const M&) = default; + }; + static_assert( __is_trivially_constructible(M, M&&), "" ); + static_assert( __is_trivially_destructible(M), "" ); + static_assert( !__is_trivially_relocatable(M), "" ); + + struct N { + mutable M m; + }; + static_assert( __is_trivially_constructible(N, N&&), "" ); + static_assert( __is_trivially_destructible(N), "" ); + static_assert( !__is_trivially_relocatable(N), "" ); + + struct [[clang::trivially_relocatable]] O { + O(const O&); + mutable int o; + }; + static_assert( __is_trivially_relocatable(O), "" ); + + struct T : N { + T(const T&) = default; + }; + static_assert( !__is_trivially_constructible(T, T&&), "" ); + static_assert( __is_trivially_destructible(T), "" ); + static_assert( !__is_trivially_relocatable(T), "" ); + + struct U : N {}; + static_assert( __is_trivially_constructible(U, U&&), "" ); + static_assert( __is_trivially_destructible(U), "" ); + static_assert( !__is_trivially_relocatable(U), "" ); + + struct V { + O o; + }; + static_assert( __is_trivially_relocatable(V), "" ); + + struct W { + O o; + W(const W&) = default; + }; + static_assert( __is_trivially_relocatable(W), "" ); +} // namespace ND11 + +// Examples from D1144R0 draft revision 14 +namespace ND14 { + struct A { + struct MA { + MA(MA&); + MA(const MA&) = default; + MA(MA&&) = default; + }; + mutable MA ma; + A(const A&) = default; + }; + static_assert(!__is_trivially_relocatable(A), "calls user-provided MA(MA&)"); + + struct B { + struct MB { + MB(const volatile MB&); + MB(const MB&) = default; + MB(MB&&) = default; + }; + volatile MB mb; + B(const B&) = default; + }; + static_assert(!__is_trivially_relocatable(B), "calls user-provided MB(const volatile MB&)"); + + struct [[clang::trivially_relocatable]] I { + I(I&&); + }; + struct J : I { + J(const J&); + J(J&&) = default; + }; + static_assert(__is_trivially_relocatable(I), "has the attribute"); + static_assert(__is_trivially_relocatable(J), "inheritance pattern used by std::vector etc."); + + struct [[clang::trivially_relocatable]] K { + K(const K&&); + K(const K&); + K(K&&); + K(K&); + volatile int m1; + mutable int m2; + ~K(); + }; + struct L : K { + K k; + }; + static_assert(__is_trivially_relocatable(K), "the attribute should override all other considerations"); + static_assert(__is_trivially_relocatable(L), "the Rule of Zero should work as expected"); +} + +// Example from Nicolas Lesser +struct NL1 { + NL1& operator=(NL1&&); +}; +static_assert(!__is_trivially_relocatable(NL1), ""); + +struct [[clang::trivially_relocatable]] NL2 { +// expected-error@-1{{cannot be applied to struct 'NL2' because it is not move-constructible}} + NL2& operator=(NL2&&); +}; +static_assert(!__is_trivially_relocatable(NL2), ""); + +union [[clang::trivially_relocatable]] NL3 { +// expected-error@-1{{cannot be applied to union 'NL3' because it is not destructible}} + struct [[clang::trivially_relocatable]] String { String(String&&); ~String(); }; + int i; + String s; +}; +static_assert(!__is_trivially_relocatable(NL3), ""); + +union [[clang::trivially_relocatable]] NL4 { + struct [[clang::trivially_relocatable]] String { String(String&&); ~String(); }; + int i; + String s; + NL4(const NL4&); + ~NL4(); +}; +static_assert(__is_trivially_relocatable(NL4), ""); + +template +struct [[clang::trivially_relocatable]] NL5 { + T t; +}; +struct NL5a { + NL5a() = default; + NL5a(NL5a&&) = delete; +}; +struct NL5b { + NL5b() = default; + NL5b(NL5b&&); +}; +static_assert(!__is_trivially_relocatable(NL5), ""); +static_assert(__is_trivially_relocatable(NL5), ""); + +struct NL6 { + NL6(volatile NL6&) = delete; + NL6(const NL6&) = default; +}; +static_assert(__is_trivially_constructible(NL6, NL6&&), ""); +static_assert(__is_trivially_destructible(NL6), ""); +static_assert(__is_trivially_relocatable(NL6), "it is trivially move-constructible and trivially destructible");