diff options
Diffstat (limited to 'compiler/isolation_check.nim')
-rw-r--r-- | compiler/isolation_check.nim | 99 |
1 files changed, 90 insertions, 9 deletions
diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 2674605dc..17fbde29e 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -11,7 +11,9 @@ ## https://github.com/nim-lang/RFCs/issues/244 for more details. import - ast, types, renderer, intsets + ast, types, renderer + +import std/intsets when defined(nimPreviewSlimSystem): import std/assertions @@ -21,6 +23,7 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool = case n.kind of nkRecList: + result = false for i in 0..<n.len: result = canAliasN(arg, n[i], marker) if result: return @@ -36,7 +39,7 @@ proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool = else: discard of nkSym: result = canAlias(arg, n.sym.typ, marker) - else: discard + else: result = false proc canAlias(arg, ret: PType; marker: var IntSet): bool = if containsOrIncl(marker, ret.id): @@ -51,17 +54,18 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool = 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) + if not result and ret.baseClass != nil: + result = canAlias(arg, ret.baseClass, marker) else: result = true of tyTuple: - for i in 0..<ret.len: - result = canAlias(arg, ret[i], marker) + result = false + for r in ret.kids: + result = canAlias(arg, r, marker) if result: break of tyArray, tySequence, tyDistinct, tyGenericInst, tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef: - result = canAlias(arg, ret.lastSon, marker) + result = canAlias(arg, ret.skipModifier, marker) of tyProc: result = ret.callConv == ccClosure else: @@ -72,6 +76,71 @@ proc isValueOnlyType(t: PType): bool = proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent} result = not types.searchTypeFor(t, wrap) +type + SearchResult = enum + NotFound, Abort, Found + +proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult + +proc containsDangerousRefAux(n: PNode; marker: var IntSet): SearchResult = + result = NotFound + case n.kind + of nkRecList: + for i in 0..<n.len: + result = containsDangerousRefAux(n[i], marker) + if result == Found: return result + of nkRecCase: + assert(n[0].kind == nkSym) + result = containsDangerousRefAux(n[0], marker) + if result == Found: return result + for i in 1..<n.len: + case n[i].kind + of nkOfBranch, nkElse: + result = containsDangerousRefAux(lastSon(n[i]), marker) + if result == Found: return result + else: discard + of nkSym: + result = containsDangerousRefAux(n.sym.typ, marker) + else: discard + +proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult = + result = NotFound + if t == nil: return result + if containsOrIncl(marker, t.id): return result + + if t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure): + result = Found + elif tfSendable in t.flags: + result = Abort + else: + # continue the type traversal: + result = NotFound + + if result != NotFound: return result + case t.kind + of tyObject: + if t.baseClass != nil: + result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker) + if result == NotFound: result = containsDangerousRefAux(t.n, marker) + of tyGenericInst, tyDistinct, tyAlias, tySink: + result = containsDangerousRefAux(skipModifier(t), marker) + of tyArray, tySet, tySequence: + result = containsDangerousRefAux(t.elementType, marker) + of tyTuple: + for a in t.kids: + result = containsDangerousRefAux(a, marker) + if result == Found: return result + else: + discard + +proc containsDangerousRef(t: PType): bool = + # a `ref` type is "dangerous" if it occurs not within a type that is like `Isolated[T]`. + # For example: + # `ref int` # dangerous + # `Isolated[ref int]` # not dangerous + var marker = initIntSet() + result = containsDangerousRefAux(t, marker) == Found + proc canAlias*(arg, ret: PType): bool = if isValueOnlyType(arg): # can alias only with addr(arg.x) and we don't care if it is not safe @@ -80,12 +149,15 @@ proc canAlias*(arg, ret: PType): bool = var marker = initIntSet() result = canAlias(arg, ret, marker) +const + SomeVar = {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp} + proc containsVariable(n: PNode): bool = case n.kind of nodesToIgnoreSet: result = false of nkSym: - result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp} + result = n.sym.kind in SomeVar else: for ch in n: if containsVariable(ch): return true @@ -109,7 +181,7 @@ proc checkIsolate*(n: PNode): bool = discard "fine, it is isolated already" else: let argType = n[i].typ - if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType): + if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType): if argType.canAlias(n.typ) or containsVariable(n[i]): # bug #19013: Alias information is not enough, we need to check for potential # "overlaps". I claim the problem can only happen by reading again from a location @@ -118,10 +190,12 @@ proc checkIsolate*(n: PNode): bool = return false result = true of nkIfStmt, nkIfExpr: + result = false for it in n: result = checkIsolate(it.lastSon) if not result: break of nkCaseStmt: + result = false for i in 1..<n.len: result = checkIsolate(n[i].lastSon) if not result: break @@ -131,6 +205,7 @@ proc checkIsolate*(n: PNode): bool = result = checkIsolate(n[i].lastSon) if not result: break of nkBracket, nkTupleConstr, nkPar: + result = false for it in n: result = checkIsolate(it) if not result: break @@ -143,6 +218,12 @@ proc checkIsolate*(n: PNode): bool = result = checkIsolate(n[^1]) else: result = false + of nkSym: + result = true + if n.sym.kind in SomeVar: + let argType = n.typ + if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType): + result = false else: # unanalysable expression: result = false |