[clang] [clang][Interp] Only zero-init first union member (PR #102744)

via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 10 07:55:32 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

<details>
<summary>Changes</summary>

Zero-initializing all of them accidentally left the last member active. Only initialize the first one.

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


3 Files Affected:

- (modified) clang/lib/AST/Interp/Compiler.cpp (+8-13) 
- (modified) clang/test/AST/Interp/records.cpp (+3-5) 
- (modified) clang/test/AST/Interp/unions.cpp (+45) 


``````````diff
diff --git a/clang/lib/AST/Interp/Compiler.cpp b/clang/lib/AST/Interp/Compiler.cpp
index d0e4d409b6580..6c4d607706c6b 100644
--- a/clang/lib/AST/Interp/Compiler.cpp
+++ b/clang/lib/AST/Interp/Compiler.cpp
@@ -1386,18 +1386,8 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
 
     if (R->isUnion()) {
       if (Inits.size() == 0) {
-        // Zero-initialize the first union field.
-        if (R->getNumFields() == 0)
-          return this->emitFinishInit(E);
-        const Record::Field *FieldToInit = R->getField(0u);
-        QualType FieldType = FieldToInit->Desc->getType();
-        if (std::optional<PrimType> T = classify(FieldType)) {
-          if (!this->visitZeroInitializer(*T, FieldType, E))
-            return false;
-          if (!this->emitInitField(*T, FieldToInit->Offset, E))
-            return false;
-        }
-        // FIXME: Non-primitive case?
+        if (!this->visitZeroRecordInitializer(R, E))
+          return false;
       } else {
         const Expr *Init = Inits[0];
         const FieldDecl *FToInit = nullptr;
@@ -3374,6 +3364,8 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
         return false;
       if (!this->emitInitField(T, Field.Offset, E))
         return false;
+      if (R->isUnion())
+        break;
       continue;
     }
 
@@ -3409,8 +3401,11 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
       assert(false);
     }
 
-    if (!this->emitPopPtr(E))
+    if (!this->emitFinishInitPop(E))
       return false;
+
+    if (R->isUnion())
+      break;
   }
 
   for (const Record::Base &B : R->bases()) {
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 9c8c1c344e1e8..343665003c23e 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -964,8 +964,6 @@ namespace TemporaryObjectExpr {
     static_assert(foo(F()) == 0, "");
   }
 
-  /// FIXME: This needs support for unions on the new interpreter.
-  /// We diagnose an uninitialized object in c++14.
 #if __cplusplus > 201402L
   namespace Unions {
     struct F {
@@ -978,10 +976,10 @@ namespace TemporaryObjectExpr {
     };
 
     constexpr int foo(F f) {
-      return f.i + f.U.f; // ref-note {{read of member 'f' of union with active member 'a'}}
+      return f.i + f.U.f; // both-note {{read of member 'f' of union with active member 'a'}}
     }
-    static_assert(foo(F()) == 0, ""); // ref-error {{not an integral constant expression}} \
-                                      // ref-note {{in call to}}
+    static_assert(foo(F()) == 0, ""); // both-error {{not an integral constant expression}} \
+                                      // both-note {{in call to}}
   }
 #endif
 
diff --git a/clang/test/AST/Interp/unions.cpp b/clang/test/AST/Interp/unions.cpp
index a51f30cd9185b..6f6bc3d6891b4 100644
--- a/clang/test/AST/Interp/unions.cpp
+++ b/clang/test/AST/Interp/unions.cpp
@@ -253,4 +253,49 @@ namespace Nested {
                               // both-note {{in call to}}
 
 }
+
+
+namespace Zeroing {
+  struct non_trivial_constructor {
+      constexpr non_trivial_constructor() : x(100) {}
+      int x;
+  };
+  union U2 {
+      int a{1000};
+      non_trivial_constructor b;
+  };
+
+  static_assert(U2().b.x == 100, ""); // both-error {{not an integral constant expression}} \
+                                      // both-note {{read of member 'b' of union with active member 'a'}}
+
+  union { int a; int b; } constexpr u1{};
+  static_assert(u1.a == 0, "");
+  static_assert(u1.b == 0, ""); // both-error {{not an integral constant expression}} \
+                                // both-note {{read of member 'b' of union with active member 'a'}}
+
+  union U { int a; int b; } constexpr u2 = U();
+  static_assert(u2.a == 0, "");
+  static_assert(u2.b == 0, ""); // both-error {{not an integral constant expression}} \
+                                // both-note {{read of member 'b' of union with active member 'a'}}
+
+
+  struct F {int x; int y; };
+  union { F a; int b; } constexpr u3{};
+  static_assert(u3.a.x == 0, "");
+
+  union U4 { F a; int b; } constexpr u4 = U4();
+  static_assert(u4.a.x == 0, "");
+
+  union { int a[5]; int b; } constexpr u5{};
+  static_assert(u5.a[0] == 0, "");
+  static_assert(u5.a[4] == 0, "");
+  static_assert(u5.b == 0, ""); // both-error {{not an integral constant expression}} \
+                                // both-note {{read of member 'b' of union with active member 'a'}}
+
+  union U6 { int a[5]; int b; } constexpr u6 = U6();
+  static_assert(u6.a[0] == 0, "");
+  static_assert(u6.a[4] == 0, "");
+  static_assert(u6.b == 0, ""); // both-error {{not an integral constant expression}} \
+                                // both-note {{read of member 'b' of union with active member 'a'}}
+}
 #endif

``````````

</details>


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


More information about the cfe-commits mailing list