Skip to content

Commit

Permalink
more checking for 'var T' as return type; refs #7373
Browse files Browse the repository at this point in the history
  • Loading branch information
Araq committed Mar 24, 2018
1 parent 88d8a14 commit 6f74767
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 3 deletions.
30 changes: 29 additions & 1 deletion compiler/parampatterns.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down
9 changes: 8 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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}))
Expand Down
2 changes: 1 addition & 1 deletion tests/varres/tvarres1.nim
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 12 additions & 0 deletions tests/varres/tvarres_via_forwarding.nim
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 6f74767

Please sign in to comment.