[clang] [clang][Interp] Handle union copy/move ctors (PR #102762)

via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 10 10:12:28 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

<details>
<summary>Changes</summary>

They don't have a body and we need to implement them ourselves. Use the Memcpy op to do that.

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


3 Files Affected:

- (modified) clang/lib/AST/Interp/Compiler.cpp (+16) 
- (modified) clang/lib/AST/Interp/InterpBuiltin.cpp (+21-5) 
- (modified) clang/test/AST/Interp/unions.cpp (+11) 


``````````diff
diff --git a/clang/lib/AST/Interp/Compiler.cpp b/clang/lib/AST/Interp/Compiler.cpp
index 5bed71392740e..ad1a2f96b9590 100644
--- a/clang/lib/AST/Interp/Compiler.cpp
+++ b/clang/lib/AST/Interp/Compiler.cpp
@@ -4775,6 +4775,22 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
     if (!R)
       return false;
 
+    if (R->isUnion() && Ctor->isCopyOrMoveConstructor()) {
+      // union copy and move ctors are special.
+      assert(cast<CompoundStmt>(Ctor->getBody())->body_empty());
+      if (!this->emitThis(Ctor))
+        return false;
+
+      auto PVD = Ctor->getParamDecl(0);
+      ParamOffset PO = this->Params[PVD]; // Must exist.
+
+      if (!this->emitGetParam(PT_Ptr, PO.Offset, Ctor))
+        return false;
+
+      return this->emitMemcpy(Ctor) && this->emitPopPtr(Ctor) &&
+             this->emitRetVoid(Ctor);
+    }
+
     InitLinkScope<Emitter> InitScope(this, InitLink::This());
     for (const auto *Init : Ctor->inits()) {
       // Scope needed for the initializers.
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index d7538c76e91d9..1841a2a4714d8 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -1658,18 +1658,34 @@ bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
   }
 
   if (DestDesc->isRecord()) {
-    assert(SrcDesc->isRecord());
-    assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
-    const Record *R = DestDesc->ElemRecord;
-    for (const Record::Field &F : R->fields()) {
+    auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
       Pointer DestField = Dest.atField(F.Offset);
       if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
         TYPE_SWITCH(*FT, {
           DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
           DestField.initialize();
+          if (Activate)
+            DestField.activate();
         });
+        return true;
+      }
+      return Invalid(S, OpPC);
+    };
+
+    assert(SrcDesc->isRecord());
+    assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
+    const Record *R = DestDesc->ElemRecord;
+    for (const Record::Field &F : R->fields()) {
+      if (R->isUnion()) {
+        // For unions, only copy the active field.
+        const Pointer &SrcField = Src.atField(F.Offset);
+        if (SrcField.isActive()) {
+          if (!copyField(F, /*Activate=*/true))
+            return false;
+        }
       } else {
-        return Invalid(S, OpPC);
+        if (!copyField(F, /*Activate=*/false))
+          return false;
       }
     }
     return true;
diff --git a/clang/test/AST/Interp/unions.cpp b/clang/test/AST/Interp/unions.cpp
index 4607df6c1d644..4c60c2c0810d4 100644
--- a/clang/test/AST/Interp/unions.cpp
+++ b/clang/test/AST/Interp/unions.cpp
@@ -346,5 +346,16 @@ namespace IndirectField {
   static_assert(s2.f == 7, "");
 }
 
+namespace CopyCtor {
+  union U {
+    int a;
+    int b;
+  };
 
+  constexpr U x = {42};
+  constexpr U y = x;
+  static_assert(y.a == 42, "");
+  static_assert(y.b == 42, ""); // both-error {{constant expression}} \
+                                // both-note {{'b' of union with active member 'a'}}
+}
 #endif

``````````

</details>


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


More information about the cfe-commits mailing list