Skip to content

[clang][Interp] Handle nested unions #102743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 10, 2024
Merged

[clang][Interp] Handle nested unions #102743

merged 1 commit into from
Aug 10, 2024

Conversation

tbaederr
Copy link
Contributor

No description provided.

@tbaederr tbaederr merged commit 3b57f6b into llvm:main Aug 10, 2024
9 checks passed
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Aug 10, 2024
@llvmbot
Copy link
Member

llvmbot commented Aug 10, 2024

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

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

3 Files Affected:

  • (modified) clang/lib/AST/Interp/Interp.cpp (+14-12)
  • (modified) clang/lib/AST/Interp/Pointer.cpp (+1-1)
  • (modified) clang/test/AST/Interp/unions.cpp (+55)
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 588680adb4616..89ac693893113 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -125,18 +125,15 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
   if (Ptr.isActive())
     return true;
 
-  // Get the inactive field descriptor.
-  const FieldDecl *InactiveField = Ptr.getField();
-  assert(InactiveField);
-
-  // Walk up the pointer chain to find the closest union.
   Pointer U = Ptr.getBase();
-  while (!U.getFieldDesc()->isUnion())
+  Pointer C = Ptr;
+  while (!U.isRoot() && U.inUnion() && !U.isActive()) {
+    C = U;
     U = U.getBase();
-
-  // Find the active field of the union.
-  const Record *R = U.getRecord();
-  assert(R && R->isUnion() && "Not a union");
+  }
+  // Get the inactive field descriptor.
+  const FieldDecl *InactiveField = C.getField();
+  assert(InactiveField);
 
   // Consider:
   // union U {
@@ -148,10 +145,15 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
   //
   // When activating x, we will also activate a. If we now try to read
   // from y, we will get to CheckActive, because y is not active. In that
-  // case we return here and let later code handle this.
-  if (!llvm::is_contained(R->getDecl()->fields(), InactiveField))
+  // case, our U will be a (not a union). We return here and let later code
+  // handle this.
+  if (!U.getFieldDesc()->isUnion())
     return true;
 
+  // Find the active field of the union.
+  const Record *R = U.getRecord();
+  assert(R && R->isUnion() && "Not a union");
+
   const FieldDecl *ActiveField = nullptr;
   for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
     const Pointer &Field = U.atField(R->getField(I)->Offset);
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index f1f7a27c1400d..466e61666c76e 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -413,7 +413,7 @@ void Pointer::activate() const {
   }
 
   Pointer B = getBase();
-  while (!B.getFieldDesc()->isUnion()) {
+  while (!B.isRoot() && B.inUnion()) {
     // FIXME: Need to de-activate other fields of parent records.
     B.getInlineDesc()->IsActive = true;
     assert(B.isActive());
diff --git a/clang/test/AST/Interp/unions.cpp b/clang/test/AST/Interp/unions.cpp
index b0b279831405d..a51f30cd9185b 100644
--- a/clang/test/AST/Interp/unions.cpp
+++ b/clang/test/AST/Interp/unions.cpp
@@ -198,4 +198,59 @@ namespace UnionMemberDtor {
   }
   static_assert(foo() == 100);
 }
+
+namespace Nested {
+  union U {
+    int a;
+    int b;
+  };
+
+  union U2 {
+    U u;
+    U u2;
+    int x;
+    int y;
+  };
+
+ constexpr int foo() { // ref-error {{constexpr function never produces a constant expression}}
+    U2 u;
+    u.u.a = 10;
+    int a = u.y; // both-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}} \
+                 // ref-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}
+
+    return 1;
+  }
+  static_assert(foo() == 1); // both-error {{not an integral constant expression}} \
+                             // both-note {{in call to}}
+
+ constexpr int foo2() {
+    U2 u;
+    u.u.a = 10;
+    return u.u.a;
+  }
+  static_assert(foo2() == 10);
+
+ constexpr int foo3() { // ref-error {{constexpr function never produces a constant expression}}
+    U2 u;
+    u.u.a = 10;
+    int a = u.u.b; // both-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}} \
+                   // ref-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}
+
+    return 1;
+  }
+  static_assert(foo3() == 1); // both-error {{not an integral constant expression}} \
+                              // both-note {{in call to}}
+
+  constexpr int foo4() { // ref-error {{constexpr function never produces a constant expression}}
+    U2 u;
+
+    u.x = 10;
+
+    return u.u.a;// both-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}} \
+                 // ref-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
+  }
+  static_assert(foo4() == 1); // both-error {{not an integral constant expression}} \
+                              // both-note {{in call to}}
+
+}
 #endif

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants