summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2020-07-07 23:59:13 +0200
committerAndreas Rumpf <rumpf_a@web.de>2020-07-08 10:48:12 +0200
commitcdce245a235f3a8c255d0f782bc088546343b6ed (patch)
tree8aafea73122a127b4f61ecf967d3a1fc22ce1f83 /compiler
parentb17e1efc67a64ce6acd2ddf4f432879f3bf739e2 (diff)
downloadNim-cdce245a235f3a8c255d0f782bc088546343b6ed.tar.gz
fixes #14899
Diffstat (limited to 'compiler')
-rw-r--r--compiler/injectdestructors.nim41
1 files changed, 37 insertions, 4 deletions
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index e57898ae8..291cd36aa 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -37,6 +37,9 @@ type
     final: seq[PNode] # finally section
     needsTry: bool
     parent: ptr Scope
+    escapingSyms: IntSet # a construct like (block: let x = f(); x)
+                         # means that 'x' escapes. We then destroy it
+                         # in the parent's scope (and also allocate it there).
 
 proc nestedScope(parent: var Scope): Scope =
   Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent))
@@ -400,8 +403,11 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode =
     checkForErrorPragma(c, t, ri, "=")
   result = genCopyNoCheck(c, dest, ri)
 
-proc addTopVar(c: var Con; s: var Scope; v: PNode) =
-  s.vars.add v.sym
+proc addTopVar(c: var Con; s: var Scope; v: PNode): ptr Scope =
+  result = addr(s)
+  while v.sym.id in result.escapingSyms and result.parent != nil:
+    result = result.parent
+  result[].vars.add v.sym
 
 proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
   let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info)
@@ -584,9 +590,31 @@ proc cycleCheck(n: PNode; c: var Con) =
       message(c.graph.config, n.info, warnCycleCreated, msg)
       break
 
+proc markEscapingVars(n: PNode; s: var Scope) =
+  case n.kind
+  of nkSym:
+    s.escapingSyms.incl n.sym.id
+  of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref,
+     nkAddr, nkHiddenAddr, nkStringToCString, nkCStringToString, nkObjDownConv,
+     nkObjUpConv:
+    markEscapingVars(n[0], s)
+  of nkCast, nkHiddenSubConv, nkHiddenStdConv, nkConv:
+    markEscapingVars(n[1], s)
+  of nkTupleConstr, nkBracket, nkPar, nkClosure:
+    for i in 0 ..< n.len:
+      markEscapingVars(n[i], s)
+  of nkObjConstr:
+    for i in 1 ..< n.len:
+      markEscapingVars(n[i], s)
+  of nkStmtList, nkStmtListExpr:
+    if n.len > 0:
+      markEscapingVars(n[^1], s)
+  else:
+    discard "no arbitrary tree traversal here"
+
 proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) =
   # move the variable declaration to the top of the frame:
-  c.addTopVar s, v
+  let owningScope = c.addTopVar(s, v)
   if isUnpackedTuple(v):
     if c.inLoop > 0:
       # unpacked tuple needs reset at every loop iteration
@@ -596,7 +624,7 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) =
     if sfGlobal in v.sym.flags and s.parent == nil:
       c.graph.globalDestructors.add genDestroy(c, v)
     else:
-      s.final.add genDestroy(c, v)
+      owningScope[].final.add genDestroy(c, v)
   if ri.kind == nkEmpty and c.inLoop > 0:
     res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true)
   elif ri.kind != nkEmpty:
@@ -617,6 +645,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) =
     for i in 0..<n.len-1:
       result.add p(n[i], c, s, normal)
     result.add maybeVoid(n[^1], s)
+    markEscapingVars(n[^1], s)
 
   of nkCaseStmt:
     result = copyNode(n)
@@ -630,6 +659,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) =
       for j in 0 ..< it.len-1:
         branch[j] = copyTree(it[j])
       var ofScope = nestedScope(s)
+      markEscapingVars(it[^1], ofScope)
       let ofResult = maybeVoid(it[^1], ofScope)
       branch[^1] = toTree(ofScope, ofResult, treeFlags)
       result.add branch
@@ -650,6 +680,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) =
     if alwaysStmt: result.typ = nil
     result.add n[0]
     var bodyScope = nestedScope(s)
+    markEscapingVars(n[1], bodyScope)
     let bodyResult = processCall(n[1], bodyScope)
     result.add toTree(bodyScope, bodyResult, treeFlags)
     rememberParent(s, bodyScope)
@@ -667,6 +698,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) =
         branch[0] = toTree(branchScope, cond, {producesValue, onlyCareAboutVars})
 
       branchScope.parent = addr(s)
+      markEscapingVars(it[^1], branchScope)
       var branchResult = processCall(it[^1], branchScope)
       branch[^1] = toTree(branchScope, branchResult, treeFlags)
       result.add branch
@@ -676,6 +708,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) =
     result = copyNode(n)
     if alwaysStmt: result.typ = nil
     var tryScope = nestedScope(s)
+    markEscapingVars(n[0], tryScope)
     var tryResult = maybeVoid(n[0], tryScope)
     result.add toTree(tryScope, tryResult, treeFlags)
     rememberParent(s, tryScope)