[clang] [clang] Implement a bitwise_copyable builtin type trait. (PR #86512)

via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 25 07:58:57 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Haojian Wu (hokein)

<details>
<summary>Changes</summary>

This patch implements a `__is_bitwise_copyable` builtin in clang.

The bitwise copyable types act as the trivially copyable types, but they support a wider range of types (e.g. classes with virtual methods) -- their underlying types can be safely copied by `memcpy` or `memmove`, the clang compiler guarantees that both source and destination objects have the same *object* representations after the copy operation, and the lifetime of the destination object implicitly starts.

A particular use case of this builtin is to clone an object via memcopy (without running the constructor):

```
Message* clone(const Message* src, char* buffer, int size) {
  if constexpr __is_bitwise_copyable(Message) {
    // bitwise copy to buffer, and implicitly create objects at the buffer
    __builtin_memcpy(buffer, src, size);
    return std::launder(reinterpret_cast<Message*>(buffer));
  }
  // Fallback the operator new, which calls the constructor to start the lifetime.
  return new(buffer) Message(src);
}
```

Note that the definition of bitwise copyable is not tied to the Rule Of Five, so users of this builtin must guarantee that program semantic constraints are satisfied, e.g. no double resource deallocations.

Context: https://discourse.llvm.org/t/extension-for-creating-objects-via-memcpy

---
Full diff: https://github.com/llvm/llvm-project/pull/86512.diff


5 Files Affected:

- (modified) clang/include/clang/AST/Type.h (+12) 
- (modified) clang/include/clang/Basic/TokenKinds.def (+1) 
- (modified) clang/lib/AST/Type.cpp (+23) 
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+4) 
- (added) clang/test/SemaCXX/builtin-is-bitwise-copyable.cpp (+26) 


``````````diff
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 1942b0e67f65a3..9f819185d954cd 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -917,6 +917,18 @@ 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 bitwise copyable type.
+  ///
+  /// This is an extension in clang: bitwise copyable types act as trivially
+  /// copyable types, underlying bytes of bitwise copyable type can be safely
+  /// copied by memcpy or memmove. Clang guarantees that both source and
+  /// destination objects have the same **object** representations after the
+  /// copy, and the lifetime of the destination object implicitly starts.
+  ///
+  /// bitwise copyable types cover a wider range of types, e.g. classes with
+  /// virtual methods.
+  bool isBitwiseCopyableType(const ASTContext &Context) const;
+
   /// Return true if this is a trivially copyable type
   bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;
 
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 3a96f8a4d22bd1..bd0f15c8b56d9e 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -526,6 +526,7 @@ TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX)
 #include "clang/Basic/TransformTypeTraits.def"
 
 // Clang-only C++ Type Traits
+TYPE_TRAIT_1(__is_bitwise_copyable, IsBitwiseCopyable, KEYCXX)
 TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
 TYPE_TRAIT_1(__is_trivially_equality_comparable, IsTriviallyEqualityComparable, KEYCXX)
 TYPE_TRAIT_1(__is_bounded_array, IsBoundedArray, KEYCXX)
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 22666184c56ccf..0164c72f0d5cf2 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2667,6 +2667,29 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
                                      /*IsCopyConstructible=*/false);
 }
 
+bool QualType::isBitwiseCopyableType(const ASTContext & Context) const {
+  QualType CanonicalType = getCanonicalType();
+  if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType())
+    return false;
+  // Trivially copyable types are bitwise copyable, e.g. scalar types.
+  if (CanonicalType.isTriviallyCopyableType(Context))
+    return true;
+
+  if (CanonicalType->isArrayType())
+    return Context.getBaseElementType(CanonicalType)
+        .isBitwiseCopyableType(Context);
+
+  if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
+    for (auto *const Field : RD->fields()) {
+      QualType T = Context.getBaseElementType(Field->getType());
+      if (!T.isBitwiseCopyableType(Context))
+        return false;
+    }
+    return true;
+  }
+  return false;
+}
+
 bool QualType::isTriviallyCopyConstructibleType(
     const ASTContext &Context) const {
   return isTriviallyCopyableTypeImpl(*this, Context,
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c34a40fa7c81ac..dc20ec47f5b574 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5056,6 +5056,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
   case UTT_IsStandardLayout:
   case UTT_IsPOD:
   case UTT_IsLiteral:
+  // Clang extension:
+  case UTT_IsBitwiseCopyable:
   // By analogy, is_trivially_relocatable and is_trivially_equality_comparable
   // impose the same constraints.
   case UTT_IsTriviallyRelocatable:
@@ -5547,6 +5549,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
     return C.hasUniqueObjectRepresentations(T);
   case UTT_IsTriviallyRelocatable:
     return T.isTriviallyRelocatableType(C);
+  case UTT_IsBitwiseCopyable:
+    return T.isBitwiseCopyableType(C);
   case UTT_IsReferenceable:
     return T.isReferenceable();
   case UTT_CanPassInRegs:
diff --git a/clang/test/SemaCXX/builtin-is-bitwise-copyable.cpp b/clang/test/SemaCXX/builtin-is-bitwise-copyable.cpp
new file mode 100644
index 00000000000000..68e5dd21aa47d8
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-is-bitwise-copyable.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+// Scalar types are bitwise copyable.
+static_assert(__is_bitwise_copyable(int));
+static_assert(__is_bitwise_copyable(int*));
+// array
+static_assert(__is_bitwise_copyable(int[10]));
+
+
+struct Forward; // expected-note 2{{forward declaration of 'Forward'}}
+static_assert(!__is_bitwise_copyable(Forward)); // expected-error {{incomplete type 'Forward' used in type trait expression}}
+
+struct Foo { int a; };
+static_assert(__is_bitwise_copyable(Foo));
+
+struct DynamicClass { virtual int Foo(); };
+static_assert(__is_bitwise_copyable(DynamicClass));
+
+template <typename T>
+void TemplateFunction() {
+  static_assert(__is_bitwise_copyable(T)); // expected-error {{incomplete type 'Forward' used in type trait expression}}
+}
+void CallTemplateFunc() {
+  TemplateFunction<Forward>(); // expected-note {{in instantiation of function template specialization}}
+  TemplateFunction<Foo>();
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/86512


More information about the cfe-commits mailing list