isolation_check.nim 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2020 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Implementation of the check that `recover` needs, see
  10. ## https://github.com/nim-lang/RFCs/issues/244 for more details.
  11. import
  12. ast, types, renderer
  13. import std/intsets
  14. when defined(nimPreviewSlimSystem):
  15. import std/assertions
  16. proc canAlias(arg, ret: PType; marker: var IntSet): bool
  17. proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
  18. case n.kind
  19. of nkRecList:
  20. result = false
  21. for i in 0..<n.len:
  22. result = canAliasN(arg, n[i], marker)
  23. if result: return
  24. of nkRecCase:
  25. assert(n[0].kind == nkSym)
  26. result = canAliasN(arg, n[0], marker)
  27. if result: return
  28. for i in 1..<n.len:
  29. case n[i].kind
  30. of nkOfBranch, nkElse:
  31. result = canAliasN(arg, lastSon(n[i]), marker)
  32. if result: return
  33. else: discard
  34. of nkSym:
  35. result = canAlias(arg, n.sym.typ, marker)
  36. else: result = false
  37. proc canAlias(arg, ret: PType; marker: var IntSet): bool =
  38. if containsOrIncl(marker, ret.id):
  39. return false
  40. if ret.kind in {tyPtr, tyPointer}:
  41. # unsafe so we don't care:
  42. return false
  43. if compareTypes(arg, ret, dcEqIgnoreDistinct):
  44. return true
  45. case ret.kind
  46. of tyObject:
  47. if isFinal(ret):
  48. result = canAliasN(arg, ret.n, marker)
  49. if not result and ret.baseClass != nil:
  50. result = canAlias(arg, ret.baseClass, marker)
  51. else:
  52. result = true
  53. of tyTuple:
  54. result = false
  55. for r in ret.kids:
  56. result = canAlias(arg, r, marker)
  57. if result: break
  58. of tyArray, tySequence, tyDistinct, tyGenericInst,
  59. tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
  60. result = canAlias(arg, ret.skipModifier, marker)
  61. of tyProc:
  62. result = ret.callConv == ccClosure
  63. else:
  64. result = false
  65. proc isValueOnlyType(t: PType): bool =
  66. # t doesn't contain pointers and references
  67. proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
  68. result = not types.searchTypeFor(t, wrap)
  69. type
  70. SearchResult = enum
  71. NotFound, Abort, Found
  72. proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult
  73. proc containsDangerousRefAux(n: PNode; marker: var IntSet): SearchResult =
  74. result = NotFound
  75. case n.kind
  76. of nkRecList:
  77. for i in 0..<n.len:
  78. result = containsDangerousRefAux(n[i], marker)
  79. if result == Found: return result
  80. of nkRecCase:
  81. assert(n[0].kind == nkSym)
  82. result = containsDangerousRefAux(n[0], marker)
  83. if result == Found: return result
  84. for i in 1..<n.len:
  85. case n[i].kind
  86. of nkOfBranch, nkElse:
  87. result = containsDangerousRefAux(lastSon(n[i]), marker)
  88. if result == Found: return result
  89. else: discard
  90. of nkSym:
  91. result = containsDangerousRefAux(n.sym.typ, marker)
  92. else: discard
  93. proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
  94. result = NotFound
  95. if t == nil: return result
  96. if containsOrIncl(marker, t.id): return result
  97. if t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure):
  98. result = Found
  99. elif tfSendable in t.flags:
  100. result = Abort
  101. else:
  102. # continue the type traversal:
  103. result = NotFound
  104. if result != NotFound: return result
  105. case t.kind
  106. of tyObject:
  107. if t.baseClass != nil:
  108. result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker)
  109. if result == NotFound: result = containsDangerousRefAux(t.n, marker)
  110. of tyGenericInst, tyDistinct, tyAlias, tySink:
  111. result = containsDangerousRefAux(skipModifier(t), marker)
  112. of tyArray, tySet, tySequence:
  113. result = containsDangerousRefAux(t.elementType, marker)
  114. of tyTuple:
  115. for a in t.kids:
  116. result = containsDangerousRefAux(a, marker)
  117. if result == Found: return result
  118. else:
  119. discard
  120. proc containsDangerousRef(t: PType): bool =
  121. # a `ref` type is "dangerous" if it occurs not within a type that is like `Isolated[T]`.
  122. # For example:
  123. # `ref int` # dangerous
  124. # `Isolated[ref int]` # not dangerous
  125. var marker = initIntSet()
  126. result = containsDangerousRefAux(t, marker) == Found
  127. proc canAlias*(arg, ret: PType): bool =
  128. if isValueOnlyType(arg):
  129. # can alias only with addr(arg.x) and we don't care if it is not safe
  130. result = false
  131. else:
  132. var marker = initIntSet()
  133. result = canAlias(arg, ret, marker)
  134. const
  135. SomeVar = {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
  136. proc containsVariable(n: PNode): bool =
  137. case n.kind
  138. of nodesToIgnoreSet:
  139. result = false
  140. of nkSym:
  141. result = n.sym.kind in SomeVar
  142. else:
  143. for ch in n:
  144. if containsVariable(ch): return true
  145. result = false
  146. proc checkIsolate*(n: PNode): bool =
  147. if types.containsTyRef(n.typ):
  148. # XXX Maybe require that 'n.typ' is acyclic. This is not much
  149. # worse than the already exisiting inheritance and closure restrictions.
  150. case n.kind
  151. of nkCharLit..nkNilLit:
  152. result = true
  153. of nkCallKinds:
  154. # XXX: as long as we don't update the analysis while examining arguments
  155. # we can do an early check of the return type, otherwise this is a
  156. # bug and needs to be moved below
  157. if tfNoSideEffect notin n[0].typ.flags:
  158. return false
  159. for i in 1..<n.len:
  160. if checkIsolate(n[i]):
  161. discard "fine, it is isolated already"
  162. else:
  163. let argType = n[i].typ
  164. if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
  165. if argType.canAlias(n.typ) or containsVariable(n[i]):
  166. # bug #19013: Alias information is not enough, we need to check for potential
  167. # "overlaps". I claim the problem can only happen by reading again from a location
  168. # that materialized which is only possible if a variable that contains a `ref`
  169. # is involved.
  170. return false
  171. result = true
  172. of nkIfStmt, nkIfExpr:
  173. result = false
  174. for it in n:
  175. result = checkIsolate(it.lastSon)
  176. if not result: break
  177. of nkCaseStmt:
  178. result = false
  179. for i in 1..<n.len:
  180. result = checkIsolate(n[i].lastSon)
  181. if not result: break
  182. of nkObjConstr:
  183. result = true
  184. for i in 1..<n.len:
  185. result = checkIsolate(n[i].lastSon)
  186. if not result: break
  187. of nkBracket, nkTupleConstr, nkPar:
  188. result = false
  189. for it in n:
  190. result = checkIsolate(it)
  191. if not result: break
  192. of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
  193. result = checkIsolate(n[1])
  194. of nkObjUpConv, nkObjDownConv, nkDotExpr:
  195. result = checkIsolate(n[0])
  196. of nkStmtList, nkStmtListExpr:
  197. if n.len > 0:
  198. result = checkIsolate(n[^1])
  199. else:
  200. result = false
  201. of nkSym:
  202. result = true
  203. if n.sym.kind in SomeVar:
  204. let argType = n.typ
  205. if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
  206. result = false
  207. else:
  208. # unanalysable expression:
  209. result = false
  210. else:
  211. # no ref, no cry:
  212. result = true