summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semexprs.nim41
-rw-r--r--tests/destructor/tdont_return_unowned_from_owned.nim18
2 files changed, 48 insertions, 11 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 38e079fdd..ead9dab27 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1526,23 +1526,52 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
       x.typ.flags.incl tfVarIsPtr
       #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
 
-proc asgnToResult(c: PContext, n, le, ri: PNode) =
+proc borrowCheck(c: PContext, n, le, ri: PNode) =
+  const
+    PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
+                  nkBracketExpr, nkAddr, nkHiddenAddr,
+                  nkObjDownConv, nkObjUpConv}
+    PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
+
+  proc getRoot(n: PNode; followDeref: bool): PNode =
+    result = n
+    while true:
+      case result.kind
+      of nkDerefExpr, nkHiddenDeref:
+        if followDeref: result = result[0]
+        else: break
+      of PathKinds0:
+        result = result[0]
+      of PathKinds1:
+        result = result[1]
+      else: break
+
   proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} =
+    let n = getRoot(ri, followDeref = false)
     result = (ri.kind in nkCallKinds+{nkObjConstr}) or
-      (ri.kind == nkSym and ri.sym.owner == c.p.owner)
+      (n.kind == nkSym and n.sym.owner == c.p.owner)
+
+  proc escapes(c: PContext; le: PNode): bool {.inline.} =
+    # param[].foo[] = self  definitely escapes, we don't need to
+    # care about pointer derefs:
+    let n = getRoot(le, followDeref = true)
+    result = n.kind == nkSym and n.sym.kind == skParam
 
   # Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x':
   const absInst = abstractInst - {tyOwned}
   if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and
       le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and
       scopedLifetime(c, ri):
-    localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
-      "use 'owned(" & typeToString(le.typ) & ")' as the return type")
+    if le.kind == nkSym and le.sym.kind == skResult:
+      localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
+        "use 'owned(" & typeToString(le.typ) & ")' as the return type")
+    elif escapes(c, le):
+      localError(c.config, n.info,
+        "assignment produces a dangling ref: the unowned ref lives longer than the owned ref")
 
 template resultTypeIsInferrable(typ: PType): untyped =
   typ.isMetaType and typ.kind != tyTypeDesc
 
-
 proc goodLineInfo(arg: PNode): TLineinfo =
   if arg.kind == nkStmtListExpr and arg.len > 0:
     goodLineInfo(arg[^1])
@@ -1629,7 +1658,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
           c.p.owner.typ.sons[0] = rhsTyp
         else:
           typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
-      asgnToResult(c, n, n.sons[0], rhs)
+    borrowCheck(c, n, lhs, rhs)
 
     n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
     liftTypeBoundOps(c, lhs.typ, lhs.info)
diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim
index 490134ee9..5794dec1d 100644
--- a/tests/destructor/tdont_return_unowned_from_owned.nim
+++ b/tests/destructor/tdont_return_unowned_from_owned.nim
@@ -9,13 +9,13 @@ proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
 
 expression: new(result)
 tdont_return_unowned_from_owned.nim(30, 6) Error: illformed AST:
+tdont_return_unowned_from_owned.nim(38, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(39, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
 '''
-  errormsg: "illformed AST:"
-  line: 30
+  errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type"
+  line: 43
 """
-
-
-
 # bug #11073
 type
   Obj = ref object
@@ -33,3 +33,11 @@ let a = newObjA()
 let b = newObjB()
 let c = newObjC()
 
+proc testA(result: var (RootRef, RootRef)) =
+  let r: owned RootRef = RootRef()
+  result[0] = r
+  result[1] = RootRef()
+
+proc testB(): RootRef =
+  let r: owned RootRef = RootRef()
+  result = r