Skip to content

Commit 481627e

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 5a74477 commit 481627e

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
@@ -917,6 +917,18 @@ class QualType {
917917
/// Return true if this is a trivially copyable type (C++0x [basic.types]p9)
918918
bool isTriviallyCopyableType(const ASTContext &Context) const;
919919

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

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX)
526526
#include "clang/Basic/TransformTypeTraits.def"
527527

528528
// Clang-only C++ Type Traits
529+
TYPE_TRAIT_1(__is_bitwise_copyable, IsBitwiseCopyable, KEYCXX)
529530
TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
530531
TYPE_TRAIT_1(__is_trivially_equality_comparable, IsTriviallyEqualityComparable, KEYCXX)
531532
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
@@ -2667,6 +2667,29 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
26672667
/*IsCopyConstructible=*/false);
26682668
}
26692669

2670+
bool QualType::isBitwiseCopyableType(const ASTContext & Context) const {
2671+
QualType CanonicalType = getCanonicalType();
2672+
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType())
2673+
return false;
2674+
// Trivially copyable types are bitwise copyable, e.g. scalar types.
2675+
if (CanonicalType.isTriviallyCopyableType(Context))
2676+
return true;
2677+
2678+
if (CanonicalType->isArrayType())
2679+
return Context.getBaseElementType(CanonicalType)
2680+
.isBitwiseCopyableType(Context);
2681+
2682+
if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
2683+
for (auto *const Field : RD->fields()) {
2684+
QualType T = Context.getBaseElementType(Field->getType());
2685+
if (!T.isBitwiseCopyableType(Context))
2686+
return false;
2687+
}
2688+
return true;
2689+
}
2690+
return false;
2691+
}
2692+
26702693
bool QualType::isTriviallyCopyConstructibleType(
26712694
const ASTContext &Context) const {
26722695
return isTriviallyCopyableTypeImpl(*this, Context,

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5056,6 +5056,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
50565056
case UTT_IsStandardLayout:
50575057
case UTT_IsPOD:
50585058
case UTT_IsLiteral:
5059+
// Clang extension:
5060+
case UTT_IsBitwiseCopyable:
50595061
// By analogy, is_trivially_relocatable and is_trivially_equality_comparable
50605062
// impose the same constraints.
50615063
case UTT_IsTriviallyRelocatable:
@@ -5547,6 +5549,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
55475549
return C.hasUniqueObjectRepresentations(T);
55485550
case UTT_IsTriviallyRelocatable:
55495551
return T.isTriviallyRelocatableType(C);
5552+
case UTT_IsBitwiseCopyable:
5553+
return T.isBitwiseCopyableType(C);
55505554
case UTT_IsReferenceable:
55515555
return T.isReferenceable();
55525556
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)