summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2019-12-05 16:59:06 +0100
committerMiran <narimiran@disroot.org>2019-12-05 16:59:06 +0100
commit3fbb3bfd3f440c059d6290c12834a38a61da98f2 (patch)
treeb4561f510b23ce4fe2a19e58e15246c5ec5293a0 /compiler
parent9b0e874687177af4f758b70994b3a08f963a75cd (diff)
downloadNim-3fbb3bfd3f440c059d6290c12834a38a61da98f2.tar.gz
ARC related bugfixes and refactorings (#12781)
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim20
-rw-r--r--compiler/ccgstmts.nim13
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/injectdestructors.nim427
-rw-r--r--compiler/semtypinst.nim4
5 files changed, 219 insertions, 247 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 9fbcab144..208e99c4a 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1456,7 +1456,7 @@ proc isGCedMem*(t: PType): bool {.inline.} =
   result = t.kind in {tyString, tyRef, tySequence} or
            t.kind == tyProc and t.callConv == ccClosure
 
-proc propagateToOwner*(owner, elem: PType) =
+proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
   const HaveTheirOwnEmpty = {tySequence, tyOpt, tySet, tyPtr, tyRef, tyProc}
   owner.flags = owner.flags + (elem.flags * {tfHasMeta, tfTriggersCompileTime})
   if tfNotNil in elem.flags:
@@ -1472,19 +1472,13 @@ proc propagateToOwner*(owner, elem: PType) =
   if elem.isMetaType:
     owner.flags.incl tfHasMeta
 
-  if tfHasAsgn in elem.flags:
+  let mask = elem.flags * {tfHasAsgn, tfHasOwned}
+  if mask != {} and propagateHasAsgn:
     let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
     if o2.kind in {tyTuple, tyObject, tyArray,
                    tySequence, tyOpt, tySet, tyDistinct, tyOpenArray, tyVarargs}:
-      o2.flags.incl tfHasAsgn
-      owner.flags.incl tfHasAsgn
-
-  if tfHasOwned in elem.flags:
-    let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
-    if o2.kind in {tyTuple, tyObject, tyArray,
-                   tySequence, tyOpt, tySet, tyDistinct, tyOpenArray, tyVarargs}:
-      o2.flags.incl tfHasOwned
-      owner.flags.incl tfHasOwned
+      o2.flags.incl mask
+      owner.flags.incl mask
 
   if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
                        tyGenericInvocation, tyPtr}:
@@ -1494,11 +1488,11 @@ proc propagateToOwner*(owner, elem: PType) =
       # ensure this doesn't bite us in sempass2.
       owner.flags.incl tfHasGCedMem
 
-proc rawAddSon*(father, son: PType) =
+proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
   when not defined(nimNoNilSeqs):
     if isNil(father.sons): father.sons = @[]
   father.sons.add(son)
-  if not son.isNil: propagateToOwner(father, son)
+  if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
 
 proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) =
   when not defined(nimNoNilSeqs):
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 59db5cb72..34fb06af8 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -1041,7 +1041,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
     linefmt(p, cpsStmts, "#popSafePoint();$n", [])
     genRestoreFrameAfterException(p)
   elif 1 < t.len and t[1].kind == nkExceptBranch:
-    startBlock(p, "if (#getCurrentException()) {$n")
+    startBlock(p, "if (#nimBorrowCurrentException()) {$n")
   else:
     startBlock(p)
   p.nestedTryStmts[^1].inExcept = true
@@ -1068,7 +1068,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
         else:
           genTypeInfo(p.module, t[i][j].typ, t[i][j].info)
         let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
-        appcg(p.module, orExpr, "#isObj(#getCurrentException()->$1, $2)", [memberName, checkFor])
+        appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
 
       if i > 1: line(p, cpsStmts, "else ")
       startBlock(p, "if ($1) {$n", [orExpr])
@@ -1082,7 +1082,14 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
   endBlock(p) # end of else block
   if i < t.len and t[i].kind == nkFinally:
     p.finallySafePoints.add(safePoint)
-    genSimpleBlock(p, t[i][0])
+    startBlock(p)
+    genStmts(p, t[i][0])
+    # pretend we handled the exception in a 'finally' so that we don't
+    # re-raise the unhandled one but instead keep the old one (it was
+    # not popped either):
+    if not quirkyExceptions and getCompilerProc(p.module.g.graph, "nimLeaveFinally") != nil:
+      linefmt(p, cpsStmts, "if ($1.status != 0) #nimLeaveFinally();$n", [safePoint])
+    endBlock(p)
     discard pop(p.finallySafePoints)
   if not quirkyExceptions:
     linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 12acd7825..a456b7d51 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1884,7 +1884,7 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
     if not moduleHasChanged(m.g.graph, m.module):
       result = false
     elif not equalsFile(code, cfile.cname):
