[Lldb-commits] [lldb] 5394e2a - [lldb][test] Modernize and expand C union test (#208662)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Jul 10 07:33:09 PDT 2026
Author: Raphael Isemann
Date: 2026-07-10T15:33:04+01:00
New Revision: 5394e2aec901f34dc3f92976529dab9ce7829d93
URL: https://github.com/llvm/llvm-project/commit/5394e2aec901f34dc3f92976529dab9ce7829d93
DIFF: https://github.com/llvm/llvm-project/commit/5394e2aec901f34dc3f92976529dab9ce7829d93.diff
LOG: [lldb][test] Modernize and expand C union test (#208662)
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.
Added:
Modified:
lldb/test/API/lang/c/unions/TestUnionMembers.py
lldb/test/API/lang/c/unions/main.c
Removed:
################################################################################
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
diff erent 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
}
More information about the lldb-commits
mailing list