summary refs log tree commit diff stats
path: root/compiler/liftdestructors.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/liftdestructors.nim')
-rw-r--r--compiler/liftdestructors.nim253
1 files changed, 193 insertions, 60 deletions
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 6b36dd6fb..aea244a78 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -113,9 +113,13 @@ proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
 proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
   result = newAsgnStmt(x, newOpCall(op, y))
 
+proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
+  result = optNimV2 in c.graph.config.globalOptions and
+    (tfHasGCedMem in t.flags or t.isGCedMem)
+
 proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
                         field: PSym): bool =
-  if tfHasAsgn in t.flags:
+  if tfHasAsgn in t.flags or useNoGc(c, t):
     var op: PSym
     if sameType(t, c.asgnForType):
       # generate recursive call:
@@ -136,15 +140,28 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     body.add newAsgnCall(c.graph, op, x, y)
     result = true
 
-proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
-  case c.kind
-  of attachedDestructor:
-    let op = t.destructor
-    if op != nil:
+proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
+  let op = t.destructor
+  if op != nil:
+    markUsed(c.graph.config, c.info, op, c.graph.usageSym)
+    onUse(c.info, op)
+    body.add destructorCall(c.graph, op, x)
+    result = true
+  elif useNoGc(c, t):
+    if sameType(t, c.asgnForType) and c.kind == attachedDestructor:
+      let op = c.fn
       markUsed(c.graph.config, c.info, op, c.graph.usageSym)
       onUse(c.info, op)
       body.add destructorCall(c.graph, op, x)
       result = true
+    else:
+      internalError(c.graph.config, c.info,
+        "type-bound operator could not be resolved")
+
+proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
+  case c.kind
+  of attachedDestructor:
+    result = addDestructorCall(c, t, body, x)
   of attachedAsgn:
     result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
   of attachedSink:
@@ -185,12 +202,15 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
 
 proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
   result = newNodeI(nkWhileStmt, c.info, 2)
-  let cmp = genBuiltin(c.graph, mLeI, "<=", i)
-  cmp.add genHigh(c.graph, dest)
+  let cmp = genBuiltin(c.graph, mLtI, "<", i)
+  cmp.add genLen(c.graph, dest)
   cmp.typ = getSysType(c.graph, c.info, tyBool)
   result.sons[0] = cmp
   result.sons[1] = newNodeI(nkStmtList, c.info)
 
+proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
+  result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
+
 proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
   let incCall = genBuiltin(c.graph, mInc, "inc", i)
   incCall.add lowerings.newIntLit(c.graph, c.info, 1)
@@ -203,42 +223,166 @@ proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
   lenCall.typ = getSysType(g, x.info, tyInt)
   result.add lenCall
 
+proc setLenCall(g: ModuleGraph; x, y: PNode): PNode =
+  let lenCall = genBuiltin(g, mLengthSeq, "len", y)
+  lenCall.typ = getSysType(g, x.info, tyInt)
+  result = genBuiltin(g, mSetLengthSeq, "setLen", genAddr(g, x))
+  result.add lenCall
+
+proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  let i = declareCounter(c, body, firstOrd(c.graph.config, t))
+  let whileLoop = genWhileLoop(c, i, x)
+  let elemType = t.lastSon
+  liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
+                                              y.at(i, elemType))
+  addIncStmt(c, whileLoop.sons[1], i)
+  body.add whileLoop
+
+proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case c.kind
+  of attachedAsgn, attachedDeepCopy:
+    # we generate:
+    # setLen(dest, y.len)
+    # var i = 0
+    # while i < y.len: dest[i] = y[i]; inc(i)
+    # This is usually more efficient than a destroy/create pair.
+    body.add setLenCall(c.graph, x, y)
+    forallElements(c, t, body, x, y)
+  of attachedSink:
+    let moveCall = genBuiltin(c.graph, mMove, "move", x)
+    moveCall.add y
+    doAssert t.destructor != nil
+    moveCall.add destructorCall(c.graph, t.destructor, x)
+    body.add moveCall
+    when false:
+      # we generate:
+      #  if a.len != 0 and a.p != b.p:
+      #    `=destroy`(x)
+      #  a.len = b.len
+      #  a.p = b.p
+      # Note: '@' is either '.' or '->'.
+      body.add genIf(c, genVerbatim("dest@len != 0 && dest@p != src.p", c.info),
+        destructorCall(c.graph, t.destructor, x))
+      body.add genVerbatim("dest@len=src.len; dest@p=src.p;", c.info)
+  of attachedDestructor:
+    # destroy all elements:
+    forallElements(c, t, body, x, y)
+    body.add genBuiltin(c.graph, mDestroy, "destroy", x)
+    when false:
+      var deallocStmt = genVerbatim("dest@region->dealloc(dest@region, dest@p, " &
+          "(dest@p->cap * sizeof($)) + sizeof(NI) + sizeof(void*)); dest@len = 0;", c.info)
+      deallocStmt.typ = t.lastSon
+      body.add genIf(c, genVerbatim("dest@len != 0 && dest@region", c.info), deallocStmt)
+
+proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  seqOp(c, t, body, x, y)
+
+proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case c.kind
+  of attachedSink:
+    # we 'nil' y out afterwards so we *need* to take over its reference
+    # count value:
+    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+    body.add newAsgnStmt(x, y)
+  of attachedAsgn:
+    body.add callCodegenProc(c.graph, "nimIncWeakRef", c.info, y)
+    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+    body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+  of attachedDeepCopy: assert(false, "cannot happen")
+
+proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  var actions = newNodeI(nkStmtList, c.info)
+
+  let elemType = t.lastSon
+  #liftBodyAux(c, elemType, actions, genDeref(x), genDeref(y))
+  #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
+
+  if isFinal(elemType):
+    discard addDestructorCall(c, elemType, actions, x)
+    actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
+  else:
+    discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), x)
+    actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
+
+  case c.kind
+  of attachedSink, attachedAsgn:
+    body.add genIf(c, x, actions)
+    body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, x, actions)
+  of attachedDeepCopy: assert(false, "cannot happen")
+
+proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  if c.kind == attachedDeepCopy:
+    # a big problem is that we don't know the enviroment's type here, so we
+    # have to go through some indirection; we delegate this to the codegen:
+    let call = newNodeI(nkCall, c.info, 2)
+    call.typ = t
+    call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
+    call.sons[1] = y
+    body.add newAsgnStmt(x, call)
+  elif optNimV2 in c.graph.config.globalOptions:
+    case c.kind
+    of attachedSink, attachedAsgn: discard
+    of attachedDestructor: discard
+    of attachedDeepCopy: assert(false, "cannot happen")
+
+proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  discard "to implement"
+
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case t.kind
   of tyNone, tyEmpty, tyVoid: discard
   of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
