Quuxplusone updated this revision to Diff 158822.
Quuxplusone marked 20 inline comments as done.
Quuxplusone added a comment.
Further removal of dead code based on @Rakete1111's feedback.
Repository:
rC Clang
https://reviews.llvm.org/D50119
Files:
docs/LanguageExtensions.rst
include/clang/AST/DeclCXX.h
include/clang/AST/Type.h
include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/Features.def
include/clang/Basic/TokenKinds.def
include/clang/Basic/TypeTraits.h
lib/AST/ASTImporter.cpp
lib/AST/DeclCXX.cpp
lib/AST/Type.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/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,495 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// expected-diagnostics
+
+static_assert(__has_extension(trivially_relocatable), "");
+
+// It shall appear at most once in each attribute-list
+// and no attribute-argument-clause shall be present.
+
+struct [[trivially_relocatable, trivially_relocatable]] B1 {};
+// expected-error@-1{{attribute 'trivially_relocatable' cannot appear multiple times in an attribute specifier}}
+
+struct [[trivially_relocatable]] [[trivially_relocatable]] B2 {}; // should really be an error
+
+struct [[trivially_relocatable(42)]] B3 {};
+// expected-error@-1{{attribute 'trivially_relocatable' cannot have an argument list}}
+
+
+// The first declaration of a type shall specify the
+// trivially_relocatable attribute if any declaration of that
+// type specifies the trivially_relocatable attribute.
+
+struct [[trivially_relocatable]] A1 {}; // ok
+struct [[trivially_relocatable]] A1;
+
+struct [[trivially_relocatable]] A2; // ok
+struct [[trivially_relocatable]] A2 {};
+
+struct [[trivially_relocatable]] A3 {}; // ok
+struct A3;
+
+struct [[trivially_relocatable]] A4; // ok
+struct A4 {};
+
+struct A5 {};
+struct [[trivially_relocatable]] A5;
+// expected-error@-1{{type A5 declared 'trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}}
+// expected-warning@-3{{attribute declaration must precede definition}}
+// expected-note@-5{{previous definition is here}}
+
+struct A6;
+struct [[trivially_relocatable]] A6 {};
+// expected-error@-1{{type A6 declared 'trivially_relocatable' after its first declaration}}
+// expected-note@-3{{declaration missing 'trivially_relocatable' attribute is here}}
+
+
+// If a type T is declared with the trivially_relocatable attribute, and T is either
+// not move-constructible or not destructible, the program is ill-formed.
+
+struct NonDestructible {
+ NonDestructible(const NonDestructible&) = default;
+ NonDestructible(NonDestructible&&) = default;
+ ~NonDestructible() = delete;
+};
+struct NonCopyConstructible {
+ NonCopyConstructible(const NonCopyConstructible&) = delete;
+};
+struct NonMoveConstructible {
+ NonMoveConstructible(const NonMoveConstructible&) = default;
+ NonMoveConstructible(NonMoveConstructible&&) = delete;
+};
+static_assert(!__is_trivially_relocatable(NonDestructible), "");
+static_assert(!__is_trivially_relocatable(NonCopyConstructible), "");
+static_assert(!__is_constructible(NonCopyConstructible, NonCopyConstructible&&), "");
+static_assert(!__is_trivially_relocatable(NonMoveConstructible), "");
+static_assert(!__is_constructible(NonMoveConstructible, NonMoveConstructible&&), "");
+
+struct [[trivially_relocatable]] D1 { ~D1() = delete; };
+// expected-error@-1{{cannot be applied to struct 'D1' because it is not destructible}}
+
+struct [[trivially_relocatable]] D2 : private NonDestructible { };
+// expected-error@-1{{cannot be applied to struct 'D2' because it is not destructible}}
+
+struct [[trivially_relocatable]] D3 { D3(const D3&) = delete; };
+// expected-error@-1{{cannot be applied to struct 'D3' because it is not move-constructible}}
+
+struct [[trivially_relocatable]] D4 { D4(const D4&) = default; D4(D4&&) = delete; };
+// expected-error@-1{{cannot be applied to struct 'D4' because it is not move-constructible}}
+
+struct [[trivially_relocatable]] D5 : private NonCopyConstructible { };
+// expected-error@-1{{cannot be applied to struct 'D5' because it is not move-constructible}}
+static_assert(!__is_constructible(D5, D5&&), "");
+
+struct [[trivially_relocatable]] D6 : private NonMoveConstructible { D6(D6&&) = default; };
+// expected-error@-1{{cannot be applied to struct 'D6' because it is not move-constructible}}
+
+template<class T>
+struct [[trivially_relocatable]] DT1 : private T { }; // ok
+
+struct D7 {
+ DT1<NonDestructible> m;
+};
+
+class [[trivially_relocatable]] D8 {
+ DT1<NonDestructible> m;
+};
+// expected-error@-3{{cannot be applied to class 'D8' because it is not destructible}}
+
+
+// Now test specific types for trivial relocatability.
+
+static_assert(__is_trivially_relocatable(char), "");
+static_assert(__is_trivially_relocatable(int), "");
+static_assert(__is_trivially_relocatable(int*), "");
+static_assert(!__is_trivially_relocatable(int&), "");
+static_assert(__is_trivially_relocatable(float), "");
+static_assert(__is_trivially_relocatable(double), "");
+static_assert(!__is_trivially_relocatable(void), "");
+static_assert(__is_trivially_relocatable(char[1]), "");
+static_assert(__is_trivially_relocatable(char[]), "");
+
+static_assert(__is_trivially_relocatable(const int), "");
+static_assert(__is_trivially_relocatable(volatile int), "");
+static_assert(!__is_trivially_relocatable(const int&), "");
+static_assert(!__is_trivially_relocatable(volatile int&), "");
+
+struct C1 { int x; }; static_assert(__is_trivially_relocatable(C1), "");
+struct C2 { const int x; }; static_assert(__is_trivially_relocatable(C2), "");
+struct C3 { volatile int x; }; static_assert(__is_trivially_relocatable(C3), "");
+struct C4 { int *x; }; static_assert(__is_trivially_relocatable(C4), "");
+struct C5 { const int *x; }; static_assert(__is_trivially_relocatable(C5), "");
+struct C6 { volatile int *x; }; static_assert(__is_trivially_relocatable(C6), "");
+struct C7 { int& x; }; static_assert(__is_trivially_relocatable(C7), "");
+struct C8 { const int& x; }; static_assert(__is_trivially_relocatable(C8), "");
+struct C9 { volatile int& x; }; static_assert(__is_trivially_relocatable(C9), "");
+
+enum E { x = 1, y = 2 };
+static_assert(__is_trivially_relocatable(E), "");
+static_assert(__is_trivially_relocatable(E[1]), "");
+
+struct T1 {};
+static_assert(__is_trivially_relocatable(T1), "");
+
+struct T2 { int x; E y; int *z; };
+static_assert(__is_trivially_relocatable(T2), "");
+
+struct T3 { int x; T3(T3&&) = default; };
+static_assert(__is_trivially_relocatable(T3), "");
+
+struct T4 { int x; ~T4() = default; };
+static_assert(__is_trivially_relocatable(T4), "trivially copy-constructible, and no move constructor");
+
+struct T4a { T4 a; };
+static_assert(__is_trivially_relocatable(T4a), "trivially copy-constructible, and no move constructor");
+
+
+struct VD {
+ VD(const VD&) = default;
+ VD(VD&&) = default;
+ virtual ~VD() = default;
+};
+void relocate_example(VD&& src) {
+ VD dst(static_cast<VD&&>(src)); // this DEFINITELY calls the trivial move-constructor
+ src.~VD(); // this MAY virtually dispatch to a non-trivial destructor
+}
+static_assert(!__is_trivially_relocatable(VD), "");
+
+
+struct VD2 final {
+ VD2(const VD2&) = default;
+ VD2(VD2&&) = default;
+ virtual ~VD2() = default;
+};
+void relocate_example(VD2&& src) {
+ VD2 dst(static_cast<VD2&&>(src)); // this DEFINITELY calls the trivial move-constructor
+ src.~VD2(); // because "final", this CANNOT virtually dispatch to a non-trivial destructor
+}
+static_assert(__is_trivially_relocatable(VD2), "");
+
+
+struct VD3 {
+ VD3(const VD3&) = default;
+ VD3(VD3&&) = default;
+ virtual ~VD3() final = default;
+};
+void relocate_example(VD3&& src) {
+ VD3 dst(static_cast<VD3&&>(src)); // this DEFINITELY calls the trivial move-constructor
+ src.~VD3(); // because "final", this CANNOT virtually dispatch to a non-trivial destructor
+}
+static_assert(__is_trivially_relocatable(VD3), "");
+
+
+struct VB : public virtual T1 {
+ VB(const VB&) = default;
+ VB(VB&&) = default;
+ ~VB() = default;
+};
+void relocate_example(VB&& src) {
+ VB dst(static_cast<VB&&>(src)); // this MAY copy the virtual bases of "src" in a way not tantamount to memcpy
+ src.~VB(); // this calls the trivial destructor
+}
+static_assert(__is_trivially_destructible(VB), "");
+static_assert(!__is_trivially_constructible(VB, VB&&), "");
+static_assert(!__is_trivially_relocatable(VB), "");
+
+struct VB2 final : public virtual T1 {
+ VB2(const VB2&) = default;
+ VB2(VB2&&) = default;
+ ~VB2() = default;
+};
+void relocate_example(VB2&& src) {
+ VB2 dst(static_cast<VB2&&>(src)); // this MAY STILL copy the VBPTR of "src" in a way not tantamount to memcpy
+ src.~VB2(); // this calls the trivial destructor
+}
+static_assert(__is_trivially_destructible(VB2), "");
+static_assert(!__is_trivially_constructible(VB2, VB2&&), "");
+static_assert(!__is_trivially_relocatable(VB2), "");
+
+
+struct CCNMC {
+ CCNMC(const CCNMC&) = default;
+ // no move constructor at all
+ ~CCNMC() = default;
+};
+void relocate_example(CCNMC&& src) {
+ CCNMC dst(static_cast<CCNMC&&>(src)); // this calls the trivial copy-constructor
+ src.~CCNMC(); // this calls the trivial destructor
+}
+static_assert(__is_constructible(CCNMC, CCNMC&&), "");
+static_assert(__is_trivially_relocatable(CCNMC), "");
+
+
+struct CCDMC {
+ CCDMC(const CCDMC&) = default;
+ CCDMC(CCDMC&&) = delete;
+ ~CCDMC() = default;
+};
+void relocate_example(CCDMC&& src) {
+ // CCDMC dst(static_cast<CCDMC&&>(src)); // this is not permitted
+ src.~CCDMC(); // this calls the trivial destructor
+}
+static_assert(!__is_constructible(CCDMC, CCDMC&&), "");
+static_assert(!__is_trivially_relocatable(CCDMC), "");
+
+struct DD { ~DD() = delete; };
+static_assert(__is_trivially_copyable(DD), "");
+static_assert(!__is_trivially_destructible(DD), "");
+static_assert(!__is_trivially_relocatable(DD), "");
+
+
+struct T5 { int x; T5(T5&&) {} };
+static_assert(!__is_trivially_relocatable(T5), "");
+
+struct T6 { int x; ~T6() {} };
+static_assert(!__is_trivially_relocatable(T6), "");
+
+struct T7 { int x; T7(const T7&) {} };
+static_assert(!__is_trivially_relocatable(T7), "T7 has no implicitly declared move constructor");
+
+struct T8 { virtual void f() {} int x; };
+static_assert(__is_trivially_relocatable(T8), "T8 has a vptr but that's fine");
+
+struct [[trivially_relocatable]] T9 { int x; T9(T9&&) {} };
+static_assert(__is_trivially_relocatable(T9), "T9 isn't naturally, but it has the attribute");
+
+struct [[trivially_relocatable]] T10 { int x; ~T10() {} };
+static_assert(__is_trivially_relocatable(T10), "T10 isn't naturally, but it has the attribute");
+
+struct T11 {
+ T11();
+ T1 a;
+ T2 b;
+ T3 c;
+ T4 d;
+ T8 e;
+ T9 f;
+ T10 g;
+};
+static_assert(__is_trivially_relocatable(T11), "all fields have trivially relocatable types");
+
+struct T12 {
+ T1 a;
+ T2 b;
+ T3 c;
+ T5 d; // not trivially relocatable
+ T8 e;
+ T9 f;
+ T10 g;
+};
+static_assert(!__is_trivially_relocatable(T12), "not all fields have trivially relocatable types");
+
+struct T13 : T1, T2, T3, T4 {};
+static_assert(__is_trivially_relocatable(T13), "all bases have trivially relocatable types");
+
+struct T14 : T1, T6, T3, T4 {};
+static_assert(!__is_trivially_relocatable(T14), "all bases have trivially relocatable types");
+
+template<class... Ts>
+struct T15 : Ts... {};
+
+static_assert(__is_trivially_relocatable(T15<T1,T2,T3>), "all bases have trivially relocatable types");
+static_assert(!__is_trivially_relocatable(T15<T1,T6,T3>), "not all bases have trivially relocatable types");
+
+template<class... Ts>
+struct [[trivially_relocatable]] T16 : Ts... {};
+
+static_assert(__is_trivially_relocatable(T16<T1,T2,T3>), "all bases have trivially relocatable types");
+static_assert(__is_trivially_relocatable(T16<T1,T6,T3>), "not naturally, but it has the attribute");
+
+struct T17 : T15<T10> {}; // T10 is trivially relocatable
+static_assert(__is_trivially_relocatable(T17), "");
+static_assert(__is_trivially_relocatable(T15<T17>), "");
+static_assert(__is_trivially_relocatable(T16<T17>), "");
+
+struct T18 : T15<T12> {}; // T12 is not trivially relocatable
+static_assert(!__is_trivially_relocatable(T18), "");
+static_assert(!__is_trivially_relocatable(T15<T18>), "");
+static_assert(__is_trivially_relocatable(T16<T18>), "not naturally, but it has the attribute");
+
+
+struct T19 {
+ struct [[trivially_relocatable]] UniquePtr {
+ UniquePtr();
+ UniquePtr(const UniquePtr&) = delete;
+ UniquePtr(UniquePtr&&);
+ ~UniquePtr();
+ };
+ UniquePtr m;
+ T19(const T19&) {}
+ T19(T19&&) = default;
+};
+
+static_assert(!__is_trivially_constructible(T19, const T19&), "user-provided copy constructor");
+static_assert(!__is_trivially_constructible(T19, T19&&), "defaulted non-trivial move constructor");
+static_assert(!__is_trivially_destructible(T19), "defaulted non-trivial destructor");
+static_assert(__is_trivially_relocatable(T19), "nevertheless, the Rule of Zero applies here");
+
+
+struct T20 {
+ struct [[trivially_relocatable]] SharedPtr {
+ SharedPtr();
+ SharedPtr(const SharedPtr&);
+ SharedPtr(SharedPtr&&);
+ ~SharedPtr();
+ };
+ SharedPtr m;
+ T20(const T20&) = default;
+ ~T20() = default;
+ // no move constructor
+};
+void relocate_example(T20&& src) {
+ T20 dst(static_cast<T20&&>(src)); // this calls the defaulted copy constructor and makes a COPY of the SharedPtr
+ src.~T20(); // this calls the destructor and deletes the original copy
+}
+static_assert(__is_trivially_relocatable(T20::SharedPtr), "because it's annotated");
+static_assert(!__is_trivially_constructible(T20, const T20&), "defaulted, non-trivial copy constructor");
+static_assert(__is_constructible(T20, T20&&), "uses the copy constructor");
+static_assert(!__is_trivially_constructible(T20, T20&&), "uses the copy constructor");
+static_assert(!__is_trivially_destructible(T20), "defaulted non-trivial destructor");
+static_assert(__is_trivially_relocatable(T20), "I'm not sure but I think copy-and-destroy should always be assumed tantamount to move-and-destroy");
+
+
+struct T21 {
+ struct [[trivially_relocatable]] SharedPtr {
+ SharedPtr();
+ SharedPtr(const SharedPtr&);
+ SharedPtr(SharedPtr&&);
+ ~SharedPtr();
+ };
+ SharedPtr m;
+ T21(const T21&); // user-provided
+ ~T21() = default;
+ // no move constructor
+};
+void relocate_example(T21&& src) {
+ T21 dst(static_cast<T21&&>(src)); // this calls the user-provided copy constructor
+ src.~T21(); // this calls the defaulted destructor
+}
+static_assert(__is_trivially_relocatable(T21::SharedPtr), "because it's annotated");
+static_assert(!__is_trivially_constructible(T21, const T21&), "non-defaulted, non-trivial copy constructor");
+static_assert(__is_constructible(T21, T21&&), "uses the copy constructor");
+static_assert(!__is_trivially_constructible(T21, T21&&), "uses the copy constructor");
+static_assert(!__is_trivially_destructible(T21), "defaulted non-trivial destructor");
+static_assert(!__is_trivially_relocatable(T21), "Relocating T21 calls T21's user-provided copy constructor, which we don't know what it does");
+
+
+struct T22 {
+ struct [[trivially_relocatable]] MoveOnly { MoveOnly(MoveOnly&&); };
+ struct CopyOnly { ~CopyOnly() = default; };
+ MoveOnly m1;
+ CopyOnly m2;
+};
+void relocate_example(T22&& src) {
+ T22 dst(static_cast<T22&&>(src)); // this moves m1 (user-provided) and copies m2 (trivial, defaulted)
+ src.~T22(); // this destroys m1 (trivial, defaulted) and m2 (trivial, defaulted)
+}
+static_assert(!__is_constructible(T22::MoveOnly, const T22::MoveOnly&), "");
+static_assert(__is_constructible(T22::MoveOnly, T22::MoveOnly&&), "");
+static_assert(!__is_trivially_constructible(T22::MoveOnly, T22::MoveOnly&&), "");
+static_assert(__is_trivially_relocatable(T22::MoveOnly), "because it's annotated");
+static_assert(__is_constructible(T22::CopyOnly, const T22::CopyOnly&), "");
+static_assert(__is_constructible(T22::CopyOnly, T22::CopyOnly&&), "");
+static_assert(__is_trivially_constructible(T22::CopyOnly, const T22::CopyOnly&), "");
+static_assert(__is_trivially_constructible(T22::CopyOnly, T22::CopyOnly&&), "");
+static_assert(__is_trivially_relocatable(T22::CopyOnly), "because its copy constructor is defaulted and its move constructor doesn't exist");
+static_assert(!__is_constructible(T22, const T22&), "m1 is not copyable");
+static_assert(__is_constructible(T22, T22&&), "");
+static_assert(!__is_trivially_constructible(T22, T22&&), "m2 is not trivially moveable");
+static_assert(__is_trivially_destructible(T22), "both members are trivially destructible");
+static_assert(__is_trivially_relocatable(T22), "because its members are trivially relocatable");
+
+
+struct T23 {
+ struct Evil { Evil(Evil&); Evil(Evil&&) = default; ~Evil() = default; };
+ mutable Evil m;
+};
+void relocate_example(T23&& src) {
+ T23 dst(static_cast<T23&&>(src)); // this moves m (trivial, defaulted)
+ src.~T23(); // this destroys m (trivial, defaulted)
+}
+static_assert(__is_trivially_relocatable(T23::Evil), "because it is trivially move-constructible and destructible");
+static_assert(__is_constructible(T23, T23&&), "");
+static_assert(__is_constructible(T23, T23&), "");
+static_assert(!__is_constructible(T23, const T23&), "");
+static_assert(!__is_trivially_relocatable(T23), "because its copy operation is evil");
+
+struct T23a {
+ struct Evil { Evil(Evil&); Evil(const Evil&) = default; ~Evil() = default; };
+ mutable Evil m;
+};
+void relocate_example(T23a&& src) {
+ T23a dst(static_cast<T23a&&>(src)); // this copies m using the non-defaulted copy constructor
+ src.~T23a(); // this destroys m (trivial, defaulted)
+}
+static_assert(__is_trivially_relocatable(T23a::Evil), "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), "");
+
+// Example from Nicolas Lesser
+struct NL1 {
+ NL1& operator=(NL1&&);
+};
+static_assert(!__is_trivially_relocatable(NL1), "");
+
+struct [[trivially_relocatable]] NL2 {
+// expected-error@-1{{cannot be applied to struct 'NL2' because it is not move-constructible}}
+ NL2& operator=(NL2&&);
+};
+static_assert(!__is_trivially_relocatable(NL2), "");
+
+union [[trivially_relocatable]] NL3 {
+// expected-error@-1{{cannot be applied to union 'NL3' because it is not destructible}}
+ struct [[trivially_relocatable]] String { String(String&&); ~String(); };
+ int i;
+ String s;
+};
+static_assert(!__is_trivially_relocatable(NL3), "");
+
+union [[trivially_relocatable]] NL4 {
+ struct [[trivially_relocatable]] String { String(String&&); ~String(); };
+ int i;
+ String s;
+ NL4(const NL4&);
+ ~NL4();
+};
+static_assert(__is_trivially_relocatable(NL4), "");
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 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
@@ -6057,6 +6057,21 @@
Record->setTrivialForCallFlags(M);
}
+ // A move-constructible, destructible object type T is a
+ // trivially relocatable type if it ...
+ if ((CSM == CXXMoveConstructor || CSM == CXXDestructor) &&
+ (M->isUserProvided() || M->isDeleted())) {
+ // - has either a defaulted, non-deleted move constructor or no
+ // move constructor and a defaulted, non-deleted copy constructor,
+ // - has a defaulted, non-deleted destructor,
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ } else if (CSM == CXXDestructor && M->isVirtual()) {
+ // - either is final, or has a final destructor,
+ // or has a non-virtual destructor ...
+ if (!M->hasAttr<FinalAttr>() && !Record->hasAttr<FinalAttr>())
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ }
+
if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() &&
M->hasAttr<DLLExportAttr>()) {
if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
@@ -6073,6 +6088,19 @@
}
}
+ for (auto *F : Record->fields()) {
+ if (F->isMutable()) {
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ break;
+ }
+
+ QualType FT = F->getType();
+ if (!FT->isReferenceType() && !FT.isTriviallyRelocatableType(Context)) {
+ Record->setIsNotNaturallyTriviallyRelocatable();
+ break;
+ }
+ }
+
if (HasMethodWithOverrideControl &&
HasOverridingMethodWithoutOverrideControl) {
// At least one method has the 'override' control declared.
@@ -6125,6 +6153,50 @@
// is especially required for cases like vtable assumption loads.
MarkVTableUsed(Record->getInnerLocStart(), Record);
}
+
+ if (getLangOpts().CPlusPlus11 && 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())
+ 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;
+ Record->dropAttr<TriviallyRelocatableAttr>();
+ } 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;
+ Record->dropAttr<TriviallyRelocatableAttr>();
+ }
+ }
+ }
+ }
}
/// 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
@@ -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/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -3808,6 +3808,7 @@
case ParsedAttr::AT_Deprecated:
case ParsedAttr::AT_FallThrough:
case ParsedAttr::AT_CXX11NoReturn:
+ case ParsedAttr::AT_TriviallyRelocatable:
return true;
case ParsedAttr::AT_WarnUnusedResult:
return !ScopeName && AttrName->getName().equals("nodiscard");
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -2242,6 +2242,19 @@
return false;
}
+bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
+ QualType T = Context.getBaseElementType(*this);
+ if (T->isIncompleteType())
+ return false;
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
+ return RD->isTriviallyRelocatable();
+ } else {
+ // Non-class types are always both move-constructible and destructible,
+ // so just check whether a non-class type is trivially copyable.
+ return T.isTriviallyCopyableType(Context);
+ }
+}
+
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
return !Context.getLangOpts().ObjCAutoRefCount &&
Context.getLangOpts().ObjCWeak &&
Index: lib/AST/DeclCXX.cpp
===================================================================
--- lib/AST/DeclCXX.cpp
+++ lib/AST/DeclCXX.cpp
@@ -91,7 +91,8 @@
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
HasTrivialSpecialMembersForCall(SMF_All),
DeclaredNonTrivialSpecialMembers(0),
- DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true),
+ DeclaredNonTrivialSpecialMembersForCall(0),
+ IsNaturallyTriviallyRelocatable(true), HasIrrelevantDestructor(true),
HasConstexprNonCopyMoveConstructor(false),
HasDefaultedDefaultConstructor(false),
DefaultedDefaultConstructorIsConstexpr(true),
@@ -278,6 +279,9 @@
if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C))
data().HasNonLiteralTypeFieldsOrBases = true;
+ if (Base->isVirtual() || !BaseClassDecl->isTriviallyRelocatable())
+ setIsNotNaturallyTriviallyRelocatable();
+
// Now go through all virtual bases of this base and add them.
for (const auto &VBase : BaseClassDecl->vbases()) {
// Add this base if it's not already in the list.
Index: lib/AST/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/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{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>;
@@ -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/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -810,6 +810,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,11 @@
/// SMF_MoveConstructor, and SMF_Destructor are meaningful here.
unsigned DeclaredNonTrivialSpecialMembersForCall : 6;
+ /// True when this class's bases and fields are all trivially relocatable
+ /// or references, and the class itself has a defaulted move constructor
+ /// and a defaulted destructor.
+ unsigned IsNaturallyTriviallyRelocatable : 1;
+
/// True when this class has a destructor with no semantic effect.
unsigned HasIrrelevantDestructor : 1;
@@ -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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits