Skip to content

Commit 0562c1a

Browse files
committed
make var/pointer types not match if base type has to be converted
split again from nim-lang#24038, fixes status-im/nimbus-eth2#6554 (comment)
1 parent da7670c commit 0562c1a

File tree

5 files changed

+244
-4
lines changed

5 files changed

+244
-4
lines changed

compiler/sigmatch.nim

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,9 +1017,21 @@ proc inferStaticsInRange(c: var TCandidate,
10171017
doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
10181018

10191019
template subtypeCheck() =
1020-
if result <= isSubrange and f.last.skipTypes(abstractInst).kind in {
1021-
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
1020+
case result
1021+
of isIntConv:
10221022
result = isNone
1023+
of isSubrange:
1024+
discard # XXX should be isNone with preview define, warnings
1025+
of isConvertible:
1026+
if f.last.skipTypes(abstractInst).kind != tyOpenArray:
1027+
# exclude var openarray which compiler supports
1028+
result = isNone
1029+
of isSubtype:
1030+
if f.last.skipTypes(abstractInst).kind in {
1031+
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
1032+
# compiler can't handle subtype conversions with pointer indirection
1033+
result = isNone
1034+
else: discard
10231035

10241036
proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
10251037
# this proc is always called for a pair of matching types

tests/errmsgs/t22097.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
discard """
2-
errormsg: "for a 'var' type a variable needs to be passed; but 'uint16(x)' is immutable"
2+
errormsg: "type mismatch: got <uint8>"
33
"""
44

55
proc toUInt16(x: var uint16) =
66
discard
77

88
var x = uint8(1)
9-
toUInt16 x
9+
toUInt16 x
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
discard """
2+
action: reject
3+
nimout: '''
4+
but expression 'int(a)' is immutable, not 'var'
5+
'''
6+
"""
7+
8+
proc `++`(n: var int) =
9+
n += 1
10+
11+
var a: int32 = 15
12+
13+
++int(a) #[tt.Error
14+
^ type mismatch: got <int>]#
15+
16+
echo a

tests/int/twrongvarconv.nim

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
proc `++`(n: var int) =
2+
n += 1
3+
4+
var a: int32 = 15
5+
6+
++a #[tt.Error
7+
^ type mismatch: got <int32>]#
8+
9+
echo a

tests/overload/tvaruintconv.nim

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
2+
# failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
3+
4+
import
5+
std/[typetraits, macros]
6+
7+
type
8+
DefaultFlavor = object
9+
10+
template serializationFormatImpl(Name: untyped) {.dirty.} =
11+
type Name = object
12+
13+
template serializationFormat(Name: untyped) =
14+
serializationFormatImpl(Name)
15+
16+
template setReader(Format, FormatReader: distinct type) =
17+
when arity(FormatReader) > 1:
18+
template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
19+
else:
20+
template ReaderType(T: type Format): type = FormatReader
21+
template Reader(T: type Format): type = FormatReader
22+
23+
template useDefaultReaderIn(T: untyped, Flavor: type) =
24+
mixin Reader
25+
26+
template readValue(r: var Reader(Flavor), value: var T) =
27+
mixin readRecordValue
28+
readRecordValue(r, value)
29+
30+
from stew/shims/macros import field, isTuple, recordFields, skipPragma
31+
32+
type
33+
FieldTag[RecordType: object; fieldName: static string] = distinct void
34+
35+
func declval*(T: type): T {.compileTime.} =
36+
default(ptr T)[]
37+
38+
macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
39+
var typeAst = getType(T)[1]
40+
var typeImpl: NimNode
41+
let isSymbol = not typeAst.isTuple
42+
43+
if not isSymbol:
44+
typeImpl = typeAst
45+
else:
46+
typeImpl = getImpl(typeAst)
47+
result = newStmtList()
48+
49+
var i = 0
50+
for field in recordFields(typeImpl):
51+
let
52+
fieldIdent = field.name
53+
realFieldName = newLit($fieldIdent.skipPragma)
54+
fieldName = realFieldName
55+
fieldIndex = newLit(i)
56+
57+
let fieldNameDefs =
58+
if isSymbol:
59+
quote:
60+
const fieldName {.inject, used.} = `fieldName`
61+
const realFieldName {.inject, used.} = `realFieldName`
62+
else:
63+
quote:
64+
const fieldName {.inject, used.} = $`fieldIndex`
65+
const realFieldName {.inject, used.} = $`fieldIndex`
66+
67+
let field =
68+
if isSymbol:
69+
quote do: declval(`T`).`fieldIdent`
70+
else:
71+
quote do: declval(`T`)[`fieldIndex`]
72+
73+
result.add quote do:
74+
block:
75+
`fieldNameDefs`
76+
77+
template FieldType: untyped {.inject, used.} = typeof(`field`)
78+
79+
`body`
80+
81+
# echo repr(result)
82+
83+
template enumAllSerializedFields(T: type, body): untyped =
84+
enumAllSerializedFieldsImpl(T, body)
85+
86+
type
87+
FieldReader[RecordType, Reader] = tuple[
88+
fieldName: string,
89+
reader: proc (rec: var RecordType, reader: var Reader)
90+
{.gcsafe, nimcall.}
91+
]
92+
93+
proc totalSerializedFieldsImpl(T: type): int =
94+
mixin enumAllSerializedFields
95+
enumAllSerializedFields(T): inc result
96+
97+
template totalSerializedFields(T: type): int =
98+
(static(totalSerializedFieldsImpl(T)))
99+
100+
template GetFieldType(FT: type FieldTag): type =
101+
typeof field(declval(FT.RecordType), FT.fieldName)
102+
103+
proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
104+
numFields: static[int]):
105+
array[numFields, FieldReader[RecordType, ReaderType]] =
106+
mixin enumAllSerializedFields, handleReadException
107+
var idx = 0
108+
109+
enumAllSerializedFields(RecordType):
110+
proc readField(obj: var RecordType, reader: var ReaderType)
111+
{.gcsafe, nimcall.} =
112+
113+
mixin readValue
114+
115+
type F = FieldTag[RecordType, realFieldName]
116+
field(obj, realFieldName) = reader.readValue(GetFieldType(F))
117+
118+
result[idx] = (fieldName, readField)
119+
inc idx
120+
121+
proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
122+
mixin readValue
123+
type T = RecordType
124+
const numFields = totalSerializedFields(T)
125+
var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
126+
if tbl == nil:
127+
tbl = new typeof(tbl)
128+
tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
129+
return addr(tbl[])
130+
131+
proc readValue(reader: var auto, T: type): T =
132+
mixin readValue
133+
reader.readValue(result)
134+
135+
template decode(Format: distinct type,
136+
input: string,
137+
RecordType: distinct type): auto =
138+
mixin Reader
139+
block: # https://github.com/nim-lang/Nim/issues/22874
140+
var reader: Reader(Format)
141+
reader.readValue(RecordType)
142+
143+
template readValue(Format: type,
144+
ValueType: type): untyped =
145+
mixin Reader, init, readValue
146+
var reader: Reader(Format)
147+
readValue reader, ValueType
148+
149+
template parseArrayImpl(numElem: untyped,
150+
actionValue: untyped) =
151+
actionValue
152+
153+
serializationFormat Json
154+
template createJsonFlavor(FlavorName: untyped,
155+
skipNullFields = false) {.dirty.} =
156+
type FlavorName = object
157+
158+
template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
159+
type
160+
JsonReader[Flavor = DefaultFlavor] = object
161+
162+
Json.setReader JsonReader
163+
164+
template parseArray(r: var JsonReader; body: untyped) =
165+
parseArrayImpl(idx): body
166+
167+
template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
168+
parseArrayImpl(idx): body
169+
170+
proc readRecordValue[T](r: var JsonReader, value: var T) =
171+
type
172+
ReaderType {.used.} = type r
173+
T = type value
174+
175+
discard T.fieldReadersTable(ReaderType)
176+
177+
proc readValue[T](r: var JsonReader, value: var T) =
178+
mixin readValue
179+
180+
when value is seq:
181+
r.parseArray:
182+
readValue(r, value[0])
183+
184+
elif value is object:
185+
readRecordValue(r, value)
186+
187+
type
188+
RemoteSignerInfo = object
189+
id: uint32
190+
RemoteKeystore = object
191+
192+
proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
193+
discard reader.readValue(seq[RemoteSignerInfo])
194+
195+
createJsonFlavor RestJson
196+
useDefaultReaderIn(RemoteSignerInfo, RestJson)
197+
proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
198+
discard reader.readValue(string)
199+
200+
discard Json.decode("", RemoteKeystore)
201+
block: # https://github.com/nim-lang/Nim/issues/22874
202+
var reader: Reader(RestJson)
203+
discard reader.readValue(RemoteSignerInfo)

0 commit comments

Comments
 (0)