Quuxplusone created this revision.
Quuxplusone added a reviewer: rsmith.
Quuxplusone added a project: clang.
Herald added a subscriber: cfe-commits.

This is the compiler half of C++ proposal 1144 "Object relocation in terms of 
move plus destroy," as seen on https://godbolt.org/g/zUUAVW and 
https://quuxplusone.github.io/blog/2018/07/18/announcing-trivially-relocatable/ 
.

There are two parts to this compiler support:

- the type trait `__is_trivially_relocatable(T)`, which is similar in spirit to 
`__is_trivially_destructible(T)`, in that it lets the programmer access 
information that the compiler itself already knows; and
- the warranting attribute `[[trivially_relocatable]]`, which is similar in 
spirit to `[[trivial_abi]]`, in that it lets the programmer communicate back to 
the compiler that a certain user-defined type should be //assumed// to have 
this property even though it would not //naturally// have the property all else 
being equal.

The official home of this branch thus far has been 
https://github.com/Quuxplusone/clang/tree/trivially-relocatable , but I figure 
that a Clang review would be a good way to get some more eyeballs on it, plus, 
if we can get it into Clang trunk then I wouldn't have to keep rebasing it 
every week.


Repository:
  rC Clang

https://reviews.llvm.org/D50119

Files:
  docs/LanguageExtensions.rst
  include/clang/AST/DeclCXX.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
  include/clang/Sema/Sema.h
  lib/AST/ASTImporter.cpp
  lib/AST/DeclCXX.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,478 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// expected-diagnostics
