'isolate' builtin; refs https://github.com/nim-lang/RFCs/issues/244 (#15011)
This commit is contained in:
parent
bb1adf6a70
commit
71dd5f85b9
9 changed files with 194 additions and 6 deletions
|
|
@ -658,7 +658,7 @@ type
|
||||||
mSwap, mIsNil, mArrToSeq,
|
mSwap, mIsNil, mArrToSeq,
|
||||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||||
mMove, mWasMoved, mDestroy,
|
mMove, mWasMoved, mDestroy,
|
||||||
mDefault, mUnown, mAccessEnv, mReset,
|
mDefault, mUnown, mIsolate, mAccessEnv, mReset,
|
||||||
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
||||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||||
mOrdinal,
|
mOrdinal,
|
||||||
|
|
|
||||||
|
|
@ -2209,7 +2209,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
|
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
|
||||||
of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
|
of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
|
||||||
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
|
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
|
||||||
of mStrToStr, mUnown: expr(p, e[1], d)
|
of mStrToStr, mUnown, mIsolate: expr(p, e[1], d)
|
||||||
of mEnumToStr:
|
of mEnumToStr:
|
||||||
if optTinyRtti in p.config.globalOptions:
|
if optTinyRtti in p.config.globalOptions:
|
||||||
genEnumToStr(p, e, d)
|
genEnumToStr(p, e, d)
|
||||||
|
|
|
||||||
117
compiler/isolation_check.nim
Normal file
117
compiler/isolation_check.nim
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2020 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## Implementation of the check that `recover` needs, see
|
||||||
|
## https://github.com/nim-lang/RFCs/issues/244 for more details.
|
||||||
|
|
||||||
|
import
|
||||||
|
ast, types, renderer, idents, intsets, options, msgs
|
||||||
|
|
||||||
|
proc canAlias(arg, ret: PType; marker: var IntSet): bool
|
||||||
|
|
||||||
|
proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
|
||||||
|
case n.kind
|
||||||
|
of nkRecList:
|
||||||
|
for i in 0..<n.len:
|
||||||
|
result = canAliasN(arg, n[i], marker)
|
||||||
|
if result: return
|
||||||
|
of nkRecCase:
|
||||||
|
assert(n[0].kind == nkSym)
|
||||||
|
result = canAliasN(arg, n[0], marker)
|
||||||
|
if result: return
|
||||||
|
for i in 1..<n.len:
|
||||||
|
case n[i].kind
|
||||||
|
of nkOfBranch, nkElse:
|
||||||
|
result = canAliasN(arg, lastSon(n[i]), marker)
|
||||||
|
if result: return
|
||||||
|
else: discard
|
||||||
|
of nkSym:
|
||||||
|
result = canAlias(arg, n.sym.typ, marker)
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
proc canAlias(arg, ret: PType; marker: var IntSet): bool =
|
||||||
|
if containsOrIncl(marker, ret.id):
|
||||||
|
return false
|
||||||
|
|
||||||
|
if ret.kind in {tyPtr, tyPointer}:
|
||||||
|
# unsafe so we don't care:
|
||||||
|
return false
|
||||||
|
if compareTypes(arg, ret, dcEqIgnoreDistinct):
|
||||||
|
return true
|
||||||
|
case ret.kind
|
||||||
|
of tyObject:
|
||||||
|
if isFinal(ret):
|
||||||
|
result = canAliasN(arg, ret.n, marker)
|
||||||
|
if not result and ret.len > 0 and ret[0] != nil:
|
||||||
|
result = canAlias(arg, ret[0], marker)
|
||||||
|
else:
|
||||||
|
result = true
|
||||||
|
of tyTuple:
|
||||||
|
for i in 0..<ret.len:
|
||||||
|
result = canAlias(arg, ret[i], marker)
|
||||||
|
if result: break
|
||||||
|
of tyArray, tySequence, tyDistinct, tyGenericInst,
|
||||||
|
tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
|
||||||
|
result = canAlias(arg, ret.lastSon, marker)
|
||||||
|
of tyProc:
|
||||||
|
result = ret.callConv == ccClosure
|
||||||
|
else:
|
||||||
|
result = false
|
||||||
|
|
||||||
|
proc canAlias(arg, ret: PType): bool =
|
||||||
|
var marker = initIntSet()
|
||||||
|
result = canAlias(arg, ret, marker)
|
||||||
|
|
||||||
|
proc checkIsolate*(n: PNode): bool =
|
||||||
|
if types.containsTyRef(n.typ):
|
||||||
|
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
||||||
|
# worse than the already exisiting inheritance and closure restrictions.
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkNilLit:
|
||||||
|
result = true
|
||||||
|
of nkCallKinds:
|
||||||
|
if n[0].typ.flags * {tfGcSafe, tfNoSideEffect} == {}:
|
||||||
|
return false
|
||||||
|
for i in 1..<n.len:
|
||||||
|
if checkIsolate(n[i]):
|
||||||
|
discard "fine, it is isolated already"
|
||||||
|
else:
|
||||||
|
let argType = n[i].typ
|
||||||
|
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
|
||||||
|
if argType.canAlias(n.typ):
|
||||||
|
return false
|
||||||
|
result = true
|
||||||
|
of nkIfStmt, nkIfExpr:
|
||||||
|
for it in n:
|
||||||
|
result = checkIsolate(it.lastSon)
|
||||||
|
if not result: break
|
||||||
|
of nkCaseStmt, nkObjConstr:
|
||||||
|
for i in 1..<n.len:
|
||||||
|
result = checkIsolate(n[i].lastSon)
|
||||||
|
if not result: break
|
||||||
|
of nkBracket, nkTupleConstr, nkPar:
|
||||||
|
for it in n:
|
||||||
|
result = checkIsolate(it)
|
||||||
|
if not result: break
|
||||||
|
of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
|
||||||
|
result = checkIsolate(n[1])
|
||||||
|
of nkObjUpConv, nkObjDownConv, nkDotExpr:
|
||||||
|
result = checkIsolate(n[0])
|
||||||
|
of nkStmtList, nkStmtListExpr:
|
||||||
|
if n.len > 0:
|
||||||
|
result = checkIsolate(n[^1])
|
||||||
|
else:
|
||||||
|
result = false
|
||||||
|
else:
|
||||||
|
# unanalysable expression:
|
||||||
|
result = false
|
||||||
|
else:
|
||||||
|
# no ref, no cry:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
|
@ -653,7 +653,7 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||||
useMagic(p, "nimFloatToString")
|
useMagic(p, "nimFloatToString")
|
||||||
applyFormat "cstrToNimstr(nimFloatToString($1))"
|
applyFormat "cstrToNimstr(nimFloatToString($1))"
|
||||||
of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
|
of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
|
||||||
of mStrToStr, mUnown: applyFormat("$1", "$1")
|
of mStrToStr, mUnown, mIsolate: applyFormat("$1", "$1")
|
||||||
else:
|
else:
|
||||||
assert false, $op
|
assert false, $op
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ import
|
||||||
procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
|
procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||||
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
||||||
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
|
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
|
||||||
lowerings, plugins/active, rod, lineinfos, strtabs, int128
|
lowerings, plugins/active, rod, lineinfos, strtabs, int128,
|
||||||
|
isolation_check
|
||||||
|
|
||||||
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
|
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -558,6 +558,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
let constructed = result[1].typ.base
|
let constructed = result[1].typ.base
|
||||||
if constructed.requiresInit:
|
if constructed.requiresInit:
|
||||||
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
|
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
|
||||||
|
of mIsolate:
|
||||||
|
if not checkIsolate(n[1]):
|
||||||
|
localError(c.config, n.info, "expression cannot be isolated: " & $n[1])
|
||||||
|
result = n
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -997,7 +997,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
c.genNarrow(n[1], d)
|
c.genNarrow(n[1], d)
|
||||||
c.genAsgnPatch(n[1], d)
|
c.genAsgnPatch(n[1], d)
|
||||||
c.freeTemp(d)
|
c.freeTemp(d)
|
||||||
of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest)
|
of mOrd, mChr, mArrToSeq, mUnown, mIsolate: c.gen(n[1], dest)
|
||||||
of mNew, mNewFinalize:
|
of mNew, mNewFinalize:
|
||||||
unused(c, n, dest)
|
unused(c, n, dest)
|
||||||
c.genNew(n)
|
c.genNew(n)
|
||||||
|
|
|
||||||
31
lib/std/isolation.nim
Normal file
31
lib/std/isolation.nim
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2020 Nim contributors
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements the `Isolated[T]` type for
|
||||||
|
## safe construction of isolated subgraphs that can be
|
||||||
|
## passed efficiently to different channels and threads.
|
||||||
|
|
||||||
|
type
|
||||||
|
Isolated*[T] = object ## Isolated data can only be moved, not copied.
|
||||||
|
value: T
|
||||||
|
|
||||||
|
proc `=`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.}
|
||||||
|
|
||||||
|
proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} =
|
||||||
|
# delegate to value's sink operation
|
||||||
|
`=sink`(dest.value, src.value)
|
||||||
|
|
||||||
|
proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} =
|
||||||
|
# delegate to value's destroy operation
|
||||||
|
`=destroy`(dest.value)
|
||||||
|
|
||||||
|
func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".}
|
||||||
|
## Create an isolated subgraph from the expression `value`.
|
||||||
|
## Please read https://github.com/nim-lang/RFCs/issues/244
|
||||||
|
## for more details.
|
||||||
36
tests/isolate/tisolate.nim
Normal file
36
tests/isolate/tisolate.nim
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "expression cannot be isolated: select(a, b)"
|
||||||
|
line: 34
|
||||||
|
"""
|
||||||
|
|
||||||
|
import std / isolation
|
||||||
|
|
||||||
|
import json, streams
|
||||||
|
|
||||||
|
proc f(): seq[int] =
|
||||||
|
@[1, 2, 3]
|
||||||
|
|
||||||
|
type
|
||||||
|
Node = ref object
|
||||||
|
x: string
|
||||||
|
|
||||||
|
proc g(): Node = nil
|
||||||
|
|
||||||
|
proc select(a, b: Node): Node =
|
||||||
|
a
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
discard isolate f()
|
||||||
|
|
||||||
|
|
||||||
|
discard isolate g()
|
||||||
|
|
||||||
|
discard isolate select(Node(x: "a"), nil)
|
||||||
|
discard isolate select(Node(x: "a"), Node(x: "b"))
|
||||||
|
|
||||||
|
discard isolate parseJson(newFileStream("my.json"), "my.json")
|
||||||
|
|
||||||
|
var a, b: Node
|
||||||
|
discard isolate select(a, b)
|
||||||
|
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue