-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Nim Version
2.2.4
Description
type X = object
v: ptr byte
proc f() =
var data = newSeq[byte](10)
var xxx = (ref X)(v: addr data[0])
echo repr(x[].v)In the above example, X contains a pointer that gets initialized to the memory owned by data - while unsafe, this technique allows low-level libraries to work around the lack of stable view types, such as in serialization where high-performance deserialization can be implemented without copying the source data.
The usage of ptr in this example looks like it should be safe because scope-wise, data outlives xxx and therefore one would assume that accessing v via the pointer is safe.
Not so, however. In the generated C code, the last access to data is addr data[0] which means that the C optimiser can reuse the stack memory of data for repr locals and thus the only source of "traced" pointers to data is gone.
repr then invokes the GC to allocate strings and depending on what was going on in the GC earlier, crashes and UB ensues.
There are two ways to address this problem:
- insert a "fake" reference to data at the end of the scope, to inform the C compiler of the lifetime - I believe this is what happens with orc/arc
- in refc, trace
ptrconservatively when it appears as a an object field or variable - this would mean followingX.vwhen doing tracing and noticing thatvis an internal pointer todataand thereforedatashould not be collected.
This latter approach already "more or less" happens for local variables because the stack is conservatively traced - extending this facility to ptr in fields would make the GC safer overall and the extra tracing should be cheap (compared to fake end-of-life references)
Current Output
Expected Output
Known Workarounds
No response
Additional Information
No response