+
+static_assert(__has_extension(trivially_relocatable), "");
+
+// It shall appear at most once in each attribute-list
+// and no attribute-argument-clause shall be present.
+
+struct [[trivially_relocatable, trivially_relocatable]] B1 {};
+// expected-error@-1{{attribute 'trivially_relocatable' cannot appear multiple times in an attribute specifier}}
+
+struct [[trivially_relocatable]] [[trivially_relocatable]] B2 {}; // should really be an error
+
+struct [[trivially_relocatable(42)]] B3 {};
+// expected-error@-1{{attribute 'trivially_relocatable' cannot have an argument list}}
+
+
+//   The first declaration of a type shall specify the
+//   trivially_relocatable attribute if any declaration of that
+//   type specifies the trivially_relocatable attribute.
+
+struct [[trivially_relocatable]] A1 {};  // ok
+struct [[trivially_relocatable]] A1;
+
+struct [[trivially_relocatable]] A2;  // ok
+struct [[trivially_relocatable]] A2 {};
+
+struct [[trivially_relocatable]] A3 {};  // ok
+struct A3;
+
+struct [[trivially_relocatable]] A4;  // ok
+struct A4 {};
+
+struct A5 {};
+struct [[trivially_relocatable]] A5;
+// expected-error@-1{{type A5 declared 'trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}}
+// expected-warning@-3{{attribute declaration must precede definition}}
+// expected-note@-5{{previous definition is here}}
+
+struct A6;
+struct [[trivially_relocatable]] A6 {};
+// expected-error@-1{{type A6 declared 'trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}}
+
+
+// If a type T is declared with the trivially_relocatable attribute, and T is either
+// not move-constructible or not destructible, the program is ill-formed.
+
+struct NonDestructible {
+    NonDestructible(const NonDestructible&) = default;
+    NonDestructible(NonDestructible&&) = default;
+    ~NonDestructible() = delete;
+};
+struct NonCopyConstructible {
+    NonCopyConstructible(const NonCopyConstructible&) = delete;
+};
+struct NonMoveConstructible {
+    NonMoveConstructible(const NonMoveConstructible&) = default;
+    NonMoveConstructible(NonMoveConstructible&&) = delete;
+};
+static_assert(!__is_trivially_relocatable(NonDestructible), "");
+static_assert(!__is_trivially_relocatable(NonCopyConstructible), "");
+static_assert(!__is_constructible(NonCopyConstructible, NonCopyConstructible&&), "");
+static_assert(!__is_trivially_relocatable(NonMoveConstructible), "");
+static_assert(!__is_constructible(NonMoveConstructible, NonMoveConstructible&&), "");
+
+struct [[trivially_relocatable]] D1 { ~D1() = delete; };
+// expected-error@-1{{cannot be applied to class 'D1' because it is not destructible}}
+
+struct [[trivially_relocatable]] D2 : private NonDestructible { };
+// expected-error@-1{{cannot be applied to class 'D2' because it is not destructible}}
+
+struct [[trivially_relocatable]] D3 { D3(const D3&) = delete; };
+// expected-error@-1{{cannot be applied to class 'D3' because it is not move-constructible}}
+
+struct [[trivially_relocatable]] D4 { D4(const D4&) = default; D4(D4&&) = delete; };
+// expected-error@-1{{cannot be applied to class 'D4' because it is not move-constructible}}
+
+struct [[trivially_relocatable]] D5 : private NonCopyConstructible { };
+// expected-error@-1{{cannot be applied to class 'D5' because it is not move-constructible}}
+static_assert(!__is_constructible(D5, D5&&), "");
+
+struct [[trivially_relocatable]] D6 : private NonMoveConstructible { D6(D6&&) = default; };
+// expected-error@-1{{cannot be applied to class 'D6' because it is not move-constructible}}
+
+template<class T>
+struct [[trivially_relocatable]] DT1 : private T { };  // ok
+
+struct D7 {
+    DT1<NonDestructible> m;
+};
+
+struct [[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), "its defaulted move-constructor is trivial");
+static_assert(__is_constructible(T23a, T23a&&), "");
+static_assert(__is_constructible(T23a, T23a&), "");
+static_assert(__is_constructible(T23a, const T23a&), "");
+static_assert(!__is_trivially_relocatable(T23a), "because its copy operation is evil");
+
+
+// Example from D1144R0
+struct string {
+    char *data_;
+    unsigned long size_ = 0;
+    unsigned long capacity_ = 0;
+    string() = default;
+    string(const char *s);
+    string(string&& s);
+    ~string();
+};
+static_assert(!__is_trivially_relocatable(string), "");
+
+// Example from D1144R0
+struct offset_ptr {
+    unsigned long value_;
+    offset_ptr();
+    offset_ptr(void *p);
+    offset_ptr(const offset_ptr& rhs);
+    offset_ptr& operator=(const offset_ptr& rhs);
+    ~offset_ptr() = default;
+};
+static_assert(!__is_trivially_relocatable(offset_ptr), "");
+
+// Example from D1144R0
+struct registered_object {
+    registered_object();
+    registered_object(registered_object&&) = default;
+    registered_object(const registered_object&) = default;
+    registered_object& operator=(registered_object&&) = default;
+    registered_object& operator=(const registered_object&) = default;
+    ~registered_object();
+};
+struct Widget : registered_object {};
+static_assert(!__is_trivially_relocatable(registered_object), "");
+static_assert(!__is_trivially_relocatable(Widget), "");
+
+
+// Regression tests.
+struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}}
+struct Regression1 {
+  Incomplete i; // expected-error {{field has incomplete type 'Incomplete'}}
+};
+
+template<class T>
+struct Regression2 {
+  ~Regression2();
+};
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
@@ -6050,6 +6050,7 @@
   Record->push_back(Data.HasTrivialSpecialMembersForCall);
   Record->push_back(Data.DeclaredNonTrivialSpecialMembers);
   Record->push_back(Data.DeclaredNonTrivialSpecialMembersForCall);
+  Record->push_back(Data.IsNaturallyTriviallyRelocatable);
   Record->push_back(Data.HasIrrelevantDestructor);
   Record->push_back(Data.HasConstexprNonCopyMoveConstructor);
   Record->push_back(Data.HasDefaultedDefaultConstructor);
Index: lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- lib/Serialization/ASTReaderDecl.cpp
+++ lib/Serialization/ASTReaderDecl.cpp
@@ -1665,6 +1665,7 @@
   Data.HasTrivialSpecialMembersForCall = Record.readInt();
   Data.DeclaredNonTrivialSpecialMembers = Record.readInt();
   Data.DeclaredNonTrivialSpecialMembersForCall = Record.readInt();