-      if false:
+      when false:
         #m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"):
         if fileExists(cfile.cname):
           copyFile(cfile.cname.string, cfile.cname.string & ".backup")
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index de723b776..7a8c18f13 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -170,6 +170,7 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
       op = canon.attachedOps[kind]
 
   if op == nil:
+    #echo dest.typ.id
     globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
       "' operator not found for type " & typeToString(t))
   elif op.ast[genericParamsPos].kind != nkEmpty:
@@ -250,22 +251,25 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
   # generate: (let tmp = v; reset(v); tmp)
   # XXX: Strictly speaking we can only move if there is a ``=sink`` defined
   # or if no ``=sink`` is defined and also no assignment.
-  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
+  if not hasDestructor(n.typ):
+    result = copyTree(n)
+  else:
+    result = newNodeIT(nkStmtListExpr, n.info, n.typ)
 
-  var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
-  temp.typ = n.typ
-  var v = newNodeI(nkLetSection, n.info)
-  let tempAsNode = newSymNode(temp)
+    var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
+    temp.typ = n.typ
+    var v = newNodeI(nkLetSection, n.info)
+    let tempAsNode = newSymNode(temp)
 
-  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
-  vpart[0] = tempAsNode
-  vpart[1] = c.emptyNode
-  vpart[2] = n
-  v.add(vpart)
+    var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
+    vpart[0] = tempAsNode
+    vpart[1] = c.emptyNode
+    vpart[2] = n
+    v.add(vpart)
 
-  result.add v
-  result.add genWasMoved(skipConv(n), c)
-  result.add tempAsNode
+    result.add v
+    result.add genWasMoved(skipConv(n), c)
+    result.add tempAsNode
 
 proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
   assert s.kind == nkSym and s.sym.kind == skParam
@@ -273,8 +277,12 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
      localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
          "` is already consumed at " & toFileLineCol(c. graph.config, s.info))
 
-proc p(n: PNode; c: var Con; consumed = false): PNode
-proc pArg(arg: PNode; c: var Con; isSink: bool): PNode
+type ProcessMode = enum
+  normal
+  consumed
+  sinkArg
+
+proc p(n: PNode; c: var Con; mode: ProcessMode = normal): PNode
 proc moveOrCopy(dest, ri: PNode; c: var Con): PNode
 
 proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':'
@@ -366,6 +374,10 @@ template handleNested(n: untyped, processCall: untyped) =
         branch.add if node.typ == nil: p(node, c) #noreturn
                    else: processCall
       result.add branch
+  of nkWhen: # This should be a "when nimvm" node.
+    result = copyTree(n)
+    template node: untyped = n[1][0]
+    result[1][0] = processCall
   else: assert(false)
 
 proc ensureDestruction(arg: PNode; c: var Con): PNode =
@@ -384,75 +396,6 @@ proc ensureDestruction(arg: PNode; c: var Con): PNode =
   else:
     result = arg
 
-proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
-  if 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!
-      result = copyNode(arg)
-      let parameters = arg[0].typ
-      let L = if parameters != nil: parameters.len else: 0
-      result.add pArg(arg[0], c, isSink = false)
-      for i in 1..<arg.len:
-        result.add pArg(arg[i], c, i < L and isSinkTypeForParam(parameters[i]))
-    elif arg.containsConstSeq:
-      # const sequences are not mutable and so we need to pass a copy to the
-      # sink parameter (bug #11524). Note that the string implementation is
-      # different and can deal with 'const string sunk into var'.
-      result = passCopyToSink(arg, c)
-    elif arg.kind in nkLiterals:
-      # literals are save to share accross ASTs (for now!)
-      result = arg # literal to sink parameter: nothing to do
-    elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure}:
-      # object construction to sink parameter: nothing to do
-      result = copyTree(arg)
-      for i in ord(arg.kind in {nkObjConstr, nkClosure})..<arg.len:
-        if arg[i].kind == nkExprColonExpr:
-          result[i][1] = pArg(arg[i][1], c, isSink = true)
-        else:
-          result[i] = pArg(arg[i], c, isSink = true)
-      #if arg.kind == nkClosure:
-      #  result = handleClosureCall(result, c)
-      # Not required here because the nkClosure will be consumed!
-    elif arg.kind == nkSym and isSinkParam(arg.sym):
-      # Sinked params can be consumed only once. We need to reset the memory
-      # to disable the destructor which we have not elided
-      sinkParamIsLastReadCheck(c, arg)
-      result = destructiveMoveVar(arg, c)
-    elif isAnalysableFieldAccess(arg, c.owner) and isLastRead(arg, c):
-      # it is the last read, can be sinked. We need to reset the memory
-      # to disable the destructor which we have not elided
-      result = destructiveMoveVar(arg, c)
-    elif arg.kind in {nkStmtListExpr, nkBlockExpr, nkBlockStmt, nkIfExpr, nkIfStmt, nkCaseStmt}:
-      handleNested(arg): pArg(node, c, isSink)
-    elif arg.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}:
-      result = copyTree(arg)
-      result[1] = pArg(arg[1], c, isSink = true)
-    elif arg.kind in {nkObjDownConv, nkObjUpConv}:
-      result = copyTree(arg)
-      result[0] = pArg(arg[0], c, isSink = true)
-    else:
-      # an object that is not temporary but passed to a 'sink' parameter
-      # results in a copy.
-      result = passCopyToSink(arg, c)
-  elif arg.kind == nkBracket:
-    # Treat `f([...])` like `f(...)`
-    result = copyNode(arg)
-    for son in arg:
-      result.add pArg(son, c, isSinkTypeForParam(son.typ))
-  elif arg.kind in nkCallKinds and arg.typ != nil and hasDestructor(arg.typ):
-    # produce temp creation
-    result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
-    let tmp = getTemp(c, arg.typ, arg.info)
-    let res = p(arg, c)
-    var sinkExpr = genSink(c, tmp, res)
-    sinkExpr.add res
-    result.add sinkExpr
-    result.add tmp
-    c.destroys.add genDestroy(c, tmp)
-  else:
-    result = p(arg, c)
-
 proc isCursor(n: PNode): bool =
   case n.kind
   of nkSym:
@@ -493,154 +436,172 @@ proc cycleCheck(n: PNode; c: var Con) =
       message(c.graph.config, n.info, warnCycleCreated, msg)
       break
 
-proc p(n: PNode; c: var Con; consumed = false): PNode =
-  case n.kind
-  of nkCallKinds:
-    let parameters = n[0].typ
-    let L = if parameters != nil: parameters.len else: 0
-    result = shallowCopy(n)
-    for i in 1..<n.len:
-      result[i] = pArg(n[i], c, i < L and isSinkTypeForParam(parameters[i]))
-    if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
-      result[0] = copyTree(n[0])
-      if c.graph.config.selectedGC in {gcHooks, gcDestructors}:
-        let destroyOld = genDestroy(c, result[1])
-        result = newTree(nkStmtList, destroyOld, result)
-    else:
-      result[0] = pArg(n[0], c, isSink = false)
-  of nkDiscardStmt: # Small optimization
-    result = shallowCopy(n)
-    if n[0].kind != nkEmpty:
-      result[0] = pArg(n[0], c, false)
+proc p(n: PNode; c: var Con; mode: ProcessMode = normal): PNode =
+  if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, nkIfExpr, nkCaseStmt, nkWhen}:
+    handleNested(n): p(node, c, mode)
+  elif mode == sinkArg:
+    if n.containsConstSeq:
+      # const sequences are not mutable and so we need to pass a copy to the
+      # sink parameter (bug #11524). Note that the string implementation is
+      # different and can deal with 'const string sunk into var'.
+      result = passCopyToSink(n, c)
+    elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure} + nkCallKinds + nkLiterals:
+      result = p(n, c, consumed)
+    elif n.kind == nkSym and isSinkParam(n.sym):
+      # Sinked params can be consumed only once. We need to reset the memory
+      # to disable the destructor which we have not elided
+      sinkParamIsLastReadCheck(c, n)
+      result = destructiveMoveVar(n, c)
+    elif isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
+      # it is the last read, can be sinkArg. We need to reset the memory
+      # to disable the destructor which we have not elided
+      result = destructiveMoveVar(n, c)
+    elif n.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}:
+      result = copyTree(n)
+      if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and
+          n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
+        # allow conversions from owned to unowned via this little hack:
+        let nTyp = n[1].typ
+        n[1].typ = n.typ
+        result[1] = p(n[1], c, sinkArg)
+        result[1].typ = nTyp
+      else:
+        result[1] = p(n[1], c, sinkArg)
+    elif n.kind in {nkObjDownConv, nkObjUpConv}:
+      result = copyTree(n)
+      result[0] = p(n[0], c, sinkArg)
     else:
