isolation_check.nim 4.6 KB

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