[clang] f2b31f0 - re-roll-forward "[clang] Mark `trivial_abi` types as "trivially relocatable"".""
Dmitri Gribenko via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 28 05:54:06 PDT 2022
Author: Devin Jeanpierre
Date: 2022-04-28T14:53:59+02:00
New Revision: f2b31f06b79a6cfb7eb3146dfc1d514da52142e9
URL: https://github.com/llvm/llvm-project/commit/f2b31f06b79a6cfb7eb3146dfc1d514da52142e9
DIFF: https://github.com/llvm/llvm-project/commit/f2b31f06b79a6cfb7eb3146dfc1d514da52142e9.diff
LOG: re-roll-forward "[clang] Mark `trivial_abi` types as "trivially relocatable"".""
This reverts commit b0bc93da926a943cdc2d8b04f8dcbe23a774520c.
Changes: `s/_WIN32/_WIN64/g` in clang/test/SemaCXX/attr-trivial-abi.cpp.
The calling convention is specific to 64-bit windows. It's even in the name: `CCK_MicrosoftWin64`.
After this, the test passes with both `-triple i686-pc-win32` and `-triple x86_64-pc-win32`. Phew!
Reviewed By: gribozavr2
Differential Revision: https://reviews.llvm.org/D123059
Added:
Modified:
clang/docs/LanguageExtensions.rst
clang/include/clang/AST/Type.h
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/TokenKinds.def
clang/lib/AST/Type.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/test/SemaCXX/attr-trivial-abi.cpp
clang/test/SemaCXX/type-traits.cpp
clang/test/SemaObjCXX/arc-type-traits.mm
clang/test/SemaObjCXX/objc-weak-type-traits.mm
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 0391102fb222c..fbc881a724bb2 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1434,6 +1434,11 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_trivially_constructible`` (C++, GNU, Microsoft)
* ``__is_trivially_copyable`` (C++, GNU, Microsoft)
* ``__is_trivially_destructible`` (C++, MSVC 2013)
+* ``__is_trivially_relocatable`` (Clang): Returns true if moving an object
+ of the given type, and then destroying the source object, is known to be
+ functionally equivalent to copying the underlying bytes and then dropping the
+ source object on the floor. This is true of trivial types and types which
+ were made trivially relocatable via the ``clang::trivial_abi`` attribute.
* ``__is_union`` (C++, GNU, Microsoft, Embarcadero)
* ``__is_unsigned`` (C++, Embarcadero):
Returns false for enumeration types. Note, before Clang 13, returned true for
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 1a8549bedb4f9..45b75fe7960c3 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -830,6 +830,8 @@ class QualType {
/// 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;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 00d6b035ff3cf..02b6031fd5aa5 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3333,6 +3333,9 @@ If a type is trivial for the purposes of calls, has a non-trivial destructor,
and is passed as an argument by value, the convention is that the callee will
destroy the object before returning.
+If a type is trivial for the purpose of calls, it is assumed to be trivially
+relocatable for the purpose of ``__is_trivially_relocatable``.
+
Attribute ``trivial_abi`` has no effect in the following cases:
- The class directly declares a virtual base or virtual methods.
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 5092780d8f60f..093389615c263 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -511,6 +511,7 @@ TYPE_TRAIT_1(__has_unique_object_representations,
KEYWORD(__underlying_type , KEYCXX)
// Clang-only C++ Type Traits
+TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
// Embarcadero Expression Traits
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index bb9900ea6eea4..92450e8f5f2f5 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2505,6 +2505,25 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
return false;
}
+bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
+ QualType BaseElementType = Context.getBaseElementType(*this);
+
+ if (BaseElementType->isIncompleteType()) {
+ return false;
+ } else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
+ return RD->canPassInRegisters();
+ } else {
+ switch (isNonTrivialToPrimitiveDestructiveMove()) {
+ case PCK_Trivial:
+ return !isDestructedType();
+ case PCK_ARCStrong:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
+
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
return !Context.getLangOpts().ObjCAutoRefCount &&
Context.getLangOpts().ObjCWeak &&
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 7b3a18a503816..2af4a85c91c21 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -11,8 +11,6 @@
///
//===----------------------------------------------------------------------===//
-#include "clang/Sema/Template.h"
-#include "clang/Sema/SemaInternal.h"
#include "TreeTransform.h"
#include "TypeLocBuilder.h"
#include "clang/AST/ASTContext.h"
@@ -27,6 +25,7 @@
#include "clang/Basic/AlignedAllocation.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TypeTraits.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Initialization.h"
@@ -34,7 +33,9 @@
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaLambda.h"
+#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/STLExtras.h"
@@ -4797,6 +4798,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
case UTT_IsStandardLayout:
case UTT_IsPOD:
case UTT_IsLiteral:
+ // By analogy, is_trivially_relocatable imposes the same constraints.
+ case UTT_IsTriviallyRelocatable:
// Per the GCC type traits documentation, T shall be a complete type, cv void,
// or an array of unknown bound. But GCC actually imposes the same constraints
// as above.
@@ -5261,6 +5264,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return !T->isIncompleteType();
case UTT_HasUniqueObjectRepresentations:
return C.hasUniqueObjectRepresentations(T);
+ case UTT_IsTriviallyRelocatable:
+ return T.isTriviallyRelocatableType(C);
}
}
diff --git a/clang/test/SemaCXX/attr-trivial-abi.cpp b/clang/test/SemaCXX/attr-trivial-abi.cpp
index 334b471d2ab87..deae99f7d0890 100644
--- a/clang/test/SemaCXX/attr-trivial-abi.cpp
+++ b/clang/test/SemaCXX/attr-trivial-abi.cpp
@@ -5,27 +5,48 @@ void __attribute__((trivial_abi)) foo(); // expected-warning {{'trivial_abi' att
// Should not crash.
template <class>
class __attribute__((trivial_abi)) a { a(a &&); };
+#ifdef _WIN64
+// On Windows, to be trivial-for-calls, an object must be trivially copyable.
+// (And it is only trivially relocatable, currently, if it is trivial for calls.)
+// In this case, it is suppressed by an explicitly defined move constructor.
+// Similar concerns apply to later tests that have #ifdef _WIN64.
+static_assert(!__is_trivially_relocatable(a<int>), "");
+#else
+static_assert(__is_trivially_relocatable(a<int>), "");
+#endif
struct [[clang::trivial_abi]] S0 {
int a;
};
+static_assert(__is_trivially_relocatable(S0), "");
struct __attribute__((trivial_abi)) S1 {
int a;
};
+static_assert(__is_trivially_relocatable(S1), "");
struct __attribute__((trivial_abi)) S3 { // expected-warning {{'trivial_abi' cannot be applied to 'S3'}} expected-note {{is polymorphic}}
virtual void m();
};
+static_assert(!__is_trivially_relocatable(S3), "");
struct S3_2 {
virtual void m();
} __attribute__((trivial_abi)); // expected-warning {{'trivial_abi' cannot be applied to 'S3_2'}} expected-note {{is polymorphic}}
+static_assert(!__is_trivially_relocatable(S3_2), "");
struct __attribute__((trivial_abi)) S3_3 { // expected-warning {{'trivial_abi' cannot be applied to 'S3_3'}} expected-note {{has a field of a non-trivial class type}}
S3_3(S3_3 &&);
S3_2 s32;
};
+#ifdef __ORBIS__
+// The ClangABI4OrPS4 calling convention kind passes classes in registers if the
+// copy constructor is trivial for calls *or deleted*, while other platforms do
+// not accept deleted constructors.
+static_assert(__is_trivially_relocatable(S3_3), "");
+#else
+static_assert(!__is_trivially_relocatable(S3_3), "");
+#endif
// Diagnose invalid trivial_abi even when the type is templated because it has a non-trivial field.
template <class T>
@@ -33,16 +54,20 @@ struct __attribute__((trivial_abi)) S3_4 { // expected-warning {{'trivial_abi' c
S3_4(S3_4 &&);
S3_2 s32;
};
+static_assert(!__is_trivially_relocatable(S3_4<int>), "");
struct S4 {
int a;
};
+static_assert(__is_trivially_relocatable(S4), "");
struct __attribute__((trivial_abi)) S5 : public virtual S4 { // expected-warning {{'trivial_abi' cannot be applied to 'S5'}} expected-note {{has a virtual base}}
};
+static_assert(!__is_trivially_relocatable(S5), "");
struct __attribute__((trivial_abi)) S9 : public S4 {
};
+static_assert(__is_trivially_relocatable(S9), "");
struct __attribute__((trivial_abi(1))) S8 { // expected-error {{'trivial_abi' attribute takes no arguments}}
int a;
@@ -55,6 +80,8 @@ struct __attribute__((trivial_abi)) S10 {
};
S10<int *> p1;
+static_assert(__is_trivially_relocatable(S10<int>), "");
+static_assert(!__is_trivially_relocatable(S10<S3>), "");
template <class T>
struct S14 {
@@ -66,11 +93,15 @@ struct __attribute__((trivial_abi)) S15 : S14<T> {
};
S15<int> s15;
+static_assert(__is_trivially_relocatable(S15<int>), "");
+static_assert(!__is_trivially_relocatable(S15<S3>), "");
template <class T>
struct __attribute__((trivial_abi)) S16 {
S14<T> a;
};
+static_assert(__is_trivially_relocatable(S16<int>), "");
+static_assert(!__is_trivially_relocatable(S16<S3>), "");
S16<int> s16;
@@ -79,34 +110,62 @@ struct __attribute__((trivial_abi)) S17 {
};
S17<int> s17;
+static_assert(__is_trivially_relocatable(S17<int>), "");
+static_assert(__is_trivially_relocatable(S17<S3>), "");
namespace deletedCopyMoveConstructor {
struct __attribute__((trivial_abi)) CopyMoveDeleted { // expected-warning {{'trivial_abi' cannot be applied to 'CopyMoveDeleted'}} expected-note {{copy constructors and move constructors are all deleted}}
CopyMoveDeleted(const CopyMoveDeleted &) = delete;
CopyMoveDeleted(CopyMoveDeleted &&) = delete;
};
+#ifdef __ORBIS__
+static_assert(__is_trivially_relocatable(CopyMoveDeleted), "");
+#else
+static_assert(!__is_trivially_relocatable(CopyMoveDeleted), "");
+#endif
struct __attribute__((trivial_abi)) S18 { // expected-warning {{'trivial_abi' cannot be applied to 'S18'}} expected-note {{copy constructors and move constructors are all deleted}}
CopyMoveDeleted a;
};
+#ifdef __ORBIS__
+static_assert(__is_trivially_relocatable(S18), "");
+#else
+static_assert(!__is_trivially_relocatable(S18), "");
+#endif
struct __attribute__((trivial_abi)) CopyDeleted {
CopyDeleted(const CopyDeleted &) = delete;
CopyDeleted(CopyDeleted &&) = default;
};
+#ifdef _WIN64
+static_assert(!__is_trivially_relocatable(CopyDeleted), "");
+#else
+static_assert(__is_trivially_relocatable(CopyDeleted), "");
+#endif
struct __attribute__((trivial_abi)) MoveDeleted {
MoveDeleted(const MoveDeleted &) = default;
MoveDeleted(MoveDeleted &&) = delete;
};
+static_assert(__is_trivially_relocatable(MoveDeleted), "");
struct __attribute__((trivial_abi)) S19 { // expected-warning {{'trivial_abi' cannot be applied to 'S19'}} expected-note {{copy constructors and move constructors are all deleted}}
CopyDeleted a;
MoveDeleted b;
};
+#ifdef __ORBIS__
+static_assert(__is_trivially_relocatable(S19), "");
+#else
+static_assert(!__is_trivially_relocatable(S19), "");
+#endif
// This is fine since the move constructor isn't deleted.
struct __attribute__((trivial_abi)) S20 {
int &&a; // a member of rvalue reference type deletes the copy constructor.
};
+#ifdef _WIN64
+static_assert(!__is_trivially_relocatable(S20), "");
+#else
+static_assert(__is_trivially_relocatable(S20), "");
+#endif
} // namespace deletedCopyMoveConstructor
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index d576e4388d6f3..e46908e66febe 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -2854,3 +2854,64 @@ void test() { (void) __is_constructible(int, T32768(int)); }
#undef T16384
#undef T32768
} // namespace type_trait_expr_numargs_overflow
+
+namespace is_trivially_relocatable {
+
+static_assert(!__is_trivially_relocatable(void), "");
+static_assert(__is_trivially_relocatable(int), "");
+static_assert(__is_trivially_relocatable(int[]), "");
+
+enum Enum {};
+static_assert(__is_trivially_relocatable(Enum), "");
+static_assert(__is_trivially_relocatable(Enum[]), "");
+
+union Union {int x;};
+static_assert(__is_trivially_relocatable(Union), "");
+static_assert(__is_trivially_relocatable(Union[]), "");
+
+struct Trivial {};
+static_assert(__is_trivially_relocatable(Trivial), "");
+static_assert(__is_trivially_relocatable(Trivial[]), "");
+
+struct Incomplete; // expected-note {{forward declaration of 'is_trivially_relocatable::Incomplete'}}
+bool unused = __is_trivially_relocatable(Incomplete); // expected-error {{incomplete type}}
+
+struct NontrivialDtor {
+ ~NontrivialDtor() {}
+};
+static_assert(!__is_trivially_relocatable(NontrivialDtor), "");
+static_assert(!__is_trivially_relocatable(NontrivialDtor[]), "");
+
+struct NontrivialCopyCtor {
+ NontrivialCopyCtor(const NontrivialCopyCtor&) {}
+};
+static_assert(!__is_trivially_relocatable(NontrivialCopyCtor), "");
+static_assert(!__is_trivially_relocatable(NontrivialCopyCtor[]), "");
+
+struct NontrivialMoveCtor {
+ NontrivialMoveCtor(NontrivialMoveCtor&&) {}
+};
+static_assert(!__is_trivially_relocatable(NontrivialMoveCtor), "");
+static_assert(!__is_trivially_relocatable(NontrivialMoveCtor[]), "");
+
+struct [[clang::trivial_abi]] TrivialAbiNontrivialDtor {
+ ~TrivialAbiNontrivialDtor() {}
+};
+static_assert(__is_trivially_relocatable(TrivialAbiNontrivialDtor), "");
+static_assert(__is_trivially_relocatable(TrivialAbiNontrivialDtor[]), "");
+
+struct [[clang::trivial_abi]] TrivialAbiNontrivialCopyCtor {
+ TrivialAbiNontrivialCopyCtor(const TrivialAbiNontrivialCopyCtor&) {}
+};
+static_assert(__is_trivially_relocatable(TrivialAbiNontrivialCopyCtor), "");
+static_assert(__is_trivially_relocatable(TrivialAbiNontrivialCopyCtor[]), "");
+
+// A more complete set of tests for the behavior of trivial_abi can be found in
+// clang/test/SemaCXX/attr-trivial-abi.cpp
+struct [[clang::trivial_abi]] TrivialAbiNontrivialMoveCtor {
+ TrivialAbiNontrivialMoveCtor(TrivialAbiNontrivialMoveCtor&&) {}
+};
+static_assert(__is_trivially_relocatable(TrivialAbiNontrivialMoveCtor), "");
+static_assert(__is_trivially_relocatable(TrivialAbiNontrivialMoveCtor[]), "");
+
+} // namespace is_trivially_relocatable
diff --git a/clang/test/SemaObjCXX/arc-type-traits.mm b/clang/test/SemaObjCXX/arc-type-traits.mm
index 12993a910e544..8694fe717c633 100644
--- a/clang/test/SemaObjCXX/arc-type-traits.mm
+++ b/clang/test/SemaObjCXX/arc-type-traits.mm
@@ -12,7 +12,7 @@
#define TRAIT_IS_FALSE(Trait, Type) char JOIN2(Trait,__LINE__)[Trait(Type)? -1 : 1]
#define TRAIT_IS_TRUE_2(Trait, Type1, Type2) char JOIN2(Trait,__LINE__)[Trait(Type1, Type2)? 1 : -1]
#define TRAIT_IS_FALSE_2(Trait, Type1, Type2) char JOIN2(Trait,__LINE__)[Trait(Type1, Type2)? -1 : 1]
-
+
struct HasStrong { id obj; };
struct HasWeak { __weak id obj; };
struct HasUnsafeUnretained { __unsafe_unretained id obj; };
@@ -213,3 +213,11 @@
TRAIT_IS_TRUE_2(__is_trivially_constructible, HasUnsafeUnretained, HasUnsafeUnretained);
TRAIT_IS_TRUE_2(__is_trivially_constructible, HasUnsafeUnretained, HasUnsafeUnretained&&);
+// __is_trivially_relocatable
+TRAIT_IS_TRUE(__is_trivially_relocatable, __strong id);
+TRAIT_IS_FALSE(__is_trivially_relocatable, __weak id);
+TRAIT_IS_TRUE(__is_trivially_relocatable, __autoreleasing id);
+TRAIT_IS_TRUE(__is_trivially_relocatable, __unsafe_unretained id);
+TRAIT_IS_TRUE(__is_trivially_relocatable, HasStrong);
+TRAIT_IS_FALSE(__is_trivially_relocatable, HasWeak);
+TRAIT_IS_TRUE(__is_trivially_relocatable, HasUnsafeUnretained);
diff --git a/clang/test/SemaObjCXX/objc-weak-type-traits.mm b/clang/test/SemaObjCXX/objc-weak-type-traits.mm
index f425f47bd6fd4..423f22837efdf 100644
--- a/clang/test/SemaObjCXX/objc-weak-type-traits.mm
+++ b/clang/test/SemaObjCXX/objc-weak-type-traits.mm
@@ -8,7 +8,7 @@
#define TRAIT_IS_FALSE(Trait, Type) static_assert(!Trait(Type), "")
#define TRAIT_IS_TRUE_2(Trait, Type1, Type2) static_assert(Trait(Type1, Type2), "")
#define TRAIT_IS_FALSE_2(Trait, Type1, Type2) static_assert(!Trait(Type1, Type2), "")
-
+
struct HasStrong { id obj; };
struct HasWeak { __weak id obj; };
struct HasUnsafeUnretained { __unsafe_unretained id obj; };
@@ -208,3 +208,12 @@
TRAIT_IS_FALSE_2(__is_trivially_constructible, HasWeak, HasWeak&&);
TRAIT_IS_TRUE_2(__is_trivially_constructible, HasUnsafeUnretained, HasUnsafeUnretained);
TRAIT_IS_TRUE_2(__is_trivially_constructible, HasUnsafeUnretained, HasUnsafeUnretained&&);
+
+// __is_trivially_relocatable
+TRAIT_IS_TRUE(__is_trivially_relocatable, __strong id);
+TRAIT_IS_FALSE(__is_trivially_relocatable, __weak id);
+TRAIT_IS_TRUE(__is_trivially_relocatable, __autoreleasing id);
+TRAIT_IS_TRUE(__is_trivially_relocatable, __unsafe_unretained id);
+TRAIT_IS_TRUE(__is_trivially_relocatable, HasStrong);
+TRAIT_IS_FALSE(__is_trivially_relocatable, HasWeak);
+TRAIT_IS_TRUE(__is_trivially_relocatable, HasUnsafeUnretained);
More information about the cfe-commits
mailing list