Skip to content

Commit 10d4b9e

Browse files
committed
[clang] Implement a bitwise_copyable builtin type trait.
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 `memcopy` 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
1 parent 17cb8a5 commit 10d4b9e

File tree

5 files changed

+66
-0
lines changed

5 files changed

+66
-0
lines changed

clang/include/clang/AST/Type.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,18 @@ class QualType {
918918
/// Return true if this is a trivially copyable type (C++0x [basic.types]p9)
919919
bool isTriviallyCopyableType(const ASTContext &Context) const;
920920

921+
/// Return true if this is a bitwise copyable type.
922+
///
923+
/// This is an extension in clang: bitwise copyable types act as trivially
924+
/// copyable types, underlying bytes of bitwise copyable type can be safely
925+
/// copied by memcpy or memmove. Clang guarantees that both source and
926+
/// destination objects have the same **object** representations after the
927+
/// copy, and the lifetime of the destination object implicitly starts.
928+
///
929+
/// bitwise copyable types cover a wider range of types, e.g. classes with
930+
/// virtual methods.
931+
bool isBitwiseCopyableType(const ASTContext &Context) const;
932+
921933
/// Return true if this is a trivially copyable type
922934
bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;
923935

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBas
527527
#include "clang/Basic/TransformTypeTraits.def"
528528

529529
// Clang-only C++ Type Traits
530+
TYPE_TRAIT_1(__is_bitwise_copyable, IsBitwiseCopyable, KEYCXX)
530531
TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
531532
TYPE_TRAIT_1(__is_trivially_equality_comparable, IsTriviallyEqualityComparable, KEYCXX)
532533
TYPE_TRAIT_1(__is_bounded_array, IsBoundedArray, KEYCXX)

clang/lib/AST/Type.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2718,6 +2718,29 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
27182718
/*IsCopyConstructible=*/false);
27192719
}
27202720

2721+
bool QualType::isBitwiseCopyableType(const ASTContext & Context) const {
2722+
QualType CanonicalType = getCanonicalType();
2723+
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType())
2724+
return false;
2725+
// Trivially copyable types are bitwise copyable, e.g. scalar types.
2726+
if (CanonicalType.isTriviallyCopyableType(Context))
2727+
return true;
2728+
2729+
if (CanonicalType->isArrayType())
2730+
return Context.getBaseElementType(CanonicalType)
2731+
.isBitwiseCopyableType(Context);
2732+
2733+
if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
2734+
for (auto *const Field : RD->fields()) {
2735+
QualType T = Context.getBaseElementType(Field->getType());
2736+
if (!T.isBitwiseCopyableType(Context))
2737+
return false;
2738+
}
2739+
return true;
2740+
}
2741+
return false;
2742+
}
2743+
27212744
bool QualType::isTriviallyCopyConstructibleType(
27222745
const ASTContext &Context) const {
27232746
return isTriviallyCopyableTypeImpl(*this, Context,

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5109,6 +5109,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
51095109
case UTT_IsStandardLayout:
51105110
case UTT_IsPOD:
51115111
case UTT_IsLiteral:
5112+
// Clang extension:
5113+
case UTT_IsBitwiseCopyable:
51125114
// By analogy, is_trivially_relocatable and is_trivially_equality_comparable
51135115
// impose the same constraints.
51145116
case UTT_IsTriviallyRelocatable:
@@ -5594,6 +5596,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
55945596
return C.hasUniqueObjectRepresentations(T);
55955597
case UTT_IsTriviallyRelocatable:
55965598
return T.isTriviallyRelocatableType(C);
5599+
case UTT_IsBitwiseCopyable:
5600+
return T.isBitwiseCopyableType(C);
55975601
case UTT_IsReferenceable:
55985602
return T.isReferenceable();
55995603
case UTT_CanPassInRegs:
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
2+
3+
// Scalar types are bitwise copyable.
4+
static_assert(__is_bitwise_copyable(int));
5+
static_assert(__is_bitwise_copyable(int*));
6+
// array
7+
static_assert(__is_bitwise_copyable(int[10]));
8+
9+
10+
struct Forward; // expected-note 2{{forward declaration of 'Forward'}}
11+
static_assert(!__is_bitwise_copyable(Forward)); // expected-error {{incomplete type 'Forward' used in type trait expression}}
12+
13+
struct Foo { int a; };
14+
static_assert(__is_bitwise_copyable(Foo));
15+
16+
struct DynamicClass { virtual int Foo(); };
17+
static_assert(__is_bitwise_copyable(DynamicClass));
18+
19+
template <typename T>
20+
void TemplateFunction() {
21+
static_assert(__is_bitwise_copyable(T)); // expected-error {{incomplete type 'Forward' used in type trait expression}}
22+
}
23+
void CallTemplateFunc() {
24+
TemplateFunction<Forward>(); // expected-note {{in instantiation of function template specialization}}
25+
TemplateFunction<Foo>();
26+
}

0 commit comments

Comments
 (0)