diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2018-03-24 08:36:58 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-03-24 08:37:09 +0100 |
commit | 6f747674be8eacc6d0fbd62b5dcfdb75c939bcc1 (patch) | |
tree | 99305141f474518bdac7ff8bf953d4eacc95e032 | |
parent | 88d8a14fb48af6d681aa61a8d2b4268bbc5555c7 (diff) | |
download | Nim-6f747674be8eacc6d0fbd62b5dcfdb75c939bcc1.tar.gz |
more checking for 'var T' as return type; refs #7373
-rw-r--r-- | compiler/parampatterns.nim | 30 | ||||
-rw-r--r-- | compiler/semexprs.nim | 9 | ||||
-rw-r--r-- | tests/varres/tvarres1.nim | 2 | ||||
-rw-r--r-- | tests/varres/tvarres_via_forwarding.nim | 12 |
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() |