llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Raphael Isemann (Teemperor)

<details>
<summary>Changes</summary>

Replace most of the test's setup code with modern test utils and expand the 
test case to cover more common or potentially problematic uses of unions.

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


2 Files Affected:

- (modified) lldb/test/API/lang/c/unions/TestUnionMembers.py (+58-33) 
- (modified) lldb/test/API/lang/c/unions/main.c (+58-13) 


``````````diff
diff --git a/lldb/test/API/lang/c/unions/TestUnionMembers.py 
b/lldb/test/API/lang/c/unions/TestUnionMembers.py
index d9acc00f8269e..263f76a313ecf 100644
--- a/lldb/test/API/lang/c/unions/TestUnionMembers.py
+++ b/lldb/test/API/lang/c/unions/TestUnionMembers.py
@@ -1,44 +1,69 @@
 import lldb
+from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
-import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test import lldbutil
 
 
-class TestUnionMembers(TestBase):
-    def test_union_members(self):
-        self._load_exe()
+class TestCase(TestBase):
+    def test(self):
+        self.build_and_run()
 
-        # Set breakpoints
-        bp = self.target.BreakpointCreateBySourceRegex("Break here", 
self.src_file_spec)
-        self.assertTrue(bp.IsValid() and bp.GetNumLocations() >= 1, 
VALID_BREAKPOINT)
+        # Reinterpreting a larger member through smaller ones depends on the
+        # host byte order. The values below assume little-endian; on big-endian
+        # hosts those assertions are skipped.
+        # TODO: Add test assertions for big endian.
+        little_endian = self.target().GetByteOrder() == lldb.eByteOrderLittle
 
