Quuxplusone updated this revision to Diff 161190. Quuxplusone added a comment.
A type with no move-constructor, and some non-defaulted copy-constructors (even if it *also* has some defaulted copy-constructors) should not be considered naturally trivially relocatable. The paper already said this, but the Clang patch was misimplemented. Thanks to @Rakete1111 for either finding or inspiring this fix. :) I suspect there may be some way of doing this without the extra bitflag, but I didn't immediately see how. Repository: rC Clang https://reviews.llvm.org/D50119 Files: docs/LanguageExtensions.rst include/clang/AST/DeclCXX.h include/clang/AST/Type.h include/clang/Basic/Attr.td include/clang/Basic/AttrDocs.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Basic/Features.def include/clang/Basic/TokenKinds.def include/clang/Basic/TypeTraits.h lib/AST/ASTImporter.cpp lib/AST/DeclCXX.cpp lib/AST/Type.cpp lib/Parse/ParseDeclCXX.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclAttr.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaExprCXX.cpp lib/Serialization/ASTReaderDecl.cpp lib/Serialization/ASTWriter.cpp test/Lexer/has_extension_cxx.cpp test/Misc/pragma-attribute-supported-attributes-list.test test/SemaCXX/trivially-relocatable.cpp
Index: test/SemaCXX/trivially-relocatable.cpp =================================================================== --- /dev/null +++ test/SemaCXX/trivially-relocatable.cpp @@ -0,0 +1,518 @@ +// 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<class T> +struct [[trivially_relocatable]] DT1 : private T { }; // ok + +struct D7 { + DT1<NonDestructible> m; +}; + +class [[trivially_relocatable]] D8 { + DT1<NonDestructible> 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<VD&&>(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<VD2&&>(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<VD3&&>(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<VB&&>(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<VB2&&>(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<CCNMC&&>(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<CCDMC&&>(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<class... Ts> +struct T15 : Ts... {}; + +static_assert(__is_trivially_relocatable(T15<T1,T2,T3>), "all bases have trivially relocatable types"); +static_assert(!__is_trivially_relocatable(T15<T1,T6,T3>), "not all bases have trivially relocatable types"); + +template<class... Ts> +struct [[trivially_relocatable]] T16 : Ts... {}; + +static_assert(__is_trivially_relocatable(T16<T1,T2,T3>), "all bases have trivially relocatable types"); +static_assert(__is_trivially_relocatable(T16<T1,T6,T3>), "not naturally, but it has the attribute"); + +struct T17 : T15<T10> {}; // T10 is trivially relocatable +static_assert(__is_trivially_relocatable(T17), ""); +static_assert(__is_trivially_relocatable(T15<T17>), ""); +static_assert(__is_trivially_relocatable(T16<T17>), ""); + +struct T18 : T15<T12> {}; // T12 is not trivially relocatable +static_assert(!__is_trivially_relocatable(T18), ""); +static_assert(!__is_trivially_relocatable(T15<T18>), ""); +static_assert(__is_trivially_relocatable(T16<T18>), "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<T20&&>(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<T21&&>(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<T22&&>(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<T23&&>(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<T23a&&>(src)); // this copies m using the non-defaulted copy constructor + src.~T23a(); // this destroys m (trivial, defaulted) +} +static_assert(!__is_trivially_relocatable(T23a::Evil), "it has no move constructor, and one of its two copy constructors is non-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), ""); + +// 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<class T> +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<NL5a>), ""); +static_assert(__is_trivially_relocatable(NL5<NL5b>), ""); + +struct NL6 { + NL6(volatile NL6&) = delete; + NL6(const NL6&) = default; +}; +static_assert(__is_trivially_constructible(NL6, const NL6&), ""); +static_assert(!__is_constructible(NL6, NL6&), ""); +static_assert(!__is_trivially_relocatable(NL6), "it has no move constructor and some deleted copy constructor"); 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/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: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -6051,14 +6051,16 @@ 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); Record->push_back(Data.DefaultedDefaultConstructorIsConstexpr); Record->push_back(Data.HasConstexprDefaultConstructor); Record->push_back(Data.HasNonLiteralTypeFieldsOrBases); Record->push_back(Data.ComputedVisibleConversions); Record->push_back(Data.UserProvidedDefaultConstructor); + Record->push_back(Data.NonDefaultedCopyConstructor); Record->push_back(Data.DeclaredSpecialMembers); Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForVBase); Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1671,14 +1671,16 @@ 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(); Data.DefaultedDefaultConstructorIsConstexpr = Record.readInt(); Data.HasConstexprDefaultConstructor = Record.readInt(); Data.HasNonLiteralTypeFieldsOrBases = Record.readInt(); Data.ComputedVisibleConversions = Record.readInt(); Data.UserProvidedDefaultConstructor = Record.readInt(); + Data.NonDefaultedCopyConstructor = Record.readInt(); Data.DeclaredSpecialMembers = Record.readInt(); Data.ImplicitCopyConstructorCanHaveConstParamForVBase = Record.readInt(); Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase = Record.readInt(); @@ -1812,14 +1814,16 @@ OR_FIELD(HasTrivialSpecialMembersForCall) OR_FIELD(DeclaredNonTrivialSpecialMembers) OR_FIELD(DeclaredNonTrivialSpecialMembersForCall) + MATCH_FIELD(IsNaturallyTriviallyRelocatable) MATCH_FIELD(HasIrrelevantDestructor) OR_FIELD(HasConstexprNonCopyMoveConstructor) OR_FIELD(HasDefaultedDefaultConstructor) MATCH_FIELD(DefaultedDefaultConstructorIsConstexpr) OR_FIELD(HasConstexprDefaultConstructor) MATCH_FIELD(HasNonLiteralTypeFieldsOrBases) // ComputedVisibleConversions is handled below. MATCH_FIELD(UserProvidedDefaultConstructor) + MATCH_FIELD(NonDefaultedCopyConstructor) OR_FIELD(DeclaredSpecialMembers) MATCH_FIELD(ImplicitCopyConstructorCanHaveConstParamForVBase) MATCH_FIELD(ImplicitCopyConstructorCanHaveConstParamForNonVBase) 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/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -6064,6 +6064,21 @@ Record->setTrivialForCallFlags(M); } + // A move-constructible, destructible object type T is a + // trivially relocatable type if it ... + if ((CSM == CXXMoveConstructor || CSM == CXXDestructor) && + (M->isUserProvided() || M->isDeleted())) { + // - has either a defaulted, non-deleted move constructor or no + // move constructor and a defaulted, non-deleted copy constructor, + // - 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<FinalAttr>() && !Record->hasAttr<FinalAttr>()) + Record->setIsNotNaturallyTriviallyRelocatable(); + } + if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() && M->hasAttr<DLLExportAttr>()) { if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && @@ -6132,6 +6147,52 @@ // is especially required for cases like vtable assumption loads. MarkVTableUsed(Record->getInnerLocStart(), Record); } + + if (getLangOpts().CPlusPlus11 && Record->isTriviallyRelocatable() && + !Record->hasMoveConstructor() && !Record->isDependentContext()) { + // Check that all of the class's copy constructors are defaulted + // and non-deleted; and check that move-construction is possible. + if (Record->hasNonDefaultedCopyConstructor()) { + Record->setIsNotNaturallyTriviallyRelocatable(); + } else { + SpecialMemberOverloadResult SMOR = LookupSpecialMember( + Record, CXXMoveConstructor, false, false, false, false, false); + if (SMOR.getKind() != SpecialMemberOverloadResult::Success) + Record->setIsNotNaturallyTriviallyRelocatable(); + } + } + + if (getLangOpts().CPlusPlus11 && + Record->hasAttr<TriviallyRelocatableAttr>()) { + if (!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->dropAttr<TriviallyRelocatableAttr>(); + 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->dropAttr<TriviallyRelocatableAttr>(); + 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/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -5560,6 +5560,24 @@ AL.getAttributeSpellingListIndex())); } +template<class AttrType> +static void checkAttributeNotOnFirstDecl(Sema &S, Decl *D, const ParsedAttr &AL) { + Decl *FirstD = D->getCanonicalDecl(); + if (FirstD != D && !FirstD->hasAttr<AttrType>()) { + NamedDecl *ND = dyn_cast<NamedDecl>(D); + S.Diag(AL.getLoc(), diag::err_attribute_missing_on_first_decl) + << (ND ? ND->getDeclName().getAsString() : "<unnamed>") << 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<TriviallyRelocatableAttr>(S, D, AL); + handleSimpleAttribute<TriviallyRelocatableAttr>(S, D, AL); +} + DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex) { if (D->hasAttr<DLLExportAttr>()) { @@ -6441,6 +6459,9 @@ case ParsedAttr::AT_TrivialABI: handleSimpleAttribute<TrivialABIAttr>(S, D, AL); break; + case ParsedAttr::AT_TriviallyRelocatable: + handleTriviallyRelocatableAttr(S, D, AL); + break; case ParsedAttr::AT_MSNoVTable: handleSimpleAttribute<MSNoVTableAttr>(S, D, AL); break; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -15790,6 +15790,14 @@ Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); } + if (CXXRecord) { + QualType FT = FD->getType(); + if (FD->isMutable()) + 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/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/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2242,6 +2242,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/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -91,13 +91,15 @@ 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), HasConstexprDefaultConstructor(false), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), - UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), + UserProvidedDefaultConstructor(false), NonDefaultedCopyConstructor(false), + DeclaredSpecialMembers(0), ImplicitCopyConstructorCanHaveConstParamForVBase(true), ImplicitCopyConstructorCanHaveConstParamForNonVBase(true), ImplicitCopyAssignmentHasConstParam(true), @@ -278,6 +280,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. @@ -726,6 +731,8 @@ if (Quals & Qualifiers::Const) data().HasDeclaredCopyConstructorWithConstParam = true; + if (Constructor->isUserProvided() || (Constructor->isDeleted() && !Constructor->isImplicit())) + data().NonDefaultedCopyConstructor = true; } else if (Constructor->isMoveConstructor()) SMKind |= SMF_MoveConstructor; } 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; @@ -1379,6 +1381,8 @@ // ComputedVisibleConversions not imported. ToData.UserProvidedDefaultConstructor = FromData.UserProvidedDefaultConstructor; + ToData.NonDefaultedCopyConstructor + = FromData.NonDefaultedCopyConstructor; ToData.DeclaredSpecialMembers = FromData.DeclaredSpecialMembers; ToData.ImplicitCopyConstructorCanHaveConstParamForVBase = FromData.ImplicitCopyConstructorCanHaveConstParamForVBase; 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/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/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/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2935,6 +2935,11 @@ def ext_cannot_use_trivial_abi : ExtWarn< "'trivial_abi' cannot be applied to %0">, InGroup<IgnoredAttributes>; +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<Availability>; @@ -8195,6 +8200,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/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/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -2075,6 +2075,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/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -795,6 +795,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/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; @@ -509,6 +514,10 @@ /// explicitly deleted or defaulted). unsigned UserProvidedDefaultConstructor : 1; + /// Whether we have any non-defaulted (user-provided or explicitly deleted) + /// copy constructor. + unsigned NonDefaultedCopyConstructor : 1; + /// The special members which have been declared for this class, /// either by the user or implicitly. unsigned DeclaredSpecialMembers : 6; @@ -1001,6 +1010,10 @@ return data().UserProvidedDefaultConstructor; } + bool hasNonDefaultedCopyConstructor() const { + return data().NonDefaultedCopyConstructor; + } + /// Determine whether this class has a user-declared copy constructor. /// /// When false, a copy constructor will be implicitly declared. @@ -1508,6 +1521,16 @@ (SMF_CopyConstructor | SMF_MoveConstructor | SMF_Destructor); } + /// Determine whether this class is trivially relocatable + bool isTriviallyRelocatable() const { + return data().IsNaturallyTriviallyRelocatable || + hasAttr<TriviallyRelocatableAttr>(); + } + + void setIsNotNaturallyTriviallyRelocatable() { + data().IsNaturallyTriviallyRelocatable = false; + } + /// Determine whether declaring a const variable with this type is ok /// per core issue 253. bool allowConstDefaultInit() const { 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)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits