Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions compiler/ccgreset.nim
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
cSizeof(getTypeDesc(p.module, typ)))
else:
specializeResetN(p, accessor, typ.n, typ)
if isCaseObj(typ.n):
# The active branch was released above. Clear the complete object so
# stale bytes from overlapping branches cannot be traced by the GC.
# type
# Foo = object
# case kind: bool
# of true:
# a: ref Bar # 8 bytes (pointer)
# of false:
# b: int # 4 bytes
# specializeResetT for b emits accessor.b = 0 — writes 4 bytes
# But the union is 8 bytes wide (sized by the largest branch)
# The remaining 4 bytes where a used to live are untouched
# Those stale bytes could contain a heap pointer the GC traces → crash
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
cCast(CPointer, cAddr(accessor)),
cSizeof(getTypeDesc(p.module, typ)))
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:
Expand Down
56 changes: 56 additions & 0 deletions tests/gc/tmove_case_object.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
discard """
matrix: "--mm:refc; --mm:orc"
"""

type
A = object of RootObj

V = object
case g: bool
of true:
v: A
of false:
e: string

var r = V(g: true, v: A())
discard move r
GC_fullCollect()

type
Kind = enum nested, other
Nested = object
case kind: Kind
of nested:
case enabled: bool
of true: payload: A
of false: message: string
of other:
discard

var n = Nested(kind: nested, enabled: true, payload: A())
discard move n
GC_fullCollect()

# Moving from the other branch must keep its value alive and leave the source
# in the default state.
var s = V(g: false, e: "hello")
let moved = move s
doAssert moved.e == "hello"
doAssert not s.g
doAssert s.e.len == 0

# Reinitializing the zeroed value must also restore embedded object type
# headers.
type W = object
a: A
value: V
text: string

var w = W(a: A(), value: V(g: true, v: A()), text: "content")
let movedW = move w
doAssert movedW.text == "content"
doAssert cast[ptr pointer](addr w.a)[] != nil
doAssert not w.value.g
doAssert w.value.e.len == 0
doAssert w.text.len == 0
GC_fullCollect()
Loading