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)
|