nilcheck.nim 44 KB

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