+  Data.IsNaturallyTriviallyRelocatable = Record.readInt();
   Data.HasIrrelevantDestructor = Record.readInt();
   Data.HasConstexprNonCopyMoveConstructor = Record.readInt();
   Data.HasDefaultedDefaultConstructor = Record.readInt();
@@ -1806,6 +1807,7 @@
   OR_FIELD(HasTrivialSpecialMembersForCall)
   OR_FIELD(DeclaredNonTrivialSpecialMembers)
   OR_FIELD(DeclaredNonTrivialSpecialMembersForCall)
+  MATCH_FIELD(IsNaturallyTriviallyRelocatable)
   MATCH_FIELD(HasIrrelevantDestructor)
   OR_FIELD(HasConstexprNonCopyMoveConstructor)
   OR_FIELD(HasDefaultedDefaultConstructor)
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -4415,6 +4415,7 @@
   case UTT_IsDestructible:
   case UTT_IsNothrowDestructible:
   case UTT_IsTriviallyDestructible:
+  case UTT_IsTriviallyRelocatable:
   case UTT_HasUniqueObjectRepresentations:
     if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
       return true;
@@ -4704,7 +4705,8 @@
       }
     }
     return true;
-
+  case UTT_IsTriviallyRelocatable:
+    return Self.IsTriviallyRelocatableType(T);
   case UTT_HasTrivialDestructor:
     // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
     //   If __is_pod (type) is true or type is a reference type
@@ -4860,6 +4862,19 @@
   }
 }
 
+bool Sema::IsTriviallyRelocatableType(QualType QT) const {
+  QualType T = Context.getBaseElementType(QT);
+  if (T->isIncompleteType())
+    return false;
+  if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
+    return RD->isTriviallyRelocatable();
+  } else {
+    // Non-class types are always both move-constructible and destructible,
+    // so just check whether a non-class type is trivially copyable.
+    return T.isTriviallyCopyableType(Context);
+  }
+}
+
 static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
                                     QualType RhsT, SourceLocation KeyLoc);
 
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -6057,6 +6057,19 @@
         Record->setTrivialForCallFlags(M);
       }
 
+      if ((CSM == CXXMoveConstructor || CSM == CXXDestructor) &&
+          (M->isUserProvided() || M->isDeleted())) {
+        //puts("because 6086");
+        Record->setIsNotNaturallyTriviallyRelocatable();
+      } else if (CSM == CXXDestructor && M->isVirtual()) {
+        if (M->hasAttr<FinalAttr>() || Record->hasAttr<FinalAttr>()) {
+          // Consider removing this case to simplify the Standard wording.
+        } else {
+          //puts("because 6090");
+          Record->setIsNotNaturallyTriviallyRelocatable();
+        }
+      }
+
       if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() &&
           M->hasAttr<DLLExportAttr>()) {
         if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
@@ -6073,6 +6086,21 @@
     }
   }
 
+  for (auto *F : Record->fields()) {
+    if (F->isMutable()) {
+      //puts("because 6126");
+      Record->setIsNotNaturallyTriviallyRelocatable();
+      break;
+    }
+
+    QualType FT = F->getType();
+    if (!FT->isReferenceType() && !IsTriviallyRelocatableType(FT)) {
+      //puts("because 6130");
+      Record->setIsNotNaturallyTriviallyRelocatable();
+      break;
+    }
+  }
+
   if (HasMethodWithOverrideControl &&
       HasOverridingMethodWithoutOverrideControl) {
     // At least one method has the 'override' control declared.
@@ -6125,6 +6153,47 @@
     // is especially required for cases like vtable assumption loads.
     MarkVTableUsed(Record->getInnerLocStart(), Record);
   }
