t23186.nim 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. # issue #23186
  2. block: # simplified
  3. template typedTempl(x: int, body): untyped =
  4. body
  5. proc generic1[T]() =
  6. discard
  7. proc generic2[T]() =
  8. typedTempl(1):
  9. let x = generic1[T]
  10. generic2[int]()
  11. import std/macros
  12. when not compiles(len((1, 2))):
  13. import std/typetraits
  14. func len(x: tuple): int =
  15. arity(type(x))
  16. block: # full issue example
  17. type FieldDescription = object
  18. name: NimNode
  19. func isTuple(t: NimNode): bool =
  20. t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
  21. proc collectFieldsFromRecList(result: var seq[FieldDescription],
  22. n: NimNode,
  23. parentCaseField: NimNode = nil,
  24. parentCaseBranch: NimNode = nil,
  25. isDiscriminator = false) =
  26. case n.kind
  27. of nnkRecList:
  28. for entry in n:
  29. collectFieldsFromRecList result, entry,
  30. parentCaseField, parentCaseBranch
  31. of nnkIdentDefs:
  32. for i in 0 ..< n.len - 2:
  33. var field: FieldDescription
  34. field.name = n[i]
  35. if field.name.kind == nnkPragmaExpr:
  36. field.name = field.name[0]
  37. if field.name.kind == nnkPostfix:
  38. field.name = field.name[1]
  39. result.add field
  40. of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
  41. discard
  42. else:
  43. doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
  44. proc collectFieldsInHierarchy(result: var seq[FieldDescription],
  45. objectType: NimNode) =
  46. var objectType = objectType
  47. if objectType.kind == nnkRefTy:
  48. objectType = objectType[0]
  49. let recList = objectType[2]
  50. collectFieldsFromRecList result, recList
  51. proc recordFields(typeImpl: NimNode): seq[FieldDescription] =
  52. let objectType = case typeImpl.kind
  53. of nnkObjectTy: typeImpl
  54. of nnkTypeDef: typeImpl[2]
  55. else:
  56. macros.error("object type expected", typeImpl)
  57. return
  58. collectFieldsInHierarchy(result, objectType)
  59. proc skipPragma(n: NimNode): NimNode =
  60. if n.kind == nnkPragmaExpr: n[0]
  61. else: n
  62. func declval(T: type): T =
  63. doAssert false,
  64. "declval should be used only in `typeof` expressions and concepts"
  65. default(ptr T)[]
  66. macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
  67. var typeAst = getType(T)[1]
  68. var typeImpl: NimNode
  69. let isSymbol = not typeAst.isTuple
  70. if not isSymbol:
  71. typeImpl = typeAst
  72. else:
  73. typeImpl = getImpl(typeAst)
  74. result = newStmtList()
  75. var i = 0
  76. for field in recordFields(typeImpl):
  77. let
  78. fieldIdent = field.name
  79. realFieldName = newLit($fieldIdent.skipPragma)
  80. fieldName = realFieldName
  81. fieldIndex = newLit(i)
  82. let fieldNameDefs =
  83. if isSymbol:
  84. quote:
  85. const fieldName {.inject, used.} = `fieldName`
  86. const realFieldName {.inject, used.} = `realFieldName`
  87. else:
  88. quote:
  89. const fieldName {.inject, used.} = $`fieldIndex`
  90. const realFieldName {.inject, used.} = $`fieldIndex`
  91. # we can't access .Fieldn, so our helper knows
  92. # to parseInt this
  93. let field =
  94. if isSymbol:
  95. quote do: declval(`T`).`fieldIdent`
  96. else:
  97. quote do: declval(`T`)[`fieldIndex`]
  98. result.add quote do:
  99. block:
  100. `fieldNameDefs`
  101. type FieldType {.inject, used.} = type(`field`)
  102. `body`
  103. i += 1
  104. template enumAllSerializedFields(T: type, body): untyped =
  105. when T is ref|ptr:
  106. type TT = type(default(T)[])
  107. enumAllSerializedFieldsImpl(TT, body)
  108. else:
  109. enumAllSerializedFieldsImpl(T, body)
  110. type
  111. MemRange = object
  112. startAddr: ptr byte
  113. length: int
  114. SszNavigator[T] = object
  115. m: MemRange
  116. func sszMount(data: openArray[byte], T: type): SszNavigator[T] =
  117. let startAddr = unsafeAddr data[0]
  118. SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
  119. func sszMount(data: openArray[char], T: type): SszNavigator[T] =
  120. let startAddr = cast[ptr byte](unsafeAddr data[0])
  121. SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
  122. template sszMount(data: MemRange, T: type): SszNavigator[T] =
  123. SszNavigator[T](m: data)
  124. func navigateToField[T](
  125. n: SszNavigator[T],
  126. FieldType: type): SszNavigator[FieldType] =
  127. default(SszNavigator[FieldType])
  128. type
  129. FieldInfo = ref object
  130. navigator: proc (m: MemRange): MemRange {.
  131. gcsafe, noSideEffect, raises: [IOError] .}
  132. func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string](
  133. m: MemRange): MemRange =
  134. var typedNavigator = sszMount(m, RecordType)
  135. discard navigateToField(typedNavigator, FieldType)
  136. default(MemRange)
  137. func genTypeInfo(T: type) =
  138. when T is object:
  139. enumAllSerializedFields(T):
  140. discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName])
  141. type
  142. Foo = object
  143. bar: Bar
  144. BarList = seq[uint64]
  145. Bar = object
  146. b: BarList
  147. baz: Baz
  148. Baz = object
  149. i: uint64
  150. genTypeInfo(Foo)