summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/destroyer.nim191
-rw-r--r--tests/destructor/tmove_objconstr.nim33
2 files changed, 161 insertions, 63 deletions
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 2d21a6019..8471d70d1 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -408,9 +408,12 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
   else:
     result.add newTree(nkAsgn, tmp, p(n, c))
   result.add tmp
-
+      
 proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
-  if isSink:
+  if arg.typ == nil:
+    # typ is nil if we are in if/case branch with noreturn
+    result = copyTree(arg)
+  elif isSink:
     if arg.kind in nkCallKinds:
       # recurse but skip the call expression in order to prevent
       # destructor injections: Rule 5.1 is different from rule 5.4!
@@ -420,7 +423,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
       result.add arg[0]
       for i in 1..<arg.len:
         result.add pArg(arg[i], c, i < L and parameters[i].kind == tySink)
-    elif arg.kind in {nkObjConstr, nkCharLit..nkFloat128Lit}:
+    elif arg.kind in {nkObjConstr, nkTupleConstr, nkCharLit..nkFloat128Lit}:
       discard "object construction to sink parameter: nothing to do"
       result = arg
     elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c):
@@ -431,6 +434,37 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
     elif arg.kind == nkSym and isSinkParam(arg.sym):
       # mark the sink parameter as used:
       result = destructiveMoveSink(arg, c)
+    elif arg.kind in {nkStmtListExpr, nkBlockExpr, nkBlockStmt}:
+        result = copyNode(arg)
+        for i in 0..arg.len-2:
+          result.add p(arg[i], c)
+        result.add pArg(arg[^1], c, isSink)
+    elif arg.kind in {nkIfExpr, nkIfStmt}:
+      result = copyNode(arg)
+      for i in 0..<arg.len:
+        var branch = copyNode(arg[i])
+        if arg[i].kind in {nkElifBranch, nkElifExpr}:   
+          branch.add p(arg[i][0], c)
+          branch.add pArg(arg[i][1], c, isSink)
+        else:
+          branch.add pArg(arg[i][0], c, isSink)
+        result.add branch
+    elif arg.kind == nkCaseStmt:
+      result = copyNode(arg)
+      result.add p(arg[0], c)
+      for i in 1..<arg.len:
+        var branch: PNode
+        if arg[i].kind == nkOfbranch:
+          branch = arg[i] # of branch conditions are constants
+          branch[^1] = pArg(arg[i][^1], c, isSink)
+        elif arg[i].kind in {nkElifBranch, nkElifExpr}:
+          branch = copyNode(arg[i])   
+          branch.add p(arg[i][0], c)
+          branch.add pArg(arg[i][1], c, isSink)
+        else:
+          branch = copyNode(arg[i]) 
+          branch.add pArg(arg[i][0], c, isSink)
+        result.add branch     
     else:
       # an object that is not temporary but passed to a 'sink' parameter
       # results in a copy.
@@ -439,64 +473,100 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
     result = p(arg, c)
 
 proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
-  case ri.kind
-  of nkCallKinds:
-    result = genSink(c, dest.typ, dest, ri)
-    # watch out and no not transform 'ri' twice if it's a call:
-    let ri2 = copyNode(ri)
-    let parameters = ri[0].typ
-    let L = if parameters != nil: parameters.len else: 0
-    ri2.add ri[0]
-    for i in 1..<ri.len:
-      ri2.add pArg(ri[i], c, i < L and parameters[i].kind == tySink)
-    #recurse(ri, ri2)
-    result.add ri2
-  of nkBracketExpr:
-    if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
-      # unpacking of tuple: move out the elements 
+  if ri.typ == nil:
+    # typ is nil if we are in if/case branch with noreturn
+    result = copyTree(ri)
+  else:
+    case ri.kind
+    of nkCallKinds:
       result = genSink(c, dest.typ, dest, ri)
