views: yet another bugfix (#15447)

* views: yet another bugfix

* views: extended the spec

* views: take into account potential hidden mutations via proc calls
This commit is contained in:
Andreas Rumpf 2020-10-01 15:04:36 +02:00 • committed by GitHub
commit ab405c936e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 10 deletions

View file

@ -707,11 +707,6 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
localError(c.c.config, n.info, "invalid expression") localError(c.c.config, n.info, "invalid expression")
result = n result = n
proc stupidStmtListExpr(n: PNode): bool =
for i in 0..<n.len-1:
if n[i].kind notin {nkEmpty, nkCommentStmt}: return false
result = true
result = n result = n
case n.kind case n.kind
of nkIdent: of nkIdent:

View file

@ -190,3 +190,8 @@ proc getRoot*(n: PNode): PSym =
of nkCallKinds: of nkCallKinds:
if getMagic(n) == mSlice: result = getRoot(n[1]) if getMagic(n) == mSlice: result = getRoot(n[1])
else: discard else: discard
proc stupidStmtListExpr*(n: PNode): bool =
for i in 0..<n.len-1:
if n[i].kind notin {nkEmpty, nkCommentStmt}: return false
result = true

View file

@ -29,7 +29,7 @@
## for a high-level description of how borrow checking works. ## for a high-level description of how borrow checking works.
import ast, types, lineinfos, options, msgs, renderer, typeallowed import ast, types, lineinfos, options, msgs, renderer, typeallowed
from trees import getMagic, whichPragma from trees import getMagic, whichPragma, stupidStmtListExpr
from wordrecg import wNoSideEffect from wordrecg import wNoSideEffect
from isolation_check import canAlias from isolation_check import canAlias
@ -309,6 +309,11 @@ proc pathExpr(node: PNode; owner: PSym): PNode =
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast, of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast,
nkObjUpConv, nkObjDownConv: nkObjUpConv, nkObjDownConv:
n = n.lastSon n = n.lastSon
of nkStmtList, nkStmtListExpr:
if n.len > 0 and stupidStmtListExpr(n):
n = n.lastSon
else:
break
of nkCallKinds: of nkCallKinds:
if n.len > 1: if n.len > 1:
if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice: if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice:
@ -551,6 +556,10 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) =
localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view") localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view")
of noView: discard "nothing to do" of noView: discard "nothing to do"
proc containsPointer(t: PType): bool =
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr}
result = types.searchTypeFor(t, wrap)
proc deps(c: var Partitions; dest, src: PNode) = proc deps(c: var Partitions; dest, src: PNode) =
if borrowChecking in c.goals: if borrowChecking in c.goals:
borrowingAsgn(c, dest, src) borrowingAsgn(c, dest, src)
@ -559,9 +568,7 @@ proc deps(c: var Partitions; dest, src: PNode) =
allRoots(dest, targets) allRoots(dest, targets)
allRoots(src, sources) allRoots(src, sources)
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} let destIsComplex = containsPointer(dest.typ)
let destIsComplex = types.searchTypeFor(dest.typ, wrap)
for t in targets: for t in targets:
if dest.kind != nkSym and c.inNoSideEffectSection == 0: if dest.kind != nkSym and c.inNoSideEffectSection == 0:
@ -607,6 +614,14 @@ const
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr} nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr}
proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) =
if constParameters in c.goals and tfNoSideEffect in callee.flags:
discard "we know there are no hidden mutations through an immutable parameter"
elif c.inNoSideEffectSection == 0 and containsPointer(n.typ):
var roots: seq[PSym]
allRoots(n, roots)
for r in roots: potentialMutation(c, r, n.info)
proc traverse(c: var Partitions; n: PNode) = proc traverse(c: var Partitions; n: PNode) =
inc c.abstractTime inc c.abstractTime
case n.kind case n.kind
@ -640,6 +655,7 @@ proc traverse(c: var Partitions; n: PNode) =
let parameters = n[0].typ let parameters = n[0].typ
let L = if parameters != nil: parameters.len else: 0 let L = if parameters != nil: parameters.len else: 0
let m = getMagic(n)
for i in 1..<n.len: for i in 1..<n.len:
let it = n[i] let it = n[i]
@ -659,6 +675,8 @@ proc traverse(c: var Partitions; n: PNode) =
# 'paramType[0]' is still a view type, this is not a typo! # 'paramType[0]' is still a view type, this is not a typo!
if directViewType(paramType[0]) == noView and classifyViewType(paramType[0]) != noView: if directViewType(paramType[0]) == noView and classifyViewType(paramType[0]) != noView:
borrowingCall(c, paramType[0], n, i) borrowingCall(c, paramType[0], n, i)
elif borrowChecking in c.goals and m == mNone:
potentialMutationViaArg(c, n[i], parameters)
of nkAddr, nkHiddenAddr: of nkAddr, nkHiddenAddr:
traverse(c, n[0]) traverse(c, n[0])

View file

@ -1988,6 +1988,22 @@ borrow operation of ``v`` even though a human being can easily see that it
will never do that at runtime. will never do that at runtime.
Start of a borrow
-----------------
A borrow starts with one of the following:
- The assignment of a non-view-type to a view-type.
- The assingment of a location that is derived from a local parameter
to a view-type.
End of a borrow
---------------
A borrow operation ends with the last usage of the view variable.
Reborrows Reborrows
--------- ---------

View file

@ -1,5 +1,5 @@
discard """ discard """
cmd: "nim c --experimental:strictFuncs $file" cmd: "nim c --experimental:strictFuncs --experimental:views $file"
""" """
import tables, streams, nre, parsecsv import tables, streams, nre, parsecsv