Skip to content

Commit

Permalink
'isolate' builtin; refs nim-lang/RFCs#244 (#15011)
Browse files Browse the repository at this point in the history
  • Loading branch information
Araq authored Jul 20, 2020
1 parent bb1adf6 commit 71dd5f8
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 6 deletions.
2 changes: 1 addition & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
117 changes: 117 additions & 0 deletions compiler/isolation_check.nim
Original file line number Diff line number Diff line change
@@ -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

2 changes: 1 addition & 1 deletion compiler/jsgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 4 additions & 1 deletion compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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

2 changes: 1 addition & 1 deletion compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 31 additions & 0 deletions lib/std/isolation.nim
Original file line number Diff line number Diff line change
@@ -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 changes: 36 additions & 0 deletions tests/isolate/tisolate.nim
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 71dd5f8

Please sign in to comment.