tsizeof.nim 17 KB

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