guards.nim 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements the 'implies' relation for guards.
  10. import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
  11. saturate, modulegraphs, options, lineinfos, int128
  12. when defined(nimPreviewSlimSystem):
  13. import std/assertions
  14. const
  15. someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
  16. mEqStr, mEqSet, mEqCString}
  17. # set excluded here as the semantics are vastly different:
  18. someLe = {mLeI, mLeF64, mLeU, mLeEnum,
  19. mLeCh, mLeB, mLePtr, mLeStr}
  20. someLt = {mLtI, mLtF64, mLtU, mLtEnum,
  21. mLtCh, mLtB, mLtPtr, mLtStr}
  22. someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
  23. someIn = {mInSet}
  24. someHigh = {mHigh}
  25. # we don't list unsigned here because wrap around semantics suck for
  26. # proving anything:
  27. someAdd = {mAddI, mAddF64, mSucc}
  28. someSub = {mSubI, mSubF64, mPred}
  29. someMul = {mMulI, mMulF64}
  30. someDiv = {mDivI, mDivF64}
  31. someMod = {mModI}
  32. someMax = {mMaxI}
  33. someMin = {mMinI}
  34. someBinaryOp = someAdd+someSub+someMul+someMax+someMin
  35. proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
  36. proc isLocation(n: PNode): bool = not n.isValue
  37. proc isLet(n: PNode): bool =
  38. if n.kind == nkSym:
  39. if n.sym.kind in {skLet, skTemp, skForVar}:
  40. result = true
  41. elif n.sym.kind == skParam and skipTypes(n.sym.typ,
  42. abstractInst).kind notin {tyVar}:
  43. result = true
  44. else:
  45. result = false
  46. else:
  47. result = false
  48. proc isVar(n: PNode): bool =
  49. n.kind == nkSym and n.sym.kind in {skResult, skVar} and
  50. {sfAddrTaken} * n.sym.flags == {}
  51. proc isLetLocation(m: PNode, isApprox: bool): bool =
  52. # consider: 'n[].kind' --> we really need to support 1 deref op even if this
  53. # is technically wrong due to aliasing :-( We could introduce "soft" facts
  54. # for this; this would still be very useful for warnings and also nicely
  55. # solves the 'var' problems. For now we fix this by requiring much more
  56. # restrictive expressions for the 'not nil' checking.
  57. var n = m
  58. var derefs = 0
  59. while true:
  60. case n.kind
  61. of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
  62. n = n[0]
  63. of nkDerefExpr:
  64. n = n[0]
  65. inc derefs
  66. of nkHiddenDeref:
  67. n = n[0]
  68. if not isApprox: inc derefs
  69. of nkBracketExpr:
  70. if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
  71. n = n[0]
  72. else: return
  73. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  74. n = n[1]
  75. else:
  76. break
  77. result = n.isLet and derefs <= ord(isApprox)
  78. if not result and isApprox:
  79. result = isVar(n)
  80. proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
  81. proc swapArgs(fact: PNode, newOp: PSym): PNode =
  82. result = newNodeI(nkCall, fact.info, 3)
  83. result[0] = newSymNode(newOp)
  84. result[1] = fact[2]
  85. result[2] = fact[1]
  86. proc neg(n: PNode; o: Operators): PNode =
  87. if n == nil: return nil
  88. case n.getMagic
  89. of mNot:
  90. result = n[1]
  91. of someLt:
  92. # not (a < b) == a >= b == b <= a
  93. result = swapArgs(n, o.opLe)
  94. of someLe:
  95. result = swapArgs(n, o.opLt)
  96. of mInSet:
  97. if n[1].kind != nkCurly: return nil
  98. let t = n[2].typ.skipTypes(abstractInst)
  99. result = newNodeI(nkCall, n.info, 3)
  100. result[0] = n[0]
  101. result[2] = n[2]
  102. if t.kind == tyEnum:
  103. var s = newNodeIT(nkCurly, n.info, n[1].typ)
  104. for e in t.n:
  105. let eAsNode = newIntNode(nkIntLit, e.sym.position)
  106. if not inSet(n[1], eAsNode): s.add eAsNode
  107. result[1] = s
  108. #elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
  109. # result[1] = complement(n[1])
  110. else:
  111. # not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
  112. # XXX todo
  113. result = nil
  114. of mOr:
  115. # not (a or b) --> not a and not b
  116. let
  117. a = n[1].neg(o)
  118. b = n[2].neg(o)
  119. if a != nil and b != nil:
  120. result = newNodeI(nkCall, n.info, 3)
  121. result[0] = newSymNode(o.opAnd)
  122. result[1] = a
  123. result[2] = b
  124. elif a != nil:
  125. result = a
  126. elif b != nil:
  127. result = b
  128. else:
  129. result = nil
  130. else:
  131. # leave not (a == 4) as it is
  132. result = newNodeI(nkCall, n.info, 2)
  133. result[0] = newSymNode(o.opNot)
  134. result[1] = n
  135. proc buildCall*(op: PSym; a: PNode): PNode =
  136. result = newNodeI(nkCall, a.info, 2)
  137. result[0] = newSymNode(op)
  138. result[1] = a
  139. proc buildCall*(op: PSym; a, b: PNode): PNode =
  140. result = newNodeI(nkInfix, a.info, 3)
  141. result[0] = newSymNode(op)
  142. result[1] = a
  143. result[2] = b
  144. proc `|+|`(a, b: PNode): PNode =
  145. result = copyNode(a)
  146. if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |+| b.intVal
  147. else: result.floatVal = a.floatVal + b.floatVal
  148. proc `|-|`(a, b: PNode): PNode =
  149. result = copyNode(a)
  150. if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |-| b.intVal
  151. else: result.floatVal = a.floatVal - b.floatVal
  152. proc `|*|`(a, b: PNode): PNode =
  153. result = copyNode(a)
  154. if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |*| b.intVal
  155. else: result.floatVal = a.floatVal * b.floatVal
  156. proc `|div|`(a, b: PNode): PNode =
  157. result = copyNode(a)
  158. if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal
  159. else: result.floatVal = a.floatVal / b.floatVal
  160. proc negate(a, b, res: PNode; o: Operators): PNode =
  161. if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
  162. var b = copyNode(b)
  163. b.intVal = -b.intVal
  164. if a.kind in {nkCharLit..nkUInt64Lit}:
  165. b.intVal = b.intVal |+| a.intVal
  166. result = b
  167. else:
  168. result = buildCall(o.opAdd, a, b)
  169. elif b.kind in {nkFloatLit..nkFloat64Lit}:
  170. var b = copyNode(b)
  171. b.floatVal = -b.floatVal
  172. result = buildCall(o.opAdd, a, b)
  173. else:
  174. result = res
  175. proc zero(): PNode = nkIntLit.newIntNode(0)
  176. proc one(): PNode = nkIntLit.newIntNode(1)
  177. proc minusOne(): PNode = nkIntLit.newIntNode(-1)
  178. proc lowBound*(conf: ConfigRef; x: PNode): PNode =
  179. result = nkIntLit.newIntNode(firstOrd(conf, x.typ))
  180. result.info = x.info
  181. proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
  182. let typ = x.typ.skipTypes(abstractInst)
  183. result = if typ.kind == tyArray:
  184. nkIntLit.newIntNode(lastOrd(conf, typ))
  185. elif typ.kind == tySequence and x.kind == nkSym and
  186. x.sym.kind == skConst:
  187. nkIntLit.newIntNode(x.sym.astdef.len-1)
  188. else:
  189. o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
  190. result.info = x.info
  191. proc reassociation(n: PNode; o: Operators): PNode =
  192. result = n
  193. # (foo+5)+5 --> foo+10; same for '*'
  194. case result.getMagic
  195. of someAdd:
  196. if result[2].isValue and
  197. result[1].getMagic in someAdd and result[1][2].isValue:
  198. result = o.opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
  199. if result[2].intVal == 0:
  200. result = result[1]
  201. of someMul:
  202. if result[2].isValue and
  203. result[1].getMagic in someMul and result[1][2].isValue:
  204. result = o.opMul.buildCall(result[1][1], result[1][2] |*| result[2])
  205. if result[2].intVal == 1:
  206. result = result[1]
  207. elif result[2].intVal == 0:
  208. result = zero()
  209. else: discard
  210. proc pred(n: PNode): PNode =
  211. if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
  212. result = copyNode(n)
  213. dec result.intVal
  214. else:
  215. result = n
  216. proc buildLe*(o: Operators; a, b: PNode): PNode =
  217. result = o.opLe.buildCall(a, b)
  218. proc canon*(n: PNode; o: Operators): PNode =
  219. if n.safeLen >= 1:
  220. result = shallowCopy(n)
  221. for i in 0..<n.len:
  222. result[i] = canon(n[i], o)
  223. elif n.kind == nkSym and n.sym.kind == skLet and
  224. n.sym.astdef.getMagic in (someEq + someAdd + someMul + someMin +
  225. someMax + someHigh + someSub + someLen + someDiv):
  226. result = n.sym.astdef.copyTree
  227. else:
  228. result = n
  229. case result.getMagic
  230. of someEq, someAdd, someMul, someMin, someMax:
  231. # these are symmetric; put value as last:
  232. if result[1].isValue and not result[2].isValue:
  233. result = swapArgs(result, result[0].sym)
  234. # (4 + foo) + 2 --> (foo + 4) + 2
  235. of someHigh:
  236. # high == len+(-1)
  237. result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne())
  238. of someSub:
  239. # x - 4 --> x + (-4)
  240. result = negate(result[1], result[2], result, o)
  241. of someLen:
  242. result[0] = o.opLen.newSymNode
  243. of someLt - {mLtF64}:
  244. # x < y same as x <= y-1:
  245. let y = n[2].canon(o)
  246. let p = pred(y)
  247. let minus = if p != y: p else: o.opAdd.buildCall(y, minusOne()).canon(o)
  248. result = o.opLe.buildCall(n[1].canon(o), minus)
  249. else: discard
  250. result = skipConv(result)
  251. result = reassociation(result, o)
  252. # most important rule: (x-4) <= a.len --> x <= a.len+4
  253. case result.getMagic
  254. of someLe:
  255. let x = result[1]
  256. let y = result[2]
  257. if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and
  258. isLetLocation(x[1], true):
  259. case x.getMagic
  260. of someSub:
  261. result = buildCall(result[0].sym, x[1],
  262. reassociation(o.opAdd.buildCall(y, x[2]), o))
  263. of someAdd:
  264. # Rule A:
  265. let plus = negate(y, x[2], nil, o).reassociation(o)
  266. if plus != nil: result = buildCall(result[0].sym, x[1], plus)
  267. else: discard
  268. elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
  269. isLetLocation(y[1], true):
  270. # a.len < x-3
  271. case y.getMagic
  272. of someSub:
  273. result = buildCall(result[0].sym, y[1],
  274. reassociation(o.opAdd.buildCall(x, y[2]), o))
  275. of someAdd:
  276. let plus = negate(x, y[2], nil, o).reassociation(o)
  277. # ensure that Rule A will not trigger afterwards with the
  278. # additional 'not isLetLocation' constraint:
  279. if plus != nil and not isLetLocation(x, true):
  280. result = buildCall(result[0].sym, plus, y[1])
  281. else: discard
  282. elif x.isValue and y.getMagic in someAdd and y[2].kind == x.kind:
  283. # 0 <= a.len + 3
  284. # -3 <= a.len
  285. result[1] = x |-| y[2]
  286. result[2] = y[1]
  287. elif x.isValue and y.getMagic in someSub and y[2].kind == x.kind:
  288. # 0 <= a.len - 3
  289. # 3 <= a.len
  290. result[1] = x |+| y[2]
  291. result[2] = y[1]
  292. else: discard
  293. proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
  294. canon(if b != 0: o.opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a, o)
  295. proc usefulFact(n: PNode; o: Operators): PNode =
  296. case n.getMagic
  297. of someEq:
  298. if skipConv(n[2]).kind == nkNilLit and (
  299. isLetLocation(n[1], false) or isVar(n[1])):
  300. result = o.opIsNil.buildCall(n[1])
  301. else:
  302. if isLetLocation(n[1], true) or isLetLocation(n[2], true):
  303. # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
  304. result = n
  305. elif n[1].getMagic in someLen or n[2].getMagic in someLen:
  306. result = n
  307. else:
  308. result = nil
  309. of someLe+someLt:
  310. if isLetLocation(n[1], true) or isLetLocation(n[2], true):
  311. # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
  312. result = n
  313. elif n[1].getMagic in someLen or n[2].getMagic in someLen:
  314. # XXX Rethink this whole idea of 'usefulFact' for semparallel
  315. result = n
  316. else:
  317. result = nil
  318. of mIsNil:
  319. if isLetLocation(n[1], false) or isVar(n[1]):
  320. result = n
  321. else:
  322. result = nil
  323. of someIn:
  324. if isLetLocation(n[1], true):
  325. result = n
  326. else:
  327. result = nil
  328. of mAnd:
  329. let
  330. a = usefulFact(n[1], o)
  331. b = usefulFact(n[2], o)
  332. if a != nil and b != nil:
  333. result = newNodeI(nkCall, n.info, 3)
  334. result[0] = newSymNode(o.opAnd)
  335. result[1] = a
  336. result[2] = b
  337. elif a != nil:
  338. result = a
  339. elif b != nil:
  340. result = b
  341. else:
  342. result = nil
  343. of mNot:
  344. let a = usefulFact(n[1], o)
  345. if a != nil:
  346. result = a.neg(o)
  347. else:
  348. result = nil
  349. of mOr:
  350. # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
  351. # with that knowledge...
  352. # DeMorgan helps a little though:
  353. # not a or not b --> not (a and b)
  354. # (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
  355. # not (x != 3 and y != 2)
  356. let
  357. a = usefulFact(n[1], o).neg(o)
  358. b = usefulFact(n[2], o).neg(o)
  359. if a != nil and b != nil:
  360. result = newNodeI(nkCall, n.info, 3)
  361. result[0] = newSymNode(o.opAnd)
  362. result[1] = a
  363. result[2] = b
  364. result = result.neg(o)
  365. else:
  366. result = nil
  367. elif n.kind == nkSym and n.sym.kind == skLet:
  368. # consider:
  369. # let a = 2 < x
  370. # if a:
  371. # ...
  372. # We make can easily replace 'a' by '2 < x' here:
  373. if n.sym.astdef != nil:
  374. result = usefulFact(n.sym.astdef, o)
  375. else:
  376. result = nil
  377. elif n.kind == nkStmtListExpr:
  378. result = usefulFact(n.lastSon, o)
  379. else:
  380. result = nil
  381. type
  382. TModel* = object
  383. s*: seq[PNode] # the "knowledge base"
  384. g*: ModuleGraph
  385. beSmart*: bool
  386. proc addFact*(m: var TModel, nn: PNode) =
  387. let n = usefulFact(nn, m.g.operators)
  388. if n != nil:
  389. if not m.beSmart:
  390. m.s.add n
  391. else:
  392. let c = canon(n, m.g.operators)
  393. if c.getMagic == mAnd:
  394. addFact(m, c[1])
  395. addFact(m, c[2])
  396. else:
  397. m.s.add c
  398. proc addFactNeg*(m: var TModel, n: PNode) =
  399. let n = n.neg(m.g.operators)
  400. if n != nil: addFact(m, n)
  401. proc sameOpr(a, b: PSym): bool =
  402. case a.magic
  403. of someEq: result = b.magic in someEq
  404. of someLe: result = b.magic in someLe
  405. of someLt: result = b.magic in someLt
  406. of someLen: result = b.magic in someLen
  407. of someAdd: result = b.magic in someAdd
  408. of someSub: result = b.magic in someSub
  409. of someMul: result = b.magic in someMul
  410. of someDiv: result = b.magic in someDiv
  411. else: result = a == b
  412. proc sameTree*(a, b: PNode): bool =
  413. result = false
  414. if a == b:
  415. result = true
  416. elif a != nil and b != nil and a.kind == b.kind:
  417. case a.kind
  418. of nkSym:
  419. result = a.sym == b.sym
  420. if not result and a.sym.magic != mNone:
  421. result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
  422. of nkIdent: result = a.ident.id == b.ident.id
  423. of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
  424. of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
  425. of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
  426. of nkType: result = a.typ == b.typ
  427. of nkEmpty, nkNilLit: result = true
  428. else:
  429. if a.len == b.len:
  430. for i in 0..<a.len:
  431. if not sameTree(a[i], b[i]): return
  432. result = true
  433. proc hasSubTree(n, x: PNode): bool =
  434. if n.sameTree(x): result = true
  435. else:
  436. case n.kind
  437. of nkEmpty..nkNilLit:
  438. result = n.sameTree(x)
  439. of nkFormalParams:
  440. result = false
  441. else:
  442. result = false
  443. for i in 0..<n.len:
  444. if hasSubTree(n[i], x): return true
  445. proc invalidateFacts*(s: var seq[PNode], n: PNode) =
  446. # We are able to guard local vars (as opposed to 'let' variables)!
  447. # 'while p != nil: f(p); p = p.next'
  448. # This is actually quite easy to do:
  449. # Re-assignments (incl. pass to a 'var' param) trigger an invalidation
  450. # of every fact that contains 'v'.
  451. #
  452. # if x < 4:
  453. # if y < 5
  454. # x = unknown()
  455. # # we invalidate 'x' here but it's known that x >= 4
  456. # # for the else anyway
  457. # else:
  458. # echo x
  459. #
  460. # The same mechanism could be used for more complex data stored on the heap;
  461. # procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
  462. # could CSE these expressions then and help C's optimizer.
  463. for i in 0..high(s):
  464. if s[i] != nil and s[i].hasSubTree(n): s[i] = nil
  465. proc invalidateFacts*(m: var TModel, n: PNode) =
  466. invalidateFacts(m.s, n)
  467. proc valuesUnequal(a, b: PNode): bool =
  468. if a.isValue and b.isValue:
  469. result = not sameValue(a, b)
  470. else:
  471. result = false
  472. proc impliesEq(fact, eq: PNode): TImplication =
  473. let (loc, val) = if isLocation(eq[1]): (1, 2) else: (2, 1)
  474. case fact[0].sym.magic
  475. of someEq:
  476. if sameTree(fact[1], eq[loc]):
  477. # this is not correct; consider: a == b; a == 1 --> unknown!
  478. if sameTree(fact[2], eq[val]): result = impYes
  479. elif valuesUnequal(fact[2], eq[val]): result = impNo
  480. else:
  481. result = impUnknown
  482. elif sameTree(fact[2], eq[loc]):
  483. if sameTree(fact[1], eq[val]): result = impYes
  484. elif valuesUnequal(fact[1], eq[val]): result = impNo
  485. else:
  486. result = impUnknown
  487. else:
  488. result = impUnknown
  489. of mInSet:
  490. # remember: mInSet is 'contains' so the set comes first!
  491. if sameTree(fact[2], eq[loc]) and isValue(eq[val]):
  492. if inSet(fact[1], eq[val]): result = impYes
  493. else: result = impNo
  494. else:
  495. result = impUnknown
  496. of mNot, mOr, mAnd:
  497. result = impUnknown
  498. assert(false, "impliesEq")
  499. else: result = impUnknown
  500. proc leImpliesIn(x, c, aSet: PNode): TImplication =
  501. if c.kind in {nkCharLit..nkUInt64Lit}:
  502. # fact: x <= 4; question x in {56}?
  503. # --> true if every value <= 4 is in the set {56}
  504. #
  505. var value = newIntNode(c.kind, firstOrd(nil, x.typ))
  506. # don't iterate too often:
  507. if c.intVal - value.intVal < 1000:
  508. var i, pos, neg: int = 0
  509. while value.intVal <= c.intVal:
  510. if inSet(aSet, value): inc pos
  511. else: inc neg
  512. inc i; inc value.intVal
  513. if pos == i: result = impYes
  514. elif neg == i: result = impNo
  515. else:
  516. result = impUnknown
  517. else:
  518. result = impUnknown
  519. else:
  520. result = impUnknown
  521. proc geImpliesIn(x, c, aSet: PNode): TImplication =
  522. if c.kind in {nkCharLit..nkUInt64Lit}:
  523. # fact: x >= 4; question x in {56}?
  524. # --> true iff every value >= 4 is in the set {56}
  525. #
  526. var value = newIntNode(c.kind, c.intVal)
  527. let max = lastOrd(nil, x.typ)
  528. # don't iterate too often:
  529. if max - getInt(value) < toInt128(1000):
  530. var i, pos, neg: int = 0
  531. while value.intVal <= max:
  532. if inSet(aSet, value): inc pos
  533. else: inc neg
  534. inc i; inc value.intVal
  535. if pos == i: result = impYes
  536. elif neg == i: result = impNo
  537. else: result = impUnknown
  538. else:
  539. result = impUnknown
  540. else:
  541. result = impUnknown
  542. proc compareSets(a, b: PNode): TImplication =
  543. if equalSets(nil, a, b): result = impYes
  544. elif intersectSets(nil, a, b).len == 0: result = impNo
  545. else: result = impUnknown
  546. proc impliesIn(fact, loc, aSet: PNode): TImplication =
  547. case fact[0].sym.magic
  548. of someEq:
  549. if sameTree(fact[1], loc):
  550. if inSet(aSet, fact[2]): result = impYes
  551. else: result = impNo
  552. elif sameTree(fact[2], loc):
  553. if inSet(aSet, fact[1]): result = impYes
  554. else: result = impNo
  555. else:
  556. result = impUnknown
  557. of mInSet:
  558. if sameTree(fact[2], loc):
  559. result = compareSets(fact[1], aSet)
  560. else:
  561. result = impUnknown
  562. of someLe:
  563. if sameTree(fact[1], loc):
  564. result = leImpliesIn(fact[1], fact[2], aSet)
  565. elif sameTree(fact[2], loc):
  566. result = geImpliesIn(fact[2], fact[1], aSet)
  567. else:
  568. result = impUnknown
  569. of someLt:
  570. if sameTree(fact[1], loc):
  571. result = leImpliesIn(fact[1], fact[2].pred, aSet)
  572. elif sameTree(fact[2], loc):
  573. # 4 < x --> 3 <= x
  574. result = geImpliesIn(fact[2], fact[1].pred, aSet)
  575. else:
  576. result = impUnknown
  577. of mNot, mOr, mAnd:
  578. result = impUnknown
  579. assert(false, "impliesIn")
  580. else: result = impUnknown
  581. proc valueIsNil(n: PNode): TImplication =
  582. if n.kind == nkNilLit: impYes
  583. elif n.kind in {nkStrLit..nkTripleStrLit, nkBracket, nkObjConstr}: impNo
  584. else: impUnknown
  585. proc impliesIsNil(fact, eq: PNode): TImplication =
  586. case fact[0].sym.magic
  587. of mIsNil:
  588. if sameTree(fact[1], eq[1]):
  589. result = impYes
  590. else:
  591. result = impUnknown
  592. of someEq:
  593. if sameTree(fact[1], eq[1]):
  594. result = valueIsNil(fact[2].skipConv)
  595. elif sameTree(fact[2], eq[1]):
  596. result = valueIsNil(fact[1].skipConv)
  597. else:
  598. result = impUnknown
  599. of mNot, mOr, mAnd:
  600. result = impUnknown
  601. assert(false, "impliesIsNil")
  602. else: result = impUnknown
  603. proc impliesGe(fact, x, c: PNode): TImplication =
  604. assert isLocation(x)
  605. case fact[0].sym.magic
  606. of someEq:
  607. if sameTree(fact[1], x):
  608. if isValue(fact[2]) and isValue(c):
  609. # fact: x = 4; question x >= 56? --> true iff 4 >= 56
  610. if leValue(c, fact[2]): result = impYes
  611. else: result = impNo
  612. else:
  613. result = impUnknown
  614. elif sameTree(fact[2], x):
  615. if isValue(fact[1]) and isValue(c):
  616. if leValue(c, fact[1]): result = impYes
  617. else: result = impNo
  618. else:
  619. result = impUnknown
  620. else:
  621. result = impUnknown
  622. of someLt:
  623. if sameTree(fact[1], x):
  624. if isValue(fact[2]) and isValue(c):
  625. # fact: x < 4; question N <= x? --> false iff N <= 4
  626. if leValue(fact[2], c): result = impNo
  627. else: result = impUnknown
  628. # fact: x < 4; question 2 <= x? --> we don't know
  629. else:
  630. result = impUnknown
  631. elif sameTree(fact[2], x):
  632. # fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3
  633. if isValue(fact[1]) and isValue(c):
  634. if leValue(c.pred, fact[1]): result = impYes
  635. else: result = impUnknown
  636. else:
  637. result = impUnknown
  638. else:
  639. result = impUnknown
  640. of someLe:
  641. if sameTree(fact[1], x):
  642. if isValue(fact[2]) and isValue(c):
  643. # fact: x <= 4; question x >= 56? --> false iff 4 <= 56
  644. if leValue(fact[2], c): result = impNo
  645. # fact: x <= 4; question x >= 2? --> we don't know
  646. else:
  647. result = impUnknown
  648. else:
  649. result = impUnknown
  650. elif sameTree(fact[2], x):
  651. # fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
  652. if isValue(fact[1]) and isValue(c):
  653. if leValue(c, fact[1]): result = impYes
  654. else: result = impUnknown
  655. else:
  656. result = impUnknown
  657. else:
  658. result = impUnknown
  659. of mNot, mOr, mAnd:
  660. result = impUnknown
  661. assert(false, "impliesGe")
  662. else: result = impUnknown
  663. proc impliesLe(fact, x, c: PNode): TImplication =
  664. if not isLocation(x):
  665. if c.isValue:
  666. if leValue(x, x): return impYes
  667. else: return impNo
  668. return impliesGe(fact, c, x)
  669. case fact[0].sym.magic
  670. of someEq:
  671. if sameTree(fact[1], x):
  672. if isValue(fact[2]) and isValue(c):
  673. # fact: x = 4; question x <= 56? --> true iff 4 <= 56
  674. if leValue(fact[2], c): result = impYes
  675. else: result = impNo
  676. else:
  677. result = impUnknown
  678. elif sameTree(fact[2], x):
  679. if isValue(fact[1]) and isValue(c):
  680. if leValue(fact[1], c): result = impYes
  681. else: result = impNo
  682. else:
  683. result = impUnknown
  684. else:
  685. result = impUnknown
  686. of someLt:
  687. if sameTree(fact[1], x):
  688. if isValue(fact[2]) and isValue(c):
  689. # fact: x < 4; question x <= N? --> true iff N-1 <= 4
  690. if leValue(fact[2], c.pred): result = impYes
  691. else:
  692. result = impUnknown
  693. # fact: x < 4; question x <= 2? --> we don't know
  694. else:
  695. result = impUnknown
  696. elif sameTree(fact[2], x):
  697. # fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3
  698. if isValue(fact[1]) and isValue(c):
  699. if leValue(c, fact[1]): result = impNo
  700. else: result = impUnknown
  701. else:
  702. result = impUnknown
  703. else:
  704. result = impUnknown
  705. of someLe:
  706. if sameTree(fact[1], x):
  707. if isValue(fact[2]) and isValue(c):
  708. # fact: x <= 4; question x <= 56? --> true iff 4 <= 56
  709. if leValue(fact[2], c): result = impYes
  710. else: result = impUnknown
  711. # fact: x <= 4; question x <= 2? --> we don't know
  712. else:
  713. result = impUnknown
  714. elif sameTree(fact[2], x):
  715. # fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3
  716. if isValue(fact[1]) and isValue(c):
  717. if leValue(c, fact[1].pred): result = impNo
  718. else:result = impUnknown
  719. else:
  720. result = impUnknown
  721. else:
  722. result = impUnknown
  723. of mNot, mOr, mAnd:
  724. result = impUnknown
  725. assert(false, "impliesLe")
  726. else: result = impUnknown
  727. proc impliesLt(fact, x, c: PNode): TImplication =
  728. # x < 3 same as x <= 2:
  729. let p = c.pred
  730. if p != c:
  731. result = impliesLe(fact, x, p)
  732. else:
  733. # 4 < x same as 3 <= x
  734. let q = x.pred
  735. if q != x:
  736. result = impliesLe(fact, q, c)
  737. else:
  738. result = impUnknown
  739. proc `~`(x: TImplication): TImplication =
  740. case x
  741. of impUnknown: impUnknown
  742. of impNo: impYes
  743. of impYes: impNo
  744. proc factImplies(fact, prop: PNode): TImplication =
  745. case fact.getMagic
  746. of mNot:
  747. # Consider:
  748. # enum nkBinary, nkTernary, nkStr
  749. # fact: not (k <= nkBinary)
  750. # question: k in {nkStr}
  751. # --> 'not' for facts is entirely different than 'not' for questions!
  752. # it's provably wrong if every value > 4 is in the set {56}
  753. # That's because we compute the implication and 'a -> not b' cannot
  754. # be treated the same as 'not a -> b'
  755. # (not a) -> b compute as not (a -> b) ???
  756. # == not a or not b == not (a and b)
  757. let arg = fact[1]
  758. case arg.getMagic
  759. of mIsNil, mEqRef:
  760. return ~factImplies(arg, prop)
  761. of mAnd:
  762. # not (a and b) means not a or not b:
  763. # a or b --> both need to imply 'prop'
  764. let a = factImplies(arg[1], prop)
  765. let b = factImplies(arg[2], prop)
  766. if a == b: return ~a
  767. return impUnknown
  768. else:
  769. return impUnknown
  770. of mAnd:
  771. result = factImplies(fact[1], prop)
  772. if result != impUnknown: return result
  773. return factImplies(fact[2], prop)
  774. else: discard
  775. case prop[0].sym.magic
  776. of mNot: result = ~fact.factImplies(prop[1])
  777. of mIsNil: result = impliesIsNil(fact, prop)
  778. of someEq: result = impliesEq(fact, prop)
  779. of someLe: result = impliesLe(fact, prop[1], prop[2])
  780. of someLt: result = impliesLt(fact, prop[1], prop[2])
  781. of mInSet: result = impliesIn(fact, prop[2], prop[1])
  782. else: result = impUnknown
  783. proc doesImply*(facts: TModel, prop: PNode): TImplication =
  784. assert prop.kind in nkCallKinds
  785. result = impUnknown
  786. for f in facts.s:
  787. # facts can be invalidated, in which case they are 'nil':
  788. if not f.isNil:
  789. result = f.factImplies(prop)
  790. if result != impUnknown: return
  791. proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
  792. result = doesImply(m, m.g.operators.opIsNil.buildCall(arg).neg(m.g.operators))
  793. proc simpleSlice*(a, b: PNode): BiggestInt =
  794. # returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
  795. # as if it is (i+0)..(i+0).
  796. if guards.sameTree(a, b):
  797. if a.getMagic in someAdd and a[2].kind in {nkCharLit..nkUInt64Lit}:
  798. result = a[2].intVal
  799. else:
  800. result = 0
  801. else:
  802. result = -1
  803. template isMul(x): untyped = x.getMagic in someMul
  804. template isDiv(x): untyped = x.getMagic in someDiv
  805. template isAdd(x): untyped = x.getMagic in someAdd
  806. template isSub(x): untyped = x.getMagic in someSub
  807. template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit}
  808. template isIntVal(x, y): untyped = x.intVal == y
  809. import macros
  810. macro `=~`(x: PNode, pat: untyped): bool =
  811. proc m(x, pat, conds: NimNode) =
  812. case pat.kind
  813. of nnkInfix:
  814. case $pat[0]
  815. of "*": conds.add getAst(isMul(x))
  816. of "/": conds.add getAst(isDiv(x))
  817. of "+": conds.add getAst(isAdd(x))
  818. of "-": conds.add getAst(isSub(x))
  819. else:
  820. error("invalid pattern")
  821. m(newTree(nnkBracketExpr, x, newLit(1)), pat[1], conds)
  822. m(newTree(nnkBracketExpr, x, newLit(2)), pat[2], conds)
  823. of nnkPar:
  824. if pat.len == 1:
  825. m(x, pat[0], conds)
  826. else:
  827. error("invalid pattern")
  828. of nnkIdent:
  829. let c = newTree(nnkStmtListExpr, newLetStmt(pat, x))
  830. conds.add c
  831. # XXX why is this 'isVal(pat)' and not 'isVal(x)'?
  832. if ($pat)[^1] == 'c': c.add(getAst(isVal(x)))
  833. else: c.add bindSym"true"
  834. of nnkIntLit:
  835. conds.add(getAst(isIntVal(x, pat.intVal)))
  836. else:
  837. error("invalid pattern")
  838. var conds = newTree(nnkBracket)
  839. m(x, pat, conds)
  840. result = nestList(ident"and", conds)
  841. proc isMinusOne(n: PNode): bool =
  842. n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
  843. proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
  844. proc ple(m: TModel; a, b: PNode): TImplication =
  845. template `<=?`(a,b): untyped = ple(m,a,b) == impYes
  846. template `>=?`(a,b): untyped = ple(m, nkIntLit.newIntNode(b), a) == impYes
  847. # 0 <= 3
  848. if a.isValue and b.isValue:
  849. return if leValue(a, b): impYes else: impNo
  850. # use type information too: x <= 4 iff high(x) <= 4
  851. if b.isValue and a.typ != nil and a.typ.isOrdinalType:
  852. if lastOrd(nil, a.typ) <= b.intVal: return impYes
  853. # 3 <= x iff low(x) <= 3
  854. if a.isValue and b.typ != nil and b.typ.isOrdinalType:
  855. if a.intVal <= firstOrd(nil, b.typ): return impYes
  856. # x <= x
  857. if sameTree(a, b): return impYes
  858. # 0 <= x.len
  859. if b.getMagic in someLen and a.isValue:
  860. if a.intVal <= 0: return impYes
  861. # x <= y+c if 0 <= c and x <= y
  862. # x <= y+(-c) if c <= 0 and y >= x
  863. if b.getMagic in someAdd:
  864. if zero() <=? b[2] and a <=? b[1]: return impYes
  865. # x <= y-c if x+c <= y
  866. if b[2] <=? zero() and (canon(m.g.operators.opSub.buildCall(a, b[2]), m.g.operators) <=? b[1]):
  867. return impYes
  868. # x+c <= y if c <= 0 and x <= y
  869. if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes
  870. # x <= y*c if 1 <= c and x <= y and 0 <= y
  871. if b.getMagic in someMul:
  872. if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes
  873. if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
  874. a[1][2].isValue:
  875. # simplify (x div 4) * 2 <= y to x div (c div d) <= y
  876. if ple(m, buildCall(m.g.operators.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
  877. return impYes
  878. # x*3 + x == x*4. It follows that:
  879. # x*3 + y <= x*4 if y <= x and 3 <= 4
  880. if a =~ x*dc + y and b =~ x2*ec:
  881. if sameTree(x, x2):
  882. let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
  883. if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
  884. return impYes
  885. elif a =~ x*dc and b =~ x2*ec + y:
  886. #echo "BUG cam ehrer e ", a, " <=? ", b
  887. if sameTree(x, x2):
  888. let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
  889. if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
  890. return impYes
  891. # x+c <= x+d if c <= d. Same for *, - etc.
  892. if a.getMagic in someBinaryOp and a.getMagic == b.getMagic:
  893. if sameTree(a[1], b[1]) and a[2] <=? b[2]: return impYes
  894. elif sameTree(a[2], b[2]) and a[1] <=? b[1]: return impYes
  895. # x div c <= y if 1 <= c and 0 <= y and x <= y:
  896. if a.getMagic in someDiv:
  897. if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes
  898. # x div c <= x div d if d <= c
  899. if b.getMagic in someDiv:
  900. if sameTree(a[1], b[1]) and b[2] <=? a[2]: return impYes
  901. # x div z <= x - 1 if z <= x
  902. if a[2].isValue and b.getMagic in someAdd and b[2].isMinusOne:
  903. if a[2] <=? a[1] and sameTree(a[1], b[1]): return impYes
  904. # slightly subtle:
  905. # x <= max(y, z) iff x <= y or x <= z
  906. # note that 'x <= max(x, z)' is a special case of the above rule
  907. if b.getMagic in someMax:
  908. if a <=? b[1] or a <=? b[2]: return impYes
  909. # min(x, y) <= z iff x <= z or y <= z
  910. if a.getMagic in someMin:
  911. if a[1] <=? b or a[2] <=? b: return impYes
  912. # use the knowledge base:
  913. return pleViaModel(m, a, b)
  914. #return doesImply(m, o.opLe.buildCall(a, b))
  915. type TReplacements = seq[tuple[a, b: PNode]]
  916. proc replaceSubTree(n, x, by: PNode): PNode =
  917. if sameTree(n, x):
  918. result = by
  919. elif hasSubTree(n, x):
  920. result = shallowCopy(n)
  921. for i in 0..n.safeLen-1:
  922. result[i] = replaceSubTree(n[i], x, by)
  923. else:
  924. result = n
  925. proc applyReplacements(n: PNode; rep: TReplacements): PNode =
  926. result = n
  927. for x in rep: result = result.replaceSubTree(x.a, x.b)
  928. proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
  929. # now check for inferrable facts: a <= b and b <= c implies a <= c
  930. result = impUnknown
  931. for i in 0..m.s.high:
  932. let fact = m.s[i]
  933. if fact != nil and fact.getMagic in someLe:
  934. # mark as used:
  935. m.s[i] = nil
  936. # i <= len-100
  937. # i <=? len-1
  938. # --> true if (len-100) <= (len-1)
  939. let x = fact[1]
  940. let y = fact[2]
  941. # x <= y.
  942. # Question: x <= b? True iff y <= b.
  943. if sameTree(x, a):
  944. if ple(m, y, b) == impYes: return impYes
  945. if y.getMagic in someAdd and b.getMagic in someAdd and sameTree(y[1], b[1]):
  946. if ple(m, b[2], y[2]) == impYes:
  947. return impYes
  948. # x <= y implies a <= b if a <= x and y <= b
  949. if ple(m, a, x) == impYes:
  950. if ple(m, y, b) == impYes:
  951. return impYes
  952. #if pleViaModelRec(m, y, b): return impYes
  953. # fact: 16 <= i
  954. # x y
  955. # question: i <= 15? no!
  956. result = impliesLe(fact, a, b)
  957. if result != impUnknown:
  958. return result
  959. when false:
  960. # given: x <= y; y==a; x <= a this means: a <= b if x <= b
  961. if sameTree(y, a):
  962. result = ple(m, b, x)
  963. if result != impUnknown:
  964. return result
  965. proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
  966. # compute replacements:
  967. var replacements: TReplacements = @[]
  968. for fact in model.s:
  969. if fact != nil and fact.getMagic in someEq:
  970. let a = fact[1]
  971. let b = fact[2]
  972. if a.kind == nkSym: replacements.add((a,b))
  973. else: replacements.add((b,a))
  974. var m: TModel
  975. var a = aa
  976. var b = bb
  977. if replacements.len > 0:
  978. m.s = @[]
  979. m.g = model.g
  980. # make the other facts consistent:
  981. for fact in model.s:
  982. if fact != nil and fact.getMagic notin someEq:
  983. # XXX 'canon' should not be necessary here, but it is
  984. m.s.add applyReplacements(fact, replacements).canon(m.g.operators)
  985. a = applyReplacements(aa, replacements)
  986. b = applyReplacements(bb, replacements)
  987. else:
  988. # we have to make a copy here, because the model will be modified:
  989. m = model
  990. result = pleViaModelRec(m, a, b)
  991. proc proveLe*(m: TModel; a, b: PNode): TImplication =
  992. let x = canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
  993. #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
  994. result = ple(m, x[1], x[2])
  995. if result == impUnknown:
  996. # try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
  997. let y = canon(m.g.operators.opLe.buildCall(m.g.operators.opAdd.buildCall(b, one()), a), m.g.operators)
  998. result = ~ple(m, y[1], y[2])
  999. proc addFactLe*(m: var TModel; a, b: PNode) =
  1000. m.s.add canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
  1001. proc addFactLt*(m: var TModel; a, b: PNode) =
  1002. let bb = m.g.operators.opAdd.buildCall(b, minusOne())
  1003. addFactLe(m, a, bb)
  1004. proc settype(n: PNode): PType =
  1005. result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner)
  1006. var idgen: IdGenerator = nil
  1007. addSonSkipIntLit(result, n.typ, idgen)
  1008. proc buildOf(it, loc: PNode; o: Operators): PNode =
  1009. var s = newNodeI(nkCurly, it.info, it.len-1)
  1010. s.typ = settype(loc)
  1011. for i in 0..<it.len-1: s[i] = it[i]
  1012. result = newNodeI(nkCall, it.info, 3)
  1013. result[0] = newSymNode(o.opContains)
  1014. result[1] = s
  1015. result[2] = loc
  1016. proc buildElse(n: PNode; o: Operators): PNode =
  1017. var s = newNodeIT(nkCurly, n.info, settype(n[0]))
  1018. for i in 1..<n.len-1:
  1019. let branch = n[i]
  1020. assert branch.kind != nkElse
  1021. if branch.kind == nkOfBranch:
  1022. for j in 0..<branch.len-1:
  1023. s.add(branch[j])
  1024. result = newNodeI(nkCall, n.info, 3)
  1025. result[0] = newSymNode(o.opContains)
  1026. result[1] = s
  1027. result[2] = n[0]
  1028. proc addDiscriminantFact*(m: var TModel, n: PNode) =
  1029. var fact = newNodeI(nkCall, n.info, 3)
  1030. fact[0] = newSymNode(m.g.operators.opEq)
  1031. fact[1] = n[0]
  1032. fact[2] = n[1]
  1033. m.s.add fact
  1034. proc addAsgnFact*(m: var TModel, key, value: PNode) =
  1035. var fact = newNodeI(nkCall, key.info, 3)
  1036. fact[0] = newSymNode(m.g.operators.opEq)
  1037. fact[1] = key
  1038. fact[2] = value
  1039. m.s.add fact
  1040. proc sameSubexprs*(m: TModel; a, b: PNode): bool =
  1041. # This should be used to check whether two *path expressions* refer to the
  1042. # same memory location according to 'm'. This is tricky:
  1043. # lock a[i].guard:
  1044. # ...
  1045. # access a[i].guarded
  1046. #
  1047. # Here a[i] is the same as a[i] iff 'i' and 'a' are not changed via '...'.
  1048. # However, nil checking requires exactly the same mechanism! But for now
  1049. # we simply use sameTree and live with the unsoundness of the analysis.
  1050. var check = newNodeI(nkCall, a.info, 3)
  1051. check[0] = newSymNode(m.g.operators.opEq)
  1052. check[1] = a
  1053. check[2] = b
  1054. result = m.doesImply(check) == impYes
  1055. proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
  1056. let branch = n[i]
  1057. if branch.kind == nkOfBranch:
  1058. m.s.add buildOf(branch, n[0], m.g.operators)
  1059. else:
  1060. m.s.add n.buildElse(m.g.operators).neg(m.g.operators)
  1061. proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
  1062. if check[1].kind == nkCurly:
  1063. result = copyTree(check)
  1064. if access.kind == nkDotExpr:
  1065. var a = copyTree(access)
  1066. a[1] = check[2]
  1067. result[2] = a
  1068. # 'access.kind != nkDotExpr' can happen for object constructors
  1069. # which we don't check yet
  1070. else:
  1071. # it is some 'not'
  1072. assert check.getMagic == mNot
  1073. result = buildProperFieldCheck(access, check[1], o).neg(o)
  1074. proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef; produceError: bool) =
  1075. for i in 1..<n.len:
  1076. let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
  1077. if check != nil and m.doesImply(check) != impYes:
  1078. if produceError:
  1079. localError(conf, n.info, "field access outside of valid case branch: " & renderTree(n[0]))
  1080. else:
  1081. message(conf, n.info, warnProveField, renderTree(n[0]))
  1082. break