1discard """
2  targets: "c cpp"
3  output: '''
4body executed
5body executed
6OK
7macros api OK
8'''
9"""
10
11# This is for Azure. The keyword ``alignof`` only exists in ``c++11``
12# and newer. On Azure gcc does not default to c++11 yet.
13when defined(cpp) and not defined(windows):
14  {.passC: "-std=c++11".}
15
16# Object offsets are different for inheritance objects when compiling
17# to c++.
18
19type
20  TMyEnum = enum
21    tmOne, tmTwo, tmThree, tmFour
22
23  TMyArray1 = array[3, uint8]
24  TMyArray2 = array[1..3, int32]
25  TMyArray3 = array[TMyEnum, float64]
26
27var failed = false
28
29const
30  mysize1 = sizeof(TMyArray1)
31  mysize2 = sizeof(TMyArray2)
32  mysize3 = sizeof(TMyArray3)
33
34doAssert mysize1 == 3
35doAssert mysize2 == 12
36doAssert mysize3 == 32
37
38import macros, typetraits
39
40macro testSizeAlignOf(args: varargs[untyped]): untyped =
41  result = newStmtList()
42  for arg in args:
43    result.add quote do:
44      let
45        c_size = c_sizeof(`arg`)
46        nim_size = sizeof(`arg`)
47        c_align = c_alignof(type(`arg`))
48        nim_align = alignof(`arg`)
49
50      if nim_size != c_size or nim_align != c_align:
51        var msg = strAlign(`arg`.type.name & ": ")
52        if nim_size != c_size:
53          msg.add  " size(got, expected):  " & $nim_size & " != " & $c_size
54        if nim_align != c_align:
55          msg.add  " align(get, expected): " & $nim_align & " != " & $c_align
56        echo msg
57        failed = true
58
59
60macro testOffsetOf(a, b: untyped): untyped =
61  let typeName = newLit(a.repr)
62  let member   = newLit(b.repr)
63  result = quote do:
64    let
65      c_offset   = c_offsetof(`a`,`b`)
66      nim_offset = offsetof(`a`,`b`)
67    if c_offset != nim_offset:
68      echo `typeName`, ".", `member`, " offsetError, C: ", c_offset, " nim: ", nim_offset
69      failed = true
70
71proc strAlign(arg: string): string =
72  const minLen = 22
73  result = arg
74  for i in 0 ..< minLen - arg.len:
75    result &= ' '
76
77macro c_offsetof(fieldAccess: typed): int32 =
78  ## Bullet proof implementation that works on actual offsetof operator
79  ## in the c backend. Assuming of course this implementation is
80  ## correct.
81  let s = if fieldAccess.kind == nnkCheckedFieldExpr: fieldAccess[0]
82          else: fieldAccess
83  let a = s[0].getTypeInst
84  let b = s[1]
85  result = quote do:
86    var res: int32
87    {.emit: [res, " = offsetof(", `a`, ", ", `b`, ");"] .}
88    res
89
90template c_offsetof(t: typedesc, a: untyped): int32 =
91  var x: ptr t
92  c_offsetof(x[].a)
93
94macro c_sizeof(a: typed): int32 =
95  ## Bullet proof implementation that works using the sizeof operator
96  ## in the c backend. Assuming of course this implementation is
97  ## correct.
98  result = quote do:
99    var res: int32
100    {.emit: [res, " = sizeof(", `a`, ");"] .}
101    res
102
103macro c_alignof(arg: untyped): untyped =
104  ## Bullet proof implementation that works on actual alignment
105  ## behavior measured at runtime.
106  let typeSym = genSym(nskType, "AlignTestType"&arg.repr)
107  result = quote do:
108    type
109      `typeSym` = object
110        causeAlign: byte
111        member: `arg`
112    c_offsetof(`typeSym`, member)
113
114macro testAlign(arg:untyped):untyped =
115  let prefix = newLit(arg.lineinfo & "  alignof " & arg.repr & " ")
116  result = quote do:
117    let cAlign = c_alignof(`arg`)
118    let nimAlign = alignof(`arg`)
119    if cAlign != nimAlign:
120      echo `prefix`, cAlign, " != ", nimAlign
121      failed = true
122
123macro testSize(arg:untyped):untyped =
124  let prefix = newLit(arg.lineinfo & "  sizeof " & arg.repr & " ")
125  result = quote do:
126    let cSize = c_sizeof(`arg`)
127    let nimSize = sizeof(`arg`)
128    if cSize != nimSize:
129      echo `prefix`, cSize, " != ", nimSize
130      failed = true
131
132type
133  MyEnum {.pure.} = enum
134    ValueA
135    ValueB
136    ValueC
137
138  OtherEnum {.pure, size: 8.} = enum
139    ValueA
140    ValueB
141
142  Enum1 {.pure, size: 1.} = enum
143    ValueA
144    ValueB
145
146  Enum2 {.pure, size: 2.} = enum
147    ValueA
148    ValueB
149
150  Enum4 {.pure, size: 4.} = enum
151    ValueA
152    ValueB
153
154  Enum8 {.pure, size: 8.} = enum
155    ValueA
156    ValueB
157
158  # Must have more than 32 elements so that set[MyEnum33] will become compile to an int64.
159  MyEnum33 {.pure.} = enum
160    Value1, Value2, Value3, Value4, Value5, Value6,
161    Value7, Value8, Value9, Value10, Value11, Value12,
162    Value13, Value14, Value15, Value16, Value17, Value18,
163    Value19, Value20, Value21, Value22, Value23, Value24,
164    Value25, Value26, Value27, Value28, Value29, Value30,
165    Value31, Value32, Value33
166
167proc transformObjectconfigPacked(arg: NimNode): NimNode =
168  let debug = arg.kind == nnkPragmaExpr
169
170  if arg.eqIdent("objectconfig"):
171    result = ident"packed"
172  else:
173    result = copyNimNode(arg)
174    for child in arg:
175      result.add transformObjectconfigPacked(child)
176
177proc removeObjectconfig(arg: NimNode): NimNode =
178  if arg.kind == nnkPragmaExpr and arg[1][0].eqIdent "objectconfig":
179    result = arg[0]
180  else:
181    result = copyNimNode(arg)
182    for child in arg:
183      result.add removeObjectconfig(child)
184
185macro testinstance(body: untyped): untyped =
186  let bodyPure = removeObjectconfig(body)
187  let bodyPacked = transformObjectconfigPacked(body)
188
189  result = quote do:
190    proc pureblock(): void =
191      const usePacked {.inject.} = false
192      `bodyPure`
193
194    pureblock()
195
196    proc packedblock(): void =
197      const usePacked {.inject.} = true
198      `bodyPacked`
199
200    packedblock()
201
202proc testPrimitiveTypes(): void =
203  testAlign(pointer)
204  testAlign(int)
205  testAlign(uint)
206  testAlign(int8)
207  testAlign(int16)
208  testAlign(int32)
209  testAlign(int64)
210  testAlign(uint8)
211  testAlign(uint16)
212  testAlign(uint32)
213  testAlign(uint64)
214  testAlign(float)
215  testAlign(float32)
216  testAlign(float64)
217
218  testAlign(MyEnum)
219  testAlign(OtherEnum)
220  testAlign(Enum1)
221  testAlign(Enum2)
222  testAlign(Enum4)
223  testAlign(Enum8)
224
225testPrimitiveTypes()
226
227testinstance:
228  type
229
230    EnumObjectA  {.objectconfig.} = object
231      a : Enum1
232      b : Enum2
233      c : Enum4
234      d : Enum8
235
236    EnumObjectB  {.objectconfig.} = object
237      a : Enum8
238      b : Enum4
239      c : Enum2
240      d : Enum1
241
242    TrivialType  {.objectconfig.} = object
243      x,y,z: int8
244
245    SimpleAlignment {.objectconfig.} = object
246      # behaves differently on 32bit Windows and 32bit Linux
247      a,b: int8
248      c: int64
249
250    AlignAtEnd {.objectconfig.} = object
251      a: int64
252      b,c: int8
253
254    SimpleBranch {.objectconfig.} = object
255      case kind: MyEnum
256      of MyEnum.ValueA:
257        a: int16
258      of MyEnum.ValueB:
259        b: int32
260      of MyEnum.ValueC:
261        c: int64
262
263    PaddingBeforeBranchA {.objectconfig.} = object
264      cause: int8
265      case kind: MyEnum
266      of MyEnum.ValueA:
267        a: int16
268      of MyEnum.ValueB:
269        b: int32
270      of MyEnum.ValueC:
271        c: int64
272
273    PaddingBeforeBranchB {.objectconfig.} = object
274      cause: int8
275      case kind: MyEnum
276      of MyEnum.ValueA:
277        a: int8
278      of MyEnum.ValueB:
279        b: int16
280      of MyEnum.ValueC:
281        c: int32
282
283    PaddingAfterBranch {.objectconfig.} = object
284      case kind: MyEnum
285      of MyEnum.ValueA:
286        a: int8
287      of MyEnum.ValueB:
288        b: int16
289      of MyEnum.ValueC:
290        c: int32
291      cause: int64
292
293    RecursiveStuff {.objectconfig.} = object
294      case kind: MyEnum    # packedOffset:    0
295      of MyEnum.ValueA:    # packedOffset:
296        a: int16           # packedOffset:    1
297      of MyEnum.ValueB:    # packedOffset:
298        b: int32           # packedOffset:    1
299      of MyEnum.ValueC:    # packedOffset:
300        case kind2: MyEnum # packedOffset:    1
301        of MyEnum.ValueA:  # packedOffset:
302          ca1: int8
303          ca2: int32
304        of MyEnum.ValueB:  # packedOffset:
305          cb: int32        # packedOffset:    2
306        of MyEnum.ValueC:  # packedOffset:
307          cc: int64        # packedOffset:    2
308        d1: int8
309        d2: int64
310
311    Foobar {.objectconfig.} = object
312      case kind: OtherEnum
313      of OtherEnum.ValueA:
314        a: uint8
315      of OtherEnum.ValueB:
316        b: int8
317      c: int8
318
319    PaddingOfSetEnum33 {.objectconfig.} = object
320      cause: int8
321      theSet: set[MyEnum33]
322
323    Bazing {.objectconfig.} = object of RootObj
324      a: int64
325      # TODO test on 32 bit system
326      # only there the object header is smaller than the first member
327
328    InheritanceA {.objectconfig.} = object of RootObj
329      a: char
330
331    InheritanceB {.objectconfig.} = object of InheritanceA
332      b: char
333
334    InheritanceC {.objectconfig.} = object of InheritanceB
335      c: char
336
337    # from issue 4763
338    GenericObject[T] {.objectconfig.} = object
339      a: int32
340      b: T
341
342    # this type mixes `packed` with `align`.
343    MyCustomAlignPackedObject {.objectconfig.} = object
344      a: char
345      b {.align: 32.}: int32 # align overrides `packed` for this field.
346      c: char
347      d: int32  # unaligned
348
349    Kind = enum
350      K1, K2
351
352    AnotherEnum = enum
353      X1, X2, X3
354
355    MyObject = object
356      s: string
357      case k: Kind
358      of K1: nil
359      of K2:
360          x: float
361          y: int32
362      z: AnotherEnum
363
364    Stack[N: static int, T: object] = object
365      pad: array[128 - sizeof(array[N, ptr T]) - sizeof(int) - sizeof(pointer), byte]
366      stack: array[N, ptr T]
367      len*: int
368      rawMem: ptr array[N, T]
369
370    Stack2[T: object] = object
371      pad: array[128 - sizeof(array[sizeof(T), ptr T]), byte]
372
373
374  const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time
375
376  proc main(): void =
377    var t : TrivialType
378    var a : SimpleAlignment
379    var b : AlignAtEnd
380    var c : SimpleBranch
381    var d : PaddingBeforeBranchA
382    var e : PaddingBeforeBranchB
383    var f : PaddingAfterBranch
384    var g : RecursiveStuff
385    var ro : RootObj
386    var go : GenericObject[int64]
387    var po : PaddingOfSetEnum33
388    var capo: MyCustomAlignPackedObject
389    var issue15516: MyObject
390    var issue12636_1: Stack[5, MyObject]
391    var issue12636_2: Stack2[MyObject]
392
393    var
394      e1: Enum1
395      e2: Enum2
396      e4: Enum4
397      e8: Enum8
398    var
399      eoa: EnumObjectA
400      eob: EnumObjectB
401
402    testAlign(SimpleAlignment)
403
404    # sanity check to ensure both branches are actually executed
405    when usePacked:
406      doAssert sizeof(SimpleAlignment) == 10
407    else:
408      doAssert sizeof(SimpleAlignment) > 10
409
410    testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo, issue15516, issue12636_1, issue12636_2)
411
412    type
413      WithBitsize {.objectconfig.} = object
414        bitfieldA {.bitsize: 16.}: uint32
415        bitfieldB {.bitsize: 16.}: uint32
416
417    var wbs: WithBitsize
418    testSize(wbs)
419
420    testOffsetOf(TrivialType, x)
421    testOffsetOf(TrivialType, y)
422    testOffsetOf(TrivialType, z)
423
424    testOffsetOf(SimpleAlignment, a)
425    testOffsetOf(SimpleAlignment, b)
426    testOffsetOf(SimpleAlignment, c)
427
428    testOffsetOf(AlignAtEnd, a)
429    testOffsetOf(AlignAtEnd, b)
430    testOffsetOf(AlignAtEnd, c)
431
432    testOffsetOf(SimpleBranch, a)
433    testOffsetOf(SimpleBranch, b)
434    testOffsetOf(SimpleBranch, c)
435
436    testOffsetOf(PaddingBeforeBranchA, cause)
437    testOffsetOf(PaddingBeforeBranchA, a)
438    testOffsetOf(PaddingBeforeBranchB, cause)
439    testOffsetOf(PaddingBeforeBranchB, a)
440
441    testOffsetOf(PaddingAfterBranch, a)
442    testOffsetOf(PaddingAfterBranch, cause)
443
444    testOffsetOf(Foobar, c)
445
446    testOffsetOf(PaddingOfSetEnum33, cause)
447    testOffsetOf(PaddingOfSetEnum33, theSet)
448
449    testOffsetOf(Bazing, a)
450    testOffsetOf(InheritanceA, a)
451    testOffsetOf(InheritanceB, b)
452    testOffsetOf(InheritanceC, c)
453
454    testOffsetOf(EnumObjectA, a)
455    testOffsetOf(EnumObjectA, b)
456    testOffsetOf(EnumObjectA, c)
457    testOffsetOf(EnumObjectA, d)
458    testOffsetOf(EnumObjectB, a)
459    testOffsetOf(EnumObjectB, b)
460    testOffsetOf(EnumObjectB, c)
461    testOffsetOf(EnumObjectB, d)
462
463    testOffsetOf(RecursiveStuff, kind)
464    testOffsetOf(RecursiveStuff, a)
465    testOffsetOf(RecursiveStuff, b)
466    testOffsetOf(RecursiveStuff, kind2)
467    testOffsetOf(RecursiveStuff, ca1)
468    testOffsetOf(RecursiveStuff, ca2)
469    testOffsetOf(RecursiveStuff, cb)
470    testOffsetOf(RecursiveStuff, cc)
471    testOffsetOf(RecursiveStuff, d1)
472    testOffsetOf(RecursiveStuff, d2)
473
474    testOffsetOf(MyCustomAlignPackedObject, a)
475    testOffsetOf(MyCustomAlignPackedObject, b)
476    testOffsetOf(MyCustomAlignPackedObject, c)
477    testOffsetOf(MyCustomAlignPackedObject, d)
478
479    echo "body executed" # sanity check to ensure this logic isn't skipped entirely
480
481
482  main()
483
484{.emit: """/*TYPESECTION*/
485typedef struct{
486  float a; float b;
487} Foo;
488""".}
489
490type
491  Foo {.importc.} = object
492
493  Bar = object
494    b: byte
495    foo: Foo
496
497assert sizeof(Bar) == 12
498
499# bug #10082
500type
501  A = int8        # change to int16 and get sizeof(C)==6
502  B = int16
503  C {.packed.} = object
504    d {.bitsize:  1.}: A
505    e {.bitsize:  7.}: A
506    f {.bitsize: 16.}: B
507
508assert sizeof(C) == 3
509
510
511type
512  MixedBitsize {.packed.} = object
513    a: uint32
514    b {.bitsize:  8.}: uint8
515    c {.bitsize:  1.}: uint8
516    d {.bitsize:  7.}: uint8
517    e {.bitsize: 16.}: uint16
518    f: uint32
519
520doAssert sizeof(MixedBitsize) == 12
521
522
523type
524  MyUnionType {.union.} = object
525    a: int32
526    b: float32
527
528  MyCustomAlignUnion {.union.} = object
529    c: char
530    a {.align: 32.}: int
531
532  MyCustomAlignObject = object
533    c: char
534    a {.align: 32.}: int
535
536doAssert sizeof(MyUnionType) == 4
537doAssert sizeof(MyCustomAlignUnion) == 32
538doAssert alignof(MyCustomAlignUnion) == 32
539doAssert sizeof(MyCustomAlignObject) == 64
540doAssert alignof(MyCustomAlignObject) == 32
541
542
543
544
545
546
547##########################################
548# bug #9794
549##########################################
550
551type
552  imported_double {.importc: "double".} = object
553
554  Pod = object
555    v* : imported_double
556    seed*: int32
557
558  Pod2 = tuple[v: imported_double, seed: int32]
559
560proc foobar() =
561  testAlign(Pod)
562  testSize(Pod)
563  testAlign(Pod2)
564  testSize(Pod2)
565  doAssert sizeof(Pod) == sizeof(Pod2)
566  doAssert alignof(Pod) == alignof(Pod2)
567foobar()
568
569if failed:
570  quit("FAIL")
571else:
572  echo "OK"
573
574##########################################
575# sizeof macros API
576##########################################
577
578import macros
579
580type
581  Vec2f = object
582    x,y: float32
583
584  Vec4f = object
585    x,y,z,w: float32
586
587  # this type is constructed to have no platform depended alignment.
588  ParticleDataA = object
589    pos, vel: Vec2f
590    birthday: float32
591    padding: float32
592    moreStuff: Vec4f
593
594const expected = [
595  # name size align offset
596  ("pos", 8, 4, 0),
597  ("vel", 8, 4, 8),
598  ("birthday", 4, 4, 16),
599  ("padding", 4, 4, 20),
600  ("moreStuff", 16, 4, 24)
601]
602
603macro typeProcessing(arg: typed): untyped =
604  let recList = arg.getTypeImpl[2]
605  recList.expectKind nnkRecList
606  for i, identDefs in recList:
607    identDefs.expectKind nnkIdentDefs
608    identDefs.expectLen 3
609    let sym = identDefs[0]
610    sym.expectKind nnkSym
611    doAssert expected[i][0] == sym.strVal
612    doAssert expected[i][1] == getSize(sym)
613    doAssert expected[i][2] == getAlign(sym)
614    doAssert expected[i][3] == getOffset(sym)
615
616  result = newCall(bindSym"echo", newLit("macros api OK"))
617
618proc main() =
619  var mylocal: ParticleDataA
620  typeProcessing(mylocal)
621
622main()
623
624# issue #11320 use UncheckedArray
625
626type
627  Payload = object
628    something: int8
629    vals: UncheckedArray[int32]
630
631proc payloadCheck() =
632  doAssert offsetOf(Payload, vals) == 4
633  doAssert sizeof(Payload) == 4
634
635payloadCheck()
636
637# offsetof tuple types
638
639type
640  MyTupleType = tuple
641    a: float64
642    b: float64
643    c: float64
644
645  MyOtherTupleType = tuple
646    a: float64
647    b: imported_double
648    c: float64
649
650  MyCaseObject = object
651    val1: imported_double
652    case kind: bool
653    of true:
654      val2,val3: float32
655    else:
656      val4,val5: int32
657
658doAssert offsetof(MyTupleType, a) == 0
659doAssert offsetof(MyTupleType, b) == 8
660doAssert offsetof(MyTupleType, c) == 16
661
662doAssert offsetof(MyOtherTupleType, a) == 0
663doAssert offsetof(MyOtherTupleType, b) == 8
664
665# The following expression can only work if the offsetof expression is
666# properly forwarded for the C code generator.
667doAssert offsetof(MyOtherTupleType, c) == 16
668doAssert offsetof(Bar, foo) == 4
669doAssert offsetof(MyCaseObject, val1) == 0
670doAssert offsetof(MyCaseObject, kind) == 8
671doAssert offsetof(MyCaseObject, val2) == 12
672doAssert offsetof(MyCaseObject, val3) == 16
673doAssert offsetof(MyCaseObject, val4) == 12
674doAssert offsetof(MyCaseObject, val5) == 16
675
676template reject(e) =
677  static: assert(not compiles(e))
678
679reject:
680  const off1 = offsetof(MyOtherTupleType, c)
681
682reject:
683  const off2 = offsetof(MyOtherTupleType, b)
684
685reject:
686  const off3 = offsetof(MyCaseObject, kind)
687
688
689type
690  MyPackedCaseObject {.packed.} = object
691    val1: imported_double
692    case kind: bool
693    of true:
694      val2,val3: float32
695    else:
696      val4,val5: int32
697
698# packed case object
699
700doAssert offsetof(MyPackedCaseObject, val1) == 0
701doAssert offsetof(MyPackedCaseObject, val2) == 9
702doAssert offsetof(MyPackedCaseObject, val3) == 13
703doAssert offsetof(MyPackedCaseObject, val4) == 9
704doAssert offsetof(MyPackedCaseObject, val5) == 13
705
706reject:
707  const off5 = offsetof(MyPackedCaseObject, val2)
708
709reject:
710  const off6 = offsetof(MyPackedCaseObject, val3)
711
712reject:
713  const off7 = offsetof(MyPackedCaseObject, val4)
714
715reject:
716  const off8 = offsetof(MyPackedCaseObject, val5)
717
718
719type
720  O0 = object
721  T0 = tuple[]
722
723doAssert sizeof(O0) == 1
724doAssert sizeof(T0) == 1
725
726
727type
728  # this thing may not have padding bytes at the end
729  PackedUnion* {.union, packed.} = object
730    a*: array[11, byte]
731    b*: int64
732
733doAssert sizeof(PackedUnion) == 11
734doAssert alignof(PackedUnion) == 1
735