-    else:
-      result = genCopy(c, dest.typ, dest, ri)
-    result.add p(ri, c)
-  of nkStmtListExpr:
-    result = newNodeI(nkStmtList, ri.info)
-    for i in 0..ri.len-2:
-      result.add p(ri[i], c)
-    result.add moveOrCopy(dest, ri[^1], c)
-  of nkObjConstr:
-    result = genSink(c, dest.typ, dest, ri)
-    let ri2 = copyTree(ri)
-    for i in 1..<ri.len:
-      # everything that is passed to an object constructor is consumed,
-      # so these all act like 'sink' parameters:
-      ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
-    result.add ri2
-  of nkTupleConstr:
-    result = genSink(c, dest.typ, dest, ri)
-    let ri2 = copyTree(ri)
-    for i in 0..<ri.len:
-      # everything that is passed to an tuple constructor is consumed,
-      # so these all act like 'sink' parameters:
-      if ri[i].kind == nkExprColonExpr:
-        ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
+      # watch out and no not transform 'ri' twice if it's a call:
+      let ri2 = copyNode(ri)
+      let parameters = ri[0].typ
+      let L = if parameters != nil: parameters.len else: 0
+      ri2.add ri[0]
+      for i in 1..<ri.len:
+        ri2.add pArg(ri[i], c, i < L and parameters[i].kind == tySink)
+      #recurse(ri, ri2)
+      result.add ri2
+    of nkBracketExpr:
+      if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
+        # unpacking of tuple: move out the elements 
+        result = genSink(c, dest.typ, dest, ri)
       else:
-        ri2[i] = pArg(ri[i], c, isSink = true)
-    result.add ri2
-  of nkSym:
-    if ri.sym.kind != skParam and isLastRead(ri, c):
-      # Rule 3: `=sink`(x, z); wasMoved(z)
-      var snk = genSink(c, dest.typ, dest, ri)
-      snk.add p(ri, c)
-      result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
-    elif isSinkParam(ri.sym):
+        result = genCopy(c, dest.typ, dest, ri)
+      result.add p(ri, c)
+    of nkStmtListExpr:
+      result = newNodeI(nkStmtList, ri.info)
+      for i in 0..ri.len-2:
+        result.add p(ri[i], c)
+      result.add moveOrCopy(dest, ri[^1], c)
+    of nkBlockExpr, nkBlockStmt:
+      result = newNodeI(nkBlockStmt, ri.info)
+      result.add ri[0] ## add label
+      for i in 1..ri.len-2:
+        result.add p(ri[i], c)
+      result.add moveOrCopy(dest, ri[^1], c)
+    of nkIfExpr, nkIfStmt:
+      result = newNodeI(nkIfStmt, ri.info)
+      for i in 0..<ri.len:
+        var branch = copyNode(ri[i])
+        if ri[i].kind in {nkElifBranch, nkElifExpr}:
+          branch.add p(ri[i][0], c)
+          branch.add moveOrCopy(dest, ri[i][1], c)
+        else:
+          branch.add moveOrCopy(dest, ri[i][0], c)
+        result.add branch
+    of nkCaseStmt:
+      result = newNodeI(nkCaseStmt, ri.info)
+      result.add p(ri[0], c)
+      for i in 1..<ri.len:
+        var branch: PNode
+        if ri[i].kind == nkOfbranch:
+          branch = ri[i] # of branch conditions are constants
+          branch[^1] = moveOrCopy(dest, ri[i][^1], c)
+        elif ri[i].kind in {nkElifBranch, nkElifExpr}:
+          branch = copyNode(ri[i])   
+          branch.add p(ri[i][0], c)
+          branch.add moveOrCopy(dest, ri[i][1], c)
+        else:
+          branch = copyNode(ri[i]) 
+          branch.add moveOrCopy(dest, ri[i][0], c)
+        result.add branch
+    of nkObjConstr:
       result = genSink(c, dest.typ, dest, ri)
