summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2018-03-24 08:36:58 +0100
committerAndreas Rumpf <rumpf_a@web.de>2018-03-24 08:37:09 +0100
commit6f747674be8eacc6d0fbd62b5dcfdb75c939bcc1 (patch)
tree99305141f474518bdac7ff8bf953d4eacc95e032
parent88d8a14fb48af6d681aa61a8d2b4268bbc5555c7 (diff)
downloadNim-6f747674be8eacc6d0fbd62b5dcfdb75c939bcc1.tar.gz
more checking for 'var T' as return type; refs #7373
-rw-r--r--compiler/parampatterns.nim30
-rw-r--r--compiler/semexprs.nim9
-rw-r--r--tests/varres/tvarres1.nim2
-rw-r--r--tests/varres/tvarres_via_forwarding.nim12
4 files changed, 50 insertions, 3 deletions
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index aacd25795..6c37c976a 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -178,6 +178,34 @@ type
     arDiscriminant,           # is a discriminant
     arStrange                 # it is a strange beast like 'typedesc[var T]'
 
+proc exprRoot*(n: PNode): PSym =
+  var it = n
+  # the sem'check can generate a spurious 'nkHiddenDeref' for some
+  # cases. we skip it here:
+  if it.kind == nkHiddenDeref: it = it[0]
+  while true:
+    case it.kind
+    of nkSym: return it.sym
+    of nkDotExpr, nkBracketExpr, nkHiddenAddr,
+       nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
+      it = it[0]
+    of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+      it = it[1]
+    of nkStmtList, nkStmtListExpr:
+      if it.len > 0 and it.typ != nil: it = it.lastSon
+      else: break
+    of nkCallKinds:
+      if it.typ != nil and it.typ.kind == tyVar and it.len > 1:
+        # See RFC #7373, calls returning 'var T' are assumed to
+        # return a view into the first argument (if there is one):
+        it = it[1]
+      else:
+        break
+    else:
+      # nkHiddenDeref, nkDerefExpr: assume the 'var T' addresses
+      # the heap and so the location is not on the stack.
+      break
+
 proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
   ## 'owner' can be nil!
   result = arNone
@@ -189,7 +217,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
     let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
                 else: {skVar, skResult, skTemp}
     if n.sym.kind in kinds:
-      if owner != nil and owner.id == n.sym.owner.id and
+      if owner != nil and owner == n.sym.owner and
           sfGlobal notin n.sym.flags:
         result = arLocalLValue
       else:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 49beadc8b..f6226ad77 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1301,13 +1301,20 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
     #analyseIfAddressTakenInCall(c, result)
 
 proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
+  # See RFC #7373, calls returning 'var T' are assumed to
+  # return a view into the first argument (if there is one):
+  let root = exprRoot(n)
+  if root != nil and root.owner == c.p.owner and
+      root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags:
+    localError(n.info, "'$1' escapes its stack frame; context: '$2'" % [
+      root.name.s, renderTree(n, {renderNoComments})])
   case n.kind
   of nkHiddenAddr, nkAddr: return n
   of nkHiddenDeref, nkDerefExpr: return n.sons[0]
   of nkBracketExpr:
     if len(n) == 1: return n.sons[0]
   else: discard
-  var valid = isAssignable(c, n)
+  let valid = isAssignable(c, n)
   if valid != arLValue:
     if valid == arLocalLValue:
       localError(n.info, errXStackEscape, renderTree(n, {renderNoComments}))
diff --git a/tests/varres/tvarres1.nim b/tests/varres/tvarres1.nim
index 849805768..5a5247142 100644
--- a/tests/varres/tvarres1.nim
+++ b/tests/varres/tvarres1.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tvarres1.nim"
   line: 12
-  errormsg: "address of 'bla' may not escape its stack frame"
+  errormsg: "'bla' escapes its stack frame; context: 'bla'"
 """
 
 var
diff --git a/tests/varres/tvarres_via_forwarding.nim b/tests/varres/tvarres_via_forwarding.nim
new file mode 100644
index 000000000..8fd3dfcfd
--- /dev/null
+++ b/tests/varres/tvarres_via_forwarding.nim
@@ -0,0 +1,12 @@
+discard """
+  line: 10
+  errormsg: "'y' escapes its stack frame; context: 'forward(y)'"
+"""
+
+proc forward(x: var int): var int = result = x
+
+proc foo(): var int =
+  var y = 9
+  result = forward(y)
+
+echo foo()