-      result[0] = copyNode(n[0])
-  of nkBracket:
-    result = copyTree(n)
-    for i in 0..<n.len:
-      # everything that is passed to an array constructor is consumed,
-      # so these all act like 'sink' parameters:
-      result[i] = pArg(n[i], c, isSink = true)
-    if not consumed:
-      result = ensureDestruction(result, c)
-  of nkObjConstr:
-    result = copyTree(n)
-    for i in 1..<n.len:
-      # everything that is passed to an object constructor is consumed,
-      # so these all act like 'sink' parameters:
-      result[i][1] = pArg(n[i][1], c, isSink = true)
-    if not consumed:
-      result = ensureDestruction(result, c)
-  of nkTupleConstr, nkClosure:
-    result = copyTree(n)
-    for i in ord(n.kind == nkClosure)..<n.len:
-      # everything that is passed to an tuple constructor is consumed,
-      # so these all act like 'sink' parameters:
-      if n[i].kind == nkExprColonExpr:
-        result[i][1] = pArg(n[i][1], c, isSink = true)
+      # copy objects that are not temporary but passed to a 'sink' parameter
+      result = passCopyToSink(n, c)
+  else:
+    case n.kind
+    of nkBracket, nkObjConstr, nkTupleConstr, nkClosure:
+      result = copyTree(n)
+      for i in ord(n.kind in {nkObjConstr, nkClosure})..<n.len:
+        let m = if mode == normal: normal
+                else: sinkArg
+        if n[i].kind == nkExprColonExpr:
+          result[i][1] = p(n[i][1], c, m)
+        else:
+          result[i] = p(n[i], c, m)
+    of nkCallKinds:
+      let parameters = n[0].typ
+      let L = if parameters != nil: parameters.len else: 0
+      result = shallowCopy(n)
+      for i in 1..<n.len:
+        if i < L and isSinkTypeForParam(parameters[i]):
+          result[i] = p(n[i], c, sinkArg)
+        else:
+          result[i] = p(n[i], c, normal)
+      if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
+        result[0] = copyTree(n[0])
+        if c.graph.config.selectedGC in {gcHooks, gcDestructors}:
+          let destroyOld = genDestroy(c, result[1])
+          result = newTree(nkStmtList, destroyOld, result)
       else:
