Quuxplusone updated this revision to Diff 174513.
Quuxplusone added a comment.
Implement `[[clang::maybe_trivially_relocatable]]` along the lines suggested by
@rjmccall. The idea is that there are two levels of "opt-in-ness."
The first level, `[[clang::maybe_trivially_relocatable]]`, means "I warrant
that even though I may have user-provided, non-defaulted, special member
functions, I have designed them so that my relocation operation will not do
anything substantially different from memberwise relocation." So if all of my
member+base subobjects are trivially relocatable (and not mutable and not
volatile), then I myself will be trivially relocatable.
The second level, `[[clang::trivially_relocatable]]`, means "I warrant that
even though I may have user-provided, non-defaulted, special member functions,
and even though I may have non-trivially relocatable (or mutable or volatile)
subobjects, I have designed them so that my relocation operation will not do
anything substantially different from memcpy." So I myself will be trivially
relocatable //no matter// what my subobjects claim about themselves.
Significantly, this means that I can write a class that //overrules// a
decision made by one of its members.
- I can make a `TriviallyRelocatableWidget` that encloses a heap and a bunch of
offset_ptrs into that heap, even though a single offset_ptr in isolation is not
trivially relocatable.
- I can make a `TriviallyRelocatableWidget` that encloses a
`boost::shared_ptr<int>`, even though the maintainer of `boost::shared_ptr` may
not have marked `boost::shared_ptr` as trivially relocatable.
- (Of dubious usefulness) I can make a `BaseClass` that is not marked trivially
relocatable, and then make a `TriviallyRelocatableDerivedClass` that
//strengthens// the guarantees of its `BaseClass` while remaining
Liskov-substitutable for it.
Now that `[[clang::maybe_trivially_relocatable]]` is implemented, we can see if
it does actually simplify the libc++ patch. I will try to get to that tonight.
The current libc++ patch is here:
https://github.com/Quuxplusone/libcxx/tree/trivially-relocatable
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/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/maybe-trivially-relocatable.cpp
test/SemaCXX/trivially-relocatable.cpp
Index: test/SemaCXX/trivially-relocatable.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/trivially-relocatable.cpp
@@ -0,0 +1,645 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// expected-diagnostics
+
+static_assert(__has_extension(trivially_relocatable), "");
+
+// It shall appear at most once in each attribute-list
+// and no attribute-argument-clause shall be present.
+
+struct [[clang::trivially_relocatable, clang::trivially_relocatable]] B1 {}; // should really be an error
+
+struct [[clang::trivially_relocatable]] [[clang::trivially_relocatable]] B2 {}; // should really be an error
+
+struct [[clang::trivially_relocatable(42)]] B3 {};
+// expected-error@-1{{'trivially_relocatable' attribute takes no arguments}}
+
+
+// The first declaration of a type shall specify the
+// trivially_relocatable attribute if any declaration of that
+// type specifies the trivially_relocatable attribute.
+
+struct [[clang::trivially_relocatable]] A1 {}; // ok
+struct [[clang::trivially_relocatable]] A1;
+
+struct [[clang::trivially_relocatable]] A2; // ok
+struct [[clang::trivially_relocatable]] A2 {};
+
+struct [[clang::trivially_relocatable]] A3 {}; // ok
+struct A3;
+
+struct [[clang::trivially_relocatable]] A4; // ok
+struct A4 {};
+
+struct A5 {};
+struct [[clang::trivially_relocatable]] A5;
+// expected-error@-1{{type A5 declared 'trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}}
+// expected-warning@-3{{attribute declaration must precede definition}}
+// expected-note@-5{{previous definition is here}}
+
+struct A6;
+struct [[clang::trivially_relocatable]] A6 {};
+// expected-error@-1{{type A6 declared 'trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}}
+
+
+// If a type T is declared with the trivially_relocatable attribute, and T is
+// either not move-constructible or not destructible, the program is ill-formed.
+
+struct NonDestructible {
+ NonDestructible(const NonDestructible&) = default;
+ NonDestructible(NonDestructible&&) = default;
+ ~NonDestructible() = delete;
+};
+struct NonCopyConstructible {
+ NonCopyConstructible(const NonCopyConstructible&) = delete;
+};
+struct NonMoveConstructible {
+ NonMoveConstructible(const NonMoveConstructible&) = default;
+ NonMoveConstructible(NonMoveConstructible&&) = delete;
+ // expected-note@-1{{explicitly marked deleted here}}
+};
+static_assert(!__is_trivially_relocatable(NonDestructible), "");
+static_assert(!__is_trivially_relocatable(NonCopyConstructible), "");
+static_assert(!__is_constructible(NonCopyConstructible, NonCopyConstructible&&), "");
+static_assert(!__is_trivially_relocatable(NonMoveConstructible), "");
+static_assert(!__is_constructible(NonMoveConstructible, NonMoveConstructible&&), "");
+
+struct [[clang::trivially_relocatable]] D1 { ~D1() = delete; };
+// expected-error@-1{{cannot be applied to struct 'D1' because it is not destructible}}
+
+struct [[clang::trivially_relocatable]] D2 : private NonDestructible { };
+// expected-error@-1{{cannot be applied to struct 'D2' because it is not destructible}}
+
+struct [[clang::trivially_relocatable]] D3 { D3(const D3&) = delete; };
+// expected-error@-1{{cannot be applied to struct 'D3' because it is not move-constructible}}
+
+struct [[clang::trivially_relocatable]] D4 { D4(const D4&) = default; D4(D4&&) = delete; };
+// expected-error@-1{{cannot be applied to struct 'D4' because it is not move-constructible}}
+
+struct [[clang::trivially_relocatable]] D5 : private NonCopyConstructible { };
+// expected-error@-1{{cannot be applied to struct 'D5' because it is not move-constructible}}
+static_assert(!__is_constructible(D5, D5&&), "");
+
+struct [[clang::trivially_relocatable]] D6 : private NonMoveConstructible { D6(D6&&) = default; };
+// expected-warning@-1{{explicitly defaulted move constructor is implicitly deleted}}
+// expected-note@-2{{implicitly deleted because}}
+// expected-error@-3{{cannot be applied to struct 'D6' because it is not move-constructible}}
+
+template<class T>
+struct [[clang::trivially_relocatable]] DT1 : private T { }; // ok
+
+struct D7 {
+ DT1<NonDestructible> m;
+};
+
+class [[clang::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), "volatile member");
+struct C4 { int *x; }; static_assert(__is_trivially_relocatable(C4), "");
+struct C5 { const int *x; }; static_assert(__is_trivially_relocatable(C5), "");
+struct C6 { volatile int *x; }; static_assert(__is_trivially_relocatable(C6), "");
+struct C7 { int& x; }; static_assert(__is_trivially_relocatable(C7), "");
+struct C8 { const int& x; }; static_assert(__is_trivially_relocatable(C8), "");
+struct C9 { volatile int& x; }; static_assert(__is_trivially_relocatable(C9), "");
+
+enum E { x = 1, y = 2 };
+static_assert(__is_trivially_relocatable(E), "");
+static_assert(__is_trivially_relocatable(E[1]), "");
+
+struct T1 {};
+static_assert(__is_trivially_relocatable(T1), "");
+
+struct T2 { int x; E y; int *z; };
+static_assert(__is_trivially_relocatable(T2), "");
+
+struct T3 { int x; T3(T3&&) = default; };
+static_assert(__is_trivially_relocatable(T3), "");
+
+struct T4 { int x; ~T4() = default; };
+static_assert(__is_trivially_relocatable(T4), "trivially copy-constructible, and no move constructor");
+
+struct T4a { T4 a; };
+static_assert(__is_trivially_relocatable(T4a), "trivially copy-constructible, and no move constructor");
+
+
+struct VD {
+ VD(const VD&) = default;
+ VD(VD&&) = default;
+ virtual ~VD() = default;
+};
+void relocate_example(VD&& src) {
+ VD dst(static_cast<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 [[clang::trivially_relocatable]] T9 { int x; T9(T9&&) {} };
+static_assert(__is_trivially_relocatable(T9), "T9 isn't naturally, but it has the attribute");
+
+struct [[clang::trivially_relocatable]] T10 { int x; ~T10() {} };
+static_assert(__is_trivially_relocatable(T10), "T10 isn't naturally, but it has the attribute");
+
+struct T11 {
+ T11();
+ T1 a;
+ T2 b;
+ T3 c;
+ T4 d;
+ T8 e;
+ T9 f;
+ T10 g;
+};
+static_assert(__is_trivially_relocatable(T11), "all fields have trivially relocatable types");
+
+struct T12 {
+ T1 a;
+ T2 b;
+ T3 c;
+ T5 d; // not trivially relocatable
+ T8 e;
+ T9 f;
+ T10 g;
+};
+static_assert(!__is_trivially_relocatable(T12), "not all fields have trivially relocatable types");
+
+struct T13 : T1, T2, T3, T4 {};
+static_assert(__is_trivially_relocatable(T13), "all bases have trivially relocatable types");
+
+struct T14 : T1, T6, T3, T4 {};
+static_assert(!__is_trivially_relocatable(T14), "all bases have trivially relocatable types");
+
+template<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 [[clang::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");
+
+
+// This pattern is used heavily by libc++.
+struct T19 {
+ struct [[clang::trivially_relocatable]] Base {
+ Base(Base&&);
+ ~Base();
+ };
+ Base m;
+ T19(const T19&);
+ T19(T19&&) = default;
+};
+
+static_assert(!__is_trivially_constructible(T19, const T19&), "user-provided copy constructor");
+static_assert(!__is_trivially_constructible(T19, T19&&), "defaulted non-trivial move constructor");
+static_assert(!__is_trivially_destructible(T19), "defaulted non-trivial destructor");
+static_assert(__is_trivially_relocatable(T19), "Rule of Zero");
+
+
+struct T20 {
+ struct [[clang::trivially_relocatable]] SharedPtr {
+ SharedPtr();
+ SharedPtr(const SharedPtr&);
+ SharedPtr(SharedPtr&&);
+ ~SharedPtr();
+ };
+ SharedPtr m;
+ T20(const T20&) = default;
+ ~T20() = default;
+ // no move constructor
+};
+void relocate_example(T20&& src) {
+ T20 dst(static_cast<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 [[clang::trivially_relocatable]] SharedPtr {
+ SharedPtr();
+ SharedPtr(const SharedPtr&);
+ SharedPtr(SharedPtr&&);
+ ~SharedPtr();
+ };
+ SharedPtr m;
+ T21(const T21&); // user-provided
+ ~T21() = default;
+ // no move constructor
+};
+void relocate_example(T21&& src) {
+ T21 dst(static_cast<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 [[clang::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_constructible(T23::Evil, T23::Evil&&), "");
+static_assert(__is_trivially_destructible(T23::Evil), "");
+static_assert(__is_trivially_relocatable(T23::Evil), "trivially move-constructible and trivially destructible");
+static_assert(__is_constructible(T23, T23&), "");
+static_assert(!__is_constructible(T23, const T23&), "");
+static_assert(__is_trivially_constructible(T23, T23&&), "");
+static_assert(__is_trivially_destructible(T23), "");
+static_assert(!__is_trivially_relocatable(T23), "mutable member (even though this would be safe in practice)");
+
+struct T23a {
+ struct Evil {
+ Evil(Evil&);
+ Evil(const Evil&) = default;
+ ~Evil() = default;
+ };
+ mutable Evil m;
+};
+void relocate_example(T23a&& src) {
+ T23a dst(static_cast<T23a&&>(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; // expected-note{{explicitly marked deleted here}}
+ Evil(const Evil&) = default;
+ ~Evil() = default;
+ };
+ mutable Evil m; // expected-note{{implicitly deleted because}}
+ T23b(const T23b&) = default; // no implicit move constructor
+ // expected-warning@-1{{explicitly defaulted copy constructor is implicitly deleted}}
+};
+static_assert(__is_trivially_constructible(T23b::Evil, T23b::Evil&&), "");
+static_assert(__is_trivially_destructible(T23b::Evil), "");
+static_assert(__is_trivially_relocatable(T23b::Evil), "it has no user-provided copy constructors");
+static_assert(!__is_constructible(T23b, T23b&&), "");
+static_assert(!__is_trivially_relocatable(T23b), "because it is not move-constructible");
+
+
+// Example from D1144R0
+struct string {
+ char *data_;
+ unsigned long size_ = 0;
+ unsigned long capacity_ = 0;
+ string() = default;
+ string(const char *s);
+ string(string&& s);
+ ~string();
+};
+static_assert(!__is_trivially_relocatable(string), "");
+
+// Example from D1144R0
+struct offset_ptr {
+ unsigned long value_;
+ offset_ptr();
+ offset_ptr(void *p);
+ offset_ptr(const offset_ptr& rhs);
+ offset_ptr& operator=(const offset_ptr& rhs);
+ ~offset_ptr() = default;
+};
+static_assert(!__is_trivially_relocatable(offset_ptr), "");
+
+// Example from D1144R0
+struct registered_object {
+ registered_object();
+ registered_object(registered_object&&) = default;
+ registered_object(const registered_object&) = default;
+ registered_object& operator=(registered_object&&) = default;
+ registered_object& operator=(const registered_object&) = default;
+ ~registered_object();
+};
+struct Widget : registered_object {};
+static_assert(!__is_trivially_relocatable(registered_object), "");
+static_assert(!__is_trivially_relocatable(Widget), "");
+
+// Examples from D1144R0 draft revision 11
+namespace ND11 {
+ struct M {
+ M() = default;
+ M(M&);
+ M(const M&) = default;
+ };
+ static_assert( __is_trivially_constructible(M, M&&), "" );
+ static_assert( __is_trivially_destructible(M), "" );
+ static_assert( !__is_trivially_relocatable(M), "" );
+
+ struct N {
+ mutable M m;
+ };
+ static_assert( __is_trivially_constructible(N, N&&), "" );
+ static_assert( __is_trivially_destructible(N), "" );
+ static_assert( !__is_trivially_relocatable(N), "" );
+
+ struct [[clang::trivially_relocatable]] O {
+ O(const O&);
+ mutable int o;
+ };
+ static_assert( __is_trivially_relocatable(O), "" );
+
+ struct T : N {
+ T(const T&) = default;
+ };
+ static_assert( !__is_trivially_constructible(T, T&&), "" );
+ static_assert( __is_trivially_destructible(T), "" );
+ static_assert( !__is_trivially_relocatable(T), "" );
+
+ struct U : N {};
+ static_assert( __is_trivially_constructible(U, U&&), "" );
+ static_assert( __is_trivially_destructible(U), "" );
+ static_assert( !__is_trivially_relocatable(U), "" );
+
+ struct V {
+ O o;
+ };
+ static_assert( __is_trivially_relocatable(V), "" );
+
+ struct W {
+ O o;
+ W(const W&) = default;
+ };
+ static_assert( __is_trivially_relocatable(W), "" );
+} // namespace ND11
+
+// Examples from D1144R0 draft revision 14
+namespace ND14 {
+ struct A {
+ struct MA {
+ MA(MA&);
+ MA(const MA&) = default;
+ MA(MA&&) = default;
+ };
+ mutable MA ma;
+ A(const A&) = default;
+ };
+ static_assert(!__is_trivially_relocatable(A), "calls user-provided MA(MA&)");
+
+ struct B {
+ struct MB {
+ MB(const volatile MB&);
+ MB(const MB&) = default;
+ MB(MB&&) = default;
+ };
+ volatile MB mb;
+ B(const B&) = default;
+ };
+ static_assert(!__is_trivially_relocatable(B), "calls user-provided MB(const volatile MB&)");
+
+ struct [[clang::trivially_relocatable]] I {
+ I(I&&);
+ };
+ struct J : I {
+ J(const J&);
+ J(J&&) = default;
+ };
+ static_assert(__is_trivially_relocatable(I), "has the attribute");
+ static_assert(__is_trivially_relocatable(J), "inheritance pattern used by std::vector etc.");
+
+ struct [[clang::trivially_relocatable]] K {
+ K(const K&&);
+ K(const K&);
+ K(K&&);
+ K(K&);
+ volatile int m1;
+ mutable int m2;
+ ~K();
+ };
+ struct L : K {
+ K k;
+ };
+ static_assert(__is_trivially_relocatable(K), "the attribute should override all other considerations");
+ static_assert(__is_trivially_relocatable(L), "the Rule of Zero should work as expected");
+}
+
+// Example from Nicolas Lesser
+struct NL1 {
+ NL1& operator=(NL1&&);
+};
+static_assert(!__is_trivially_relocatable(NL1), "");
+
+struct [[clang::trivially_relocatable]] NL2 {
+// expected-error@-1{{cannot be applied to struct 'NL2' because it is not move-constructible}}
+ NL2& operator=(NL2&&);
+};
+static_assert(!__is_trivially_relocatable(NL2), "");
+
+union [[clang::trivially_relocatable]] NL3 {
+// expected-error@-1{{cannot be applied to union 'NL3' because it is not destructible}}
+ struct [[clang::trivially_relocatable]] String { String(String&&); ~String(); };
+ int i;
+ String s;
+};
+static_assert(!__is_trivially_relocatable(NL3), "");
+
+union [[clang::trivially_relocatable]] NL4 {
+ struct [[clang::trivially_relocatable]] String { String(String&&); ~String(); };
+ int i;
+ String s;
+ NL4(const NL4&);
+ ~NL4();
+};
+static_assert(__is_trivially_relocatable(NL4), "");
+
+template<class T>
+struct [[clang::trivially_relocatable]] NL5 {
+ T t;
+};
+struct NL5a {
+ NL5a() = default;
+ NL5a(NL5a&&) = delete;
+};
+struct NL5b {
+ NL5b() = default;
+ NL5b(NL5b&&);
+};
+static_assert(!__is_trivially_relocatable(NL5<NL5a>), "");
+static_assert(__is_trivially_relocatable(NL5<NL5b>), "");
+
+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");
Index: test/SemaCXX/maybe-trivially-relocatable.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/maybe-trivially-relocatable.cpp
@@ -0,0 +1,650 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// expected-diagnostics
+
+static_assert(__has_extension(trivially_relocatable), "");
+
+// It shall appear at most once in each attribute-list
+// and no attribute-argument-clause shall be present.
+
+struct [[clang::maybe_trivially_relocatable, clang::maybe_trivially_relocatable]] B1 {}; // should really be an error
+
+struct [[clang::maybe_trivially_relocatable]] [[clang::maybe_trivially_relocatable]] B2 {}; // should really be an error
+
+struct [[clang::maybe_trivially_relocatable(42)]] B3 {};
+// expected-error@-1{{'maybe_trivially_relocatable' attribute takes no arguments}}
+
+
+// The first declaration of a type shall specify the
+// trivially_relocatable attribute if any declaration of that
+// type specifies the trivially_relocatable attribute.
+
+struct [[clang::maybe_trivially_relocatable]] A1 {}; // ok
+struct [[clang::maybe_trivially_relocatable]] A1;
+
+struct [[clang::maybe_trivially_relocatable]] A2; // ok
+struct [[clang::maybe_trivially_relocatable]] A2 {};
+
+struct [[clang::maybe_trivially_relocatable]] A3 {}; // ok
+struct A3;
+
+struct [[clang::maybe_trivially_relocatable]] A4; // ok
+struct A4 {};
+
+struct A5 {};
+struct [[clang::maybe_trivially_relocatable]] A5;
+// expected-error@-1{{type A5 declared 'maybe_trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'maybe_trivially_relocatable' attribute is here}}
+// expected-warning@-3{{attribute declaration must precede definition}}
+// expected-note@-5{{previous definition is here}}
+
+struct A6;
+struct [[clang::maybe_trivially_relocatable]] A6 {};
+// expected-error@-1{{type A6 declared 'maybe_trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'maybe_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;
+ // expected-note@-1{{explicitly marked deleted here}}
+};
+static_assert(!__is_trivially_relocatable(NonDestructible), "");
+static_assert(!__is_trivially_relocatable(NonCopyConstructible), "");
+static_assert(!__is_constructible(NonCopyConstructible, NonCopyConstructible&&), "");
+static_assert(!__is_trivially_relocatable(NonMoveConstructible), "");
+static_assert(!__is_constructible(NonMoveConstructible, NonMoveConstructible&&), "");
+
+struct [[clang::maybe_trivially_relocatable]] D1 { ~D1() = delete; };
+
+struct [[clang::maybe_trivially_relocatable]] D2 : private NonDestructible { };
+
+struct [[clang::maybe_trivially_relocatable]] D3 { D3(const D3&) = delete; };
+
+struct [[clang::maybe_trivially_relocatable]] D4 { D4(const D4&) = default; D4(D4&&) = delete; };
+
+struct [[clang::maybe_trivially_relocatable]] D5 : private NonCopyConstructible { };
+static_assert(!__is_constructible(D5, D5&&), "");
+
+struct [[clang::maybe_trivially_relocatable]] D6 : private NonMoveConstructible { D6(D6&&) = default; };
+// expected-warning@-1{{explicitly defaulted move constructor is implicitly deleted}}
+// expected-note@-2{{implicitly deleted because}}
+
+template<class T>
+struct [[clang::maybe_trivially_relocatable]] DT1 : private T { }; // ok
+
+struct D7 {
+ DT1<NonDestructible> m;
+};
+
+class [[clang::maybe_trivially_relocatable]] D8 {
+ DT1<NonDestructible> m;
+};
+
+
+// Now test specific types for trivial relocatability.
+
+static_assert(__is_trivially_relocatable(char), "");
+static_assert(__is_trivially_relocatable(int), "");
+static_assert(__is_trivially_relocatable(int*), "");
+static_assert(!__is_trivially_relocatable(int&), "");
+static_assert(__is_trivially_relocatable(float), "");
+static_assert(__is_trivially_relocatable(double), "");
+static_assert(!__is_trivially_relocatable(void), "");
+static_assert(__is_trivially_relocatable(char[1]), "");
+static_assert(__is_trivially_relocatable(char[]), "");
+
+static_assert(__is_trivially_relocatable(const int), "");
+static_assert(__is_trivially_relocatable(volatile int), "");
+static_assert(!__is_trivially_relocatable(const int&), "");
+static_assert(!__is_trivially_relocatable(volatile int&), "");
+
+struct C1 { int x; }; static_assert(__is_trivially_relocatable(C1), "");
+struct C2 { const int x; }; static_assert(__is_trivially_relocatable(C2), "");
+struct C3 { volatile int x; }; static_assert(!__is_trivially_relocatable(C3), "volatile member");
+struct C4 { int *x; }; static_assert(__is_trivially_relocatable(C4), "");
+struct C5 { const int *x; }; static_assert(__is_trivially_relocatable(C5), "");
+struct C6 { volatile int *x; }; static_assert(__is_trivially_relocatable(C6), "");
+struct C7 { int& x; }; static_assert(__is_trivially_relocatable(C7), "");
+struct C8 { const int& x; }; static_assert(__is_trivially_relocatable(C8), "");
+struct C9 { volatile int& x; }; static_assert(__is_trivially_relocatable(C9), "");
+
+enum E { x = 1, y = 2 };
+static_assert(__is_trivially_relocatable(E), "");
+static_assert(__is_trivially_relocatable(E[1]), "");
+
+struct T1 {};
+static_assert(__is_trivially_relocatable(T1), "");
+
+struct T2 { int x; E y; int *z; };
+static_assert(__is_trivially_relocatable(T2), "");
+
+struct T3 { int x; T3(T3&&) = default; };
+static_assert(__is_trivially_relocatable(T3), "");
+
+struct T4 { int x; ~T4() = default; };
+static_assert(__is_trivially_relocatable(T4), "trivially copy-constructible, and no move constructor");
+
+struct T4a { T4 a; };
+static_assert(__is_trivially_relocatable(T4a), "trivially copy-constructible, and no move constructor");
+
+
+struct VD {
+ VD(const VD&) = default;
+ VD(VD&&) = default;
+ virtual ~VD() = default;
+};
+void relocate_example(VD&& src) {
+ VD dst(static_cast<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 [[clang::maybe_trivially_relocatable]] T9 { int x; T9(T9&&) {} };
+static_assert(__is_trivially_relocatable(T9), "T9 isn't naturally, but it has the attribute");
+
+struct [[clang::maybe_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 [[clang::maybe_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, and the 'maybe' attribute doesn't help");
+
+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, and the 'maybe' attribute doesn't help");
+
+
+// This pattern is used heavily by libc++.
+struct T19 {
+ struct [[clang::maybe_trivially_relocatable]] Base {
+ Base(Base&&);
+ ~Base();
+ };
+ Base m;
+ T19(const T19&);
+ T19(T19&&) = default;
+};
+
+static_assert(!__is_trivially_constructible(T19, const T19&), "user-provided copy constructor");
+static_assert(!__is_trivially_constructible(T19, T19&&), "defaulted non-trivial move constructor");
+static_assert(!__is_trivially_destructible(T19), "defaulted non-trivial destructor");
+static_assert(__is_trivially_relocatable(T19), "Rule of Zero");
+
+
+struct T20 {
+ struct [[clang::maybe_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 [[clang::maybe_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 [[clang::maybe_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_constructible(T23::Evil, T23::Evil&&), "");
+static_assert(__is_trivially_destructible(T23::Evil), "");
+static_assert(__is_trivially_relocatable(T23::Evil), "trivially move-constructible and trivially destructible");
+static_assert(__is_constructible(T23, T23&), "");
+static_assert(!__is_constructible(T23, const T23&), "");
+static_assert(__is_trivially_constructible(T23, T23&&), "");
+static_assert(__is_trivially_destructible(T23), "");
+static_assert(!__is_trivially_relocatable(T23), "mutable member (even though this would be safe in practice)");
+
+struct T23a {
+ struct Evil {
+ Evil(Evil&);
+ Evil(const Evil&) = default;
+ ~Evil() = default;
+ };
+ mutable Evil m;
+};
+void relocate_example(T23a&& src) {
+ T23a dst(static_cast<T23a&&>(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; // expected-note{{explicitly marked deleted here}}
+ Evil(const Evil&) = default;
+ ~Evil() = default;
+ };
+ mutable Evil m; // expected-note{{implicitly deleted because}}
+ T23b(const T23b&) = default; // no implicit move constructor
+ // expected-warning@-1{{explicitly defaulted copy constructor is implicitly deleted}}
+};
+static_assert(__is_trivially_constructible(T23b::Evil, T23b::Evil&&), "");
+static_assert(__is_trivially_destructible(T23b::Evil), "");
+static_assert(__is_trivially_relocatable(T23b::Evil), "it has no user-provided copy constructors");
+static_assert(!__is_constructible(T23b, T23b&&), "");
+static_assert(!__is_trivially_relocatable(T23b), "because it is not move-constructible");
+
+
+// Example from D1144R0
+struct string {
+ char *data_;
+ unsigned long size_ = 0;
+ unsigned long capacity_ = 0;
+ string() = default;
+ string(const char *s);
+ string(string&& s);
+ ~string();
+};
+static_assert(!__is_trivially_relocatable(string), "");
+
+// Example from D1144R0
+struct offset_ptr {
+ unsigned long value_;
+ offset_ptr();
+ offset_ptr(void *p);
+ offset_ptr(const offset_ptr& rhs);
+ offset_ptr& operator=(const offset_ptr& rhs);
+ ~offset_ptr() = default;
+};
+static_assert(!__is_trivially_relocatable(offset_ptr), "");
+
+// Example from D1144R0
+struct registered_object {
+ registered_object();
+ registered_object(registered_object&&) = default;
+ registered_object(const registered_object&) = default;
+ registered_object& operator=(registered_object&&) = default;
+ registered_object& operator=(const registered_object&) = default;
+ ~registered_object();
+};
+struct Widget : registered_object {};
+static_assert(!__is_trivially_relocatable(registered_object), "");
+static_assert(!__is_trivially_relocatable(Widget), "");
+
+// Examples from D1144R0 draft revision 11
+namespace ND11 {
+ struct M {
+ M() = default;
+ M(M&);
+ M(const M&) = default;
+ };
+ static_assert( __is_trivially_constructible(M, M&&), "" );
+ static_assert( __is_trivially_destructible(M), "" );
+ static_assert( !__is_trivially_relocatable(M), "" );
+
+ struct N {
+ mutable M m;
+ };
+ static_assert( __is_trivially_constructible(N, N&&), "" );
+ static_assert( __is_trivially_destructible(N), "" );
+ static_assert( !__is_trivially_relocatable(N), "" );
+
+ struct [[clang::maybe_trivially_relocatable]] O {
+ O(const O&);
+ mutable int o;
+ };
+ static_assert( !__is_trivially_relocatable(O), "mutable members disable the 'maybe' attribute" );
+
+ 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), "O is not trivially relocatable" );
+
+ struct W {
+ O o;
+ W(const W&) = default;
+ };
+ static_assert( !__is_trivially_relocatable(W), "O is not trivially relocatable" );
+} // namespace ND11
+
+// Examples from D1144R0 draft revision 14
+namespace ND14 {
+ struct A {
+ struct MA {
+ MA(MA&);
+ MA(const MA&) = default;
+ MA(MA&&) = default;
+ };
+ mutable MA ma;
+ A(const A&) = default;
+ };
+ static_assert(!__is_trivially_relocatable(A), "calls user-provided MA(MA&)");
+
+ struct B {
+ struct MB {
+ MB(const volatile MB&);
+ MB(const MB&) = default;
+ MB(MB&&) = default;
+ };
+ volatile MB mb;
+ B(const B&) = default;
+ };
+ static_assert(!__is_trivially_relocatable(B), "calls user-provided MB(const volatile MB&)");
+
+ struct [[clang::maybe_trivially_relocatable]] I {
+ I(I&&);
+ };
+ struct J : I {
+ J(const J&);
+ J(J&&) = default;
+ };
+ static_assert(__is_trivially_relocatable(I), "has the attribute");
+ static_assert(__is_trivially_relocatable(J), "inheritance pattern used by std::vector etc.");
+
+ struct [[clang::maybe_trivially_relocatable]] K {
+ K(const K&&);
+ K(const K&);
+ K(K&&);
+ K(K&);
+ volatile int m1;
+ mutable int m2;
+ ~K();
+ };
+ struct L : K {
+ K k;
+ };
+ static_assert(!__is_trivially_relocatable(K), "mutable/volatile members disable the 'maybe' attribute");
+ static_assert(!__is_trivially_relocatable(L), "K is not trivially relocatable");
+}
+
+// Example from Nicolas Lesser
+struct NL1 {
+ NL1& operator=(NL1&&);
+};
+static_assert(!__is_trivially_relocatable(NL1), "");
+
+struct [[clang::maybe_trivially_relocatable]] NL2 {
+ NL2& operator=(NL2&&);
+};
+static_assert(!__is_trivially_relocatable(NL2), "");
+
+union [[clang::maybe_trivially_relocatable]] NL3 {
+ struct [[clang::maybe_trivially_relocatable]] String { String(String&&); ~String(); };
+ int i;
+ String s;
+};
+static_assert(!__is_trivially_relocatable(NL3), "");
+
+union [[clang::maybe_trivially_relocatable]] NL4 {
+ struct [[clang::maybe_trivially_relocatable]] String { String(String&&); ~String(); };
+ int i;
+ String s;
+ NL4(const NL4&);
+ ~NL4();
+};
+static_assert(__is_trivially_relocatable(NL4), "");
+
+template<class T>
+struct [[clang::maybe_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>), "member 'NL5b t' is not trivially relocatable");
+
+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");
+
+template<class A>
+struct [[clang::maybe_trivially_relocatable]] Vec1 {
+ int i;
+ typename A::Ptr p;
+ Vec1(Vec1&&);
+ ~Vec1();
+};
+struct Vec1_A1 { struct [[clang::trivially_relocatable]] Ptr { Ptr(Ptr&&); ~Ptr(); }; };
+struct Vec1_A2 { struct Ptr { Ptr(Ptr&&); ~Ptr(); }; };
+static_assert(__is_trivially_relocatable(Vec1_A1::Ptr), "");
+static_assert(!__is_trivially_relocatable(Vec1_A2::Ptr), "");
+static_assert(__is_trivially_relocatable(Vec1<Vec1_A1>), "");
+static_assert(!__is_trivially_relocatable(Vec1<Vec1_A2>), "");
Index: test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- test/Misc/pragma-attribute-supported-attributes-list.test
+++ test/Misc/pragma-attribute-supported-attributes-list.test
@@ -2,7 +2,7 @@
// The number of supported attributes should never go down!
-// CHECK: #pragma clang attribute supports 129 attributes:
+// CHECK: #pragma clang attribute supports 130 attributes:
// CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function)
@@ -123,6 +123,7 @@
// CHECK-NEXT: Target (SubjectMatchRule_function)
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
+// CHECK-NEXT: TriviallyRelocatable (SubjectMatchRule_record)
// CHECK-NEXT: VecReturn (SubjectMatchRule_record)
// CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
// CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
Index: test/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
@@ -6046,6 +6046,8 @@
Record->push_back(Data.HasTrivialSpecialMembersForCall);
Record->push_back(Data.DeclaredNonTrivialSpecialMembers);
Record->push_back(Data.DeclaredNonTrivialSpecialMembersForCall);
+ Record->push_back(Data.IsNaturallyTriviallyRelocatable);
+ Record->push_back(Data.HasNonTriviallyRelocatableSubobject);
Record->push_back(Data.HasIrrelevantDestructor);
Record->push_back(Data.HasConstexprNonCopyMoveConstructor);
Record->push_back(Data.HasDefaultedDefaultConstructor);
Index: lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- lib/Serialization/ASTReaderDecl.cpp
+++ lib/Serialization/ASTReaderDecl.cpp
@@ -1674,6 +1674,8 @@
Data.HasTrivialSpecialMembersForCall = Record.readInt();
Data.DeclaredNonTrivialSpecialMembers = Record.readInt();
Data.DeclaredNonTrivialSpecialMembersForCall = Record.readInt();
+ Data.IsNaturallyTriviallyRelocatable = Record.readInt();
+ Data.HasNonTriviallyRelocatableSubobject = Record.readInt();
Data.HasIrrelevantDestructor = Record.readInt();
Data.HasConstexprNonCopyMoveConstructor = Record.readInt();
Data.HasDefaultedDefaultConstructor = Record.readInt();
@@ -1815,6 +1817,8 @@
OR_FIELD(HasTrivialSpecialMembersForCall)
OR_FIELD(DeclaredNonTrivialSpecialMembers)
OR_FIELD(DeclaredNonTrivialSpecialMembersForCall)
+ MATCH_FIELD(IsNaturallyTriviallyRelocatable)
+ MATCH_FIELD(HasNonTriviallyRelocatableSubobject)
MATCH_FIELD(HasIrrelevantDestructor)
OR_FIELD(HasConstexprNonCopyMoveConstructor)
OR_FIELD(HasDefaultedDefaultConstructor)
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -4424,6 +4424,7 @@
case UTT_IsDestructible:
case UTT_IsNothrowDestructible:
case UTT_IsTriviallyDestructible:
+ case UTT_IsTriviallyRelocatable:
case UTT_HasUniqueObjectRepresentations:
if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
return true;
@@ -4713,7 +4714,8 @@
}
}
return true;
-
+ case UTT_IsTriviallyRelocatable:
+ return T.isTriviallyRelocatableType(C);
case UTT_HasTrivialDestructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
// If __is_pod (type) is true or type is a reference type
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -2832,14 +2832,14 @@
return nullptr;
}
-// Check if there is a field shadowing.
-void Sema::CheckShadowInheritedFields(const SourceLocation &Loc,
- DeclarationName FieldName,
- const CXXRecordDecl *RD,
- bool DeclIsField) {
- if (Diags.isIgnored(diag::warn_shadow_field, Loc))
- return;
-
+// Check if there is a field shadowing.
+void Sema::CheckShadowInheritedFields(const SourceLocation &Loc,
+ DeclarationName FieldName,
+ const CXXRecordDecl *RD,
+ bool DeclIsField) {
+ if (Diags.isIgnored(diag::warn_shadow_field, Loc))
+ return;
+
// To record a shadowed field in a base
std::map<CXXRecordDecl*, NamedDecl*> Bases;
auto FieldShadowed = [&](const CXXBaseSpecifier *Specifier,
@@ -2873,13 +2873,13 @@
continue;
auto BaseField = It->second;
assert(BaseField->getAccess() != AS_private);
- if (AS_none !=
- CXXRecordDecl::MergeAccess(P.Access, BaseField->getAccess())) {
- Diag(Loc, diag::warn_shadow_field)
- << FieldName << RD << Base << DeclIsField;
- Diag(BaseField->getLocation(), diag::note_shadow_field);
- Bases.erase(It);
- }
+ if (AS_none !=
+ CXXRecordDecl::MergeAccess(P.Access, BaseField->getAccess())) {
+ Diag(Loc, diag::warn_shadow_field)
+ << FieldName << RD << Base << DeclIsField;
+ Diag(BaseField->getLocation(), diag::note_shadow_field);
+ Bases.erase(It);
+ }
}
}
@@ -6079,6 +6079,26 @@
Record->setTrivialForCallFlags(M);
}
+ // A move-constructible, destructible object type T is a
+ // trivially relocatable type if it ...
+ if (CSM == CXXMoveConstructor && M->isUserProvided()) {
+ // - has no user-provided move constructors,
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ } else if (CSM == CXXCopyConstructor && M->isUserProvided()) {
+ // - either has at least one move constructor or has no user-provided copy constructors,
+ if (!Record->hasMoveConstructor())
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ } else if (CSM == CXXDestructor &&
+ (M->isUserProvided() || M->isDeleted())) {
+ // - has a defaulted, non-deleted destructor,
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ } else if (CSM == CXXDestructor && M->isVirtual()) {
+ // - either is final, or has a final destructor,
+ // or has a non-virtual destructor ...
+ if (!M->hasAttr<FinalAttr>() && !Record->hasAttr<FinalAttr>())
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ }
+
if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() &&
M->hasAttr<DLLExportAttr>()) {
if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
@@ -6147,6 +6167,47 @@
// 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<TriviallyRelocatableAttr>()) {
+ 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;
+ }
+ }
+ if (Record->hasAttr<MaybeTriviallyRelocatableAttr>()) {
+ Record->dropAttr<MaybeTriviallyRelocatableAttr>();
+ }
+ } 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<TriviallyRelocatableAttr>()) {
+ 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;
+ }
+ }
+ if (Record->hasAttr<MaybeTriviallyRelocatableAttr>()) {
+ Record->dropAttr<MaybeTriviallyRelocatableAttr>();
+ }
+ }
+ }
+ }
}
/// 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
@@ -5635,6 +5635,29 @@
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);
+}
+
+static void handleMaybeTriviallyRelocatableAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ checkAttributeNotOnFirstDecl<MaybeTriviallyRelocatableAttr>(S, D, AL);
+ handleSimpleAttribute<MaybeTriviallyRelocatableAttr>(S, D, AL);
+}
+
DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range,
unsigned AttrSpellingListIndex) {
if (D->hasAttr<DLLExportAttr>()) {
@@ -5840,14 +5863,14 @@
if (AL.isDeclspecAttribute() || AL.isCXX11Attribute())
checkAttributeAtMostNumArgs(S, AL, 1);
else if (AL.isArgExpr(1) && AL.getArgAsExpr(1) &&
- !S.checkStringLiteralArgumentAttr(AL, 1, Replacement))
- return;
-
- if (!S.getLangOpts().CPlusPlus14 && AL.isCXX11Attribute() && !AL.isGNUScope())
- S.Diag(AL.getLoc(), diag::ext_cxx14_attr) << AL;
-
- D->addAttr(::new (S.Context)
- DeprecatedAttr(AL.getRange(), S.Context, Str, Replacement,
+ !S.checkStringLiteralArgumentAttr(AL, 1, Replacement))
+ return;
+
+ if (!S.getLangOpts().CPlusPlus14 && AL.isCXX11Attribute() && !AL.isGNUScope())
+ S.Diag(AL.getLoc(), diag::ext_cxx14_attr) << AL;
+
+ D->addAttr(::new (S.Context)
+ DeprecatedAttr(AL.getRange(), S.Context, Str, Replacement,
AL.getAttributeSpellingListIndex()));
}
@@ -6536,6 +6559,12 @@
case ParsedAttr::AT_TrivialABI:
handleSimpleAttribute<TrivialABIAttr>(S, D, AL);
break;
+ case ParsedAttr::AT_TriviallyRelocatable:
+ handleTriviallyRelocatableAttr(S, D, AL);
+ break;
+ case ParsedAttr::AT_MaybeTriviallyRelocatable:
+ handleMaybeTriviallyRelocatableAttr(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
@@ -15854,6 +15854,16 @@
Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs);
}
+ if (CXXRecord) {
+ QualType FT = FD->getType();
+ if (FD->isMutable() ||
+ FT.isVolatileQualified() ||
+ (!FT->isReferenceType() && !FT.isTriviallyRelocatableType(Context))) {
+ CXXRecord->setIsNotNaturallyTriviallyRelocatable();
+ CXXRecord->setHasNonTriviallyRelocatableSubobject();
+ }
+ }
+
if (Record && FD->getType().isVolatileQualified())
Record->setHasVolatileMember(true);
// Keep track of the number of named members.
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -2231,6 +2231,19 @@
return false;
}
+bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
+ QualType T = Context.getBaseElementType(getCanonicalType());
+ if (T->isIncompleteType())
+ return false;
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
+ return RD->isTriviallyRelocatable();
+ } else {
+ // Non-class types are always both move-constructible and destructible,
+ // so just check whether a non-class type is trivially copyable.
+ return T.isTriviallyCopyableType(Context);
+ }
+}
+
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
return !Context.getLangOpts().ObjCAutoRefCount &&
Context.getLangOpts().ObjCWeak &&
Index: lib/AST/DeclCXX.cpp
===================================================================
--- lib/AST/DeclCXX.cpp
+++ lib/AST/DeclCXX.cpp
@@ -91,7 +91,10 @@
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
HasTrivialSpecialMembersForCall(SMF_All),
DeclaredNonTrivialSpecialMembers(0),
- DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true),
+ DeclaredNonTrivialSpecialMembersForCall(0),
+ IsNaturallyTriviallyRelocatable(true),
+ HasNonTriviallyRelocatableSubobject(false),
+ HasIrrelevantDestructor(true),
HasConstexprNonCopyMoveConstructor(false),
HasDefaultedDefaultConstructor(false),
DefaultedDefaultConstructorIsConstexpr(true),
@@ -278,6 +281,11 @@
if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C))
data().HasNonLiteralTypeFieldsOrBases = true;
+ if (Base->isVirtual() || !BaseClassDecl->isTriviallyRelocatable()) {
+ setIsNotNaturallyTriviallyRelocatable();
+ setHasNonTriviallyRelocatableSubobject();
+ }
+
// 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/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -1785,6 +1785,10 @@
= FromData.DefaultedMoveAssignmentIsDeleted;
ToData.DefaultedDestructorIsDeleted = FromData.DefaultedDestructorIsDeleted;
ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers;
+ ToData.IsNaturallyTriviallyRelocatable
+ = FromData.IsNaturallyTriviallyRelocatable;
+ ToData.HasNonTriviallyRelocatableSubobject
+ = FromData.HasNonTriviallyRelocatableSubobject;
ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor;
ToData.HasConstexprNonCopyMoveConstructor
= FromData.HasConstexprNonCopyMoveConstructor;
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
@@ -472,6 +472,7 @@
TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX)
TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX)
TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX)
+TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
KEYWORD(__underlying_type , KEYCXX)
Index: include/clang/Basic/Features.def
===================================================================
--- include/clang/Basic/Features.def
+++ include/clang/Basic/Features.def
@@ -239,6 +239,7 @@
EXTENSION(cxx_init_captures, LangOpts.CPlusPlus11)
EXTENSION(cxx_variable_templates, LangOpts.CPlusPlus)
// Miscellaneous language extensions
+EXTENSION(trivially_relocatable, LangOpts.CPlusPlus11)
EXTENSION(overloadable_unmarked, true)
#undef EXTENSION
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2968,6 +2968,11 @@
def ext_cannot_use_trivial_abi : ExtWarn<
"'trivial_abi' cannot be applied to %0">, InGroup<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>;
@@ -8282,6 +8287,11 @@
def err_block_on_vm : Error<
"__block attribute not allowed on declaration with a variably modified type">;
+def err_attribute_missing_on_first_decl : Error<
+ "type %0 declared %1 after its first declaration">;
+def note_attribute_missing_first_decl : Note<
+ "declaration missing %0 attribute is here">;
+
def err_vec_builtin_non_vector : Error<
"first two arguments to %0 must be vectors">;
def err_vec_builtin_incompatible_vector : Error<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -2424,6 +2424,28 @@
}];
}
+def TriviallyRelocatableDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{
+The ``trivially_relocatable`` attribute can be applied to a C++ class, struct, or union.
+It warrants to the compiler that moving an object of that type, and then destroying the
+source object, is functionally equivalent to copying the underlying bytes
+and then dropping the source object on the floor.
+ }];
+}
+
+def MaybeTriviallyRelocatableDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{
+The ``maybe_trivially_relocatable`` attribute can be applied to a C++ class, struct, or union.
+It warrants to the compiler that moving an object of that type, and then destroying the
+source object, is functionally equivalent to copying the underlying bytes
+and then dropping the source object on the floor... But, if the type to which it
+is applied has any direct subobjects of non-trivially relocatable type, then this
+attribute has no effect.
+ }];
+}
+
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
@@ -2129,6 +2129,20 @@
let Documentation = [Undocumented];
}
+def TriviallyRelocatable : InheritableAttr {
+ let Spellings = [Clang<"trivially_relocatable", 0>];
+ let Subjects = SubjectList<[CXXRecord]>;
+ let Documentation = [TriviallyRelocatableDocs];
+ let LangOpts = [CPlusPlus];
+}
+
+def MaybeTriviallyRelocatable : InheritableAttr {
+ let Spellings = [Clang<"maybe_trivially_relocatable", 0>];
+ let Subjects = SubjectList<[CXXRecord]>;
+ let Documentation = [MaybeTriviallyRelocatableDocs];
+ 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
@@ -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/AST/DeclCXX.h
===================================================================
--- include/clang/AST/DeclCXX.h
+++ include/clang/AST/DeclCXX.h
@@ -468,6 +468,16 @@
/// 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 base or field which is not trivially
+ /// relocatable. This negates the effect of the
+ /// [[clang::maybe_trivially_relocatable]] attribute.
+ unsigned HasNonTriviallyRelocatableSubobject : 1;
+
/// True when this class has a destructor with no semantic effect.
unsigned HasIrrelevantDestructor : 1;
@@ -1498,6 +1508,23 @@
(SMF_CopyConstructor | SMF_MoveConstructor | SMF_Destructor);
}
+ /// Determine whether this class is trivially relocatable
+ bool isTriviallyRelocatable() const {
+ return (
+ data().IsNaturallyTriviallyRelocatable ||
+ hasAttr<TriviallyRelocatableAttr>() ||
+ (hasAttr<MaybeTriviallyRelocatableAttr>() && !data().HasNonTriviallyRelocatableSubobject)
+ );
+ }
+
+ void setIsNotNaturallyTriviallyRelocatable() {
+ data().IsNaturallyTriviallyRelocatable = false;
+ }
+
+ void setHasNonTriviallyRelocatableSubobject() {
+ data().HasNonTriviallyRelocatableSubobject = true;
+ }
+
/// 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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits