Files
Nim/tests/generics/t23186.nim
metagn e8092a5470 delay resolved procvar check for proc params + acknowledge unresolved statics (#23188)
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.
2024-01-11 07:45:11 +01:00

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)