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

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 29 09:48:27 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

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

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


2 Files Affected:

- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+17) 
- (added) clang/test/SemaCXX/GH37979.cpp (+25) 


``````````diff
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
+}

``````````

</details>


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


More information about the cfe-commits mailing list