summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/destroyer.nim26
-rw-r--r--compiler/sem.nim20
-rw-r--r--compiler/semasgn.nim46
-rw-r--r--compiler/semexprs.nim13
-rw-r--r--compiler/semmagic.nim11
-rw-r--r--compiler/transf.nim3
-rw-r--r--lib/system.nim4
-rw-r--r--tests/destructor/topttree.nim (renamed from tests/destructor/tbintree.nim)13
8 files changed, 109 insertions, 27 deletions
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index fde592f52..a868a64ba 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -93,7 +93,7 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  strutils, options, dfa, lowerings
+  strutils, options, dfa, lowerings, rodread
 
 const
   InterestingSyms = {skVar, skResult, skLet}
@@ -164,20 +164,44 @@ proc isHarmlessVar*(s: PSym; c: Con): bool =
 template interestingSym(s: PSym): bool =
   s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ)
 
+proc patchHead(n: PNode; alreadyPatched: var IntSet) =
+  if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
+    let s = n[0].sym
+    if sfFromGeneric in s.flags or true:
+      if s.name.s[0] == '=' and not containsOrIncl(alreadyPatched, s.id):
+        patchHead(s.getBody, alreadyPatched)
+      let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
+      template patch(op, field) =
+        if s.name.s == op and field != nil: #and field != s:
+          n.sons[0].sym = field
+      patch "=sink", t.sink
+      patch "=", t.assignment
+      patch "=destroy", t.destructor
+  for x in n:
+    patchHead(x, alreadyPatched)
+
+template patchHead(n: PNode) =
+  when false:
+    var alreadyPatched = initIntSet()
+    patchHead(n, alreadyPatched)
+
 proc genSink(t: PType; dest: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias})
   let op = if t.sink != nil: t.sink else: t.assignment
   assert op != nil
+  patchHead op.ast[bodyPos]
   result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
 
 proc genCopy(t: PType; dest: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias})
   assert t.assignment != nil
+  patchHead t.assignment.ast[bodyPos]
   result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest))
 
 proc genDestroy(t: PType; dest: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias})
   assert t.destructor != nil
+  patchHead t.destructor.ast[bodyPos]
   result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest))
 
 proc addTopVar(c: var Con; v: PNode) =
diff --git a/compiler/sem.nim b/compiler/sem.nim
index ebfdafea7..5e98d7a65 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -522,14 +522,18 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
   else:
     result = n
   result = semStmt(c, result)
-  # BUGFIX: process newly generated generics here, not at the end!
-  if c.lastGenericIdx < c.generics.len:
-    var a = newNodeI(nkStmtList, n.info)
-    addCodeForGenerics(c, a)
-    if sonsLen(a) > 0:
-      # a generic has been added to `a`:
-      if result.kind != nkEmpty: addSon(a, result)
-      result = a
+  when false:
+    # Code generators are lazy now and can deal with undeclared procs, so these
+    # steps are not required anymore and actually harmful for the upcoming
+    # destructor support.
+    # BUGFIX: process newly generated generics here, not at the end!
+    if c.lastGenericIdx < c.generics.len:
+      var a = newNodeI(nkStmtList, n.info)
+      addCodeForGenerics(c, a)
+      if sonsLen(a) > 0:
+        # a generic has been added to `a`:
+        if result.kind != nkEmpty: addSon(a, result)
+        result = a
   result = hloStmt(c, result)
   if gCmd == cmdInteractive and not isEmptyType(result.typ):
     result = buildEchoStmt(c, result)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index 895946281..0454ea379 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -268,10 +268,10 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   a.kind = kind
   let body = newNodeI(nkStmtList, info)
   let procname = case kind
-                 of attachedAsgn: getIdent":Asgn"
-                 of attachedSink: getIdent":Sink"
-                 of attachedDeepCopy: getIdent":DeepCopy"
-                 of attachedDestructor: getIdent":Destroy"
+                 of attachedAsgn: getIdent"="
+                 of attachedSink: getIdent"=sink"
+                 of attachedDeepCopy: getIdent"=deepcopy"
+                 of attachedDestructor: getIdent"=destroy"
 
   result = newSym(skProc, procname, typ.owner, info)
   a.fn = result
@@ -314,6 +314,24 @@ proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
   let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
   result = newAsgnCall(c, a, dest, src)
 
+proc patchHead(n: PNode; alreadyPatched: var IntSet) =
+  when true:
+    if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
+      let s = n[0].sym
+      if sfFromGeneric in s.flags:
+        if not containsOrIncl(alreadyPatched, s.id):
+          patchHead(s.getBody, alreadyPatched)
+        let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
+        template patch(op, field) =
+          if s.name.s == op and field != nil and field != s:
+            n.sons[0].sym = field
+
+        patch "=sink", t.sink
+        patch "=", t.assignment
+        patch "=destroy", t.destructor
+    for x in n:
+      patchHead(x, alreadyPatched)
+
 proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
@@ -321,6 +339,20 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   if not newDestructors or not hasDestructor(typ): return
   let typ = typ.skipTypes({tyGenericInst, tyAlias})
   # we generate the destructor first so that other operators can depend on it:
-  if typ.destructor == nil: liftBody(c, typ, attachedDestructor, info)
-  if typ.assignment == nil: liftBody(c, typ, attachedAsgn, info)
-  if typ.sink == nil: liftBody(c, typ, attachedSink, info)
+  var changed = false
+  if typ.destructor != nil and typ.destructor.magic == mAsgn:
+    typ.destructor = nil
+  if typ.destructor == nil:
+    liftBody(c, typ, attachedDestructor, info)
+    changed = true
+  if typ.assignment == nil:
+    liftBody(c, typ, attachedAsgn, info)
+    changed = true
+  if typ.sink == nil:
+    liftBody(c, typ, attachedSink, info)
+    changed = true
+  if changed:
+    var alreadyPatched = initIntSet()
+    patchHead(typ.destructor.getBody, alreadyPatched)
+    patchHead(typ.assignment.getBody, alreadyPatched)
+    patchHead(typ.sink.getBody, alreadyPatched)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 49dfbe8f4..2c80db52c 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -664,6 +664,19 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
   of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
   of skTemplate: result = semTemplateExpr(c, result, callee, flags)
   else:
+    when false:
+      if callee.name.s[0] == '=' and result.len > 1:
+        # careful, do not skip tyDistinct here:
+        let t = result[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
+
+        proc patchHead(callee: PSym; name: string; field: PSym; result: PNode) =
+          if callee.name.s == name and field != nil:
+            result.sons[0].sym = field
+
+        patchHead(callee, "=destroy", t.destructor, result)
+        patchHead(callee, "=sink", t.sink, result)
+        patchHead(callee, "=", t.assignment, result)
+
     semFinishOperands(c, result)
     activate(c, result)
     fixAbstractType(c, result)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 88f1262ae..ba19e0865 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -147,8 +147,9 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
   of "stripGenericParams":
     result = uninstantiate(operand).toNode(traitCall.info)
   of "supportsCopyMem":
-    let complexObj = containsGarbageCollectedRef(operand) or
-                     hasDestructor(operand)
+    let t = operand.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
+    let complexObj = containsGarbageCollectedRef(t) or
+                     hasDestructor(t)
     result = newIntNodeT(ord(not complexObj), traitCall)
   else:
     localError(traitCall.info, "unknown trait")
@@ -251,7 +252,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     result = semTypeOf(c, n.sons[1])
   of mArrGet: result = semArrGet(c, n, flags)
   of mArrPut: result = semArrPut(c, n, flags)
-  of mAsgn: result = semAsgnOpr(c, n)
+  of mAsgn:
+    if n[0].sym.name.s == "=":
+      result = semAsgnOpr(c, n)
+    else:
+      result = n
   of mIsPartOf: result = semIsPartOf(c, n, flags)
   of mTypeTrait: result = semTypeTraits(c, n)
   of mAstToStr:
diff --git a/compiler/transf.nim b/compiler/transf.nim
index c3d12dafe..b7383c9b8 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -365,16 +365,19 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
       n.sons[0].sons[0] = m.sons[0]
       result = PTransNode(n.sons[0])
+      PNode(result).typ = n.typ
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     var m = n.sons[0].sons[1]
     if m.kind == a or m.kind == b:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
       n.sons[0].sons[1] = m.sons[0]
       result = PTransNode(n.sons[0])
+      PNode(result).typ = n.typ
   else:
     if n.sons[0].kind == a or n.sons[0].kind == b:
       # addr ( deref ( x )) --> x
       result = PTransNode(n.sons[0].sons[0])
+      PNode(result).typ = n.typ
 
 proc generateThunk(prc: PNode, dest: PType): PNode =
   ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
diff --git a/lib/system.nim b/lib/system.nim
index 10560edaa..585c32556 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -300,10 +300,10 @@ when defined(nimArrIdx):
     x: S) {.noSideEffect, magic: "ArrPut".}
   proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".}
   when defined(nimNewRuntime):
-    proc `=destroy`*[T](x: var T) {.inline.} =
+    proc `=destroy`*[T](x: var T) {.inline, magic: "Asgn".} =
       ## generic `destructor`:idx: implementation that can be overriden.
       discard
-    proc `=sink`*[T](x: var T; y: T) {.inline.} =
+    proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} =
       ## generic `sink`:idx: implementation that can be overriden.
       shallowCopy(x, y)
 
diff --git a/tests/destructor/tbintree.nim b/tests/destructor/topttree.nim
index 12ba052cf..a6380a2d5 100644
--- a/tests/destructor/tbintree.nim
+++ b/tests/destructor/topttree.nim
@@ -19,7 +19,7 @@ var
 proc `=destroy`*[T](x: var opt[T]) =
   if x.data != nil:
     when not supportsCopyMem(T):
-      `=destroy`(x.data)
+      `=destroy`(x.data[])
     dealloc(x.data)
     inc deallocCount
     x.data = nil
@@ -69,11 +69,12 @@ proc createTree(data: float): Tree =
   result.data = data
 
 proc insert(t: var opt[Tree]; newVal: float) =
-  if it ?= t:
-    if it.data > newVal:
-      insert(it.le, newVal)
-    elif it.data < newVal:
-      insert(it.ri, newVal)
+  #if it ?= t:
+  if t.data != nil:
+    if newVal < t.data[].data:
+      insert(t.data[].le, newVal)
+    elif t.data[].data < newVal:
+      insert(t.data[].ri, newVal)
     else:
       discard "already in the tree"
   else: