mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-09 06:23:25 +00:00
fixes #23186 As explained in #23186, generics can transform `genericProc[int]` into a call `` `[]`(genericProc, int) `` which causes a problem when `genericProc` is resemmed, since it is not a resolved generic proc. `[]` needs unresolved generic procs since `mArrGet` also handles explicit generic instantiations, so delay the resolved generic proc check to `semFinishOperands` which is intentionally not called for `mArrGet`. The root issue for [t6137](https://github.com/nim-lang/Nim/blob/devel/tests/generics/t6137.nim) is also fixed (because this change breaks it otherwise), the compiler doesn't consider the possibility that an assigned generic param can be an unresolved static value (note the line `if t.kind == tyStatic: s.ast = t.n` below the change in sigmatch), now it properly errors that it couldn't instantiate it as it would for a type param. ~~The change in semtypinst is just for symmetry with the code above it which also gives a `cannot instantiate` error, it may or may not be necessary/correct.~~ Now removed, I don't think it was correct. Still possible that this has unintended consequences.
156 lines
5.1 KiB
Nim
156 lines
5.1 KiB
Nim
# 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)
|