Skip to content

Commit 8697549

Browse files
committed
fix heap-cell cleanup calling the wrong destructor
Summary ======= Fix `ref`s to objects calling the wrong destructor upon the refcount reaching zero in some narrow circumstances involving generic types and `sink` type modifiers. Details ======= Due to the sighash-based canonicalization used prior to type-bound op lifting ignoring `sink` modifiers, the same hook was lifted for both `ref Obj[proc(x: sink T)]` and `ref Obj[proc(x: T)]`, which led to the wrong destructor being called on cleanup when the object type is final. In a very narrow situation, where all of the following apply: * the `ref` type itself is generic * the object type is never used outside the `ref` type * the object type has no user-defined destructor, only an implicit one * the object type is non-final * the types reach the MIR phase in the wrong order the canonicalization led to the object not having its destructor called at all, due to the object's destructor not being lifted, thus potentially *leaking memory* if the object type requires destruction for some of its fields. Two types for which `sameType(a, b)` results in `false` should produce different sighashes, and so hashing is changed to not ignore `sink` types, fixing both issues. In addition, the scanning for types to lift the ops for in `sempass2` is fixed to handle generic ref types used as the constructor in object construction expressions, which makes the code work as intended.
1 parent e05448b commit 8697549

File tree

3 files changed

+22
-5
lines changed

3 files changed

+22
-5
lines changed

compiler/sem/sempass2.nim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,9 +1345,9 @@ proc track(tracked: PEffects, n: PNode) =
13451345
checkForSink(tracked.config, tracked.c.idgen, tracked.owner, x)
13461346
setLen(tracked.guards.s, oldFacts)
13471347
if tracked.owner.kind != skMacro:
1348-
# XXX n.typ can be nil in runnableExamples, we need to do something about it.
1349-
if n.typ != nil and n.typ.skipTypes(abstractInst).kind == tyRef:
1350-
createTypeBoundOps(tracked, n.typ.lastSon, n.info)
1348+
let skipped = n.typ.skipTypes(abstractInst)
1349+
if skipped.kind == tyRef:
1350+
createTypeBoundOps(tracked, skipped.lastSon, n.info)
13511351
createTypeBoundOps(tracked, n.typ, n.info)
13521352
of nkTupleConstr:
13531353
for i in 0..<n.len:

compiler/sem/sighashes.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
132132
c.hashType t[i], flags
133133
else:
134134
c.hashType t.lastSon, flags
135-
of tyAlias, tySink, tyUserTypeClasses, tyInferred:
135+
of tyAlias, tyUserTypeClasses, tyInferred:
136136
c.hashType t.lastSon, flags
137137
of tyBool, tyChar, tyInt..tyUInt64:
138138
# no canonicalization for integral types, so that e.g. ``pid_t`` is
@@ -179,7 +179,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
179179
c &= t.id
180180
if t.len > 0 and t[0] != nil:
181181
hashType c, t[0], flags
182-
of tyRef, tyPtr, tyGenericBody, tyVar:
182+
of tyRef, tyPtr, tyGenericBody, tyVar, tySink:
183183
c &= char(t.kind)
184184
c.hashType t.lastSon, flags
185185
of tyFromExpr:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
discard """
2+
description: '''
3+
Regression test for certain ref types calling the wrong destructor
4+
'''
5+
output: '''proc (x: int){.closure.}
6+
proc (x: sink int){.closure.}
7+
'''
8+
"""
9+
10+
type Object[T] = object
11+
x: T
12+
13+
proc `=destroy`[T](x: var Object[T]) =
14+
echo T
15+
16+
discard (ref Object[proc(x: sink int)])()
17+
discard (ref Object[proc(x: int)])()

0 commit comments

Comments
 (0)