Destructor lifting fixes #11149 (#11163)

* fixes #11149

* add test
This commit is contained in:
cooldome 2019-05-04 22:34:37 +02:00 • committed by Andreas Rumpf
commit d3db189eb4
4 changed files with 77 additions and 10 deletions

View file

@ -325,7 +325,7 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
if op == nil: if op == nil:
# give up and find the canonical type instead: # give up and find the canonical type instead:
let h = sighashes.hashType(t, {CoType, CoConsiderOwned}) let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
let canon = c.graph.canonTypes.getOrDefault(h) let canon = c.graph.canonTypes.getOrDefault(h)
if canon != nil: if canon != nil:
op = canon.attachedOps[kind] op = canon.attachedOps[kind]

View file

@ -593,12 +593,13 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.skipTypes({tyAlias}).flags != {}: return if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.skipTypes({tyAlias}).flags != {}: return
incl orig.flags, tfCheckedForDestructor incl orig.flags, tfCheckedForDestructor
let h = sighashes.hashType(orig, {CoType, CoConsiderOwned}) let h = sighashes.hashType(orig, {CoType, CoConsiderOwned, CoDistinct})
var canon = c.graph.canonTypes.getOrDefault(h) var canon = c.graph.canonTypes.getOrDefault(h)
var overwrite = false var overwrite = false
if canon == nil: if canon == nil:
c.graph.canonTypes[h] = orig let typ = orig.skipTypes({tyGenericInst, tyAlias})
canon = orig c.graph.canonTypes[h] = typ
canon = typ
elif canon != orig: elif canon != orig:
overwrite = true overwrite = true
@ -608,17 +609,17 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
# 3. we have a lifted destructor. # 3. we have a lifted destructor.
# 4. We have a custom destructor. # 4. We have a custom destructor.
# 5. We have a (custom) generic destructor. # 5. We have a (custom) generic destructor.
let typ = canon.skipTypes({tyGenericInst, tyAlias})
# we generate the destructor first so that other operators can depend on it: # we generate the destructor first so that other operators can depend on it:
for k in attachedDestructor..attachedSink: for k in attachedDestructor..attachedSink:
if typ.attachedOps[k] == nil: if canon.attachedOps[k] == nil:
discard produceSym(c, typ, k, info) discard produceSym(c, canon, k, info)
else: else:
inst(typ.attachedOps[k], typ) inst(canon.attachedOps[k], canon)
if overwrite: if overwrite:
for k in attachedDestructor..attachedSink: for k in attachedDestructor..attachedSink:
orig.attachedOps[k] = typ.attachedOps[k] orig.attachedOps[k] = canon.attachedOps[k]
if not isTrival(orig.destructor): if not isTrival(orig.destructor):
#or not isTrival(orig.assignment) or #or not isTrival(orig.assignment) or

View file

@ -36,6 +36,7 @@ type
CoOwnerSig CoOwnerSig
CoIgnoreRange CoIgnoreRange
CoConsiderOwned CoConsiderOwned
CoDistinct
proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag])
@ -97,7 +98,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
c.hashType t.sons[i], flags c.hashType t.sons[i], flags
of tyDistinct: of tyDistinct:
if {CoType, CoConsiderOwned} * flags == {CoType} or t.sym == nil: if CoDistinct in flags:
if t.sym != nil: c.hashSym(t.sym)
if t.sym == nil or tfFromGeneric in t.flags:
c.hashType t.lastSon, flags
elif CoType in flags or t.sym == nil:
c.hashType t.lastSon, flags c.hashType t.lastSon, flags
else: else:
c.hashSym(t.sym) c.hashSym(t.sym)

61
tests/destructor/topt.nim Normal file
View file

@ -0,0 +1,61 @@
discard """
output: '''5
vseq destroy
'''
"""
type
opt*[T] = object
case exists: bool
of true: val: T
of false: discard
proc some*[T](val: sink T): opt[T] {.inline.} =
## Returns an ``opt`` that has the value.
## nil is considered as none for reference types
result.exists = true
result.val = val
proc none*(T: typedesc): opt[T] {.inline.} =
## Returns an ``opt`` for this type that has no value.
# the default is the none type
discard
proc none*[T]: opt[T] {.inline.} =
## Alias for ``none(T)``.
none(T)
proc unsafeGet*[T](self: opt[T]): lent T {.inline.} =
## Returns the value of a ``some``. Behavior is undefined for ``none``.
self.val
type
VSeq*[T] = object
len: int
data: ptr UncheckedArray[T]
proc `=destroy`*[T](m: var VSeq[T]) {.inline.} =
if m.data != nil:
echo "vseq destroy"
dealloc(m.data)
m.data = nil
proc `=`*[T](m: var VSeq[T], m2: VSeq[T]) {.error.}
proc `=sink`*[T](m: var VSeq[T], m2: VSeq[T]) {.inline.} =
if m.data != m2.data:
`=destroy`(m)
m.len = m2.len
m.data = m2.data
proc newVSeq*[T](len: int): VSeq[T] =
## Only support sequence creation from scalar size because creation from
## vetorized size can't reproduce the original scalar size
result.len = len
if len > 0:
result.data = cast[ptr UncheckedArray[T]](alloc(sizeof(T) * len))
let x = some newVSeq[float](5)
echo x.unsafeGet.len
let y = none(VSeq[float])