Nim: sizeof return wrong result for union object.

Created on 9 Apr 2020  路  7Comments  路  Source: nim-lang/Nim

I get some error when I translate a C struct into nim.
I find out the problem and simplify it to following code:

Example

type
  Struct* {.packed.} = object
    a*: int64
    b*: cint

  Union* {.union.} = object
    s*: Struct
    d*: int

echo sizeof(Struct)
echo sizeof(Union)

Compile with nim c -r --cpu:amd64

Current Output

16

Expected Output

12

Additional Information

  • It was working in version 0.19.0 when add packed to the union object. The newer version don't allow it (Error: union type may not be packed).
$ nim -v
Nim Compiler Version 1.2.0 [Windows: amd64]
Compiled at 2020-04-03
Regression

Most helpful comment

All 7 comments

This has also affected my code (I was also using 0.19.0 when last using Nim). Now my DLL code involving variants passed from Delphi can't be used since I need packed unions. Is this going to be changed back to the earlier behaviour?

type
  Struct* {.packed.} = object
    a*: int64
    b*: cint

  # Union* {.union.} = object
  Union* {.union, packed.} = object
    s*: Struct
    d*: int

echo sizeof(Struct)
echo sizeof(Union)

{.union, packed.} was allowed until https://github.com/nim-lang/Nim/pull/10780;
right before that PR it gave wrong results:

20

and it gave correct results in 0.19.0

after git bisect, the regression occurred in https://github.com/nim-lang/Nim/pull/9356 which is @krux02 's work rebased + squashed as a single commit; for the original PR that led to this, see https://github.com/nim-lang/Nim/pull/5664 (might give more insight because it has individual commits)

Thanks. This is a showstopper for me using Nim going forward - I'll revert to 0.19.0 and hope that it gets fixed soon.

I tested it and compared it to the c implementation. The outputs 12 and 16 are in fact correct. The member d of type causes an alignment of 8 bytes. This alignment requirement will required padding bytes at the end of Union so that the final size is a multiple of 8.

import macros
macro c_sizeof(a: typed): int32 =
  ## Bullet proof implementation that works using the sizeof operator
  ## in the c backend. Assuming of course this implementation is
  ## correct.
  result = quote do:
    var res: int32
    {.emit: [res, " = sizeof(", `a`, ");"] .}
    res

type
  Struct* {.packed.} = object
    a*: int64
    b*: cint

  Union* {.union.} = object
    s*: Struct
    d*: int

echo "sizeof(Struct): ", sizeof(Struct), " expected: ", c_sizeof(Struct)
echo "sizeof(Union): ", sizeof(Union), " expected: ", c_sizeof(Union)

echo "alignof(Struct): ", alignof(Struct)
echo "alignof(Union): ", alignof(Union)

The problem is not about the output of size, is about that the union in nim cannot be packed. A lot of APIs need to pass the correct size of (packed) struct/union to work.

My Delphi variant interop code is now working again. Thanks all.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Vindaar picture Vindaar  路  3Comments

kobi2187 picture kobi2187  路  4Comments

capocasa picture capocasa  路  3Comments

juancarlospaco picture juancarlospaco  路  3Comments

hlaaftana picture hlaaftana  路  3Comments