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, + /// 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; @@ -1500,6 +1505,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/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -2060,6 +2060,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 @@ -2404,6 +2404,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 @@ -2935,6 +2935,12 @@ 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{class|struct|interface|union|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; @@ -8186,6 +8192,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: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4301,6 +4301,8 @@ Expr *LowerBound, SourceLocation ColonLoc, Expr *Length, SourceLocation RBLoc); + bool IsTriviallyRelocatableType(QualType QT) const; + // This struct is for use by ActOnMemberAccess to allow // BuildMemberReferenceExpr to be able to reinvoke ActOnMemberAccess after // changing the access operator from a '.' to a '->' (to see if that is the Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -1336,6 +1336,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,11 @@ if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C)) data().HasNonLiteralTypeFieldsOrBases = true; + if (Base->isVirtual() || !BaseClassDecl->isTriviallyRelocatable()) { + //puts("because 283"); + 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/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 @@ -15820,6 +15820,7 @@ if (Dtor && Dtor->isImplicit() && ShouldDeleteSpecialMember(Dtor, CXXDestructor)) { CXXRecord->setImplicitDestructorIsDeleted(); + CXXRecord->setIsNotNaturallyTriviallyRelocatable(); SetDeclDeleted(Dtor, CXXRecord->getLocation()); } } Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -5579,6 +5579,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()) { @@ -6460,6 +6478,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 @@ -6057,6 +6057,19 @@ Record->setTrivialForCallFlags(M); } + if ((CSM == CXXMoveConstructor || CSM == CXXDestructor) && + (M->isUserProvided() || M->isDeleted())) { + //puts("because 6086"); + Record->setIsNotNaturallyTriviallyRelocatable(); + } else if (CSM == CXXDestructor && M->isVirtual()) { + if (M->hasAttr() || Record->hasAttr()) { + // Consider removing this case to simplify the Standard wording. + } else { + //puts("because 6090"); + Record->setIsNotNaturallyTriviallyRelocatable(); + } + } + if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() && M->hasAttr()) { if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && @@ -6073,6 +6086,21 @@ } } + for (auto *F : Record->fields()) { + if (F->isMutable()) { + //puts("because 6126"); + Record->setIsNotNaturallyTriviallyRelocatable(); + break; + } + + QualType FT = F->getType(); + if (!FT->isReferenceType() && !IsTriviallyRelocatableType(FT)) { + //puts("because 6130"); + Record->setIsNotNaturallyTriviallyRelocatable(); + break; + } + } + if (HasMethodWithOverrideControl && HasOverridingMethodWithoutOverrideControl) { // At least one method has the 'override' control declared. @@ -6125,6 +6153,47 @@ // is especially required for cases like vtable assumption loads. MarkVTableUsed(Record->getInnerLocStart(), Record); } + + if (getLangOpts().CPlusPlus11 && + !Record->hasAttr() && + Record->isTriviallyRelocatable()) { + + if (Record->getDefinition() && !Record->isDependentContext() && !Record->isBeingDefined()) { + if (!Record->needsImplicitMoveConstructor()) { + // Check that the constructor used for move-construction is defaulted and non-deleted. + SpecialMemberOverloadResult SMOR = + LookupSpecialMember(Record, CXXMoveConstructor, false, false, false, false, false); + if (SMOR.getKind() != SpecialMemberOverloadResult::Success || !SMOR.getMethod()->isDefaulted()) { + //puts("because 6190"); + Record->setIsNotNaturallyTriviallyRelocatable(); + } + } + } + } + + if (getLangOpts().CPlusPlus11 && + Record->hasAttr() && + !isTemplateInstantiation(Record->getTemplateSpecializationKind())) { + if (Record->getDefinition() && !Record->isDependentContext() && !Record->isBeingDefined()) { + // Check that the destructor is non-deleted. + SpecialMemberOverloadResult SMOR = + LookupSpecialMember(Record, CXXDestructor, false, false, false, false, false); + if (SMOR.getKind() != SpecialMemberOverloadResult::Success) { + Diag(Record->getLocation(), diag::err_trivially_relocatable_class_is_not_relocatable) + << Record->getCanonicalDecl()->getTagKind() << Context.getRecordType(Record) << true; + } else if (Record->needsImplicitMoveConstructor() && Record->defaultedMoveConstructorIsDeleted()) { + Diag(Record->getLocation(), diag::err_trivially_relocatable_class_is_not_relocatable) + << Record->getCanonicalDecl()->getTagKind() << Context.getRecordType(Record) << false; + } 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) { + 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 @@ -6584,6 +6653,10 @@ if (ShouldDeleteSpecialMember(MD, CSM)) { if (First) { SetDeclDeleted(MD, MD->getLocation()); + if (CSM == CXXMoveConstructor || CSM == CXXDestructor) { + //puts("because 6646"); + RD->setIsNotNaturallyTriviallyRelocatable(); + } } else { // C++11 [dcl.fct.def.default]p4: // [For a] user-provided explicitly-defaulted function [...] if such a @@ -12548,6 +12621,8 @@ if (ShouldDeleteSpecialMember(MoveConstructor, CXXMoveConstructor)) { ClassDecl->setImplicitMoveConstructorIsDeleted(); + //puts("because 12631"); + ClassDecl->setIsNotNaturallyTriviallyRelocatable(); SetDeclDeleted(MoveConstructor, ClassLoc); } Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -4415,6 +4415,7 @@ case UTT_IsDestructible: case UTT_IsNothrowDestructible: case UTT_IsTriviallyDestructible: + case UTT_IsTriviallyRelocatable: case UTT_HasUniqueObjectRepresentations: if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType()) return true; @@ -4704,7 +4705,8 @@ } } return true; - + case UTT_IsTriviallyRelocatable: + return Self.IsTriviallyRelocatableType(T); case UTT_HasTrivialDestructor: // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html // If __is_pod (type) is true or type is a reference type @@ -4860,6 +4862,19 @@ } } +bool Sema::IsTriviallyRelocatableType(QualType QT) const { + QualType T = Context.getBaseElementType(QT); + 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); + } +} + static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT, QualType RhsT, SourceLocation KeyLoc); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1665,6 +1665,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(); @@ -1806,6 +1807,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 @@ -6050,6 +6050,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 72 attributes: +// CHECK: #pragma clang attribute supports 73 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -72,6 +72,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,478 @@ +// 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 class 'D1' because it is not destructible}} + +struct [[trivially_relocatable]] D2 : private NonDestructible { }; +// expected-error@-1{{cannot be applied to class 'D2' because it is not destructible}} + +struct [[trivially_relocatable]] D3 { D3(const D3&) = delete; }; +// expected-error@-1{{cannot be applied to class '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 class 'D4' because it is not move-constructible}} + +struct [[trivially_relocatable]] D5 : private NonCopyConstructible { }; +// expected-error@-1{{cannot be applied to class '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 class 'D6' because it is not move-constructible}} + +template +struct [[trivially_relocatable]] DT1 : private T { }; // ok + +struct D7 { + DT1 m; +}; + +struct [[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"); + + +struct T19 { + struct [[trivially_relocatable]] UniquePtr { + UniquePtr(); + UniquePtr(const UniquePtr&) = delete; + UniquePtr(UniquePtr&&); + ~UniquePtr(); + }; + UniquePtr 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), "nevertheless, the Rule of Zero applies here"); + + +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_relocatable(T23::Evil), "because it is trivially move-constructible and destructible"); +static_assert(__is_constructible(T23, T23&&), ""); +static_assert(__is_constructible(T23, T23&), ""); +static_assert(!__is_constructible(T23, const T23&), ""); +static_assert(!__is_trivially_relocatable(T23), "because its copy operation is evil"); + +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_relocatable(T23a::Evil), "its defaulted move-constructor is trivial"); +static_assert(__is_constructible(T23a, T23a&&), ""); +static_assert(__is_constructible(T23a, T23a&), ""); +static_assert(__is_constructible(T23a, const T23a&), ""); +static_assert(!__is_trivially_relocatable(T23a), "because its copy operation is evil"); + + +// 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), ""); + + +// Regression tests. +struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}} +struct Regression1 { + Incomplete i; // expected-error {{field has incomplete type 'Incomplete'}} +}; + +template +struct Regression2 { + ~Regression2(); +};