nilcheck.nim 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2017 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes
  10. import sequtils, strutils, sets
  11. when defined(nimPreviewSlimSystem):
  12. import std/assertions
  13. # IMPORTANT: notes not up to date, i'll update this comment again
  14. #
  15. # notes:
  16. #
  17. # Env: int => nilability
  18. # a = b
  19. # nilability a <- nilability b
  20. # deref a
  21. # if Nil error is nil
  22. # if MaybeNil error might be nil, hint add if isNil
  23. # if Safe fine
  24. # fun(arg: A)
  25. # nilability arg <- for ref MaybeNil, for not nil or others Safe
  26. # map is env?
  27. # a or b
  28. # each one forks a different env
  29. # result = union(envL, envR)
  30. # a and b
  31. # b forks a's env
  32. # if a: code
  33. # result = union(previousEnv after not a, env after code)
  34. # if a: b else: c
  35. # result = union(env after b, env after c)
  36. # result = b
  37. # nilability result <- nilability b, if return type is not nil and result not safe, error
  38. # return b
  39. # as result = b
  40. # try: a except: b finally: c
  41. # in b and c env is union of all possible try first n lines, after union of a and b and c
  42. # keep in mind canRaise and finally
  43. # case a: of b: c
  44. # similar to if
  45. # call(arg)
  46. # if it returns ref, assume it's MaybeNil: hint that one can add not nil to the return type
  47. # call(var arg) # zahary comment
  48. # if arg is ref, assume it's MaybeNil after call
  49. # loop
  50. # union of env for 0, 1, 2 iterations as Herb Sutter's paper
  51. # why 2?
  52. # return
  53. # if something: stop (break return etc)
  54. # is equivalent to if something: .. else: remain
  55. # new(ref)
  56. # ref becomes Safe
  57. # objConstr(a: b)
  58. # returns safe
  59. # each check returns its nilability and map
  60. type
  61. SeqOfDistinct[T, U] = distinct seq[U]
  62. # TODO use distinct base type instead of int?
  63. func `[]`[T, U](a: SeqOfDistinct[T, U], index: T): U =
  64. (seq[U])(a)[index.int]
  65. proc `[]=`[T, U](a: var SeqOfDistinct[T, U], index: T, value: U) =
  66. ((seq[U])(a))[index.int] = value
  67. func `[]`[T, U](a: var SeqOfDistinct[T, U], index: T): var U =
  68. (seq[U])(a)[index.int]
  69. func len[T, U](a: SeqOfDistinct[T, U]): T =
  70. (seq[U])(a).len.T
  71. func low[T, U](a: SeqOfDistinct[T, U]): T =
  72. (seq[U])(a).low.T
  73. func high[T, U](a: SeqOfDistinct[T, U]): T =
  74. (seq[U])(a).high.T
  75. proc setLen[T, U](a: var SeqOfDistinct[T, U], length: T) =
  76. ((seq[U])(a)).setLen(length.Natural)
  77. proc newSeqOfDistinct[T, U](length: T = 0.T): SeqOfDistinct[T, U] =
  78. (SeqOfDistinct[T, U])(newSeq[U](length.int))
  79. func newSeqOfDistinct[T, U](length: int = 0): SeqOfDistinct[T, U] =
  80. # newSeqOfDistinct(length.T)
  81. # ? newSeqOfDistinct[T, U](length.T)
  82. (SeqOfDistinct[T, U])(newSeq[U](length))
  83. iterator items[T, U](a: SeqOfDistinct[T, U]): U =
  84. for element in (seq[U])(a):
  85. yield element
  86. iterator pairs[T, U](a: SeqOfDistinct[T, U]): (T, U) =
  87. for i, element in (seq[U])(a):
  88. yield (i.T, element)
  89. func `$`[T, U](a: SeqOfDistinct[T, U]): string =
  90. $((seq[U])(a))
  91. proc add*[T, U](a: var SeqOfDistinct[T, U], value: U) =
  92. ((seq[U])(a)).add(value)
  93. type
  94. ## a hashed representation of a node: should be equal for structurally equal nodes
  95. Symbol = distinct int
  96. ## the index of an expression in the pre-indexed sequence of those
  97. ExprIndex = distinct int16
  98. ## the set index
  99. SetIndex = distinct int
  100. ## transition kind:
  101. ## what was the reason for changing the nilability of an expression
  102. ## useful for error messages and showing why an expression is being detected as nil / maybe nil
  103. TransitionKind = enum TArg, TAssign, TType, TNil, TVarArg, TResult, TSafe, TPotentialAlias, TDependant
  104. ## keep history for each transition
  105. History = object
  106. info: TLineInfo ## the location
  107. nilability: Nilability ## the nilability
  108. kind: TransitionKind ## what kind of transition was that
  109. node: PNode ## the node of the expression
  110. ## the context for the checker: an instance for each procedure
  111. NilCheckerContext = ref object
  112. # abstractTime: AbstractTime
  113. # partitions: Partitions
  114. # symbolGraphs: Table[Symbol, ]
  115. symbolIndices: Table[Symbol, ExprIndex] ## index for each symbol
  116. expressions: SeqOfDistinct[ExprIndex, PNode] ## a sequence of pre-indexed expressions
  117. dependants: SeqOfDistinct[ExprIndex, IntSet] ## expr indices for expressions which are compound and based on others
  118. warningLocations: HashSet[TLineInfo] ## warning locations to check we don't warn twice for stuff like warnings in for loops
  119. idgen: IdGenerator ## id generator
  120. config: ConfigRef ## the config of the compiler
  121. ## a map that is containing the current nilability for usually a branch
  122. ## and is pointing optionally to a parent map: they make a stack of maps
  123. NilMap = ref object
  124. expressions: SeqOfDistinct[ExprIndex, Nilability] ## the expressions with the same order as in NilCheckerContext
  125. history: SeqOfDistinct[ExprIndex, seq[History]] ## history for each of them
  126. # what about gc and refs?
  127. setIndices: SeqOfDistinct[ExprIndex, SetIndex] ## set indices for each expression
  128. sets: SeqOfDistinct[SetIndex, IntSet] ## disjoint sets with the aliased expressions
  129. parent: NilMap ## the parent map
  130. ## Nilability : if a value is nilable.
  131. ## we have maybe nil and nil, so we can differentiate between
  132. ## cases where we know for sure a value is nil and not
  133. ## otherwise we can have Safe, MaybeNil
  134. ## Parent: is because we just use a sequence with the same length
  135. ## instead of a table, and we need to check if something was initialized
  136. ## at all: if Parent is set, then you need to check the parent nilability
  137. ## if the parent is nil, then for now we return MaybeNil
  138. ## unreachable is the result of add(Safe, Nil) and others
  139. ## it is a result of no states left, so it's usually e.g. in unreachable else branches?
  140. Nilability* = enum Parent, Safe, MaybeNil, Nil, Unreachable
  141. ## check
  142. Check = object
  143. nilability: Nilability
  144. map: NilMap
  145. elements: seq[(PNode, Nilability)]
  146. # useful to have known resultId so we can set it in the beginning and on return
  147. const resultId: Symbol = (-1).Symbol
  148. const resultExprIndex: ExprIndex = 0.ExprIndex
  149. const noSymbol = (-2).Symbol
  150. func `<`*(a: ExprIndex, b: ExprIndex): bool =
  151. a.int16 < b.int16
  152. func `<=`*(a: ExprIndex, b: ExprIndex): bool =
  153. a.int16 <= b.int16
  154. func `>`*(a: ExprIndex, b: ExprIndex): bool =
  155. a.int16 > b.int16
  156. func `>=`*(a: ExprIndex, b: ExprIndex): bool =
  157. a.int16 >= b.int16
  158. func `==`*(a: ExprIndex, b: ExprIndex): bool =
  159. a.int16 == b.int16
  160. func `$`*(a: ExprIndex): string =
  161. $(a.int16)
  162. func `+`*(a: ExprIndex, b: ExprIndex): ExprIndex =
  163. (a.int16 + b.int16).ExprIndex
  164. # TODO overflowing / < 0?
  165. func `-`*(a: ExprIndex, b: ExprIndex): ExprIndex =
  166. (a.int16 - b.int16).ExprIndex
  167. func `$`*(a: SetIndex): string =
  168. $(a.int)
  169. func `==`*(a: SetIndex, b: SetIndex): bool =
  170. a.int == b.int
  171. func `+`*(a: SetIndex, b: SetIndex): SetIndex =
  172. (a.int + b.int).SetIndex
  173. # TODO over / under limit?
  174. func `-`*(a: SetIndex, b: SetIndex): SetIndex =
  175. (a.int - b.int).SetIndex
  176. proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check
  177. proc checkCondition(n: PNode, ctx: NilCheckerContext, map: NilMap, reverse: bool, base: bool): NilMap
  178. # the NilMap structure
  179. proc newNilMap(parent: NilMap = nil, count: int = -1): NilMap =
  180. var expressionsCount = 0
  181. if count != -1:
  182. expressionsCount = count
  183. elif not parent.isNil:
  184. expressionsCount = parent.expressions.len.int
  185. result = NilMap(
  186. expressions: newSeqOfDistinct[ExprIndex, Nilability](expressionsCount),
  187. history: newSeqOfDistinct[ExprIndex, seq[History]](expressionsCount),
  188. setIndices: newSeqOfDistinct[ExprIndex, SetIndex](expressionsCount),
  189. parent: parent)
  190. if parent.isNil:
  191. for i, expr in result.expressions:
  192. result.setIndices[i] = i.SetIndex
  193. var newSet = initIntSet()
  194. newSet.incl(i.int)
  195. result.sets.add(newSet)
  196. else:
  197. for i, exprs in parent.sets:
  198. result.sets.add(exprs)
  199. for i, index in parent.setIndices:
  200. result.setIndices[i] = index
  201. # result.sets = parent.sets
  202. # if not parent.isNil:
  203. # # optimize []?
  204. # result.expressions = parent.expressions
  205. # result.history = parent.history
  206. # result.sets = parent.sets
  207. # result.base = if parent.isNil: result else: parent.base
  208. proc `[]`(map: NilMap, index: ExprIndex): Nilability =
  209. if index < 0.ExprIndex or index >= map.expressions.len:
  210. return MaybeNil
  211. var now = map
  212. while not now.isNil:
  213. if now.expressions[index] != Parent:
  214. return now.expressions[index]
  215. now = now.parent
  216. return MaybeNil
  217. proc history(map: NilMap, index: ExprIndex): seq[History] =
  218. if index < map.expressions.len:
  219. map.history[index]
  220. else:
  221. @[]
  222. # helpers for debugging
  223. # import macros
  224. # echo-s only when nilDebugInfo is defined
  225. # macro aecho*(a: varargs[untyped]): untyped =
  226. # var e = nnkCall.newTree(ident"echo")
  227. # for b in a:
  228. # e.add(b)
  229. # result = quote:
  230. # when defined(nilDebugInfo):
  231. # `e`
  232. # end of helpers for debugging
  233. proc symbol(n: PNode): Symbol
  234. func `$`(map: NilMap): string
  235. proc reverseDirect(map: NilMap): NilMap
  236. proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check
  237. proc hasUnstructuredControlFlowJump(n: PNode): bool
  238. proc symbol(n: PNode): Symbol =
  239. ## returns a Symbol for each expression
  240. ## the goal is to get an unique Symbol
  241. ## but we have to ensure hashTree does it as we expect
  242. case n.kind:
  243. of nkIdent:
  244. # TODO ensure no idents get passed to symbol
  245. result = noSymbol
  246. of nkSym:
  247. if n.sym.kind == skResult: # credit to disruptek for showing me that
  248. result = resultId
  249. else:
  250. result = n.sym.id.Symbol
  251. of nkHiddenAddr, nkAddr:
  252. result = symbol(n[0])
  253. else:
  254. result = hashTree(n).Symbol
  255. # echo "symbol ", n, " ", n.kind, " ", result.int
  256. func `$`(map: NilMap): string =
  257. result = ""
  258. var now = map
  259. var stack: seq[NilMap] = @[]
  260. while not now.isNil:
  261. stack.add(now)
  262. now = now.parent
  263. result.add("### start\n")
  264. for i in 0 .. stack.len - 1:
  265. now = stack[i]
  266. result.add(" ###\n")
  267. for index, value in now.expressions:
  268. result.add(&" {index} {value}\n")
  269. result.add "### end\n"
  270. proc namedMapDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
  271. result = ""
  272. var now = map
  273. var stack: seq[NilMap] = @[]
  274. while not now.isNil:
  275. stack.add(now)
  276. now = now.parent
  277. result.add("### start\n")
  278. for i in 0 .. stack.len - 1:
  279. now = stack[i]
  280. result.add(" ###\n")
  281. for index, value in now.expressions:
  282. let name = ctx.expressions[index]
  283. result.add(&" {name} {index} {value}\n")
  284. result.add("### end\n")
  285. proc namedSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
  286. result = "### sets "
  287. for index, setIndex in map.setIndices:
  288. var aliasSet = map.sets[setIndex]
  289. result.add("{")
  290. let expressions = aliasSet.mapIt($ctx.expressions[it.ExprIndex])
  291. result.add(join(expressions, ", "))
  292. result.add("} ")
  293. result.add("\n")
  294. proc namedMapAndSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
  295. result = namedMapDebugInfo(ctx, map) & namedSetsDebugInfo(ctx, map)
  296. const noExprIndex = (-1).ExprIndex
  297. const noSetIndex = (-1).SetIndex
  298. proc `==`(a: Symbol, b: Symbol): bool =
  299. a.int == b.int
  300. func `$`(a: Symbol): string =
  301. $(a.int)
  302. template isConstBracket(n: PNode): bool =
  303. n.kind == nkBracketExpr and n[1].kind in nkLiterals
  304. proc index(ctx: NilCheckerContext, n: PNode): ExprIndex =
  305. # echo "n ", n, " ", n.kind
  306. let a = symbol(n)
  307. if ctx.symbolIndices.hasKey(a):
  308. return ctx.symbolIndices[a]
  309. else:
  310. #for a, e in ctx.expressions:
  311. # echo a, " ", e
  312. #echo n.kind
  313. # internalError(ctx.config, n.info, "expected " & $a & " " & $n & " to have a index")
  314. return noExprIndex
  315. #
  316. #ctx.symbolIndices[symbol(n)]
  317. proc aliasSet(ctx: NilCheckerContext, map: NilMap, n: PNode): IntSet =
  318. result = map.sets[map.setIndices[ctx.index(n)]]
  319. proc aliasSet(ctx: NilCheckerContext, map: NilMap, index: ExprIndex): IntSet =
  320. result = map.sets[map.setIndices[index]]
  321. proc store(map: NilMap, ctx: NilCheckerContext, index: ExprIndex, value: Nilability, kind: TransitionKind, info: TLineInfo, node: PNode = nil) =
  322. if index == noExprIndex:
  323. return
  324. map.expressions[index] = value
  325. map.history[index].add(History(info: info, kind: kind, node: node, nilability: value))
  326. #echo node, " ", index, " ", value
  327. #echo ctx.namedMapAndSetsDebugInfo(map)
  328. #for a, b in map.sets:
  329. # echo a, " ", b
  330. # echo map
  331. var exprAliases = aliasSet(ctx, map, index)
  332. for a in exprAliases:
  333. if a.ExprIndex != index:
  334. #echo "alias ", a, " ", index
  335. map.expressions[a.ExprIndex] = value
  336. if value == Safe:
  337. map.history[a.ExprIndex] = @[]
  338. else:
  339. map.history[a.ExprIndex].add(History(info: info, kind: TPotentialAlias, node: node, nilability: value))
  340. proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) =
  341. #echo "move out ", target
  342. var targetIndex = ctx.index(target)
  343. var targetSetIndex = map.setIndices[targetIndex]
  344. if targetSetIndex != noSetIndex:
  345. var targetSet = map.sets[targetSetIndex]
  346. if targetSet.len > 1:
  347. var other: ExprIndex = default(ExprIndex)
  348. for element in targetSet:
  349. if element.ExprIndex != targetIndex:
  350. other = element.ExprIndex
  351. break
  352. # map.sets[element].excl(targetIndex)
  353. map.sets[map.setIndices[other]].excl(targetIndex.int)
  354. var newSet = initIntSet()
  355. newSet.incl(targetIndex.int)
  356. map.sets.add(newSet)
  357. map.setIndices[targetIndex] = map.sets.len - 1.SetIndex
  358. proc moveOutDependants(ctx: NilCheckerContext, map: NilMap, node: PNode) =
  359. let index = ctx.index(node)
  360. for dependant in ctx.dependants[index]:
  361. moveOut(ctx, map, ctx.expressions[dependant.ExprIndex])
  362. proc storeDependants(ctx: NilCheckerContext, map: NilMap, node: PNode, value: Nilability) =
  363. let index = ctx.index(node)
  364. for dependant in ctx.dependants[index]:
  365. map.store(ctx, dependant.ExprIndex, value, TDependant, node.info, node)
  366. proc move(ctx: NilCheckerContext, map: NilMap, target: PNode, assigned: PNode) =
  367. #echo "move ", target, " ", assigned
  368. var targetIndex = ctx.index(target)
  369. var assignedIndex: ExprIndex
  370. var targetSetIndex = map.setIndices[targetIndex]
  371. var assignedSetIndex: SetIndex
  372. if assigned.kind == nkSym:
  373. assignedIndex = ctx.index(assigned)
  374. assignedSetIndex = map.setIndices[assignedIndex]
  375. else:
  376. assignedIndex = noExprIndex
  377. assignedSetIndex = noSetIndex
  378. if assignedIndex == noExprIndex:
  379. moveOut(ctx, map, target)
  380. elif targetSetIndex != assignedSetIndex:
  381. map.sets[targetSetIndex].excl(targetIndex.int)
  382. map.sets[assignedSetIndex].incl(targetIndex.int)
  383. map.setIndices[targetIndex] = assignedSetIndex
  384. # proc hasKey(map: NilMap, ): bool =
  385. # var now = map
  386. # result = false
  387. # while not now.isNil:
  388. # if now.locals.hasKey(graphIndex):
  389. # return true
  390. # now = now.previous
  391. iterator pairs(map: NilMap): (ExprIndex, Nilability) =
  392. for index, value in map.expressions:
  393. yield (index, map[index])
  394. proc copyMap(map: NilMap): NilMap =
  395. if map.isNil:
  396. return nil
  397. result = newNilMap(map.parent) # no need for copy? if we change only this
  398. result.expressions = map.expressions
  399. result.history = map.history
  400. result.sets = map.sets
  401. result.setIndices = map.setIndices
  402. using
  403. n: PNode
  404. conf: ConfigRef
  405. ctx: NilCheckerContext
  406. map: NilMap
  407. proc typeNilability(typ: PType): Nilability
  408. # maybe: if canRaise, return MaybeNil ?
  409. # no, because the target might be safe already
  410. # with or without an exception
  411. proc checkCall(n, ctx, map): Check =
  412. # checks each call
  413. # special case for new(T) -> result is always Safe
  414. # for the others it depends on the return type of the call
  415. # check args and handle possible mutations
  416. var isNew = false
  417. result.map = map
  418. for i, child in n:
  419. discard check(child, ctx, map)
  420. if i > 0:
  421. # var args make a new map with MaybeNil for our node
  422. # as it might have been mutated
  423. # TODO similar for normal refs and fields: find dependent exprs: brackets
  424. if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ[0].kind == tyRef:
  425. if not isNew:
  426. result.map = newNilMap(map)
  427. isNew = true
  428. # result.map[$child] = MaybeNil
  429. var arg = child
  430. while arg.kind == nkHiddenAddr:
  431. arg = arg[0]
  432. let a = ctx.index(arg)
  433. if a != noExprIndex:
  434. moveOut(ctx, result.map, arg)
  435. moveOutDependants(ctx, result.map, arg)
  436. result.map.store(ctx, a, MaybeNil, TVarArg, n.info, arg)
  437. storeDependants(ctx, result.map, arg, MaybeNil)
  438. elif not child.typ.isNil and child.typ.kind == tyRef:
  439. if child.kind in {nkSym, nkDotExpr} or isConstBracket(child):
  440. let a = ctx.index(child)
  441. if ctx.dependants[a].len > 0:
  442. if not isNew:
  443. result.map = newNilMap(map)
  444. isNew = true
  445. moveOutDependants(ctx, result.map, child)
  446. storeDependants(ctx, result.map, child, MaybeNil)
  447. if n[0].kind == nkSym and n[0].sym.magic == mNew:
  448. # new hidden deref?
  449. var value = if n[1].kind == nkHiddenDeref: n[1][0] else: n[1]
  450. let b = ctx.index(value)
  451. result.map.store(ctx, b, Safe, TAssign, value.info, value)
  452. result.nilability = Safe
  453. else:
  454. # echo "n ", n, " ", n.typ.isNil
  455. if not n.typ.isNil:
  456. result.nilability = typeNilability(n.typ)
  457. else:
  458. result.nilability = Safe
  459. # echo result.map
  460. template event(b: History): string =
  461. case b.kind:
  462. of TArg: "param with nilable type"
  463. of TNil: "it returns true for isNil"
  464. of TAssign: "assigns a value which might be nil"
  465. of TVarArg: "passes it as a var arg which might change to nil"
  466. of TResult: "it is nil by default"
  467. of TType: "it has ref type"
  468. of TSafe: "it is safe here as it returns false for isNil"
  469. of TPotentialAlias: "it might be changed directly or through an alias"
  470. of TDependant: "it might be changed because its base might be changed"
  471. proc derefWarning(n, ctx, map; kind: Nilability) =
  472. ## a warning for potentially unsafe dereference
  473. if n.info in ctx.warningLocations:
  474. return
  475. ctx.warningLocations.incl(n.info)
  476. var a: seq[History] = @[]
  477. if n.kind == nkSym:
  478. a = history(map, ctx.index(n))
  479. var res = ""
  480. var issue = case kind:
  481. of Nil: "it is nil"
  482. of MaybeNil: "it might be nil"
  483. of Unreachable: "it is unreachable"
  484. else: ""
  485. res.add("can't deref " & $n & ", " & issue)
  486. if a.len > 0:
  487. res.add("\n")
  488. for b in a:
  489. res.add(" " & event(b) & " on line " & $b.info.line & ":" & $b.info.col)
  490. message(ctx.config, n.info, warnStrictNotNil, res)
  491. proc handleNilability(check: Check; n, ctx, map) =
  492. ## handle the check:
  493. ## register a warning(error?) for Nil/MaybeNil
  494. case check.nilability:
  495. of Nil:
  496. derefWarning(n, ctx, map, Nil)
  497. of MaybeNil:
  498. derefWarning(n, ctx, map, MaybeNil)
  499. of Unreachable:
  500. derefWarning(n, ctx, map, Unreachable)
  501. else:
  502. when defined(nilDebugInfo):
  503. message(ctx.config, n.info, hintUser, "can deref " & $n)
  504. proc checkDeref(n, ctx, map): Check =
  505. ## check dereference: deref n should be ok only if n is Safe
  506. result = check(n[0], ctx, map)
  507. handleNilability(result, n[0], ctx, map)
  508. proc checkRefExpr(n, ctx; check: Check): Check =
  509. ## check ref expressions: TODO not sure when this happens
  510. result = check
  511. if n.typ.kind != tyRef:
  512. result.nilability = typeNilability(n.typ)
  513. elif tfNotNil notin n.typ.flags:
  514. # echo "ref key ", n, " ", n.kind
  515. if n.kind in {nkSym, nkDotExpr} or isConstBracket(n):
  516. let key = ctx.index(n)
  517. result.nilability = result.map[key]
  518. elif n.kind == nkBracketExpr:
  519. # sometimes false positive
  520. result.nilability = MaybeNil
  521. else:
  522. # sometimes maybe false positive
  523. result.nilability = MaybeNil
  524. proc checkDotExpr(n, ctx, map): Check =
  525. ## check dot expressions: make sure we can dereference the base
  526. result = check(n[0], ctx, map)
  527. result = checkRefExpr(n, ctx, result)
  528. proc checkBracketExpr(n, ctx, map): Check =
  529. ## check bracket expressions: make sure we can dereference the base
  530. result = check(n[0], ctx, map)
  531. # if might be deref: [] == *(a + index) for cstring
  532. handleNilability(result, n[0], ctx, map)
  533. result = check(n[1], ctx, result.map)
  534. result = checkRefExpr(n, ctx, result)
  535. # echo n, " ", result.nilability
  536. template union(l: Nilability, r: Nilability): Nilability =
  537. ## unify two states
  538. if l == r:
  539. l
  540. else:
  541. MaybeNil
  542. template add(l: Nilability, r: Nilability): Nilability =
  543. if l == r: # Safe Safe -> Safe etc
  544. l
  545. elif l == Parent: # Parent Safe -> Safe etc
  546. r
  547. elif r == Parent: # Safe Parent -> Safe etc
  548. l
  549. elif l == Unreachable or r == Unreachable: # Safe Unreachable -> Unreachable etc
  550. Unreachable
  551. elif l == MaybeNil: # Safe MaybeNil -> Safe etc
  552. r
  553. elif r == MaybeNil: # MaybeNil Nil -> Nil etc
  554. l
  555. else: # Safe Nil -> Unreachable etc
  556. Unreachable
  557. proc findCommonParent(l: NilMap, r: NilMap): NilMap =
  558. result = l.parent
  559. while not result.isNil:
  560. var rparent = r.parent
  561. while not rparent.isNil:
  562. if result == rparent:
  563. return result
  564. rparent = rparent.parent
  565. result = result.parent
  566. proc union(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap =
  567. ## unify two maps from different branches
  568. ## combine their locals
  569. ## what if they are from different parts of the same tree
  570. ## e.g.
  571. ## a -> b -> c
  572. ## -> b1
  573. ## common then?
  574. ##
  575. if l.isNil:
  576. return r
  577. elif r.isNil:
  578. return l
  579. let common = findCommonParent(l, r)
  580. result = newNilMap(common, ctx.expressions.len.int)
  581. for index, value in l:
  582. let h = history(r, index)
  583. let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0) # assert h.len > 0
  584. # echo "history", name, value, r[name], h[^1].info.line
  585. result.store(ctx, index, union(value, r[index]), TAssign, info)
  586. proc add(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap =
  587. #echo "add "
  588. #echo namedMapDebugInfo(ctx, l)
  589. #echo " : "
  590. #echo namedMapDebugInfo(ctx, r)
  591. if l.isNil:
  592. return r
  593. elif r.isNil:
  594. return l
  595. let common = findCommonParent(l, r)
  596. result = newNilMap(common, ctx.expressions.len.int)
  597. for index, value in l:
  598. let h = history(r, index)
  599. let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0)
  600. # TODO: refactor and also think: is TAssign a good one
  601. result.store(ctx, index, add(value, r[index]), TAssign, info)
  602. #echo "result"
  603. #echo namedMapDebugInfo(ctx, result)
  604. #echo ""
  605. #echo ""
  606. proc checkAsgn(target: PNode, assigned: PNode; ctx, map): Check =
  607. ## check assignment
  608. ## update map based on `assigned`
  609. if assigned.kind != nkEmpty:
  610. result = check(assigned, ctx, map)
  611. else:
  612. result = Check(nilability: typeNilability(target.typ), map: map)
  613. # we need to visit and check those, but we don't use the result for now
  614. # is it possible to somehow have another event happen here?
  615. discard check(target, ctx, map)
  616. if result.map.isNil:
  617. result.map = map
  618. if target.kind in {nkSym, nkDotExpr} or isConstBracket(target):
  619. let t = ctx.index(target)
  620. move(ctx, map, target, assigned)
  621. case assigned.kind:
  622. of nkNilLit:
  623. result.map.store(ctx, t, Nil, TAssign, target.info, target)
  624. else:
  625. result.map.store(ctx, t, result.nilability, TAssign, target.info, target)
  626. moveOutDependants(ctx, map, target)
  627. storeDependants(ctx, map, target, MaybeNil)
  628. if assigned.kind in {nkObjConstr, nkTupleConstr}:
  629. for (element, value) in result.elements:
  630. var elementNode = nkDotExpr.newTree(nkHiddenDeref.newTree(target), element)
  631. if symbol(elementNode) in ctx.symbolIndices:
  632. var elementIndex = ctx.index(elementNode)
  633. result.map.store(ctx, elementIndex, value, TAssign, target.info, elementNode)
  634. proc checkReturn(n, ctx, map): Check =
  635. ## check return
  636. # return n same as result = n; return ?
  637. result = check(n[0], ctx, map)
  638. result.map.store(ctx, resultExprIndex, result.nilability, TAssign, n.info)
  639. proc checkIf(n, ctx, map): Check =
  640. ## check branches based on condition
  641. var mapIf: NilMap = map
  642. # first visit the condition
  643. # the structure is not If(Elif(Elif, Else), Else)
  644. # it is
  645. # If(Elif, Elif, Else)
  646. var mapCondition = checkCondition(n.sons[0].sons[0], ctx, mapIf, false, true)
  647. # the state of the conditions: negating conditions before the current one
  648. var layerHistory = newNilMap(mapIf)
  649. # the state after branch effects
  650. var afterLayer: NilMap = nil
  651. # the result nilability for expressions
  652. var nilability = Safe
  653. for branch in n.sons:
  654. var branchConditionLayer = newNilMap(layerHistory)
  655. var branchLayer: NilMap
  656. var code: PNode
  657. if branch.kind in {nkIfStmt, nkElifBranch}:
  658. var mapCondition = checkCondition(branch[0], ctx, branchConditionLayer, false, true)
  659. let reverseMapCondition = reverseDirect(mapCondition)
  660. layerHistory = ctx.add(layerHistory, reverseMapCondition)
  661. branchLayer = mapCondition
  662. code = branch[1]
  663. else:
  664. branchLayer = layerHistory
  665. code = branch
  666. let branchCheck = checkBranch(code, ctx, branchLayer)
  667. # handles nil afterLayer -> returns branchCheck.map
  668. afterLayer = ctx.union(afterLayer, branchCheck.map)
  669. nilability = if n.kind == nkIfStmt: Safe else: union(nilability, branchCheck.nilability)
  670. if n.sons.len > 1:
  671. result.map = afterLayer
  672. result.nilability = nilability
  673. else:
  674. if not hasUnstructuredControlFlowJump(n[0][1]):
  675. # here it matters what happend inside, because
  676. # we might continue in the parent branch after entering this one
  677. # either we enter the branch, so we get mapIf and effect of branch -> afterLayer
  678. # or we dont , so we get mapIf and (not condition) effect -> layerHistory
  679. result.map = ctx.union(layerHistory, afterLayer)
  680. result.nilability = Safe # no expr?
  681. else:
  682. # similar to else: because otherwise we are jumping out of
  683. # the branch, so no union with the mapIf (we dont continue if the condition was true)
  684. # here it also doesn't matter for the parent branch what happened in the branch, e.g. assigning to nil
  685. # as if we continue there, we haven't entered the branch probably
  686. # so we don't do an union with afterLayer
  687. # layerHistory has the effect of mapIf and (not condition)
  688. result.map = layerHistory
  689. result.nilability = Safe
  690. proc checkFor(n, ctx, map): Check =
  691. ## check for loops
  692. ## try to repeat the unification of the code twice
  693. ## to detect what can change after a several iterations
  694. ## approach based on discussions with Zahary/Araq
  695. ## similar approach used for other loops
  696. var m = map.copyMap()
  697. var map0 = map.copyMap()
  698. #echo namedMapDebugInfo(ctx, map)
  699. m = check(n.sons[2], ctx, map).map.copyMap()
  700. if n[0].kind == nkSym:
  701. m.store(ctx, ctx.index(n[0]), typeNilability(n[0].typ), TAssign, n[0].info)
  702. # echo namedMapDebugInfo(ctx, map)
  703. var check2 = check(n.sons[2], ctx, m)
  704. var map2 = check2.map
  705. result.map = ctx.union(map0, m)
  706. result.map = ctx.union(result.map, map2)
  707. result.nilability = Safe
  708. # check:
  709. # while code:
  710. # code2
  711. # if code:
  712. # code2
  713. # if code:
  714. # code2
  715. # if code:
  716. # code2
  717. # check(code), check(code2 in code's map)
  718. proc checkWhile(n, ctx, map): Check =
  719. ## check while loops
  720. ## try to repeat the unification of the code twice
  721. var m = checkCondition(n[0], ctx, map, false, false)
  722. var map0 = map.copyMap()
  723. m = check(n.sons[1], ctx, m).map
  724. var map1 = m.copyMap()
  725. var check2 = check(n.sons[1], ctx, m)
  726. var map2 = check2.map
  727. result.map = ctx.union(map0, map1)
  728. result.map = ctx.union(result.map, map2)
  729. result.nilability = Safe
  730. proc checkInfix(n, ctx, map): Check =
  731. ## check infix operators in condition
  732. ## a and b : map is based on a; next b
  733. ## a or b : map is an union of a and b's
  734. ## a == b : use checkCondition
  735. ## else: no change, just check args
  736. result = default(Check)
  737. if n[0].kind == nkSym:
  738. var mapL: NilMap = nil
  739. var mapR: NilMap = nil
  740. if n[0].sym.magic notin {mAnd, mEqRef}:
  741. mapL = checkCondition(n[1], ctx, map, false, false)
  742. mapR = checkCondition(n[2], ctx, map, false, false)
  743. case n[0].sym.magic:
  744. of mOr:
  745. result.map = ctx.union(mapL, mapR)
  746. of mAnd:
  747. result.map = checkCondition(n[1], ctx, map, false, false)
  748. result.map = checkCondition(n[2], ctx, result.map, false, false)
  749. of mEqRef:
  750. if n[2].kind == nkIntLit:
  751. if $n[2] == "true":
  752. result.map = checkCondition(n[1], ctx, map, false, false)
  753. elif $n[2] == "false":
  754. result.map = checkCondition(n[1], ctx, map, true, false)
  755. elif n[1].kind == nkIntLit:
  756. if $n[1] == "true":
  757. result.map = checkCondition(n[2], ctx, map, false, false)
  758. elif $n[1] == "false":
  759. result.map = checkCondition(n[2], ctx, map, true, false)
  760. if result.map.isNil:
  761. result.map = map
  762. else:
  763. result.map = map
  764. else:
  765. result.map = map
  766. result.nilability = Safe
  767. proc checkIsNil(n, ctx, map; isElse: bool = false): Check =
  768. ## check isNil calls
  769. ## update the map depending on if it is not isNil or isNil
  770. result.map = newNilMap(map)
  771. let value = n[1]
  772. result.map.store(ctx, ctx.index(n[1]), if not isElse: Nil else: Safe, TArg, n.info, n)
  773. proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
  774. var name = case magic:
  775. of mEqRef: "=="
  776. of mAnd: "and"
  777. of mOr: "or"
  778. else: ""
  779. var cache = newIdentCache()
  780. var op = newSym(skVar, cache.getIdent(name), ctx.idgen, nil, r.info)
  781. op.magic = magic
  782. result = nkInfix.newTree(
  783. newSymNode(op, r.info),
  784. l,
  785. r)
  786. result.typ = newType(tyBool, nextTypeId ctx.idgen, nil)
  787. proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
  788. var cache = newIdentCache()
  789. var op = newSym(skVar, cache.getIdent("not"), ctx.idgen, nil, node.info)
  790. op.magic = mNot
  791. result = nkPrefix.newTree(
  792. newSymNode(op, node.info),
  793. node)
  794. result.typ = newType(tyBool, nextTypeId ctx.idgen, nil)
  795. proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
  796. infix(ctx, l, r, mEqRef)
  797. proc infixOr(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
  798. infix(ctx, l, r, mOr)
  799. proc checkCase(n, ctx, map): Check =
  800. # case a:
  801. # of b: c
  802. # of b2: c2
  803. # is like
  804. # if a == b:
  805. # c
  806. # elif a == b2:
  807. # c2
  808. # also a == true is a , a == false is not a
  809. let base = n[0]
  810. result.map = map.copyMap()
  811. result.nilability = Safe
  812. var a: PNode = nil
  813. for child in n:
  814. case child.kind:
  815. of nkOfBranch:
  816. if child.len < 2:
  817. # echo "case with of with < 2 ", n
  818. continue # TODO why does this happen
  819. let branchBase = child[0] # TODO a, b or a, b..c etc
  820. let code = child[^1]
  821. let test = infixEq(ctx, base, branchBase)
  822. if a.isNil:
  823. a = test
  824. else:
  825. a = infixOr(ctx, a, test)
  826. let conditionMap = checkCondition(test, ctx, map.copyMap(), false, false)
  827. let newCheck = checkBranch(code, ctx, conditionMap)
  828. result.map = ctx.union(result.map, newCheck.map)
  829. result.nilability = union(result.nilability, newCheck.nilability)
  830. of nkElifBranch:
  831. discard "TODO: maybe adapt to be similar to checkIf"
  832. of nkElse:
  833. let mapElse = checkCondition(prefixNot(ctx, a), ctx, map.copyMap(), false, false)
  834. let newCheck = checkBranch(child[0], ctx, mapElse)
  835. result.map = ctx.union(result.map, newCheck.map)
  836. result.nilability = union(result.nilability, newCheck.nilability)
  837. else:
  838. discard
  839. # notes
  840. # try:
  841. # a
  842. # b
  843. # except:
  844. # c
  845. # finally:
  846. # d
  847. #
  848. # if a doesnt raise, this is not an exit point:
  849. # so find what raises and update the map with that
  850. # (a, b); c; d
  851. # if nothing raises, except shouldn't happen
  852. # .. might be a false positive tho, if canRaise is not conservative?
  853. # so don't visit it
  854. #
  855. # nested nodes can raise as well: I hope nim returns canRaise for
  856. # their parents
  857. #
  858. # a lot of stuff can raise
  859. proc checkTry(n, ctx, map): Check =
  860. var newMap = map.copyMap()
  861. var currentMap = map
  862. # we don't analyze except if nothing canRaise in try
  863. var canRaise = false
  864. var hasFinally = false
  865. # var tryNodes: seq[PNode]
  866. # if n[0].kind == nkStmtList:
  867. # tryNodes = toSeq(n[0])
  868. # else:
  869. # tryNodes = @[n[0]]
  870. # for i, child in tryNodes:
  871. # let (childNilability, childMap) = check(child, conf, currentMap)
  872. # echo childMap
  873. # currentMap = childMap
  874. # # TODO what about nested
  875. # if child.canRaise:
  876. # newMap = union(newMap, childMap)
  877. # canRaise = true
  878. # else:
  879. # newMap = childMap
  880. let tryCheck = check(n[0], ctx, currentMap)
  881. newMap = ctx.union(currentMap, tryCheck.map)
  882. canRaise = n[0].canRaise
  883. var afterTryMap = newMap
  884. for a, branch in n:
  885. if a > 0:
  886. case branch.kind:
  887. of nkFinally:
  888. newMap = ctx.union(afterTryMap, newMap)
  889. let childCheck = check(branch[0], ctx, newMap)
  890. newMap = ctx.union(newMap, childCheck.map)
  891. hasFinally = true
  892. of nkExceptBranch:
  893. if canRaise:
  894. let childCheck = check(branch[^1], ctx, newMap)
  895. newMap = ctx.union(newMap, childCheck.map)
  896. else:
  897. discard
  898. if not hasFinally:
  899. # we might have not hit the except branches
  900. newMap = ctx.union(afterTryMap, newMap)
  901. result = Check(nilability: Safe, map: newMap)
  902. proc hasUnstructuredControlFlowJump(n: PNode): bool =
  903. ## if the node contains a direct stop
  904. ## as a continue/break/raise/return: then it means
  905. ## we should reverse some of the map in the code after the condition
  906. ## similar to else
  907. # echo "n ", n, " ", n.kind
  908. case n.kind:
  909. of nkStmtList:
  910. for child in n:
  911. if hasUnstructuredControlFlowJump(child):
  912. return true
  913. of nkReturnStmt, nkBreakStmt, nkContinueStmt, nkRaiseStmt:
  914. return true
  915. of nkIfStmt, nkIfExpr, nkElifExpr, nkElse:
  916. return false
  917. else:
  918. discard
  919. return false
  920. proc reverse(value: Nilability): Nilability =
  921. case value:
  922. of Nil: Safe
  923. of MaybeNil: MaybeNil
  924. of Safe: Nil
  925. of Parent: Parent
  926. of Unreachable: Unreachable
  927. proc reverse(kind: TransitionKind): TransitionKind =
  928. case kind:
  929. of TNil: TSafe
  930. of TSafe: TNil
  931. of TPotentialAlias: TPotentialAlias
  932. else:
  933. kind
  934. # raise newException(ValueError, "expected TNil or TSafe")
  935. proc reverseDirect(map: NilMap): NilMap =
  936. # we create a new layer
  937. # reverse the values only in this layer:
  938. # because conditions should've stored their changes there
  939. # b: Safe (not b.isNil)
  940. # b: Parent Parent
  941. # b: Nil (b.isNil)
  942. # layer block
  943. # [ Parent ] [ Parent ]
  944. # if -> if state
  945. # layer -> reverse
  946. # older older0 new
  947. # older new
  948. # [ b Nil ] [ Parent ]
  949. # elif
  950. # [ b Nil, c Nil] [ Parent ]
  951. #
  952. # if b.isNil:
  953. # # [ b Safe]
  954. # c = A() # Safe
  955. # elif not b.isNil:
  956. # # [ b Safe ] + [b Nil] MaybeNil Unreachable
  957. # # Unreachable defer can't deref b, it is unreachable
  958. # discard
  959. # else:
  960. # b
  961. # if
  962. # if: we just pass the map with a new layer for its block
  963. # elif: we just pass the original map but with a new layer is the reverse of the previous popped layer (?)
  964. # elif:
  965. # else: we just pass the original map but with a new layer which is initialized as the reverse of the
  966. # top layer of else
  967. # else:
  968. #
  969. # [ b MaybeNil ] [b Parent] [b Parent] [b Safe] [b Nil] []
  970. # Safe
  971. # c == 1
  972. # b Parent
  973. # c == 2
  974. # b Parent
  975. # not b.isNil
  976. # b Safe
  977. # c == 3
  978. # b Nil
  979. # (else)
  980. # b Nil
  981. result = map.copyMap()
  982. for index, value in result.expressions:
  983. result.expressions[index] = reverse(value)
  984. if result.history[index].len > 0:
  985. result.history[index][^1].kind = reverse(result.history[index][^1].kind)
  986. result.history[index][^1].nilability = result.expressions[index]
  987. proc checkCondition(n, ctx, map; reverse: bool, base: bool): NilMap =
  988. ## check conditions : used for if, some infix operators
  989. ## isNil(a)
  990. ## it returns a new map: you need to reverse all the direct elements for else
  991. # echo "condition ", n, " ", n.kind
  992. if n.kind == nkCall:
  993. result = newNilMap(map)
  994. for element in n:
  995. if element.kind == nkHiddenDeref and n[0].kind == nkSym and n[0].sym.magic == mIsNil:
  996. result = check(element[0], ctx, result).map
  997. else:
  998. result = check(element, ctx, result).map
  999. if n[0].kind == nkSym and n[0].sym.magic == mIsNil:
  1000. # isNil(arg)
  1001. var arg = n[1]
  1002. while arg.kind == nkHiddenDeref:
  1003. arg = arg[0]
  1004. if arg.kind in {nkSym, nkDotExpr} or isConstBracket(arg):
  1005. let a = ctx.index(arg)
  1006. result.store(ctx, a, if not reverse: Nil else: Safe, if not reverse: TNil else: TSafe, n.info, arg)
  1007. else:
  1008. discard
  1009. else:
  1010. discard
  1011. elif n.kind == nkPrefix and n[0].kind == nkSym and n[0].sym.magic == mNot:
  1012. result = checkCondition(n[1], ctx, map, not reverse, false)
  1013. elif n.kind == nkInfix:
  1014. result = newNilMap(map)
  1015. result = checkInfix(n, ctx, result).map
  1016. else:
  1017. result = check(n, ctx, map).map
  1018. result = newNilMap(map)
  1019. assert not result.isNil
  1020. assert not result.parent.isNil
  1021. proc checkResult(n, ctx, map) =
  1022. let resultNilability = map[resultExprIndex]
  1023. case resultNilability:
  1024. of Nil:
  1025. message(ctx.config, n.info, warnStrictNotNil, "return value is nil")
  1026. of MaybeNil:
  1027. message(ctx.config, n.info, warnStrictNotNil, "return value might be nil")
  1028. of Unreachable:
  1029. message(ctx.config, n.info, warnStrictNotNil, "return value is unreachable")
  1030. of Safe, Parent:
  1031. discard
  1032. proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
  1033. result = check(n, ctx, map)
  1034. # Faith!
  1035. proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
  1036. assert not map.isNil
  1037. # echo "check n ", n, " ", n.kind
  1038. # echo "map ", namedMapDebugInfo(ctx, map)
  1039. case n.kind:
  1040. of nkSym:
  1041. result = Check(nilability: map[ctx.index(n)], map: map)
  1042. of nkCallKinds:
  1043. if n.sons[0].kind == nkSym:
  1044. let callSym = n.sons[0].sym
  1045. case callSym.magic:
  1046. of mAnd, mOr:
  1047. result = checkInfix(n, ctx, map)
  1048. of mIsNil:
  1049. result = checkIsNil(n, ctx, map)
  1050. else:
  1051. result = checkCall(n, ctx, map)
  1052. else:
  1053. result = checkCall(n, ctx, map)
  1054. of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
  1055. nkCast:
  1056. result = check(n.sons[1], ctx, map)
  1057. of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
  1058. nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkElse:
  1059. result.map = map
  1060. if n.kind in {nkObjConstr, nkTupleConstr}:
  1061. # TODO deeper nested elements?
  1062. # A(field: B()) #
  1063. # field: Safe ->
  1064. var elements: seq[(PNode, Nilability)] = @[]
  1065. for i, child in n:
  1066. result = check(child, ctx, result.map)
  1067. if i > 0:
  1068. if child.kind == nkExprColonExpr:
  1069. elements.add((child[0], result.nilability))
  1070. result.elements = elements
  1071. result.nilability = Safe
  1072. else:
  1073. for child in n:
  1074. result = check(child, ctx, result.map)
  1075. of nkDotExpr:
  1076. result = checkDotExpr(n, ctx, map)
  1077. of nkDerefExpr, nkHiddenDeref:
  1078. result = checkDeref(n, ctx, map)
  1079. of nkAddr, nkHiddenAddr:
  1080. result = check(n.sons[0], ctx, map)
  1081. of nkIfStmt, nkIfExpr:
  1082. result = checkIf(n, ctx, map)
  1083. of nkAsgn, nkFastAsgn, nkSinkAsgn:
  1084. result = checkAsgn(n[0], n[1], ctx, map)
  1085. of nkVarSection:
  1086. result.map = map
  1087. for child in n:
  1088. result = checkAsgn(child[0], child[2], ctx, result.map)
  1089. of nkForStmt:
  1090. result = checkFor(n, ctx, map)
  1091. of nkCaseStmt:
  1092. result = checkCase(n, ctx, map)
  1093. of nkReturnStmt:
  1094. result = checkReturn(n, ctx, map)
  1095. of nkBracketExpr:
  1096. result = checkBracketExpr(n, ctx, map)
  1097. of nkTryStmt:
  1098. result = checkTry(n, ctx, map)
  1099. of nkWhileStmt:
  1100. result = checkWhile(n, ctx, map)
  1101. of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
  1102. nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
  1103. nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
  1104. nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
  1105. nkTypeOfExpr, nkMixinStmt, nkBindStmt:
  1106. discard "don't follow this : same as varpartitions"
  1107. result = Check(nilability: Nil, map: map)
  1108. else:
  1109. var elementMap = map.copyMap()
  1110. var elementCheck: Check
  1111. elementCheck.map = elementMap
  1112. for element in n:
  1113. elementCheck = check(element, ctx, elementCheck.map)
  1114. result = Check(nilability: Nil, map: elementCheck.map)
  1115. proc typeNilability(typ: PType): Nilability =
  1116. assert not typ.isNil
  1117. # echo "typeNilability ", $typ.flags, " ", $typ.kind
  1118. result = if tfNotNil in typ.flags:
  1119. Safe
  1120. elif typ.kind in {tyRef, tyCstring, tyPtr, tyPointer}:
  1121. #
  1122. # tyVar ? tyVarargs ? tySink ? tyLent ?
  1123. # TODO spec? tests?
  1124. MaybeNil
  1125. else:
  1126. Safe
  1127. # echo " result ", result
  1128. proc preVisitNode(ctx: NilCheckerContext, node: PNode, conf: ConfigRef) =
  1129. # echo "visit node ", node
  1130. if node.kind in {nkSym, nkDotExpr} or isConstBracket(node):
  1131. let nodeSymbol = symbol(node)
  1132. if not ctx.symbolIndices.hasKey(nodeSymbol):
  1133. ctx.symbolIndices[nodeSymbol] = ctx.expressions.len
  1134. ctx.expressions.add(node)
  1135. if node.kind in {nkDotExpr, nkBracketExpr}:
  1136. if node.kind == nkDotExpr and (not node.typ.isNil and node.typ.kind == tyRef and tfNotNil notin node.typ.flags) or
  1137. node.kind == nkBracketExpr:
  1138. let index = ctx.symbolIndices[nodeSymbol]
  1139. var baseIndex = noExprIndex
  1140. # deref usually?
  1141. # ok, we hit another case
  1142. var base = if node[0].kind notin {nkSym, nkIdent}: node[0][0] else: node[0]
  1143. if base.kind != nkIdent:
  1144. let baseSymbol = symbol(base)
  1145. if not ctx.symbolIndices.hasKey(baseSymbol):
  1146. baseIndex = ctx.expressions.len # next visit should add it
  1147. else:
  1148. baseIndex = ctx.symbolIndices[baseSymbol]
  1149. if ctx.dependants.len <= baseIndex:
  1150. ctx.dependants.setLen(baseIndex + 1.ExprIndex)
  1151. ctx.dependants[baseIndex].incl(index.int)
  1152. case node.kind:
  1153. of nkSym, nkEmpty, nkNilLit, nkType, nkIdent, nkCharLit .. nkUInt64Lit, nkFloatLit .. nkFloat64Lit, nkStrLit .. nkTripleStrLit:
  1154. discard
  1155. of nkDotExpr:
  1156. # visit only the base
  1157. ctx.preVisitNode(node[0], conf)
  1158. else:
  1159. for element in node:
  1160. ctx.preVisitNode(element, conf)
  1161. proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) =
  1162. ctx.symbolIndices = {resultId: resultExprIndex}.toTable()
  1163. var cache = newIdentCache()
  1164. ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)])
  1165. var emptySet: IntSet = initIntSet() # set[ExprIndex]
  1166. ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet])
  1167. for i, arg in s.typ.n.sons:
  1168. if i > 0:
  1169. if arg.kind != nkSym:
  1170. continue
  1171. let argSymbol = symbol(arg)
  1172. if not ctx.symbolIndices.hasKey(argSymbol):
  1173. ctx.symbolIndices[argSymbol] = ctx.expressions.len
  1174. ctx.expressions.add(arg)
  1175. ctx.preVisitNode(body, conf)
  1176. if ctx.dependants.len < ctx.expressions.len:
  1177. ctx.dependants.setLen(ctx.expressions.len)
  1178. # echo ctx.symbolIndices
  1179. # echo ctx.expressions
  1180. # echo ctx.dependants
  1181. proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
  1182. let line = s.ast.info.line
  1183. let fileIndex = s.ast.info.fileIndex.int
  1184. var filename = conf.m.fileInfos[fileIndex].fullPath.string
  1185. var context = NilCheckerContext(config: conf, idgen: idgen)
  1186. context.preVisit(s, body, conf)
  1187. var map = newNilMap(nil, context.symbolIndices.len)
  1188. for i, child in s.typ.n.sons:
  1189. if i > 0:
  1190. if child.kind != nkSym:
  1191. continue
  1192. map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child)
  1193. map.store(context, resultExprIndex, if not s.typ[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info)
  1194. # echo "checking ", s.name.s, " ", filename
  1195. let res = check(body, context, map)
  1196. var canCheck = resultExprIndex in res.map.history.low .. res.map.history.high
  1197. if res.nilability == Safe and canCheck and res.map.history[resultExprIndex].len <= 1:
  1198. res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info)
  1199. else:
  1200. if res.nilability == Safe:
  1201. res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info)
  1202. # TODO check for nilability result
  1203. # (ANotNil, BNotNil) :
  1204. # do we check on asgn nilability at all?
  1205. if not s.typ[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags:
  1206. checkResult(s.ast, context, res.map)