mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-06 04:57:49 +00:00
'isolate' builtin; refs https://github.com/nim-lang/RFCs/issues/244 (#15011)
This commit is contained in:
@@ -658,7 +658,7 @@ type
|
||||
mSwap, mIsNil, mArrToSeq,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
mMove, mWasMoved, mDestroy,
|
||||
mDefault, mUnown, mAccessEnv, mReset,
|
||||
mDefault, mUnown, mIsolate, mAccessEnv, mReset,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||
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 mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($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:
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
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")
|
||||
applyFormat "cstrToNimstr(nimFloatToString($1))"
|
||||
of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
|
||||
of mStrToStr, mUnown: applyFormat("$1", "$1")
|
||||
of mStrToStr, mUnown, mIsolate: applyFormat("$1", "$1")
|
||||
else:
|
||||
assert false, $op
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ import
|
||||
procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
||||
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
|
||||
|
||||
|
||||
@@ -558,6 +558,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
let constructed = result[1].typ.base
|
||||
if constructed.requiresInit:
|
||||
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:
|
||||
result = n
|
||||
|
||||
|
||||
@@ -997,7 +997,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
c.genNarrow(n[1], d)
|
||||
c.genAsgnPatch(n[1], 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:
|
||||
unused(c, n, dest)
|
||||
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()
|
||||
Reference in New Issue
Block a user