-        # Launch the process
-        self.process = self.target.LaunchSimple(
-            None, None, self.get_process_working_directory()
+        # Type punning: a float and an integer sharing the same 4 bytes. Both
+        # members span the whole storage, so this is endian-independent.
+        self.expect_var_path("fb", type="FloatBits")
+        self.expect_var_path("fb.f", type="float", value="1")
+        self.expect_var_path("fb.bits", type="unsigned int", 
value="1065353216")
+        self.expect_expr(
+            "fb.bits", result_type="unsigned int", result_value="1065353216"
         )
-        self.assertTrue(self.process.IsValid(), PROCESS_IS_VALID)
-        self.assertEqual(self.process.GetState(), lldb.eStateStopped, 
PROCESS_STOPPED)
 
-        thread = lldbutil.get_stopped_thread(self.process, 
lldb.eStopReasonBreakpoint)
-        self.assertTrue(thread.IsValid())
-        frame = thread.GetSelectedFrame()
-        self.assertTrue(frame.IsValid())
+        # Basic aliasing: the same int seen as two shorts and as a single byte.
+        self.expect_var_path("basic", type="Basic")
+        self.expect_var_path("basic.n", type="int", value="287454020")
+        self.expect_var_path("basic.halves", type="unsigned short[2]")
+        self.expect_expr("basic.n", result_type="int", 
result_value="287454020")
+        if little_endian:
+            self.expect_var_path(
+                "basic.halves[0]", type="unsigned short", value="13124"
+            )
+            self.expect_var_path("basic.halves[1]", type="unsigned short", 
value="4386")
+            self.expect_var_path("basic.byte", type="unsigned char", 
value="'D'")
 
-        val = frame.EvaluateExpression("u")
-        self.assertTrue(val.IsValid())
-        val = frame.EvaluateExpression("u.s")
-        self.assertTrue(val.IsValid())
-        self.assertEqual(val.GetNumChildren(), 2)
+        # A union whose storage is also described by a struct.
+        self.expect_var_path("ws", type="WithStruct")
+        self.expect_var_path("ws.packed", type="int", value="131073")
+        self.expect_var_path("ws.view", type="Halves")
+        if little_endian:
+            self.expect_var_path("ws.view.lo", type="short", value="1")
+            self.expect_var_path("ws.view.hi", type="short", value="2")
 
-    def _load_exe(self):
-        self.build()
-
-        src_file = os.path.join(self.getSourceDir(), "main.c")
-        self.src_file_spec = lldb.SBFileSpec(src_file)
-        self.assertTrue(self.src_file_spec.IsValid(), "breakpoint file")
-
-        # Get the path of the executable
-        exe_path = self.getBuildArtifact("a.out")
+        # A union nested inside another union. Both inner members sit at offset
+        # 0, so they alias each other regardless of byte order.
+        self.expect_var_path("nested", type="Nested")
+        self.expect_var_path("nested.all", type="int", value="393221")
+        self.assertEqual(
+            
self.frame().GetValueForVariablePath("nested.inner.a").GetValueAsSigned(),
+            
self.frame().GetValueForVariablePath("nested.inner.b").GetValueAsSigned(),
+        )
+        if little_endian:
+            self.expect_var_path("nested.inner.a", type="short", value="5")
+            self.expect_var_path("nested.inner.b", type="short", value="5")
 
-        # Load the executable
-        self.target = self.dbg.CreateTarget(exe_path)
-        self.assertTrue(self.target.IsValid(), VALID_TARGET)
+        # Members of an anonymous union are reached directly on the struct.
+        self.expect_var_path("anon", type="WithAnonUnion")
+        self.expect_var_path("anon.tag", type="int", value="42")
+        self.expect_var_path("anon.as_int", type="int", value="67305985")
+        self.expect_var_path("anon.as_bytes", type="unsigned char[4]")
+        if little_endian:
+            for index, byte in enumerate([1, 2, 3, 4]):
+                self.expect_var_path(
+                    f"anon.as_bytes[{index}]",
+                    type="unsigned char",
+                    value=f"'\\x0{byte}'",
+                )
diff --git a/lldb/test/API/lang/c/unions/main.c 
b/lldb/test/API/lang/c/unions/main.c
index 2c6a7d1e78211..5c89d3bd5305a 100644
--- a/lldb/test/API/lang/c/unions/main.c
+++ b/lldb/test/API/lang/c/unions/main.c
@@ -1,18 +1,63 @@
-#include <stdint.h>
+// Type punning: reinterpret the bits of a float as an integer. Both members
+// span the same 4 bytes, so the observed values don't depend on endianness.
+union FloatBits {
+  float f;
+  unsigned int bits;
+};
 
-union S
-{
-    int32_t n;     // occupies 4 bytes
-    uint16_t s[2]; // occupies 4 bytes
-    uint8_t c;     // occupies 1 byte
-};                 // the whole union occupies 4 bytes
+// Basic aliasing: the same 4 bytes seen as different types and sizes. Reading
+// the storage back through the smaller members depends on the host byte order.
+union Basic {
+  int n;
+  unsigned short halves[2];
+  unsigned char byte;
+};
 
-int main()
-{
-  union S u;
+// A union whose storage is also described by a struct ("view" pattern).
+struct Halves {
+  short lo;
+  short hi;
+};
+union WithStruct {
+  int packed;
+  struct Halves view;
+};
 
-  u.s[0] = 1234;
-  u.s[1] = 4321;
+// A union nested directly inside another union.
+union Nested {
+  int all;
+  union {
+    short a;
+    short b;
+  } inner;
+};
 
-  return 0; // Break here
+// A struct with an anonymous union member; the union's members are accessed as
+// if they were fields of the enclosing struct.
+struct WithAnonUnion {
+  int tag;
+  union {
+    int as_int;
+    unsigned char as_bytes[4];
+  };
+};
+
+int main() {
+  union FloatBits fb;
+  fb.f = 1.0f;
+
+  union Basic basic;
+  basic.n = 0x11223344;
+
+  union WithStruct ws;
+  ws.packed = 0x00020001;
+
+  union Nested nested;
+  nested.all = 0x00060005;
+
+  struct WithAnonUnion anon;
+  anon.tag = 42;
+  anon.as_int = 0x04030201;
+
+  return 0; // break here
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/208662
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to