diff options
-rw-r--r-- | compiler/semexprs.nim | 41 | ||||
-rw-r--r-- | tests/destructor/tdont_return_unowned_from_owned.nim | 18 |
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 |