-      result.add destructiveMoveSink(ri, c)
+      let ri2 = copyTree(ri)
+      for i in 1..<ri.len:
+        # everything that is passed to an object constructor is consumed,
+        # so these all act like 'sink' parameters:
+        ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
+      result.add ri2
+    of nkTupleConstr:
+      result = genSink(c, dest.typ, dest, ri)
+      let ri2 = copyTree(ri)
+      for i in 0..<ri.len:
+        # everything that is passed to an tuple constructor is consumed,
+        # so these all act like 'sink' parameters:
+        if ri[i].kind == nkExprColonExpr:
+          ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true)
+        else:
+          ri2[i] = pArg(ri[i], c, isSink = true)
+      result.add ri2
+    of nkSym:
+      if ri.sym.kind != skParam and isLastRead(ri, c):
+        # Rule 3: `=sink`(x, z); wasMoved(z)
+        var snk = genSink(c, dest.typ, dest, ri)
+        snk.add p(ri, c)
+        result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
+      elif isSinkParam(ri.sym):
+        result = genSink(c, dest.typ, dest, ri)
+        result.add destructiveMoveSink(ri, c)
+      else:
+        result = genCopy(c, dest.typ, dest, ri)
+        result.add p(ri, c)
     else:
       result = genCopy(c, dest.typ, dest, ri)
       result.add p(ri, c)
-  else:
-    result = genCopy(c, dest.typ, dest, ri)
-    result.add p(ri, c)
 
 proc p(n: PNode; c: var Con): PNode =
   case n.kind
@@ -561,7 +631,7 @@ proc p(n: PNode; c: var Con): PNode =
     recurse(n, result)
 
 proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
-  when false: # defined(nimDebugDestroys):
+  when true: # defined(nimDebugDestroys):
     echo "injecting into ", n
   var c: Con
   c.owner = owner
@@ -597,8 +667,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   else:
     result.add body
 
-  when defined(nimDebugDestroys):
-    if true:
-      echo "------------------------------------"
-      echo owner.name.s, " transformed to: "
-      echo result
+  when true:
+    echo "------------------------------------"
+    echo owner.name.s, " transformed to: "
+    echo result
diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim
index 26cc682b5..2ec167234 100644
--- a/tests/destructor/tmove_objconstr.nim
+++ b/tests/destructor/tmove_objconstr.nim
@@ -109,7 +109,14 @@ proc myfunc(x, y: int): (MySeqNonCopyable, MySeqNonCopyable) =
 
 proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] =
   var cc = newMySeq(y, 5.0)
-  (a: newMySeq(x, 1.0), b:0, c: cc)
+  (a: case x:
+    of 1: 
+      let (z1, z2) = myfunc(x,y)
+      z2
+    elif x > 5: raise newException(ValueError, "new error")
+    else: newMySeq(x, 1.0), 
+   b:0, 
+   c: if y > 0: move(cc) else: newMySeq(1, 3.0))
 
 let (seq1, seq2) = myfunc(2, 3)
 doAssert seq1.len == 2
@@ -122,4 +129,26 @@ doAssert seq3.len == 2
 doAssert seq3[0] == 1.0
 
 var seq4, seq5: MySeqNonCopyable
-(seq4, i, seq5) = myfunc2(2, 3)
\ No newline at end of file
+(seq4, i, seq5) = myfunc2(2, 3)
+
+seq4 = block:
+  var tmp = newMySeq(4, 1.0)
+  tmp[0] = 3.0
+  tmp
+
+doAssert seq4[0] == 3.0 
+
+import macros
+
+seq4 = 
+  if i > 0: newMySeq(2, 5.0) 
+  elif i < -100: raise newException(ValueError, "Parse Error")
+  else: newMySeq(2, 3.0)
+
+seq4 = 
+  case (char) i:
+    of 'A', {'W'..'Z'}: newMySeq(2, 5.0) 
+    of 'B': quit(-1)
+    else: 
+      let (x1, x2, x3) = myfunc2(2, 3)
+      x3
\ No newline at end of file