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 @@ -476,6 +476,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; @@ -1508,6 +1513,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 @@ -2092,6 +2092,14 @@ let Documentation = [Undocumented]; } +def TriviallyRelocatable : InheritableAttr { + let Spellings = [CXX11<"", "trivially_relocatable", 200809>, + CXX11<"clang", "trivially_relocatable">]; + 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 @@ -2420,6 +2420,16 @@ }]; } +def TriviallyRelocatableDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +A class type declared as ``[[trivially_relocatable]]`` 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 @@ -2939,6 +2939,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; @@ -8199,6 +8204,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 @@ -232,6 +232,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 @@ -475,6 +475,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 @@ -1365,6 +1365,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 @@ -2230,6 +2230,19 @@ return false; } +bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const { + QualType T = Context.getBaseElementType(*this); + 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/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -3808,6 +3808,7 @@ case ParsedAttr::AT_Deprecated: case ParsedAttr::AT_FallThrough: case ParsedAttr::AT_CXX11NoReturn: + case ParsedAttr::AT_TriviallyRelocatable: return true; case ParsedAttr::AT_WarnUnusedResult: return !ScopeName && AttrName->getName().equals("nodiscard"); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -15790,6 +15790,12 @@ Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); } + if (CXXRecord) { + QualType FT = FD->getType(); + 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 @@ -5560,6 +5560,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()) { @@ -6454,6 +6472,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 @@ -6064,6 +6064,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) && @@ -6132,6 +6152,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 @@ -4407,6 +4407,7 @@ case UTT_IsDestructible: case UTT_IsNothrowDestructible: case UTT_IsTriviallyDestructible: + case UTT_IsTriviallyRelocatable: case UTT_HasUniqueObjectRepresentations: if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType()) return true; @@ -4696,7 +4697,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 @@ -1671,6 +1671,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(); @@ -1812,6 +1813,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 @@ -6045,6 +6045,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 74 attributes: +// CHECK: #pragma clang attribute supports 75 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -74,6 +74,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: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType) // CHECK-NEXT: XRayInstrument (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function, SubjectMatchRule_objc_method) Index: test/SemaCXX/trivially-relocatable.cpp =================================================================== --- /dev/null +++ test/SemaCXX/trivially-relocatable.cpp @@ -0,0 +1,592 @@ +// 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 [[trivially_relocatable, trivially_relocatable]] B1 {}; +// expected-error@-1{{attribute 'trivially_relocatable' cannot appear multiple times in an attribute specifier}} + +struct [[trivially_relocatable]] [[trivially_relocatable]] B2 {}; // should really be an error + +struct [[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 [[trivially_relocatable]] A1 {}; // ok +struct [[trivially_relocatable]] A1; + +struct [[trivially_relocatable]] A2; // ok +struct [[trivially_relocatable]] A2 {}; + +struct [[trivially_relocatable]] A3 {}; // ok +struct A3; + +struct [[trivially_relocatable]] A4; // ok +struct A4 {}; + +struct A5 {}; +struct [[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 [[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 [[trivially_relocatable]] D1 { ~D1() = delete; }; +// expected-error@-1{{cannot be applied to struct 'D1' because it is not destructible}} + +struct [[trivially_relocatable]] D2 : private NonDestructible { }; +// expected-error@-1{{cannot be applied to struct 'D2' because it is not destructible}} + +struct [[trivially_relocatable]] D3 { D3(const D3&) = delete; }; +// expected-error@-1{{cannot be applied to struct 'D3' because it is not move-constructible}} + +struct [[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 [[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 [[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 [[trivially_relocatable]] DT1 : private T { }; // ok + +struct D7 { + DT1 m; +}; + +class [[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), ""); +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 [[trivially_relocatable]] T9 { int x; T9(T9&&) {} }; +static_assert(__is_trivially_relocatable(T9), "T9 isn't naturally, but it has the attribute"); + +struct [[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 [[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 [[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 [[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 [[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 [[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), ""); + +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 +namespace ND { + 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 [[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 ND + +// Example from Nicolas Lesser +struct NL1 { + NL1& operator=(NL1&&); +}; +static_assert(!__is_trivially_relocatable(NL1), ""); + +struct [[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 [[trivially_relocatable]] NL3 { +// expected-error@-1{{cannot be applied to union 'NL3' because it is not destructible}} + struct [[trivially_relocatable]] String { String(String&&); ~String(); }; + int i; + String s; +}; +static_assert(!__is_trivially_relocatable(NL3), ""); + +union [[trivially_relocatable]] NL4 { + struct [[trivially_relocatable]] String { String(String&&); ~String(); }; + int i; + String s; + NL4(const NL4&); + ~NL4(); +}; +static_assert(__is_trivially_relocatable(NL4), ""); + +template +struct [[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");