nilcheck.nim 44 KB

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