Skip to content

Commit c61e95b

Browse files
authored
fix(typeinfo): expanding seq doesn't clear locations (#1463)
## Summary New seq slots are now zeroed when expanding the seq via `invokeNewSeq` or `extendSeq`, making the behaviour consistent with `setLen` and `newSeq`, and also fixing crashes with `marshal` caused by the uninitialized memory. ## Details Zeroing the memory is not correct for types that don't have a zero- default, but those cannot be detected with just RTTI. Zeroing the memory is usually still better then leaving it as is. For the JavaScript and VM backends, the `zeroMem` call is excluded from compilation. Using `invokeNewSeq` and `extendSeq` is already not possible on these backends. Fixes #1462
1 parent 3c71f44 commit c61e95b

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

lib/core/typeinfo.nim

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin
108108

109109
template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b)
110110

111+
proc align(address, alignment: int): int =
112+
result = (address + (alignment - 1)) and not (alignment - 1)
113+
111114
proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
112115
assert(n.kind == nkCase)
113116
var d: int
@@ -180,6 +183,11 @@ proc invokeNewSeq*(x: Any, len: int) =
180183
s.len = len
181184
let elem = x.rawType.base
182185
s.p = cast[ptr NimSeqPayloadReimpl](newSeqPayload(len, elem.size, elem.align))
186+
if len > 0:
187+
# zeroing the memory might not be legal depending on the type, but there's
188+
# curently no way to check that here
189+
when declared(zeroMem): # not the case for JS and the VM
190+
zeroMem(s.p +!! align(sizeof(int), elem.align), elem.size * len)
183191

184192
proc extendSeq*(x: Any) =
185193
## Performs `setLen(x, x.len+1)`. `x` needs to represent a `seq`.
@@ -189,6 +197,13 @@ proc extendSeq*(x: Any) =
189197
let elem = x.rawType.base
190198
if s.p == nil or s.p.cap < s.len+1:
191199
s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align))
200+
201+
# zeroing the memory might not be legal depending on the type, but there's
202+
# curently no way to check that here
203+
when declared(zeroMem): # not the case for JS and the VM
204+
let headerSize = align(sizeof(int), elem.align)
205+
zeroMem(s.p +!! (headerSize + s.len * elem.size), elem.size)
206+
192207
inc s.len
193208

194209
proc setObjectRuntimeType*(x: Any) =
@@ -203,9 +218,6 @@ proc skipRange(x: PNimType): PNimType {.inline.} =
203218
result = x
204219
if result.kind == tyRange: result = result.base
205220

206-
proc align(address, alignment: int): int =
207-
result = (address + (alignment - 1)) and not (alignment - 1)
208-
209221
proc `[]`*(x: Any, i: int): Any =
210222
## Accessor for an any `x` that represents an array or a sequence.
211223
case x.rawType.kind

tests/stdlib/metaprogramming/ttypeinfo.nim

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,17 @@ block:
6969

7070
doAssert getEnumOrdinal(y, "Hello") == 0
7171
doAssert getEnumOrdinal(y, "hello") == 1
72+
73+
block seq_extension_zeroes:
74+
# make sure ``invokeNewSeq`` and ``extendSeq`` zero the new locations
75+
var s: seq[int]
76+
toAny(s).invokeNewSeq(100)
77+
78+
# check that the values are all zero and change them to something else
79+
for it in s.mitems:
80+
doAssert it == 0
81+
it = 1
82+
83+
s.setLen(0) # clear the seq, which should leave the memory untouched
84+
toAny(s).extendSeq() # grow by one
85+
doAssert s[0] == 0

0 commit comments

Comments
 (0)