Files
Nim/tests/overload/tvaruintconv.nim
metagn 1660ddf98a make var/pointer types not match if base type has to be converted (#24130)
split again from #24038, fixes
https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102

`var`/pointer types are no longer implicitly convertible to each other
if their element types either:

* require an int conversion or another conversion operation as long as
it's not to `openarray`,
* are subtypes with pointer indirection,

Previously any conversion below a subrange match would match if the
element type wasn't a pointer type, then it would error later in
`analyseIfAddressTaken`.

Different from #24038 in that the preview define that made subrange
matches also fail to match is removed for a simpler diff so that it can
be backported.
2024-09-18 17:37:18 +02:00

208 lines
5.7 KiB
Nim

discard """
action: compile
"""
# https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
# failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
import
std/[typetraits, macros]
type
DefaultFlavor = object
template serializationFormatImpl(Name: untyped) {.dirty.} =
type Name = object
template serializationFormat(Name: untyped) =
serializationFormatImpl(Name)
template setReader(Format, FormatReader: distinct type) =
when arity(FormatReader) > 1:
template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
else:
template ReaderType(T: type Format): type = FormatReader
template Reader(T: type Format): type = FormatReader
template useDefaultReaderIn(T: untyped, Flavor: type) =
mixin Reader
template readValue(r: var Reader(Flavor), value: var T) =
mixin readRecordValue
readRecordValue(r, value)
import mvaruintconv
type
FieldTag[RecordType: object; fieldName: static string] = distinct void
func declval*(T: type): T {.compileTime.} =
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`
let field =
if isSymbol:
quote do: declval(`T`).`fieldIdent`
else:
quote do: declval(`T`)[`fieldIndex`]
result.add quote do:
block:
`fieldNameDefs`
template FieldType: untyped {.inject, used.} = typeof(`field`)
`body`
# echo repr(result)
template enumAllSerializedFields(T: type, body): untyped =
enumAllSerializedFieldsImpl(T, body)
type
FieldReader[RecordType, Reader] = tuple[
fieldName: string,
reader: proc (rec: var RecordType, reader: var Reader)
{.gcsafe, nimcall.}
]
proc totalSerializedFieldsImpl(T: type): int =
mixin enumAllSerializedFields
enumAllSerializedFields(T): inc result
template totalSerializedFields(T: type): int =
(static(totalSerializedFieldsImpl(T)))
template GetFieldType(FT: type FieldTag): type =
typeof field(declval(FT.RecordType), FT.fieldName)
proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
numFields: static[int]):
array[numFields, FieldReader[RecordType, ReaderType]] =
mixin enumAllSerializedFields, handleReadException
var idx = 0
enumAllSerializedFields(RecordType):
proc readField(obj: var RecordType, reader: var ReaderType)
{.gcsafe, nimcall.} =
mixin readValue
type F = FieldTag[RecordType, realFieldName]
field(obj, realFieldName) = reader.readValue(GetFieldType(F))
result[idx] = (fieldName, readField)
inc idx
proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
mixin readValue
type T = RecordType
const numFields = totalSerializedFields(T)
var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
if tbl == nil:
tbl = new typeof(tbl)
tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
return addr(tbl[])
proc readValue(reader: var auto, T: type): T =
mixin readValue
reader.readValue(result)
template decode(Format: distinct type,
input: string,
RecordType: distinct type): auto =
mixin Reader
block: # https://github.com/nim-lang/Nim/issues/22874
var reader: Reader(Format)
reader.readValue(RecordType)
template readValue(Format: type,
ValueType: type): untyped =
mixin Reader, init, readValue
var reader: Reader(Format)
readValue reader, ValueType
template parseArrayImpl(numElem: untyped,
actionValue: untyped) =
actionValue
serializationFormat Json
template createJsonFlavor(FlavorName: untyped,
skipNullFields = false) {.dirty.} =
type FlavorName = object
template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
type
JsonReader[Flavor = DefaultFlavor] = object
Json.setReader JsonReader
template parseArray(r: var JsonReader; body: untyped) =
parseArrayImpl(idx): body
template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
parseArrayImpl(idx): body
proc readRecordValue[T](r: var JsonReader, value: var T) =
type
ReaderType {.used.} = type r
T = type value
discard T.fieldReadersTable(ReaderType)
proc readValue[T](r: var JsonReader, value: var T) =
mixin readValue
when value is seq:
r.parseArray:
readValue(r, value[0])
elif value is object:
readRecordValue(r, value)
type
RemoteSignerInfo = object
id: uint32
RemoteKeystore = object
proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
discard reader.readValue(seq[RemoteSignerInfo])
createJsonFlavor RestJson
useDefaultReaderIn(RemoteSignerInfo, RestJson)
proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
discard reader.readValue(string)
discard Json.decode("", RemoteKeystore)
block: # https://github.com/nim-lang/Nim/issues/22874
var reader: Reader(RestJson)
discard reader.readValue(RemoteSignerInfo)