summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md1
-rw-r--r--compiler/liftdestructors.nim8
-rw-r--r--compiler/nim.cfg1
-rw-r--r--lib/system.nim23
-rw-r--r--tests/arc/thard_alignment.nim4
-rw-r--r--tests/arc/topt_no_cursor.nim2
-rw-r--r--tests/ccgbugs/tforward_decl_only.nim3
-rw-r--r--tests/config.nims1
-rw-r--r--tests/converter/tconverter_unique_ptr.nim15
-rw-r--r--tests/destructor/const_smart_ptr.nim10
-rw-r--r--tests/destructor/tnonvardestructor.nim31
-rw-r--r--tests/destructor/tv2_cast.nim12
-rw-r--r--tests/gc/closureleak.nim14
13 files changed, 84 insertions, 41 deletions
diff --git a/changelog.md b/changelog.md
index 3351a6f7a..ab121f5a1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -5,6 +5,7 @@
 
 - `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
 - The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
+- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
 
 ## Standard library additions and changes
 
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 8995e1073..36d9d5b1a 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -1082,7 +1082,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
   incl result.flags, sfGeneratedOp
 
 proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
-              info: TLineInfo; idgen: IdGenerator): PSym =
+              info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
   if kind == attachedDup:
     return symDupPrototype(g, typ, owner, kind, info, idgen)
 
@@ -1092,7 +1092,8 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
   let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
                    idgen, result, info)
 
-  if kind == attachedDestructor and typ.kind in {tyRef, tyString, tySequence} and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+  if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
+     ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or typ.kind in {tyRef, tyString, tySequence}):
     dest.typ = typ
   else:
     dest.typ = makeVarType(typ.owner, typ, idgen)
@@ -1185,7 +1186,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
 proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
                                         info: TLineInfo; idgen: IdGenerator): PSym =
   assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
-  result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen)
+  # discrimantor assignments needs pointers to destroy fields; alas, we cannot use non-var destructor here
+  result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen, isDiscriminant = true)
   var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen,
                    fn: result)
   a.asgnForType = typ
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index e4425065e..8488cb9ef 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -9,6 +9,7 @@ define:nimPreviewSlimSystem
 define:nimPreviewCstringConversion
 define:nimPreviewProcConversion
 define:nimPreviewRangeDefault
+define:nimPreviewNonVarDestructor
 threads:off
 
 #import:"$projectpath/testability"
diff --git a/lib/system.nim b/lib/system.nim
index 4f2a05163..5d0bbb9c7 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -366,19 +366,24 @@ proc arrPut[I: Ordinal;T,S](a: T; i: I;
 const arcLikeMem = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc)
 
 
-when defined(nimAllowNonVarDestructor) and arcLikeMem:
-  proc `=destroy`*(x: string) {.inline, magic: "Destroy".} =
+when defined(nimAllowNonVarDestructor) and arcLikeMem and defined(nimPreviewNonVarDestructor):
+  proc `=destroy`*[T](x: T) {.inline, magic: "Destroy".} =
+    ## Generic `destructor`:idx: implementation that can be overridden.
     discard
-
-  proc `=destroy`*[T](x: seq[T]) {.inline, magic: "Destroy".} =
+else:
+  proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} =
+    ## Generic `destructor`:idx: implementation that can be overridden.
     discard
 
-  proc `=destroy`*[T](x: ref T) {.inline, magic: "Destroy".} =
-    discard
+  when defined(nimAllowNonVarDestructor) and arcLikeMem:
+    proc `=destroy`*(x: string) {.inline, magic: "Destroy".} =
+      discard
 
-proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} =
-  ## Generic `destructor`:idx: implementation that can be overridden.
-  discard
+    proc `=destroy`*[T](x: seq[T]) {.inline, magic: "Destroy".} =
+      discard
+
+    proc `=destroy`*[T](x: ref T) {.inline, magic: "Destroy".} =
+      discard
 
 when defined(nimHasDup):
   proc `=dup`*[T](x: T): T {.inline, magic: "Dup".} =
diff --git a/tests/arc/thard_alignment.nim b/tests/arc/thard_alignment.nim
index baa964c77..30cfddb05 100644
--- a/tests/arc/thard_alignment.nim
+++ b/tests/arc/thard_alignment.nim
@@ -1,9 +1,11 @@
 discard """
 disabled: "arm64"
-cmd: "nim c --gc:arc $file"
+cmd: "nim c --mm:arc -u:nimPreviewNonVarDestructor $file"
 output: "y"
 """
 
+# TODO: fixme: investigate why it failed with non-var destructors
+
 {.passC: "-march=native".}
 
 proc isAlignedCheck(p: pointer, alignment: int) = 
diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim
index b24e8e5a9..0a4984a69 100644
--- a/tests/arc/topt_no_cursor.nim
+++ b/tests/arc/topt_no_cursor.nim
@@ -97,7 +97,7 @@ try:
         finally:
           `=destroy`(splitted)
 finally:
-  `=destroy`(lan_ip)
+  `=destroy_1`(lan_ip)
 -- end of expandArc ------------------------
 --expandArc: mergeShadowScope
 
diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim
index d62c36b18..b115dcbe7 100644
--- a/tests/ccgbugs/tforward_decl_only.nim
+++ b/tests/ccgbugs/tforward_decl_only.nim
@@ -1,6 +1,5 @@
 discard """
-ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')"
-ccodecheck: "\\i !@('mymoduleInit')"
+ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' _')"
 output: "hello"
 """
 
diff --git a/tests/config.nims b/tests/config.nims
index b288bec2a..e0de65fc4 100644
--- a/tests/config.nims
+++ b/tests/config.nims
@@ -40,6 +40,7 @@ switch("define", "nimPreviewFloatRoundtrip")
 switch("define", "nimPreviewJsonutilsHoleyEnum")
 switch("define", "nimPreviewHashRef")
 switch("define", "nimPreviewRangeDefault")
+switch("define", "nimPreviewNonVarDestructor")
 
 switch("warningAserror", "UnnamedBreak")
 switch("legacy", "verboseTypeMismatch")
diff --git a/tests/converter/tconverter_unique_ptr.nim b/tests/converter/tconverter_unique_ptr.nim
index 23c1a3d96..6902f9e9e 100644
--- a/tests/converter/tconverter_unique_ptr.nim
+++ b/tests/converter/tconverter_unique_ptr.nim
@@ -22,12 +22,11 @@ proc `$`(x: MyLen): string {.borrow.}
 proc `==`(x1, x2: MyLen): bool {.borrow.}
 
 
-proc `=destroy`*(m: var MySeq) {.inline.} =
+proc `=destroy`*(m: MySeq) {.inline.} =
   if m.data != nil:
     deallocShared(m.data)
-    m.data = nil
 
-proc `=`*(m: var MySeq, m2: MySeq) =
+proc `=copy`*(m: var MySeq, m2: MySeq) =
   if m.data == m2.data: return
   if m.data != nil:
     `=destroy`(m)
@@ -77,13 +76,12 @@ converter literalToLen*(x: int{lit}): MyLen =
 # Unique pointer implementation
 #-------------------------------------------------------------
 
-proc `=destroy`*[T](p: var UniquePtr[T]) =
+proc `=destroy`*[T](p: UniquePtr[T]) =
   if p.val != nil:
     `=destroy`(p.val[])
     dealloc(p.val)
-    p.val = nil
 
-proc `=`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.}
+proc `=copy`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.}
 
 proc `=sink`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.inline.} =
   if dest.val != nil and dest.val != src.val:
@@ -118,13 +116,12 @@ type
     ## as it returns only `lent T`
     val: ptr T
 
-proc `=destroy`*[T](p: var ConstPtr[T]) =
+proc `=destroy`*[T](p: ConstPtr[T]) =
   if p.val != nil:
     `=destroy`(p.val[])
     dealloc(p.val)
-    p.val = nil
 
-proc `=`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
+proc `=copy`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
 
 proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} =
   if dest.val != nil and dest.val != src.val:
diff --git a/tests/destructor/const_smart_ptr.nim b/tests/destructor/const_smart_ptr.nim
index 4d8c7c9a3..25dd46500 100644
--- a/tests/destructor/const_smart_ptr.nim
+++ b/tests/destructor/const_smart_ptr.nim
@@ -2,13 +2,12 @@ type
   ConstPtr*[T] = object
     val: ptr T
 
-proc `=destroy`*[T](p: var ConstPtr[T]) =
+proc `=destroy`*[T](p: ConstPtr[T]) =
   if p.val != nil:
     `=destroy`(p.val[])
     dealloc(p.val)
-    p.val = nil
 
-proc `=`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
+proc `=copy`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
 
 proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} =
   if dest.val != nil and dest.val != src.val:
@@ -31,12 +30,11 @@ type
     len: int
     data: ptr UncheckedArray[float]
 
-proc `=destroy`*(m: var MySeqNonCopyable) {.inline.} =
+proc `=destroy`*(m: MySeqNonCopyable) {.inline.} =
   if m.data != nil:
     deallocShared(m.data)
-    m.data = nil
 
-proc `=`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.}
+proc `=copy`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.}
 
 proc `=sink`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.inline.} =
   if m.data != m2.data:
diff --git a/tests/destructor/tnonvardestructor.nim b/tests/destructor/tnonvardestructor.nim
index 1f59a3978..1b4413790 100644
--- a/tests/destructor/tnonvardestructor.nim
+++ b/tests/destructor/tnonvardestructor.nim
@@ -152,7 +152,7 @@ type
   Graph = object

     nodes: seq[Node]

 

-proc `=destroy`(x: var NodeObj) =

+proc `=destroy`(x: NodeObj) =

   `=destroy`(x.neighbors)

   `=destroy`(x.label)

 

@@ -210,7 +210,7 @@ block: # bug #22197
   ## If this does not exist, it also works!

   proc newFileID(): FileID = FileID(H5Id())

 

-  proc `=destroy`(grp: var H5GroupObj) =

+  proc `=destroy`(grp: H5GroupObj) =

     ## Closes the group and resets all references to nil.

     if cast[pointer](grp.fileId) != nil:

       `=destroy`(grp.file_id)

@@ -218,3 +218,30 @@ block: # bug #22197
   var grp = H5Group()

   reset(grp.file_id)

   reset(grp)

+

+import std/tables

+

+block: # bug #22286

+  type

+    A = object

+    B = object

+      a: A

+    C = object

+      b: B

+

+  proc `=destroy`(self: A) =

+    echo "destroyed"

+

+  proc `=destroy`(self: C) =

+    `=destroy`(self.b)

+

+  var c = C()

+

+block: # https://forum.nim-lang.org/t/10642

+  type AObj = object

+    name: string

+    tableField: Table[string, string]

+

+  proc `=destroy`(x: AObj) =

+    `=destroy`(x.name)

+    `=destroy`(x.tableField)

diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim
index 6fcb5994c..4ff2dc9a0 100644
--- a/tests/destructor/tv2_cast.nim
+++ b/tests/destructor/tv2_cast.nim
@@ -20,8 +20,8 @@ data =
     :tmpD_2))
   :tmpD
 `=destroy`(:tmpD_2)
-`=destroy`(:tmpD_1)
-`=destroy`(data)
+`=destroy_1`(:tmpD_1)
+`=destroy_1`(data)
 -- end of expandArc ------------------------
 --expandArc: main1
 
@@ -37,8 +37,8 @@ data =
     :tmpD_1))
   :tmpD
 `=destroy`(:tmpD_1)
-`=destroy`(data)
-`=destroy`(s)
+`=destroy_1`(data)
+`=destroy_1`(s)
 -- end of expandArc ------------------------
 --expandArc: main2
 
@@ -54,7 +54,7 @@ data =
     :tmpD_1))
   :tmpD
 `=destroy`(:tmpD_1)
-`=destroy`(data)
+`=destroy_1`(data)
 `=destroy`(s)
 -- end of expandArc ------------------------
 --expandArc: main3
@@ -73,7 +73,7 @@ data =
   :tmpD
 `=destroy`(:tmpD_2)
 `=destroy`(:tmpD_1)
-`=destroy`(data)
+`=destroy_1`(data)
 -- end of expandArc ------------------------
 '''
 """
diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim
index 0265431d0..e67beb513 100644
--- a/tests/gc/closureleak.nim
+++ b/tests/gc/closureleak.nim
@@ -11,9 +11,19 @@ var foo_counter = 0
 var alive_foos = newseq[int](0)
 
 when defined(gcDestructors):
-  proc `=destroy`(some: var TFoo) =
+  proc `=destroy`(some: TFoo) =
     alive_foos.del alive_foos.find(some.id)
-    `=destroy`(some.fn)
+    # TODO: fixme: investigate why `=destroy` requires `some.fn` to be `gcsafe`
+    # the debugging info below came from `symPrototype` in the liftdestructors
+    # proc (){.closure, gcsafe.}, {tfThread, tfHasAsgn, tfCheckedForDestructor, tfExplicitCallConv}
+    # var proc (){.closure, gcsafe.}, {tfHasGCedMem}
+    # it worked by accident with var T destructors because in the sempass2
+    #
+    # let argtype = skipTypes(a.typ, abstractInst) # !!! it does't skip `tyVar`
+    # if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
+    #   localError(tracked.config, n.info, $n & " is not GC safe")
+    {.cast(gcsafe).}:
+      `=destroy`(some.fn)
 
 else:
   proc free*(some: ref TFoo) =