[clang] Fix memcpy-operator= generation with restrict parameters. (PR #194906)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 29 09:47:44 PDT 2026


https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/194906

The below issue (and #63884) both report that we reject (and also assert, because the memcpy failed) the memcpy we're generating for a restrict field of a type with an implicit copy constructor.

First, we shouldn't be rejecting it this late, IF we wanted to reject it (I contend we do not), we should do it at the same time we reject const-members/make this a deleted operator.  Second, of course we shouldn't fail.

This patch fixes the above by inserting a bit-cast in this situation. We know this is a valid cast/copy, since we're just doing this by memcpy.

We COULD ALWAYS introduce this cast, however I fear that doing so might interfere with users of tooling, so this patch limits itself to only the 'restrict-member' case. I was unable to find any other qualifiers that BOTH opted us in for these builtin-members-copy-with-memcpy, and didn't delete the copy operator.

Fixes: #37979

>From 277be76708620fb2c2fc6a351ae8ee95c304ab8a Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Wed, 29 Apr 2026 09:18:52 -0700
Subject: [PATCH] Fix memcpy-operator= generation with restrict parameters.

The below issue (and #63884) both report that we reject (and also
assert, because the memcpy failed) the memcpy we're generating for a
restrict field of a type with an implicit copy constructor.

First, we shouldn't be rejecting it this late, IF we wanted to reject it
(I contend we do not), we should do it at the same time we reject
const-members/make this a deleted operator.  Second, of course we
shouldn't fail.

This patch fixes the above by inserting a bit-cast in this situation.
We know this is a valid cast/copy, since we're just doing this by
memcpy.

We COULD ALWAYS introduce this cast, however I fear that doing so might
interfere with users of tooling, so this patch limits itself to only the
'restrict-member' case. I was unable to find any other qualifiers that
BOTH opted us in for these builtin-members-copy-with-memcpy, and didn't
delete the copy operator.

Fixes: #37979
---
 clang/lib/Sema/SemaDeclCXX.cpp | 17 +++++++++++++++++
 clang/test/SemaCXX/GH37979.cpp | 25 +++++++++++++++++++++++++
 2 files changed, 42 insertions(+)
 create mode 100644 clang/test/SemaCXX/GH37979.cpp

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 159afdacdd110..ee3308189d46d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -14928,6 +14928,23 @@ buildMemcpyForAssignmentOp(Sema &S, SourceLocation Loc, QualType T,
       S.Context, To, UO_AddrOf, S.Context.getPointerType(To->getType()),
       VK_PRValue, OK_Ordinary, Loc, false, S.CurFPFeatureOverrides());
 
+  // If this is restrict qualified, the call to memcpy will fail as this ends up
+  // being an illegal pointer cast (as it has non-local qualifiers in
+  // difference). Sema rules prevent us from calling this with anything other
+  // than the restrict qualifiers as far as I can tell(fields cant have an AS,
+  // copy is deleted if it is const, OBJC can't get here, pointer-auth can't be
+  // applied to any problematic things). In order to avoid that problem, we can
+  // just insert a cast directly to const void * / void *.
+  if (T.isRestrictQualified()) {
+    From = ImplicitCastExpr::Create(
+        S.Context, S.Context.getPointerType(S.Context.VoidTy.withConst()),
+        CK_BitCast, From, /*BasePath=*/nullptr, VK_PRValue,
+        FPOptionsOverride());
+    To = ImplicitCastExpr::Create(
+        S.Context, S.Context.VoidPtrTy, CK_BitCast, To,
+        /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
+  }
+
   bool NeedsCollectableMemCpy = false;
   if (auto *RD = T->getBaseElementTypeUnsafe()->getAsRecordDecl())
     NeedsCollectableMemCpy = RD->hasObjectMember();
diff --git a/clang/test/SemaCXX/GH37979.cpp b/clang/test/SemaCXX/GH37979.cpp
new file mode 100644
index 0000000000000..f6ad964c88257
--- /dev/null
+++ b/clang/test/SemaCXX/GH37979.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s
+// RUN: %clang_cc1 -fsyntax-only -ast-dump  %s | FileCheck %s
+// expected-no-diagnostics
+
+struct Obj { int * __restrict myPtr[2]; };
+
+void do_copy() {
+    Obj a, b;
+    a = b;
+    // CHECK-LABEL: CXXMethodDecl{{.*}} implicit used constexpr operator= 'Obj &(const Obj &) noexcept'
+    // CHECK-NEXT: ParmVarDecl
+    // CHECK-NEXT: CompoundStmt
+    // CHECK-NEXT: CallExpr
+    // CHECK-NEXT: ImplicitCastExpr{{.*}}<BuiltinFnToFnPtr>
+    // CHECK-NEXT: DeclRefExpr{{.*}}__builtin_memcpy
+    // CHECK-NEXT: ImplicitCastExpr{{.*}}'void *' <BitCast>
+    // CHECK-NEXT: UnaryOperator{{.*}} 'int *__restrict (*)[2]' prefix '&'
+    // CHECK-NEXT: MemberExpr{{.*}} 'int *__restrict[2]' lvalue ->myPtr
+    // CHECK-NEXT: CXXThisExpr{{.*}} 'Obj *' this
+    //
+    // CHECK-NEXT: ImplicitCastExpr{{.*}}'const void *' <BitCast>
+    // CHECK-NEXT: UnaryOperator{{.*}} 'int *__restrict const __restrict (*)[2]' prefix '&'
+    // CHECK-NEXT: MemberExpr{{.*}} 'int *__restrict const __restrict[2]' lvalue .myPtr
+    // CHECK-NEXT: DeclRefExpr{{.*}} 'const Obj' lvalue ParmVar
+}



More information about the cfe-commits mailing list