tsizeof.nim 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  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. const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time
  293. proc main(): void =
  294. var t : TrivialType
  295. var a : SimpleAlignment
  296. var b : AlignAtEnd
  297. var c : SimpleBranch
  298. var d : PaddingBeforeBranchA
  299. var e : PaddingBeforeBranchB
  300. var f : PaddingAfterBranch
  301. var g : RecursiveStuff
  302. var ro : RootObj
  303. var go : GenericObject[int64]
  304. var po : PaddingOfSetEnum33
  305. var capo: MyCustomAlignPackedObject
  306. var
  307. e1: Enum1
  308. e2: Enum2
  309. e4: Enum4
  310. e8: Enum8
  311. var
  312. eoa: EnumObjectA
  313. eob: EnumObjectB
  314. testAlign(SimpleAlignment)
  315. # sanity check to ensure both branches are actually executed
  316. when usePacked:
  317. doAssert sizeof(SimpleAlignment) == 10
  318. else:
  319. doAssert sizeof(SimpleAlignment) > 10
  320. testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo)
  321. type
  322. WithBitsize {.objectconfig.} = object
  323. bitfieldA {.bitsize: 16.}: uint32
  324. bitfieldB {.bitsize: 16.}: uint32
  325. var wbs: WithBitsize
  326. testSize(wbs)
  327. testOffsetOf(TrivialType, x)
  328. testOffsetOf(TrivialType, y)
  329. testOffsetOf(TrivialType, z)
  330. testOffsetOf(SimpleAlignment, a)
  331. testOffsetOf(SimpleAlignment, b)
  332. testOffsetOf(SimpleAlignment, c)
  333. testOffsetOf(AlignAtEnd, a)
  334. testOffsetOf(AlignAtEnd, b)
  335. testOffsetOf(AlignAtEnd, c)
  336. testOffsetOf(SimpleBranch, a)
  337. testOffsetOf(SimpleBranch, b)
  338. testOffsetOf(SimpleBranch, c)
  339. testOffsetOf(PaddingBeforeBranchA, cause)
  340. testOffsetOf(PaddingBeforeBranchA, a)
  341. testOffsetOf(PaddingBeforeBranchB, cause)
  342. testOffsetOf(PaddingBeforeBranchB, a)
  343. testOffsetOf(PaddingAfterBranch, a)
  344. testOffsetOf(PaddingAfterBranch, cause)
  345. testOffsetOf(Foobar, c)
  346. testOffsetOf(PaddingOfSetEnum33, cause)
  347. testOffsetOf(PaddingOfSetEnum33, theSet)
  348. testOffsetOf(Bazing, a)
  349. testOffsetOf(InheritanceA, a)
  350. testOffsetOf(InheritanceB, b)
  351. testOffsetOf(InheritanceC, c)
  352. testOffsetOf(EnumObjectA, a)
  353. testOffsetOf(EnumObjectA, b)
  354. testOffsetOf(EnumObjectA, c)
  355. testOffsetOf(EnumObjectA, d)
  356. testOffsetOf(EnumObjectB, a)
  357. testOffsetOf(EnumObjectB, b)
  358. testOffsetOf(EnumObjectB, c)
  359. testOffsetOf(EnumObjectB, d)
  360. testOffsetOf(RecursiveStuff, kind)
  361. testOffsetOf(RecursiveStuff, a)
  362. testOffsetOf(RecursiveStuff, b)
  363. testOffsetOf(RecursiveStuff, kind2)
  364. testOffsetOf(RecursiveStuff, ca1)
  365. testOffsetOf(RecursiveStuff, ca2)
  366. testOffsetOf(RecursiveStuff, cb)
  367. testOffsetOf(RecursiveStuff, cc)
  368. testOffsetOf(RecursiveStuff, d1)
  369. testOffsetOf(RecursiveStuff, d2)
  370. testOffsetOf(MyCustomAlignPackedObject, a)
  371. testOffsetOf(MyCustomAlignPackedObject, b)
  372. testOffsetOf(MyCustomAlignPackedObject, c)
  373. testOffsetOf(MyCustomAlignPackedObject, d)
  374. echo "body executed" # sanity check to ensure this logic isn't skipped entirely
  375. main()
  376. {.emit: """/*TYPESECTION*/
  377. typedef struct{
  378. float a; float b;
  379. } Foo;
  380. """.}
  381. type
  382. Foo {.importc.} = object
  383. Bar = object
  384. b: byte
  385. foo: Foo
  386. assert sizeof(Bar) == 12
  387. # bug #10082
  388. type
  389. A = int8 # change to int16 and get sizeof(C)==6
  390. B = int16
  391. C {.packed.} = object
  392. d {.bitsize: 1.}: A
  393. e {.bitsize: 7.}: A
  394. f {.bitsize: 16.}: B
  395. assert sizeof(C) == 3
  396. type
  397. MixedBitsize {.packed.} = object
  398. a: uint32
  399. b {.bitsize: 8.}: uint8
  400. c {.bitsize: 1.}: uint8
  401. d {.bitsize: 7.}: uint8
  402. e {.bitsize: 16.}: uint16
  403. f: uint32
  404. doAssert sizeof(MixedBitsize) == 12
  405. type
  406. MyUnionType {.union.} = object
  407. a: int32
  408. b: float32
  409. MyCustomAlignUnion {.union.} = object
  410. c: char
  411. a {.align: 32.}: int
  412. MyCustomAlignObject = object
  413. c: char
  414. a {.align: 32.}: int
  415. doAssert sizeof(MyUnionType) == 4
  416. doAssert sizeof(MyCustomAlignUnion) == 32
  417. doAssert alignof(MyCustomAlignUnion) == 32
  418. doAssert sizeof(MyCustomAlignObject) == 64
  419. doAssert alignof(MyCustomAlignObject) == 32
  420. ##########################################
  421. # bug #9794
  422. ##########################################
  423. type
  424. imported_double {.importc: "double".} = object
  425. Pod = object
  426. v* : imported_double
  427. seed*: int32
  428. Pod2 = tuple[v: imported_double, seed: int32]
  429. proc foobar() =
  430. testAlign(Pod)
  431. testSize(Pod)
  432. testAlign(Pod2)
  433. testSize(Pod2)
  434. doAssert sizeof(Pod) == sizeof(Pod2)
  435. doAssert alignof(Pod) == alignof(Pod2)
  436. foobar()
  437. if failed:
  438. quit("FAIL")
  439. else:
  440. echo "OK"
  441. ##########################################
  442. # sizeof macros API
  443. ##########################################
  444. import macros
  445. type
  446. Vec2f = object
  447. x,y: float32
  448. Vec4f = object
  449. x,y,z,w: float32
  450. # this type is constructed to have no platform depended alignment.
  451. ParticleDataA = object
  452. pos, vel: Vec2f
  453. birthday: float32
  454. padding: float32
  455. moreStuff: Vec4f
  456. const expected = [
  457. # name size align offset
  458. ("pos", 8, 4, 0),
  459. ("vel", 8, 4, 8),
  460. ("birthday", 4, 4, 16),
  461. ("padding", 4, 4, 20),
  462. ("moreStuff", 16, 4, 24)
  463. ]
  464. macro typeProcessing(arg: typed): untyped =
  465. let recList = arg.getTypeImpl[2]
  466. recList.expectKind nnkRecList
  467. for i, identDefs in recList:
  468. identDefs.expectKind nnkIdentDefs
  469. identDefs.expectLen 3
  470. let sym = identDefs[0]
  471. sym.expectKind nnkSym
  472. doAssert expected[i][0] == sym.strVal
  473. doAssert expected[i][1] == getSize(sym)
  474. doAssert expected[i][2] == getAlign(sym)
  475. doAssert expected[i][3] == getOffset(sym)
  476. result = newCall(bindSym"echo", newLit("macros api OK"))
  477. proc main() =
  478. var mylocal: ParticleDataA
  479. typeProcessing(mylocal)
  480. main()
  481. # issue #11320 use UncheckedArray
  482. type
  483. Payload = object
  484. something: int8
  485. vals: UncheckedArray[int32]
  486. proc payloadCheck() =
  487. doAssert offsetOf(Payload, vals) == 4
  488. doAssert sizeof(Payload) == 4
  489. payloadCheck()
  490. # offsetof tuple types
  491. type
  492. MyTupleType = tuple
  493. a: float64
  494. b: float64
  495. c: float64
  496. MyOtherTupleType = tuple
  497. a: float64
  498. b: imported_double
  499. c: float64
  500. MyCaseObject = object
  501. val1: imported_double
  502. case kind: bool
  503. of true:
  504. val2,val3: float32
  505. else:
  506. val4,val5: int32
  507. doAssert offsetof(MyTupleType, a) == 0
  508. doAssert offsetof(MyTupleType, b) == 8
  509. doAssert offsetof(MyTupleType, c) == 16
  510. doAssert offsetof(MyOtherTupleType, a) == 0
  511. doAssert offsetof(MyOtherTupleType, b) == 8
  512. # The following expression can only work if the offsetof expression is
  513. # properly forwarded for the C code generator.
  514. doAssert offsetof(MyOtherTupleType, c) == 16
  515. doAssert offsetof(Bar, foo) == 4
  516. doAssert offsetof(MyCaseObject, val1) == 0
  517. doAssert offsetof(MyCaseObject, kind) == 8
  518. doAssert offsetof(MyCaseObject, val2) == 12
  519. doAssert offsetof(MyCaseObject, val3) == 16
  520. doAssert offsetof(MyCaseObject, val4) == 12
  521. doAssert offsetof(MyCaseObject, val5) == 16
  522. template reject(e) =
  523. static: assert(not compiles(e))
  524. reject:
  525. const off1 = offsetof(MyOtherTupleType, c)
  526. reject:
  527. const off2 = offsetof(MyOtherTupleType, b)
  528. reject:
  529. const off3 = offsetof(MyCaseObject, kind)
  530. type
  531. MyPackedCaseObject {.packed.} = object
  532. val1: imported_double
  533. case kind: bool
  534. of true:
  535. val2,val3: float32
  536. else:
  537. val4,val5: int32
  538. # packed case object
  539. doAssert offsetof(MyPackedCaseObject, val1) == 0
  540. doAssert offsetof(MyPackedCaseObject, val2) == 9
  541. doAssert offsetof(MyPackedCaseObject, val3) == 13
  542. doAssert offsetof(MyPackedCaseObject, val4) == 9
  543. doAssert offsetof(MyPackedCaseObject, val5) == 13
  544. reject:
  545. const off5 = offsetof(MyPackedCaseObject, val2)
  546. reject:
  547. const off6 = offsetof(MyPackedCaseObject, val3)
  548. reject:
  549. const off7 = offsetof(MyPackedCaseObject, val4)
  550. reject:
  551. const off8 = offsetof(MyPackedCaseObject, val5)
  552. type
  553. O0 = object
  554. T0 = tuple[]
  555. doAssert sizeof(O0) == 1
  556. doAssert sizeof(T0) == 1
  557. type
  558. # this thing may not have padding bytes at the end
  559. PackedUnion* {.union, packed.} = object
  560. a*: array[11, byte]
  561. b*: int64
  562. doAssert sizeof(PackedUnion) == 11
  563. doAssert alignof(PackedUnion) == 1