Skip to content

Broken assignment of union with bool inside variant object #25236

@bptato

Description

@bptato

Nim Version

Nim Compiler Version 2.3.1 [Linux: amd64]
Compiled at 2025-10-20
Copyright (c) 2006-2025 by Andreas Rumpf

git hash: 1eae14a
active boot switches: -d:release

Description

Following code asserts with clang 19:

type
  A* {.union.} = object
    c*: C
    b*: bool

  B* = object
    case x*: bool
    of false:
      a*: A
    of true:
      cvar*: RootRef

  C* = enum
    wrong1, wrong2, right

proc main() =
  var xs: seq[B]
  let a = B(x: false, a: A(c: right))
  var b = a
  xs.add(a)
  assert b.a.c == right

main()

Looking at the generated code, I see:

N_LIB_PRIVATE N_NIMCALL(void, eqcopy___x_u169)(tyObject_B__X9cjIG3K6Mr4MzNE0Eyi6Uw* dest_p0, tyObject_B__X9cjIG3K6Mr4MzNE0Eyi6Uw src_p1) {
	tyObject_B__X9cjIG3K6Mr4MzNE0Eyi6Uw colontmp_;
nimZeroMem(((void*) ((&colontmp_))), sizeof(tyObject_B__X9cjIG3K6Mr4MzNE0Eyi6Uw));
colontmp_ = (*dest_p0);
nimZeroMem(((void*) ((&(*dest_p0)))), sizeof(tyObject_B__X9cjIG3K6Mr4MzNE0Eyi6Uw));
(*dest_p0).x = src_p1.x;
switch ((*dest_p0).x) {
case NIM_FALSE:
	{
		nimZeroMem(((void*) ((&(*dest_p0)._x_1.a))), sizeof(tyObject_A__nYOkuL9cKb3K8tmh8t0Ft9aA));
(*dest_p0)._x_1.a.c = src_p1._x_1.a.c;
(*dest_p0)._x_1.a.b = src_p1._x_1.a.b;
}
	break;

Aside from the obvious redundancy of assigning each field in the union, C's bool is not specified to be capable of representing all 8-bit values (just 0 and 1), so reading it here is UB. In practice the code happens to work in GCC, but clang happily zeroes out the upper bytes, flipping c from right to wrong1.

Current Output

Error: unhandled exception: /tmp/x.nim(29, 3) `b.a.c == right`  [AssertionDefect]

Expected Output

no assertion

Known Workarounds

proc `=copy`(a: var A; b: A) =
  copyMem(addr a, unsafeAddr b, sizeof(A))

proc `=destroy`(a: var A) = # seems to be needed too for sink
  discard

.union may not contain GC'ed types, so I guess the compiler could generate the same.

Additional Information

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions