summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2019-04-12 13:05:57 +0200
committerAraq <rumpf_a@web.de>2019-04-12 13:28:12 +0200
commit572735bbfab2ea13fac0dde21d4e61a3fcfd8309 (patch)
tree482ff92b58433ffa579296f38d7dbf911f85a58b
parent041d15392aaf732665abab290f0cf5993d909efc (diff)
downloadNim-572735bbfab2ea13fac0dde21d4e61a3fcfd8309.tar.gz
fixes #11004
-rw-r--r--compiler/liftdestructors.nim132
-rw-r--r--tests/destructor/tnewruntime_strutils.nim7
2 files changed, 79 insertions, 60 deletions
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 9ab6be52d..8c89b2dad 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -27,9 +27,11 @@ type
     recurse: bool
     c: PContext
 
-proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
-proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo): PSym {.discardable.}
+proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
+proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
+              info: TLineInfo): PSym
+
+proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo)
 
 proc at(a, i: PNode, elemType: PType): PNode =
   result = newNodeI(nkBracketExpr, a.info, 2)
@@ -37,10 +39,10 @@ proc at(a, i: PNode, elemType: PType): PNode =
   result.sons[1] = i
   result.typ = elemType
 
-proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   for i in 0 ..< t.len:
     let lit = lowerings.newIntLit(c.graph, x.info, i)
-    liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
+    fillBody(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
 
 proc dotField(x: PNode, f: PSym): PNode =
   result = newNodeI(nkDotExpr, x.info, 2)
@@ -48,11 +50,11 @@ proc dotField(x: PNode, f: PSym): PNode =
   result.sons[1] = newSymNode(f, x.info)
   result.typ = f.typ
 
-proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
+proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
   case n.kind
   of nkSym:
     let f = n.sym
-    liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
+    fillBody(c, f.typ, body, x.dotField(f), y.dotField(f))
   of nkNilLit: discard
   of nkRecCase:
     if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
@@ -60,11 +62,11 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
       ## or the value is lost
       let prevKind = c.kind
       c.kind = attachedDestructor
-      liftBodyObj(c, n, body, x, y)
+      fillBodyObj(c, n, body, x, y)
       c.kind = prevKind
 
     # copy the selector:
-    liftBodyObj(c, n[0], body, x, y)
+    fillBodyObj(c, n[0], body, x, y)
     # we need to generate a case statement:
     var caseStmt = newNodeI(nkCaseStmt, c.info)
     # XXX generate 'if' that checks same branches
@@ -78,13 +80,13 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
       let L = branch.len
       branch.sons[L-1] = newNodeI(nkStmtList, c.info)
 
-      liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
+      fillBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
       if branch.sons[L-1].len == 0: inc emptyBranches
       caseStmt.add(branch)
     if emptyBranches != n.len-1:
       body.add(caseStmt)
   of nkRecList:
-    for t in items(n): liftBodyObj(c, t, body, x, y)
+    for t in items(n): fillBodyObj(c, t, body, x, y)
   else:
     illFormedAstLocal(n, c.graph.config)
 
@@ -150,7 +152,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     else:
       op = field
       if op == nil:
-        op = liftBody(c.c, t, c.kind, c.info)
+        op = produceSym(c.c, t, c.kind, c.info)
     if sfError in op.flags:
       incl c.fn.flags, sfError
     else:
@@ -172,7 +174,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
 proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
   var op = t.destructor
   if op == nil and useNoGc(c, t):
-    op = liftBody(c.c, t, attachedDestructor, c.info)
+    op = produceSym(c.c, t, attachedDestructor, c.info)
     doAssert op != nil
     doAssert op == t.destructor
 
@@ -280,12 +282,12 @@ 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),
+  fillBody(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) =
+proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
   of attachedAsgn, attachedDeepCopy:
     # we generate:
@@ -306,17 +308,31 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     forallElements(c, t, body, x, y)
     body.add genBuiltin(c.graph, mDestroy, "destroy", x)
 
-proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  createTypeBoundOps(c.c, t, body.info)
+  case c.kind
+  of attachedAsgn, attachedDeepCopy:
+    doAssert t.assignment != nil
+    body.add newAsgnCall(c.graph, t.assignment, x, y)
+  of attachedSink:
+    # we always inline the move for better performance:
+    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
+    # alternatively we could do this:
+    when false:
+      doAssert t.sink != nil
+      body.add newAsgnCall(c.graph, t.sink, x, y)
+  of attachedDestructor:
+    doAssert t.destructor != nil
+    body.add destructorCall(c.graph, t.destructor, x)
+
+proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
   of attachedAsgn, attachedDeepCopy:
     body.add callCodegenProc(c.graph, "nimAsgnStrV2", c.info, genAddr(c.graph, x), y)
-    # 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 setLenStrCall(c.graph, x, y)
-    #forallElements(c, t, body, x, y)
   of attachedSink:
     let moveCall = genBuiltin(c.graph, mMove, "move", x)
     moveCall.add y
@@ -345,7 +361,7 @@ 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))
+  #fillBody(c, elemType, actions, genDeref(x), genDeref(y))
   #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
 
   if isFinal(elemType):
@@ -406,7 +422,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     body.add genIf(c, xx, actions)
   of attachedDeepCopy: assert(false, "cannot happen")
 
-proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+proc fillBody(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,
@@ -442,7 +458,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       defaultOp(c, t, body, x, y)
   of tySequence:
     if useNoGc(c, t):
-      seqOp(c, t, body, x, y)
+      useSeqOrStrOp(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.
@@ -455,19 +471,19 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       defaultOp(c, t, body, x, y)
   of tyString:
     if useNoGc(c, t):
-      strOp(c, t, body, x, y)
+      useSeqOrStrOp(c, t, body, x, y)
     elif tfHasAsgn in t.flags:
       discard considerUserDefinedOp(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
   of tyObject:
     if not considerUserDefinedOp(c, t, body, x, y):
-      liftBodyObj(c, t.n, body, x, y)
+      fillBodyObj(c, t.n, body, x, y)
   of tyDistinct:
     if not considerUserDefinedOp(c, t, body, x, y):
-      liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
+      fillBody(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
   of tyTuple:
-    liftBodyTup(c, t, body, x, y)
+    fillBodyTup(c, t, body, x, y)
   of tyVarargs, tyOpenArray:
     localError(c.graph.config, c.info, "cannot copy openArray")
   of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@@ -478,45 +494,40 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     discard
   of tyVar, tyLent:
     if c.kind != attachedDestructor:
-      liftBodyAux(c, lastSon(t), body, x, y)
+      fillBody(c, lastSon(t), body, x, y)
   of tyOrdinal, tyRange, tyInferred,
      tyGenericInst, tyStatic, tyAlias, tySink:
-    liftBodyAux(c, lastSon(t), body, x, y)
+    fillBody(c, lastSon(t), body, x, y)
 
-proc liftBodyDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
+proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
   assert typ.kind == tyDistinct
   let baseType = typ[0]
   case kind
   of attachedAsgn:
     if baseType.assignment == nil:
-      discard liftBody(c, baseType, kind, info)
+      discard produceSym(c, baseType, kind, info)
     typ.assignment = baseType.assignment
     result = typ.assignment
   of attachedSink:
     if baseType.sink == nil:
-      discard liftBody(c, baseType, kind, info)
+      discard produceSym(c, baseType, kind, info)
     typ.sink = baseType.sink
     result = typ.sink
   of attachedDeepCopy:
     if baseType.deepCopy == nil:
-      discard liftBody(c, baseType, kind, info)
+      discard produceSym(c, baseType, kind, info)
     typ.deepCopy = baseType.deepCopy
     result = typ.deepCopy
   of attachedDestructor:
     if baseType.destructor == nil:
-      discard liftBody(c, baseType, kind, info)
+      discard produceSym(c, baseType, kind, info)
     typ.destructor = baseType.destructor
     result = typ.destructor
 
-proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
+proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym =
   if typ.kind == tyDistinct:
-    return liftBodyDistinctType(c, typ, kind, info)
-  when false:
-    var typ = typ
-    if c.config.selectedGC == gcDestructors and typ.kind == tySequence:
-      # use the canonical type to access the =sink and =destroy etc.
-      typ = c.graph.sysTypes[tySequence]
+    return produceSymDistinctType(c, typ, kind, info)
 
   var a: TLiftCtx
   a.info = info
@@ -552,7 +563,18 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   of attachedDeepCopy: typ.deepCopy = result
   of attachedDestructor: typ.destructor = result
 
-  liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
+  var tk: TTypeKind
+  if optNimV2 in c.graph.config.globalOptions:
+    tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
+  else:
+    tk = tyNone # no special casing for strings and seqs
+  case tk
+  of tySequence:
+    fillSeqOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
+  of tyString:
+    fillStrOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
+  else:
+    fillBody(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
 
   var n = newNodeI(nkProcDef, info, bodyPos+1)
   for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
@@ -563,16 +585,6 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   incl result.flags, sfFromGeneric
   incl result.flags, sfGeneratedOp
 
-proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
-  let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
-  result = t.assignment
-  if result.isNil:
-    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.graph, a, dest, src)
-
 template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   discard "now a nop"
 
@@ -581,7 +593,7 @@ proc patchBody(c: PContext; n: PNode; info: TLineInfo) =
     if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
       let t = n[1].typ.skipTypes(abstractVar)
       if t.destructor == nil:
-        liftBody(c, t, attachedDestructor, info)
+        discard produceSym(c, t, attachedDestructor, info)
 
       if t.destructor != nil:
         if t.destructor.ast[genericParamsPos].kind != nkEmpty:
@@ -600,7 +612,7 @@ template inst(field, t) =
 
 proc isTrival(s: PSym): bool {.inline.} = s == nil or s.ast[bodyPos].len == 0
 
-proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) =
+proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
   ## The later 'injectdestructors' pass depends on it.
@@ -625,15 +637,15 @@ proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) =
   let typ = canon.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)
+    discard produceSym(c, typ, attachedDestructor, info)
   else:
     inst(typ.destructor, typ)
   if typ.assignment == nil:
-    liftBody(c, typ, attachedAsgn, info)
+    discard produceSym(c, typ, attachedAsgn, info)
   else:
     inst(typ.assignment, typ)
   if typ.sink == nil:
-    liftBody(c, typ, attachedSink, info)
+    discard produceSym(c, typ, attachedSink, info)
   else:
     inst(typ.sink, typ)
 
diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim
index 80ed1757e..5b8684354 100644
--- a/tests/destructor/tnewruntime_strutils.nim
+++ b/tests/destructor/tnewruntime_strutils.nim
@@ -8,6 +8,11 @@ import strutils, os
 import core / allocators
 import system / ansi_c
 
+# bug #11004
+proc retTuple(): (seq[int], int) =
+  # XXX this doesn't allocate yet but probably it should
+  return (@[1], 1)
+
 proc nonStaticTests =
   doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
   when not defined(js):
@@ -177,6 +182,8 @@ proc staticTests =
   doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example  "]
   doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
 
+  discard retTuple()
+
 nonStaticTests()
 staticTests()