File: t23186.nim

package info (click to toggle)
nim 2.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,911,644 kB
  • sloc: sh: 24,603; ansic: 1,761; python: 1,492; makefile: 1,013; sql: 298; asm: 141; xml: 13
file content (155 lines) | stat: -rw-r--r-- 5,177 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# issue #23186

block: # simplified
  template typedTempl(x: int, body): untyped =
    body
  proc generic1[T]() =
    discard
  proc generic2[T]() =
    typedTempl(1):
      let x = generic1[T]
  generic2[int]()

import std/macros

when not compiles(len((1, 2))):
  import std/typetraits

  func len(x: tuple): int =
    arity(type(x))

block: # full issue example
  type FieldDescription = object
    name: NimNode
  func isTuple(t: NimNode): bool =
    t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
  proc collectFieldsFromRecList(result: var seq[FieldDescription],
                                n: NimNode,
                                parentCaseField: NimNode = nil,
                                parentCaseBranch: NimNode = nil,
                                isDiscriminator = false) =
    case n.kind
    of nnkRecList:
      for entry in n:
        collectFieldsFromRecList result, entry,
                                parentCaseField, parentCaseBranch
    of nnkIdentDefs:
      for i in 0 ..< n.len - 2:
        var field: FieldDescription
        field.name = n[i]
        if field.name.kind == nnkPragmaExpr:
          field.name = field.name[0]
        if field.name.kind == nnkPostfix:
          field.name = field.name[1]
        result.add field
    of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
      discard
    else:
      doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
  proc collectFieldsInHierarchy(result: var seq[FieldDescription],
                                objectType: NimNode) =
    var objectType = objectType
    if objectType.kind == nnkRefTy:
      objectType = objectType[0]
    let recList = objectType[2]
    collectFieldsFromRecList result, recList
  proc recordFields(typeImpl: NimNode): seq[FieldDescription] =
    let objectType = case typeImpl.kind
      of nnkObjectTy: typeImpl
      of nnkTypeDef: typeImpl[2]
      else:
        macros.error("object type expected", typeImpl)
        return
    collectFieldsInHierarchy(result, objectType)
  proc skipPragma(n: NimNode): NimNode =
    if n.kind == nnkPragmaExpr: n[0]
    else: n
  func declval(T: type): T =
    doAssert false,
      "declval should be used only in `typeof` expressions and concepts"
    default(ptr T)[]
  macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
    var typeAst = getType(T)[1]
    var typeImpl: NimNode
    let isSymbol = not typeAst.isTuple
    if not isSymbol:
      typeImpl = typeAst
    else:
      typeImpl = getImpl(typeAst)
    result = newStmtList()
    var i = 0
    for field in recordFields(typeImpl):
      let
        fieldIdent = field.name
        realFieldName = newLit($fieldIdent.skipPragma)
        fieldName = realFieldName
        fieldIndex = newLit(i)
      let fieldNameDefs =
        if isSymbol:
          quote:
            const fieldName {.inject, used.} = `fieldName`
            const realFieldName {.inject, used.} = `realFieldName`
        else:
          quote:
            const fieldName {.inject, used.} = $`fieldIndex`
            const realFieldName {.inject, used.} = $`fieldIndex`
            # we can't access .Fieldn, so our helper knows
            # to parseInt this
      let field =
        if isSymbol:
          quote do: declval(`T`).`fieldIdent`
        else:
          quote do: declval(`T`)[`fieldIndex`]
      result.add quote do:
        block:
          `fieldNameDefs`
          type FieldType {.inject, used.} = type(`field`)
          `body`
      i += 1
  template enumAllSerializedFields(T: type, body): untyped =
    when T is ref|ptr:
      type TT = type(default(T)[])
      enumAllSerializedFieldsImpl(TT, body)
    else:
      enumAllSerializedFieldsImpl(T, body)
  type
    MemRange = object
      startAddr: ptr byte
      length: int
    SszNavigator[T] = object
      m: MemRange
  func sszMount(data: openArray[byte], T: type): SszNavigator[T] =
    let startAddr = unsafeAddr data[0]
    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
  func sszMount(data: openArray[char], T: type): SszNavigator[T] =
    let startAddr = cast[ptr byte](unsafeAddr data[0])
    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
  template sszMount(data: MemRange, T: type): SszNavigator[T] =
    SszNavigator[T](m: data)
  func navigateToField[T](
      n: SszNavigator[T],
      FieldType: type): SszNavigator[FieldType] =
    default(SszNavigator[FieldType])
  type
    FieldInfo = ref object
      navigator: proc (m: MemRange): MemRange {.
        gcsafe, noSideEffect, raises: [IOError] .}
  func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string](
      m: MemRange): MemRange =
    var typedNavigator = sszMount(m, RecordType)
    discard navigateToField(typedNavigator, FieldType)
    default(MemRange)
  func genTypeInfo(T: type) =
    when T is object:
      enumAllSerializedFields(T):
        discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName])
  type
    Foo = object
      bar: Bar
    BarList = seq[uint64]
    Bar = object
      b: BarList
      baz: Baz
    Baz = object
      i: uint64
  genTypeInfo(Foo)