+
+  if (getLangOpts().CPlusPlus11 &&
+      !Record->hasAttr<TriviallyRelocatableAttr>() &&
+      Record->isTriviallyRelocatable()) {
+
+    if (Record->getDefinition() && !Record->isDependentContext() && !Record->isBeingDefined()) {
+      if (!Record->needsImplicitMoveConstructor()) {
+        // Check that the constructor used for move-construction is defaulted and non-deleted.
+        SpecialMemberOverloadResult SMOR =
+          LookupSpecialMember(Record, CXXMoveConstructor, false, false, false, false, false);
+        if (SMOR.getKind() != SpecialMemberOverloadResult::Success || !SMOR.getMethod()->isDefaulted()) {
+          //puts("because 6190");
+          Record->setIsNotNaturallyTriviallyRelocatable();
+        }
+      }
+    }
+  }
+
+  if (getLangOpts().CPlusPlus11 &&
+      Record->hasAttr<TriviallyRelocatableAttr>() &&
+      !isTemplateInstantiation(Record->getTemplateSpecializationKind())) {
+    if (Record->getDefinition() && !Record->isDependentContext() && !Record->isBeingDefined()) {
+      // Check that the destructor is non-deleted.
+      SpecialMemberOverloadResult SMOR =
+        LookupSpecialMember(Record, CXXDestructor, false, false, false, false, false);
+      if (SMOR.getKind() != SpecialMemberOverloadResult::Success) {
+        Diag(Record->getLocation(), diag::err_trivially_relocatable_class_is_not_relocatable)
+            << Record->getCanonicalDecl()->getTagKind() << Context.getRecordType(Record) << true;
+      } else if (Record->needsImplicitMoveConstructor() && Record->defaultedMoveConstructorIsDeleted()) {
+        Diag(Record->getLocation(), diag::err_trivially_relocatable_class_is_not_relocatable)
+            << Record->getCanonicalDecl()->getTagKind() << Context.getRecordType(Record) << false;
+      } else {
+        // Check that the constructor used for move-construction is non-deleted.
+        SMOR = LookupSpecialMember(Record, CXXMoveConstructor, false, false, false, false, false);
+        if (SMOR.getKind() != SpecialMemberOverloadResult::Success) {
+          Diag(Record->getLocation(), diag::err_trivially_relocatable_class_is_not_relocatable)
+            << Record->getCanonicalDecl()->getTagKind() << Context.getRecordType(Record) << false;
+        }
+      }
+    }
+  }
 }
 
 /// Look up the special member function that would be called by a special
@@ -6584,6 +6653,10 @@
   if (ShouldDeleteSpecialMember(MD, CSM)) {
     if (First) {
       SetDeclDeleted(MD, MD->getLocation());
+      if (CSM == CXXMoveConstructor || CSM == CXXDestructor) {
+        //puts("because 6646");
+        RD->setIsNotNaturallyTriviallyRelocatable();
+      }
     } else {
       // C++11 [dcl.fct.def.default]p4:
       //   [For a] user-provided explicitly-defaulted function [...] if such a
@@ -12548,6 +12621,8 @@
 
   if (ShouldDeleteSpecialMember(MoveConstructor, CXXMoveConstructor)) {
     ClassDecl->setImplicitMoveConstructorIsDeleted();
+    //puts("because 12631");
+    ClassDecl->setIsNotNaturallyTriviallyRelocatable();
     SetDeclDeleted(MoveConstructor, ClassLoc);
   }
 
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -5579,6 +5579,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>()) {
@@ -6460,6 +6478,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
@@ -15820,6 +15820,7 @@
       if (Dtor && Dtor->isImplicit() &&
           ShouldDeleteSpecialMember(Dtor, CXXDestructor)) {
         CXXRecord->setImplicitDestructorIsDeleted();
+        CXXRecord->setIsNotNaturallyTriviallyRelocatable();
         SetDeclDeleted(Dtor, CXXRecord->getLocation());
       }
     }
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/DeclCXX.cpp
===================================================================
--- lib/AST/DeclCXX.cpp
+++ lib/AST/DeclCXX.cpp
@@ -91,7 +91,8 @@
       DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
       HasTrivialSpecialMembersForCall(SMF_All),
       DeclaredNonTrivialSpecialMembers(0),
-      DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true),
+      DeclaredNonTrivialSpecialMembersForCall(0),
+      IsNaturallyTriviallyRelocatable(true), HasIrrelevantDestructor(true),
       HasConstexprNonCopyMoveConstructor(false),
       HasDefaultedDefaultConstructor(false),
       DefaultedDefaultConstructorIsConstexpr(true),
@@ -278,6 +279,11 @@
     if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C))
       data().HasNonLiteralTypeFieldsOrBases = true;
 
