summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2018-03-31 20:27:08 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-03-31 20:27:08 +0200
commit455dd3613559ef43fc7609580d7658ceaa7a36fe (patch)
treef4f169f57d4d5debae1b8eb95911168d7def7408
parent61f00da4bcb5f6e18588ee37acc066154c91b109 (diff)
downloadNim-455dd3613559ef43fc7609580d7658ceaa7a36fe.tar.gz
destructors: first version of 'sink' parameter logic
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/destroyer.nim78
2 files changed, 33 insertions, 47 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 68bf95772..a28a7e7e3 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -453,8 +453,6 @@ type
     nfPreventCg # this node should be ignored by the codegen
     nfBlockArg  # this a stmtlist appearing in a call (e.g. a do block)
     nfFromTemplate # a top-level node returned from a template
-    nfPreventDestructor # prevent destructor injectsion for the node
-                        # (but not necessarily its children)
 
   TNodeFlags* = set[TNodeFlag]
   TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: beyond that)
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 9acc89be4..8c2e0dbc3 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -112,8 +112,6 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
   not allowed as a local variable.
 
 ``move`` builtin needs to be implemented.
-
-XXX Think about nfPreventDestructor logic.
 ]##
 
 import
@@ -305,7 +303,6 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
   m.add n
   result.add m
   result.add tmp
-  incl result.flags, nfPreventDestructor
   message(n.info, hintPerformance,
     "passing '$1' to a sink parameter introduces an implicit copy; " &
     "use 'move($1)' to prevent it" % $n)
@@ -333,39 +330,6 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
   result.add v
   result.add genReset(n, c)
   result.add tempAsNode
-  incl result.flags, nfPreventDestructor
-
-proc handleSinkParams(n: PNode; c: var Con) =
-  # first pass: introduce copies for stuff passed to
-  # 'sink' parameters. Introduce destructor guards for
-  # 'sink' parameters.
-  assert n.kind in nkCallKinds
-  # Rule 5.2: Compensate for 'sink' parameters with copies
-  # at the callsite (unless of course we can prove its the
-  # last read):
-  let parameters = n.typ
-  let L = if parameters != nil: parameters.len else: 0
-  for i in 1 ..< L:
-    let t = parameters[i]
-    if t.kind == tySink:
-      if n[i].kind in constrExprs:
-        incl(n[i].flags, nfPreventDestructor)
-      elif n[i].kind == nkSym and isHarmlessVar(n[i].sym, c):
-        # if x is a variable and it its last read we eliminate its
-        # destructor invokation, but don't. We need to reset its memory
-        # to disable its destructor which we have not elided:
-        n.sons[i] = destructiveMoveVar(n[i], c)
-        when false:
-          # XXX we need to find a way to compute "all paths consume 'x'"
-          c.symsNoDestructors.incl n[i].sym.id
-          # however, not emiting the copy operation is correct here.
-      elif n[i].kind == nkSym and isSinkParam(n[i].sym):
-        # mark the sink parameter as used:
-        n.sons[i] = destructiveMoveSink(n[i], c)
-      else:
-        # an object that is not temporary but passed to a 'sink' parameter
-        # results in a copy.
-        n.sons[i] = passCopyToSink(n[i], c)
 
 proc p(n: PNode; c: var Con): PNode =
   case n.kind
@@ -401,21 +365,45 @@ proc p(n: PNode; c: var Con): PNode =
         varSection.add itCopy
         result.add varSection
   of nkCallKinds:
-    if n.typ != nil and hasDestructor(n.typ) and nfPreventDestructor notin n.flags:
+    let parameters = n.typ
+    let L = if parameters != nil: parameters.len else: 0
+    for i in 1 ..< n.len:
+      let arg = n[i]
+      if i < L and parameters[i].kind == tySink:
+        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!
+          let a = copyNode(arg)
+          recurse(arg, a)
+          n.sons[i] = a
+        elif arg.kind == nkObjConstr:
+          discard "object construction to sink parameter: nothing to do"
+        elif arg.kind == nkSym and isHarmlessVar(arg.sym, c):
+          # if x is a variable and it its last read we eliminate its
+          # destructor invokation, but don't. We need to reset its memory
+          # to disable its destructor which we have not elided:
+          n.sons[i] = destructiveMoveVar(arg, c)
+        elif arg.kind == nkSym and isSinkParam(arg.sym):
+          # mark the sink parameter as used:
+          n.sons[i] = destructiveMoveSink(arg, c)
+        else:
+          # an object that is not temporary but passed to a 'sink' parameter
+          # results in a copy.
+          n.sons[i] = passCopyToSink(arg, c)
+      else:
+        n.sons[i] = p(arg, c)
+
+    if n.typ != nil and hasDestructor(n.typ):
       discard "produce temp creation"
       result = newNodeIT(nkStmtListExpr, n.info, n.typ)
       let tmp = getTemp(c, n.typ, n.info)
-      var m = genSink(n.typ, tmp)
-      var call = copyNode(n)
-      recurse(n, call)
-      m.add call
-      result.add m
+      var sinkExpr = genSink(n.typ, tmp)
+      sinkExpr.add n
+      result.add sinkExpr
       result.add tmp
       c.destroys.add genDestroy(n.typ, tmp)
     else:
-      result = copyNode(n)
-      recurse(n, result)
-    #handleSinkParams(result, c)
+      result = n
   of nkAsgn, nkFastAsgn:
     if hasDestructor(n[0].typ):
       result = moveOrCopy(n[0], n[1], c)