-      tyPtr, tyRef, tyOpt, tyUncheckedArray:
+      tyPtr, tyOpt, tyUncheckedArray:
     defaultOp(c, t, body, x, y)
+  of tyRef:
+    if optNimV2 in c.graph.config.globalOptions:
+      weakrefOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyProc:
+    if t.callConv == ccClosure:
+      closureOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyOwned:
+    let base = t.skipTypes(abstractInstOwned)
+    if optNimV2 in c.graph.config.globalOptions:
+      case base.kind
+      of tyRef:
+        ownedRefOp(c, base, body, x, y)
+        return
+      of tyProc:
+        if base.callConv == ccClosure:
+          ownedClosureOp(c, base, body, x, y)
+          return
+      else: discard
+    defaultOp(c, base, body, x, y)
   of tyArray:
-    if tfHasAsgn in t.flags:
-      let i = declareCounter(c, body, firstOrd(c.graph.config, t))
-      let whileLoop = genWhileLoop(c, i, x)
-      let elemType = t.lastSon
-      liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
-                                                  y.at(i, elemType))
-      addIncStmt(c, whileLoop.sons[1], i)
-      body.add whileLoop
+    if tfHasAsgn in t.flags or useNoGc(c, t):
+      forallElements(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
   of tySequence:
-    # note that tfHasAsgn is propagated so we need the check on
-    # 'selectedGC' here to determine if we have the new runtime.
-    if c.graph.config.selectedGC == gcDestructors:
+    if useNoGc(c, t):
+      seqOp(c, t, body, x, y)
+    elif c.graph.config.selectedGC == gcDestructors:
+      # note that tfHasAsgn is propagated so we need the check on
+      # 'selectedGC' here to determine if we have the new runtime.
       discard considerOverloadedOp(c, t, body, x, y)
     elif tfHasAsgn in t.flags:
       if c.kind != attachedDestructor:
         body.add newSeqCall(c.graph, x, y)
-      let i = declareCounter(c, body, firstOrd(c.graph.config, t))
-      let whileLoop = genWhileLoop(c, i, x)
-      let elemType = t.lastSon
-      liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
-                                                  y.at(i, elemType))
-      addIncStmt(c, whileLoop.sons[1], i)
-      body.add whileLoop
+      forallElements(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
   of tyString:
-    if tfHasAsgn in t.flags:
+    if useNoGc(c, t):
+      strOp(c, t, body, x, y)
+    elif tfHasAsgn in t.flags:
       discard considerOverloadedOp(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
@@ -250,17 +394,6 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
   of tyTuple:
     liftBodyTup(c, t, body, x, y)
-  of tyProc:
-    if t.callConv != ccClosure or c.kind != attachedDeepCopy:
-      defaultOp(c, t, body, x, y)
-    else:
-      # a big problem is that we don't know the enviroment's type here, so we
-      # have to go through some indirection; we delegate this to the codegen:
-      let call = newNodeI(nkCall, c.info, 2)
-      call.typ = t
-      call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
-      call.sons[1] = y
-      body.add newAsgnStmt(x, call)
   of tyVarargs, tyOpenArray:
     localError(c.graph.config, c.info, "cannot copy openArray")
   of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@@ -269,7 +402,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
      tyTypeDesc, tyGenericInvocation, tyForward:
     internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
   of tyOrdinal, tyRange, tyInferred,
-     tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink, tyOwned:
+     tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
     liftBodyAux(c, lastSon(t), body, x, y)
 
 proc newProcType(info: TLineInfo; owner: PSym): PType =
@@ -290,26 +423,26 @@ proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; inf
   assert typ.kind == tyDistinct
   let baseType = typ[0]
   case kind
-    of attachedAsgn:
-      if baseType.assignment == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.assignment = baseType.assignment
-      result = typ.assignment
-    of attachedSink:
-      if baseType.sink == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.sink = baseType.sink
-      result = typ.sink
-    of attachedDeepCopy:
-      if baseType.deepCopy == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.deepCopy = baseType.deepCopy
-      result = typ.deepCopy
-    of attachedDestructor:
-      if baseType.destructor == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.destructor = baseType.destructor
-      result = typ.destructor
+  of attachedAsgn:
+    if baseType.assignment == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.assignment = baseType.assignment
+    result = typ.assignment
+  of attachedSink:
+    if baseType.sink == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.sink = baseType.sink
+    result = typ.sink
+  of attachedDeepCopy:
+    if baseType.deepCopy == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.deepCopy = baseType.deepCopy
+    result = typ.deepCopy
+  of attachedDestructor:
+    if baseType.destructor == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.destructor = baseType.destructor
+    result = typ.destructor
 
 proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym =