summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semasgn.nim92
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--tests/destructor/tcustomstrings.nim5
4 files changed, 71 insertions, 32 deletions
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index 27626a1d0..19d31ec73 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -22,7 +22,8 @@ type
     recurse: bool
 
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
-proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
+proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
+              info: TLineInfo): PSym {.discardable.}
 
 proc at(a, i: PNode, elemType: PType): PNode =
   result = newNodeI(nkBracketExpr, a.info, 2)
@@ -97,9 +98,37 @@ proc newOpCall(op: PSym; x: PNode): PNode =
   result.add(newSymNode(op))
   result.add x
 
+proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
+  result = newNodeIT(nkCall, x.info, op.typ.sons[0])
+  result.add(newSymNode(op))
+  if newDestructors:
+    result.add genAddr(c, x)
+  else:
+    result.add x
+
 proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
   result = newAsgnStmt(x, newOpCall(op, y))
 
+proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
+                        field: PSym): bool =
+  if tfHasAsgn in t.flags:
+    var op: PSym
+    if sameType(t, c.asgnForType):
+      # generate recursive call:
+      if c.recurse:
+        op = c.fn
+      else:
+        c.recurse = true
+        return false
+    else:
+      op = field
+      if op == nil:
+        op = liftBody(c.c, t, c.kind, c.info)
+    markUsed(c.info, op, c.c.graph.usageSym)
+    styleCheckUse(c.info, op)
+    body.add newAsgnCall(c.c, op, x, y)
+    result = true
+
 proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   case c.kind
   of attachedDestructor:
@@ -107,26 +136,12 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
     if op != nil:
       markUsed(c.info, op, c.c.graph.usageSym)
       styleCheckUse(c.info, op)
-      body.add newOpCall(op, x)
-      result = true
-  of attachedAsgn, attachedSink:
-    if tfHasAsgn in t.flags:
-      var op: PSym
-      if sameType(t, c.asgnForType):
-        # generate recursive call:
-        if c.recurse:
-          op = c.fn
-        else:
-          c.recurse = true
-          return false
-      else:
-        op = t.assignment
-        if op == nil:
-          op = liftBody(c.c, t, c.info)
-      markUsed(c.info, op, c.c.graph.usageSym)
-      styleCheckUse(c.info, op)
-      body.add newAsgnCall(c.c, op, x, y)
+      body.add destructorCall(c.c, op, x)
       result = true
+  of attachedAsgn:
+    result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
+  of attachedSink:
+    result = considerAsgnOrSink(c, t, body, x, y, t.sink)
   of attachedDeepCopy:
     let op = t.deepCopy
     if op != nil:
@@ -245,12 +260,20 @@ proc addParam(procType: PType; param: PSym) =
   addSon(procType.n, newSymNode(param))
   rawAddSon(procType, param.typ)
 
-proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
+proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
+              info: TLineInfo): PSym {.discardable.} =
   var a: TLiftCtx
   a.info = info
   a.c = c
+  a.kind = kind
   let body = newNodeI(nkStmtList, info)
-  result = newSym(skProc, getIdent":lifted=", typ.owner, info)
+  let procname = case kind
+                 of attachedAsgn: getIdent":Asgn"
+                 of attachedSink: getIdent":Sink"
+                 of attachedDeepCopy: getIdent":DeepCopy"
+                 of attachedDestructor: getIdent":Destroy"
+
+  result = newSym(skProc, procname, typ.owner, info)
   a.fn = result
   a.asgnForType = typ
 
@@ -261,7 +284,16 @@ proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
 
   result.typ = newProcType(info, typ.owner)
   result.typ.addParam dest
-  result.typ.addParam src
+  if kind != attachedDestructor:
+    result.typ.addParam src
+
+  # recursion is handled explicitly, but register the type based operation
+  # here in order to keep things robust against runaway recursions:
+  case kind
+  of attachedAsgn: typ.assignment = result
+  of attachedSink: typ.sink = result
+  of attachedDeepCopy: typ.deepCopy = result
+  of attachedDestructor: typ.destructor = result
 
   liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
 
@@ -272,21 +304,18 @@ proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
   n.sons[bodyPos] = body
   result.ast = n
 
-  # register late as recursion is handled differently
-  typ.assignment = result
-  #echo "Produced this ", n
 
 proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
   let t = typ.skipTypes({tyGenericInst, tyVar, tyAlias})
   result = t.assignment
   if result.isNil:
-    result = liftBody(c, t, info)
+    result = liftBody(c, t, attachedAsgn, info)
 
 proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
   let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
   result = newAsgnCall(c, a, dest, src)
 
-proc liftTypeBoundOps*(c: PContext; typ: PType) =
+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.
   ## Since this is done in the sem* routines generics already have
@@ -295,3 +324,10 @@ proc liftTypeBoundOps*(c: PContext; typ: PType) =
   ## in the generic instantiations though.
   ## The later 'destroyer' pass depends on it.
   if not newDestructors or not hasDestructor(typ): return
+  # 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)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 465b09814..49dfbe8f4 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -670,7 +670,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
     analyseIfAddressTakenInCall(c, result)
     if callee.magic != mNone:
       result = magicsAfterOverloadResolution(c, result, flags)
-    if result.typ != nil: liftTypeBoundOps(c, result.typ)
+    if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info)
   if c.matchedConcept == nil:
     result = evalAtCompileTime(c, result)
 
@@ -1392,7 +1392,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
           mode != noOverloadedAsgn:
         return overloadedAsgn(c, lhs, n.sons[1])
     else:
-      liftTypeBoundOps(c, lhs.typ)
+      liftTypeBoundOps(c, lhs.typ, lhs.info)
 
     fixAbstractType(c, n)
     asgnToResultVar(c, n, n.sons[0], n.sons[1])
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 3c26be89e..d6c2a9ea1 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -553,7 +553,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
     # this can only happen for errornous var statements:
     if typ == nil: continue
     typeAllowedCheck(a.info, typ, symkind)
-    liftTypeBoundOps(c, typ)
+    liftTypeBoundOps(c, typ, a.info)
     var tup = skipTypes(typ, {tyGenericInst, tyAlias})
     if a.kind == nkVarTuple:
       if tup.kind != tyTuple:
diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim
index 2250d4772..780b0d2f1 100644
--- a/tests/destructor/tcustomstrings.nim
+++ b/tests/destructor/tcustomstrings.nim
@@ -4,7 +4,7 @@ foo bar to appendmore here
 foo bar to appendmore here
 foo bar to appendmore here
 foo bar to appendmore here
-after 16 16'''
+after 20 20'''
   cmd: '''nim c --newruntime $file'''
 """
 
@@ -92,5 +92,8 @@ proc main(n: int) =
     a.add c
     echo cstring(a.data)
 
+  var x: array[4, mystring]
+  for i in 0..high(x): x[i] = create"added to array"
+
 main(1000)
 echo "after ", allocCount, " ", deallocCount