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