isolation_check.nim 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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, intsets
  13. when defined(nimPreviewSlimSystem):
  14. import std/assertions
  15. proc canAlias(arg, ret: PType; marker: var IntSet): bool
  16. proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
  17. case n.kind
  18. of nkRecList:
  19. for i in 0..<n.len:
  20. result = canAliasN(arg, n[i], marker)
  21. if result: return
  22. of nkRecCase:
  23. assert(n[0].kind == nkSym)
  24. result = canAliasN(arg, n[0], marker)
  25. if result: return
  26. for i in 1..<n.len:
  27. case n[i].kind
  28. of nkOfBranch, nkElse:
  29. result = canAliasN(arg, lastSon(n[i]), marker)
  30. if result: return
  31. else: discard
  32. of nkSym:
  33. result = canAlias(arg, n.sym.typ, marker)
  34. else: discard
  35. proc canAlias(arg, ret: PType; marker: var IntSet): bool =
  36. if containsOrIncl(marker, ret.id):
  37. return false
  38. if ret.kind in {tyPtr, tyPointer}:
  39. # unsafe so we don't care:
  40. return false
  41. if compareTypes(arg, ret, dcEqIgnoreDistinct):
  42. return true
  43. case ret.kind
  44. of tyObject:
  45. if isFinal(ret):
  46. result = canAliasN(arg, ret.n, marker)
  47. if not result and ret.len > 0 and ret[0] != nil:
  48. result = canAlias(arg, ret[0], marker)
  49. else:
  50. result = true
  51. of tyTuple:
  52. for i in 0..<ret.len:
  53. result = canAlias(arg, ret[i], marker)
  54. if result: break
  55. of tyArray, tySequence, tyDistinct, tyGenericInst,
  56. tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
  57. result = canAlias(arg, ret.lastSon, marker)
  58. of tyProc:
  59. result = ret.callConv == ccClosure
  60. else:
  61. result = false
  62. proc isValueOnlyType(t: PType): bool =
  63. # t doesn't contain pointers and references
  64. proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
  65. result = not types.searchTypeFor(t, wrap)
  66. proc canAlias*(arg, ret: PType): bool =
  67. if isValueOnlyType(arg):
  68. # can alias only with addr(arg.x) and we don't care if it is not safe
  69. result = false
  70. else:
  71. var marker = initIntSet()
  72. result = canAlias(arg, ret, marker)
  73. proc containsVariable(n: PNode): bool =
  74. case n.kind
  75. of nodesToIgnoreSet:
  76. result = false
  77. of nkSym:
  78. result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
  79. else:
  80. for ch in n:
  81. if containsVariable(ch): return true
  82. result = false
  83. proc checkIsolate*(n: PNode): bool =
  84. if types.containsTyRef(n.typ):
  85. # XXX Maybe require that 'n.typ' is acyclic. This is not much
  86. # worse than the already exisiting inheritance and closure restrictions.
  87. case n.kind
  88. of nkCharLit..nkNilLit:
  89. result = true
  90. of nkCallKinds:
  91. # XXX: as long as we don't update the analysis while examining arguments
  92. # we can do an early check of the return type, otherwise this is a
  93. # bug and needs to be moved below
  94. if tfNoSideEffect notin n[0].typ.flags:
  95. return false
  96. for i in 1..<n.len:
  97. if checkIsolate(n[i]):
  98. discard "fine, it is isolated already"
  99. else:
  100. let argType = n[i].typ
  101. if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
  102. if argType.canAlias(n.typ) or containsVariable(n[i]):
  103. # bug #19013: Alias information is not enough, we need to check for potential
  104. # "overlaps". I claim the problem can only happen by reading again from a location
  105. # that materialized which is only possible if a variable that contains a `ref`
  106. # is involved.
  107. return false
  108. result = true
  109. of nkIfStmt, nkIfExpr:
  110. for it in n:
  111. result = checkIsolate(it.lastSon)
  112. if not result: break
  113. of nkCaseStmt:
  114. for i in 1..<n.len:
  115. result = checkIsolate(n[i].lastSon)
  116. if not result: break
  117. of nkObjConstr:
  118. result = true
  119. for i in 1..<n.len:
  120. result = checkIsolate(n[i].lastSon)
  121. if not result: break
  122. of nkBracket, nkTupleConstr, nkPar:
  123. for it in n:
  124. result = checkIsolate(it)
  125. if not result: break
  126. of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
  127. result = checkIsolate(n[1])
  128. of nkObjUpConv, nkObjDownConv, nkDotExpr:
  129. result = checkIsolate(n[0])
  130. of nkStmtList, nkStmtListExpr:
  131. if n.len > 0:
  132. result = checkIsolate(n[^1])
  133. else:
  134. result = false
  135. else:
  136. # unanalysable expression:
  137. result = false
  138. else:
  139. # no ref, no cry:
  140. result = true