+    if (Base->isVirtual() || !BaseClassDecl->isTriviallyRelocatable()) {
+      //puts("because 283");
+      setIsNotNaturallyTriviallyRelocatable();
+    }
+
     // Now go through all virtual bases of this base and add them.
     for (const auto &VBase : BaseClassDecl->vbases()) {
       // Add this base if it's not already in the list.
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -1336,6 +1336,8 @@
       = FromData.DefaultedMoveAssignmentIsDeleted;
     ToData.DefaultedDestructorIsDeleted = FromData.DefaultedDestructorIsDeleted;
     ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers;
+    ToData.IsNaturallyTriviallyRelocatable
+      = FromData.IsNaturallyTriviallyRelocatable;
     ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor;
     ToData.HasConstexprNonCopyMoveConstructor
       = FromData.HasConstexprNonCopyMoveConstructor;
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4301,6 +4301,8 @@
                                       Expr *LowerBound, SourceLocation ColonLoc,
                                       Expr *Length, SourceLocation RBLoc);
 
+  bool IsTriviallyRelocatableType(QualType QT) const;
+
   // This struct is for use by ActOnMemberAccess to allow
   // BuildMemberReferenceExpr to be able to reinvoke ActOnMemberAccess after
   // changing the access operator from a '.' to a '->' (to see if that is the
Index: 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,12 @@
 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{class|struct|interface|union|enum}0 %1 because it is "
+  "not %select{move-constructible|destructible}2"
+  >;
+
 // Availability attribute
 def warn_availability_unknown_platform : Warning<
   "unknown platform %0 in availability macro">, InGroup<Availability>;
@@ -8186,6 +8192,11 @@
 def err_block_on_vm : Error<
   "__block attribute not allowed on declaration with a variably modified type">;
 
+def err_attribute_missing_on_first_decl : Error<
+  "type %0 declared %1 after its first declaration">;
+def note_attribute_missing_first_decl : Note<
+  "declaration missing %0 attribute is here">;
+
 def err_vec_builtin_non_vector : Error<
  "first two arguments to %0 must be vectors">;
 def err_vec_builtin_incompatible_vector : Error<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -2404,6 +2404,16 @@
   }];
 }
 
+def TriviallyRelocatableDocs : Documentation {
+  let Category = DocCatVariable;
+  let Content = [{
+A class type declared as ``[[trivially_relocatable]]`` warrants to
+the compiler that moving an object of that type, and then destroying the
+source object, is functionally equivalent to copying the underlying bytes
+and then dropping the source object on the floor.
+  }];
+}
+
 def MSInheritanceDocs : Documentation {
   let Category = DocCatType;
   let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance";
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -2060,6 +2060,14 @@
   let Documentation = [Undocumented];
 }
 
+def TriviallyRelocatable : InheritableAttr {
+  let Spellings = [CXX11<"", "trivially_relocatable", 200809>,
+                   CXX11<"clang", "trivially_relocatable">];
+  let Subjects = SubjectList<[CXXRecord]>;
+  let Documentation = [TriviallyRelocatableDocs];
+  let LangOpts = [CPlusPlus];
+}
+
 def Unused : InheritableAttr {
   let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">,
                    C2x<"", "maybe_unused">];
Index: include/clang/AST/DeclCXX.h
===================================================================
--- include/clang/AST/DeclCXX.h
+++ include/clang/AST/DeclCXX.h
@@ -468,6 +468,11 @@
     /// SMF_MoveConstructor, and SMF_Destructor are meaningful here.
     unsigned DeclaredNonTrivialSpecialMembersForCall : 6;
 
+    /// True when this class's bases and fields are all trivially relocatable,
+    /// and the class itself has a defaulted move constructor and a defaulted
+    /// destructor.
+    unsigned IsNaturallyTriviallyRelocatable : 1;
+
     /// True when this class has a destructor with no semantic effect.
     unsigned HasIrrelevantDestructor : 1;
 
@@ -1500,6 +1505,16 @@
         (SMF_CopyConstructor | SMF_MoveConstructor | SMF_Destructor);
   }
 
+  /// Determine whether this class is trivially relocatable
+  bool isTriviallyRelocatable() const {
+    return data().IsNaturallyTriviallyRelocatable ||
+           hasAttr<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

Reply via email to