-        result[i] = pArg(n[i], c, isSink = true)
-    if not consumed:
-      result = ensureDestruction(result, c)
-  of nkVarSection, nkLetSection:
-    # transform; var x = y to  var x; x op y  where op is a move or copy
-    result = newNodeI(nkStmtList, n.info)
-    for it in n:
-      var ri = it[^1]
-      if it.kind == nkVarTuple and hasDestructor(ri.typ):
-        let x = lowerTupleUnpacking(c.graph, it, c.owner)
-        result.add p(x, c)
-      elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0]):
-        for j in 0..<it.len-2:
-          let v = it[j]
-          if v.kind == nkSym:
-            if sfCompileTime in v.sym.flags: continue
-            # move the variable declaration to the top of the frame:
-            c.addTopVar v
-            # make sure it's destroyed at the end of the proc:
-            if not isUnpackedTuple(it[0].sym):
-              c.destroys.add genDestroy(c, v)
-          if ri.kind == nkEmpty and c.inLoop > 0:
-            ri = genDefaultCall(v.typ, c, v.info)
-          if ri.kind != nkEmpty:
-            let r = moveOrCopy(v, ri, c)
-            result.add r
-      else: # keep the var but transform 'ri':
-        var v = copyNode(n)
-        var itCopy = copyNode(it)
-        for j in 0..<it.len-1:
-          itCopy.add it[j]
-        itCopy.add p(it[^1], c)
-        v.add itCopy
-        result.add v
-  of nkAsgn, nkFastAsgn:
-    if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
-        not isCursor(n[0]):
-      # rule (self-assignment-removal):
-      if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
-        result = newNodeI(nkEmpty, n.info)
+        result[0] = p(n[0], c, normal)
+
+      if mode == normal:
+        result = ensureDestruction(result, c)
+    of nkDiscardStmt: # Small optimization
+      result = shallowCopy(n)
+      if n[0].kind != nkEmpty:
+        result[0] = p(n[0], c, normal)
+      else:
+        result[0] = copyNode(n[0])
+    of nkVarSection, nkLetSection:
+      # transform; var x = y to  var x; x op y  where op is a move or copy
+      result = newNodeI(nkStmtList, n.info)
+      for it in n:
+        var ri = it[^1]
+        if it.kind == nkVarTuple and hasDestructor(ri.typ):
+          let x = lowerTupleUnpacking(c.graph, it, c.owner)
+          result.add p(x, c, consumed)
+        elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0]):
+          for j in 0..<it.len-2:
+            let v = it[j]
+            if v.kind == nkSym:
+              if sfCompileTime in v.sym.flags: continue
+              # move the variable declaration to the top of the frame:
+              c.addTopVar v
+              # make sure it's destroyed at the end of the proc:
+              if not isUnpackedTuple(it[0].sym):
+                c.destroys.add genDestroy(c, v)
+            if ri.kind == nkEmpty and c.inLoop > 0:
+              ri = genDefaultCall(v.typ, c, v.info)
+            if ri.kind != nkEmpty:
+              let r = moveOrCopy(v, ri, c)
+              result.add r
+        else: # keep the var but transform 'ri':
+          var v = copyNode(n)
+          var itCopy = copyNode(it)
+          for j in 0..<it.len-1:
+            itCopy.add it[j]
+          itCopy.add p(it[^1], c)
+          v.add itCopy
+          result.add v
+    of nkAsgn, nkFastAsgn:
+      if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
+          not isCursor(n[0]):
+        # rule (self-assignment-removal):
+        if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
+          result = newNodeI(nkEmpty, n.info)
+        else:
+          if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
+            cycleCheck(n, c)
+          result = moveOrCopy(n[0], n[1], c)
       else:
-        if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
-          cycleCheck(n, c)
-        result = moveOrCopy(n[0], n[1], c)
-    else:
-      result = copyNode(n)
-      result.add copyTree(n[0])
-      result.add p(n[1], c)
-  of nkRaiseStmt:
-    if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
-      if n[0].kind in nkCallKinds:
-        let call = p(n[0], c)
         result = copyNode(n)
-        result.add call
+        result.add copyTree(n[0])
+        result.add p(n[1], c, consumed)
+    of nkRaiseStmt:
+      if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
+        if n[0].kind in nkCallKinds:
+          let call = p(n[0], c)
+          result = copyNode(n)
+          result.add call
+        else:
+          let tmp = getTemp(c, n[0].typ, n.info)
+          var m = genCopyNoCheck(c, tmp, n[0])
+          m.add p(n[0], c)
+          result = newTree(nkStmtList, genWasMoved(tmp, c), m)
+          var toDisarm = n[0]
+          if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
+          if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
+            result.add genWasMoved(toDisarm, c)
+          result.add newTree(nkRaiseStmt, tmp)
       else:
-        let tmp = getTemp(c, n[0].typ, n.info)
-        var m = genCopyNoCheck(c, tmp, n[0])
-        m.add p(n[0], c)
-        result = newTree(nkStmtList, genWasMoved(tmp, c), m)
-        var toDisarm = n[0]
-        if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
-        if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
-          result.add genWasMoved(toDisarm, c)
-        result.add newTree(nkRaiseStmt, tmp)
-    else:
+        result = copyNode(n)
+        if n[0].kind != nkEmpty:
+          result.add p(n[0], c, sinkArg)
+        else:
+          result.add copyNode(n[0])
+    of nkWhileStmt:
       result = copyNode(n)
+      inc c.inLoop
       result.add p(n[0], c)
-  of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
-      nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef,
-      nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt,
-      nkPragma, nkCommentStmt, nkBreakStmt, nkBreakState:
-    result = n
-  of nkWhileStmt:
-    result = copyNode(n)
-    inc c.inLoop
-    result.add p(n[0], c)
-    result.add p(n[1], c)
-    dec c.inLoop
-  of nkWhen: # This should be a "when nimvm" node.
-    result = copyTree(n)
-    result[1][0] = p(result[1][0], c)
-  of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, nkIfExpr, nkCaseStmt:
-    handleNested(n): p(node, c, consumed)
-  else:
-    result = shallowCopy(n)
-    for i in 0..<n.len:
-      result[i] = p(n[i], c, consumed)
+      result.add p(n[1], c)
+      dec c.inLoop
+    of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
+       nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
+       nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
+       nkExportStmt, nkPragma, nkCommentStmt, nkBreakStmt, nkBreakState:
+      result = n
+    else:
+      result = shallowCopy(n)
+      for i in 0..<n.len:
+        result[i] = p(n[i], c, mode)
 
 proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
-  # unfortunately, this needs to be kept consistent with the cases
-  # we handle in the 'case of' statement below:
-  const movableNodeKinds = (nkCallKinds + {nkSym, nkTupleConstr, nkClosure, nkObjConstr,
-                                           nkBracket, nkBracketExpr, nkNilLit})
-
   case ri.kind
   of nkCallKinds:
     result = genSink(c, dest, ri)
-    result.add p(ri, c, consumed = true)
+    result.add p(ri, c, consumed)
   of nkBracketExpr:
     if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
       # unpacking of tuple: move out the elements
       result = genSink(c, dest, ri)
-      result.add p(ri, c, consumed = true)
+      result.add p(ri, c, consumed)
     elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
       # Rule 3: `=sink`(x, z); wasMoved(z)
       var snk = genSink(c, dest, ri)
@@ -648,17 +609,17 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
       result = newTree(nkStmtList, snk, genWasMoved(ri, c))
     else:
       result = genCopy(c, dest, ri)
-      result.add p(ri, c, consumed = true)
+      result.add p(ri, c, consumed)
   of nkBracket:
     # array constructor
     if ri.len > 0 and isDangerousSeq(ri.typ):
       result = genCopy(c, dest, ri)
     else:
       result = genSink(c, dest, ri)
-    result.add p(ri, c, consumed = true)
+    result.add p(ri, c, consumed)
   of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
     result = genSink(c, dest, ri)
-    result.add p(ri, c, consumed = true)
+    result.add p(ri, c, consumed)
   of nkSym:
     if isSinkParam(ri.sym):
       # Rule 3: `=sink`(x, z); wasMoved(z)
@@ -674,18 +635,26 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
       result = newTree(nkStmtList, snk, genWasMoved(ri, c))
     else:
       result = genCopy(c, dest, ri)
-      result.add p(ri, c, consumed = true)
+      result.add p(ri, c, consumed)
   of nkHiddenSubConv, nkHiddenStdConv, nkConv:
-    result = moveOrCopy(dest, ri[1], c)
-    if not sameType(ri.typ, ri[1].typ):
+    when false:
+      result = moveOrCopy(dest, ri[1], c)
+      if not sameType(ri.typ, ri[1].typ):
+        let copyRi = copyTree(ri)
+        copyRi[1] = result[^1]
+        result[^1] = copyRi
+    else:
+      result = genSink(c, dest, ri)
+      result.add p(ri, c, sinkArg)
+  of nkObjDownConv, nkObjUpConv:
+    when false:
+      result = moveOrCopy(dest, ri[0], c)
       let copyRi = copyTree(ri)
-      copyRi[1] = result[^1]
+      copyRi[0] = result[^1]
       result[^1] = copyRi
-  of nkObjDownConv, nkObjUpConv:
-    result = moveOrCopy(dest, ri[0], c)
-    let copyRi = copyTree(ri)
-    copyRi[0] = result[^1]
-    result[^1] = copyRi
+    else:
+      result = genSink(c, dest, ri)
+      result.add p(ri, c, sinkArg)
   of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt:
     handleNested(ri): moveOrCopy(dest, node, c)
   else:
@@ -697,7 +666,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
       result = newTree(nkStmtList, snk, genWasMoved(ri, c))
     else:
       result = genCopy(c, dest, ri)
-      result.add p(ri, c, consumed = true)
+      result.add p(ri, c, consumed)
 
 proc computeUninit(c: var Con) =
   if not c.uninitComputed:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 786a9e4f8..6d9e9873e 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -307,6 +307,8 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   if not (t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil)):
     result.flags.excl tfInstClearedFlags
+  else:
+    result.flags.excl tfHasAsgn
   when false:
     if newDestructors:
       result.assignment = nil
@@ -380,7 +382,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   for i in 1..<t.len:
     # if one of the params is not concrete, we cannot do anything
     # but we already raised an error!
-    rawAddSon(result, header[i])
+    rawAddSon(result, header[i], propagateHasAsgn = false)
 
   if body.kind == tyError:
     return