drnim.nim 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273
  1. #
  2. #
  3. # Doctor Nim
  4. # (c) Copyright 2020 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. #[
  10. - introduce Phi nodes to complete the SSA representation
  11. - the analysis has to take 'break', 'continue' and 'raises' into account
  12. - We need to map arrays to Z3 and test for something like 'forall(i, (i in 3..4) -> (a[i] > 3))'
  13. - We need teach DrNim what 'inc', 'dec' and 'swap' mean, for example
  14. 'x in n..m; inc x' implies 'x in n+1..m+1'
  15. ]#
  16. import std / [
  17. parseopt, strutils, os, tables, times, intsets, hashes
  18. ]
  19. import ".." / compiler / [
  20. ast, astalgo, types, renderer,
  21. commands, options, msgs,
  22. platform, trees, wordrecg, guards,
  23. idents, lineinfos, cmdlinehelper, modulegraphs, condsyms,
  24. pathutils, passes, passaux, sem, modules
  25. ]
  26. import z3 / z3_api
  27. when not defined(windows):
  28. # on UNIX we use static linking because UNIX's lib*.so system is broken
  29. # beyond repair and the neckbeards don't understand software development.
  30. {.passL: "dist/z3/build/libz3.a".}
  31. const
  32. HelpMessage = "DrNim Version $1 [$2: $3]\n" &
  33. "Compiled at $4\n" &
  34. "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
  35. const
  36. Usage = """
  37. drnim [options] [projectfile]
  38. Options: Same options that the Nim compiler supports. Plus:
  39. --assumeUnique Assume unique `ref` pointers. This makes the analysis unsound
  40. but more useful for wild Nim code such as the Nim compiler
  41. itself.
  42. """
  43. proc getCommandLineDesc(conf: ConfigRef): string =
  44. result = (HelpMessage % [system.NimVersion, platform.OS[conf.target.hostOS].name,
  45. CPU[conf.target.hostCPU].name, CompileDate]) &
  46. Usage
  47. proc helpOnError(conf: ConfigRef) =
  48. msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
  49. msgQuit(0)
  50. type
  51. CannotMapToZ3Error = object of ValueError
  52. Z3Exception = object of ValueError
  53. VersionScope = distinct int
  54. DrnimContext = ref object
  55. z3: Z3_context
  56. graph: ModuleGraph
  57. idgen: IdGenerator
  58. facts: seq[(PNode, VersionScope)]
  59. varVersions: seq[int] # this maps variable IDs to their current version.
  60. varSyms: seq[PSym] # mirrors 'varVersions'
  61. o: Operators
  62. hasUnstructedCf: int
  63. currOptions: TOptions
  64. owner: PSym
  65. mangler: seq[PSym]
  66. opImplies: PSym
  67. DrCon = object
  68. graph: ModuleGraph
  69. idgen: IdGenerator
  70. mapping: Table[string, Z3_ast]
  71. canonParameterNames: bool
  72. assumeUniqueness: bool
  73. up: DrnimContext
  74. var
  75. assumeUniqueness: bool
  76. proc echoFacts(c: DrnimContext) =
  77. echo "FACTS:"
  78. for i in 0 ..< c.facts.len:
  79. let f = c.facts[i]
  80. echo f[0], " version ", int(f[1])
  81. proc isLoc(m: PNode; assumeUniqueness: bool): bool =
  82. # We can reason about "locations" and map them to Z3 constants.
  83. # For code that is full of "ref" (e.g. the Nim compiler itself) that
  84. # is too limiting
  85. proc isLet(n: PNode): bool =
  86. if n.kind == nkSym:
  87. if n.sym.kind in {skLet, skTemp, skForVar}:
  88. result = true
  89. elif n.sym.kind == skParam and skipTypes(n.sym.typ,
  90. abstractInst).kind != tyVar:
  91. result = true
  92. var n = m
  93. while true:
  94. case n.kind
  95. of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv, nkHiddenDeref:
  96. n = n[0]
  97. of nkDerefExpr:
  98. n = n[0]
  99. if not assumeUniqueness: return false
  100. of nkBracketExpr:
  101. if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
  102. n = n[0]
  103. else: return
  104. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  105. n = n[1]
  106. else:
  107. break
  108. if n.kind == nkSym:
  109. case n.sym.kind
  110. of skLet, skTemp, skForVar, skParam:
  111. result = true
  112. #of skParam:
  113. # result = skipTypes(n.sym.typ, abstractInst).kind != tyVar
  114. of skResult, skVar:
  115. result = {sfAddrTaken} * n.sym.flags == {}
  116. else:
  117. discard
  118. proc currentVarVersion(c: DrnimContext; s: PSym; begin: VersionScope): int =
  119. # we need to take into account both en- and disabled var bindings here,
  120. # hence the 'abs' call:
  121. result = 0
  122. for i in countdown(int(begin)-1, 0):
  123. if abs(c.varVersions[i]) == s.id: inc result
  124. proc previousVarVersion(c: DrnimContext; s: PSym; begin: VersionScope): int =
  125. # we need to ignore currently disabled var bindings here,
  126. # hence no 'abs' call here.
  127. result = -1
  128. for i in countdown(int(begin)-1, 0):
  129. if c.varVersions[i] == s.id: inc result
  130. proc disamb(c: DrnimContext; s: PSym): int =
  131. # we group by 's.name.s' to compute the stable name ID.
  132. result = 0
  133. for i in 0 ..< c.mangler.len:
  134. if s == c.mangler[i]: return result
  135. if s.name.s == c.mangler[i].name.s: inc result
  136. c.mangler.add s
  137. proc stableName(result: var string; c: DrnimContext; n: PNode; version: VersionScope;
  138. isOld: bool) =
  139. # we can map full Nim expressions like 'f(a, b, c)' to Z3 variables.
  140. # We must be careful to select a unique, stable name for these expressions
  141. # based on structural equality. 'stableName' helps us with this problem.
  142. # In the future we will also use this string for the caching mechanism.
  143. case n.kind
  144. of nkEmpty, nkNilLit, nkType: discard
  145. of nkIdent:
  146. result.add n.ident.s
  147. of nkSym:
  148. result.add n.sym.name.s
  149. if n.sym.magic == mNone:
  150. let d = disamb(c, n.sym)
  151. if d != 0:
  152. result.add "`scope="
  153. result.addInt d
  154. let v = if isOld: c.previousVarVersion(n.sym, version)
  155. else: c.currentVarVersion(n.sym, version)
  156. if v > 0:
  157. result.add '`'
  158. result.addInt v
  159. else:
  160. result.add "`magic="
  161. result.addInt ord(n.sym.magic)
  162. of nkBindStmt:
  163. # we use 'bind x 3' to use the 3rd version of variable 'x'. This
  164. # is easier than using 'old' which is position relative.
  165. assert n.len == 2
  166. assert n[0].kind == nkSym
  167. assert n[1].kind == nkIntLit
  168. let s = n[0].sym
  169. let v = int(n[1].intVal)
  170. result.add s.name.s
  171. let d = disamb(c, s)
  172. if d != 0:
  173. result.add "`scope="
  174. result.addInt d
  175. if v > 0:
  176. result.add '`'
  177. result.addInt v
  178. of nkCharLit..nkUInt64Lit:
  179. result.addInt n.intVal
  180. of nkFloatLit..nkFloat64Lit:
  181. result.addFloat n.floatVal
  182. of nkStrLit..nkTripleStrLit:
  183. result.add strutils.escape n.strVal
  184. of nkDotExpr:
  185. stableName(result, c, n[0], version, isOld)
  186. result.add '.'
  187. stableName(result, c, n[1], version, isOld)
  188. of nkBracketExpr:
  189. stableName(result, c, n[0], version, isOld)
  190. result.add '['
  191. stableName(result, c, n[1], version, isOld)
  192. result.add ']'
  193. of nkCallKinds:
  194. if n.len == 2:
  195. stableName(result, c, n[1], version, isOld)
  196. result.add '.'
  197. case getMagic(n)
  198. of mLengthArray, mLengthOpenArray, mLengthSeq, mLengthStr:
  199. result.add "len"
  200. of mHigh:
  201. result.add "high"
  202. of mLow:
  203. result.add "low"
  204. else:
  205. stableName(result, c, n[0], version, isOld)
  206. elif n.kind == nkInfix and n.len == 3:
  207. result.add '('
  208. stableName(result, c, n[1], version, isOld)
  209. result.add ' '
  210. stableName(result, c, n[0], version, isOld)
  211. result.add ' '
  212. stableName(result, c, n[2], version, isOld)
  213. result.add ')'
  214. else:
  215. stableName(result, c, n[0], version, isOld)
  216. result.add '('
  217. for i in 1..<n.len:
  218. if i > 1: result.add ", "
  219. stableName(result, c, n[i], version, isOld)
  220. result.add ')'
  221. else:
  222. result.add $n.kind
  223. result.add '('
  224. for i in 0..<n.len:
  225. if i > 0: result.add ", "
  226. stableName(result, c, n[i], version, isOld)
  227. result.add ')'
  228. proc stableName(c: DrnimContext; n: PNode; version: VersionScope;
  229. isOld = false): string =
  230. stableName(result, c, n, version, isOld)
  231. template allScopes(c): untyped = VersionScope(c.varVersions.len)
  232. template currentScope(c): untyped = VersionScope(c.varVersions.len)
  233. proc notImplemented(msg: string) {.noinline.} =
  234. when defined(debug):
  235. writeStackTrace()
  236. echo msg
  237. raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & msg)
  238. proc notImplemented(n: PNode) {.noinline.} =
  239. when defined(debug):
  240. writeStackTrace()
  241. raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & $n)
  242. proc notImplemented(t: PType) {.noinline.} =
  243. when defined(debug):
  244. writeStackTrace()
  245. raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & typeToString t)
  246. proc translateEnsures(e, x: PNode): PNode =
  247. if e.kind == nkSym and e.sym.kind == skResult:
  248. result = x
  249. else:
  250. result = shallowCopy(e)
  251. for i in 0 ..< safeLen(e):
  252. result[i] = translateEnsures(e[i], x)
  253. proc typeToZ3(c: DrCon; t: PType): Z3_sort =
  254. template ctx: untyped = c.up.z3
  255. case t.skipTypes(abstractInst+{tyVar}).kind
  256. of tyEnum, tyInt..tyInt64:
  257. result = Z3_mk_int_sort(ctx)
  258. of tyBool:
  259. result = Z3_mk_bool_sort(ctx)
  260. of tyFloat..tyFloat128:
  261. result = Z3_mk_fpa_sort_double(ctx)
  262. of tyChar, tyUInt..tyUInt64:
  263. result = Z3_mk_bv_sort(ctx, 64)
  264. #cuint(getSize(c.graph.config, t) * 8))
  265. else:
  266. notImplemented(t)
  267. template binary(op, a, b): untyped =
  268. var arr = [a, b]
  269. op(ctx, cuint(2), addr(arr[0]))
  270. proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]): Z3_ast
  271. proc nodeToDomain(c: var DrCon; n, q: PNode; opAnd: PSym): PNode =
  272. assert n.kind == nkInfix
  273. let opLe = createMagic(c.graph, c.idgen, "<=", mLeI)
  274. case $n[0]
  275. of "..":
  276. result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLe, q, n[2]))
  277. of "..<":
  278. let opLt = createMagic(c.graph, c.idgen, "<", mLtI)
  279. result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLt, q, n[2]))
  280. else:
  281. notImplemented(n)
  282. template quantorToZ3(fn) {.dirty.} =
  283. template ctx: untyped = c.up.z3
  284. var bound = newSeq[Z3_app](n.len-2)
  285. let opAnd = createMagic(c.graph, c.idgen, "and", mAnd)
  286. var known: PNode
  287. for i in 1..n.len-2:
  288. let it = n[i]
  289. doAssert it.kind == nkInfix
  290. let v = it[1].sym
  291. let name = Z3_mk_string_symbol(ctx, v.name.s)
  292. let vz3 = Z3_mk_const(ctx, name, typeToZ3(c, v.typ))
  293. c.mapping[stableName(c.up, it[1], allScopes(c.up))] = vz3
  294. bound[i-1] = Z3_to_app(ctx, vz3)
  295. let domain = nodeToDomain(c, it[2], it[1], opAnd)
  296. if known == nil:
  297. known = domain
  298. else:
  299. known = buildCall(opAnd, known, domain)
  300. var dummy: seq[PNode]
  301. assert known != nil
  302. let x = nodeToZ3(c, buildCall(createMagic(c.graph, c.idgen, "->", mImplies),
  303. known, n[^1]), scope, dummy)
  304. result = fn(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
  305. proc forallToZ3(c: var DrCon; n: PNode; scope: VersionScope): Z3_ast = quantorToZ3(Z3_mk_forall_const)
  306. proc existsToZ3(c: var DrCon; n: PNode; scope: VersionScope): Z3_ast = quantorToZ3(Z3_mk_exists_const)
  307. proc paramName(c: DrnimContext; n: PNode): string =
  308. case n.sym.kind
  309. of skParam: result = "arg" & $n.sym.position
  310. of skResult: result = "result"
  311. else: result = stableName(c, n, allScopes(c))
  312. proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]): Z3_ast =
  313. template ctx: untyped = c.up.z3
  314. template rec(n): untyped = nodeToZ3(c, n, scope, vars)
  315. case n.kind
  316. of nkSym:
  317. let key = if c.canonParameterNames: paramName(c.up, n) else: stableName(c.up, n, scope)
  318. result = c.mapping.getOrDefault(key)
  319. if pointer(result) == nil:
  320. let name = Z3_mk_string_symbol(ctx, key)
  321. result = Z3_mk_const(ctx, name, typeToZ3(c, n.sym.typ))
  322. c.mapping[key] = result
  323. vars.add n
  324. of nkCharLit..nkUInt64Lit:
  325. if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyInt..tyInt64}:
  326. # optimized for the common case
  327. result = Z3_mk_int64(ctx, clonglong(n.intval), Z3_mk_int_sort(ctx))
  328. elif n.typ != nil and n.typ.kind == tyBool:
  329. result = if n.intval != 0: Z3_mk_true(ctx) else: Z3_mk_false(ctx)
  330. elif n.typ != nil and isUnsigned(n.typ):
  331. result = Z3_mk_unsigned_int64(ctx, cast[uint64](n.intVal), typeToZ3(c, n.typ))
  332. else:
  333. let zt = if n.typ == nil: Z3_mk_int_sort(ctx) else: typeToZ3(c, n.typ)
  334. result = Z3_mk_numeral(ctx, $getOrdValue(n), zt)
  335. of nkFloatLit..nkFloat64Lit:
  336. result = Z3_mk_fpa_numeral_double(ctx, n.floatVal, Z3_mk_fpa_sort_double(ctx))
  337. of nkCallKinds:
  338. assert n.len > 0
  339. let operator = getMagic(n)
  340. case operator
  341. of mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
  342. mEqStr, mEqSet, mEqCString:
  343. result = Z3_mk_eq(ctx, rec n[1], rec n[2])
  344. of mLeI, mLeEnum, mLeCh, mLeB, mLePtr, mLeStr:
  345. result = Z3_mk_le(ctx, rec n[1], rec n[2])
  346. of mLtI, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr:
  347. result = Z3_mk_lt(ctx, rec n[1], rec n[2])
  348. of mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq:
  349. # len(x) needs the same logic as 'x' itself
  350. if isLoc(n[1], c.assumeUniqueness):
  351. let key = stableName(c.up, n, scope)
  352. result = c.mapping.getOrDefault(key)
  353. if pointer(result) == nil:
  354. let name = Z3_mk_string_symbol(ctx, key)
  355. result = Z3_mk_const(ctx, name, Z3_mk_int_sort(ctx))
  356. c.mapping[key] = result
  357. vars.add n
  358. else:
  359. notImplemented(n)
  360. of mHigh:
  361. let addOpr = createMagic(c.graph, c.idgen, "+", mAddI)
  362. let lenOpr = createMagic(c.graph, c.idgen, "len", mLengthOpenArray)
  363. let asLenExpr = addOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(-1))
  364. result = rec asLenExpr
  365. of mLow:
  366. result = rec lowBound(c.graph.config, n[1])
  367. of mAddI, mSucc:
  368. result = binary(Z3_mk_add, rec n[1], rec n[2])
  369. of mSubI, mPred:
  370. result = binary(Z3_mk_sub, rec n[1], rec n[2])
  371. of mMulI:
  372. result = binary(Z3_mk_mul, rec n[1], rec n[2])
  373. of mDivI:
  374. result = Z3_mk_div(ctx, rec n[1], rec n[2])
  375. of mModI:
  376. result = Z3_mk_mod(ctx, rec n[1], rec n[2])
  377. of mMaxI:
  378. # max(a, b) <=> ite(a < b, b, a)
  379. result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
  380. rec n[2], rec n[1])
  381. of mMinI:
  382. # min(a, b) <=> ite(a < b, a, b)
  383. result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
  384. rec n[1], rec n[2])
  385. of mLeU:
  386. result = Z3_mk_bvule(ctx, rec n[1], rec n[2])
  387. of mLtU:
  388. result = Z3_mk_bvult(ctx, rec n[1], rec n[2])
  389. of mAnd:
  390. # 'a and b' <=> ite(a, b, false)
  391. result = Z3_mk_ite(ctx, rec n[1], rec n[2], Z3_mk_false(ctx))
  392. #result = binary(Z3_mk_and, rec n[1], rec n[2])
  393. of mOr:
  394. result = Z3_mk_ite(ctx, rec n[1], Z3_mk_true(ctx), rec n[2])
  395. #result = binary(Z3_mk_or, rec n[1], rec n[2])
  396. of mXor:
  397. result = Z3_mk_xor(ctx, rec n[1], rec n[2])
  398. of mNot:
  399. result = Z3_mk_not(ctx, rec n[1])
  400. of mImplies:
  401. result = Z3_mk_implies(ctx, rec n[1], rec n[2])
  402. of mIff:
  403. result = Z3_mk_iff(ctx, rec n[1], rec n[2])
  404. of mForall:
  405. result = forallToZ3(c, n, scope)
  406. of mExists:
  407. result = existsToZ3(c, n, scope)
  408. of mLeF64:
  409. result = Z3_mk_fpa_leq(ctx, rec n[1], rec n[2])
  410. of mLtF64:
  411. result = Z3_mk_fpa_lt(ctx, rec n[1], rec n[2])
  412. of mAddF64:
  413. result = Z3_mk_fpa_add(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  414. of mSubF64:
  415. result = Z3_mk_fpa_sub(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  416. of mMulF64:
  417. result = Z3_mk_fpa_mul(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  418. of mDivF64:
  419. result = Z3_mk_fpa_div(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  420. of mShrI:
  421. # XXX handle conversions from int to uint here somehow
  422. result = Z3_mk_bvlshr(ctx, rec n[1], rec n[2])
  423. of mAshrI:
  424. result = Z3_mk_bvashr(ctx, rec n[1], rec n[2])
  425. of mShlI:
  426. result = Z3_mk_bvshl(ctx, rec n[1], rec n[2])
  427. of mBitandI:
  428. result = Z3_mk_bvand(ctx, rec n[1], rec n[2])
  429. of mBitorI:
  430. result = Z3_mk_bvor(ctx, rec n[1], rec n[2])
  431. of mBitxorI:
  432. result = Z3_mk_bvxor(ctx, rec n[1], rec n[2])
  433. of mOrd, mChr:
  434. result = rec n[1]
  435. of mOld:
  436. let key = if c.canonParameterNames: (paramName(c.up, n[1]) & ".old")
  437. else: stableName(c.up, n[1], scope, isOld = true)
  438. result = c.mapping.getOrDefault(key)
  439. if pointer(result) == nil:
  440. let name = Z3_mk_string_symbol(ctx, key)
  441. result = Z3_mk_const(ctx, name, typeToZ3(c, n[1].typ))
  442. c.mapping[key] = result
  443. # XXX change the logic in `addRangeInfo` for this
  444. #vars.add n
  445. else:
  446. # sempass2 adds some 'fact' like 'x = f(a, b)' (see addAsgnFact)
  447. # 'f(a, b)' can have an .ensures annotation and we need to make use
  448. # of this information.
  449. # we need to map 'f(a, b)' to a Z3 variable of this name
  450. let op = n[0].typ
  451. if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
  452. ensuresEffects < op.n[0].len:
  453. let ensures = op.n[0][ensuresEffects]
  454. if ensures != nil and ensures.kind != nkEmpty:
  455. let key = stableName(c.up, n, scope)
  456. result = c.mapping.getOrDefault(key)
  457. if pointer(result) == nil:
  458. let name = Z3_mk_string_symbol(ctx, key)
  459. result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
  460. c.mapping[key] = result
  461. vars.add n
  462. if pointer(result) == nil:
  463. notImplemented(n)
  464. of nkStmtListExpr, nkPar:
  465. var isTrivial = true
  466. for i in 0..n.len-2:
  467. isTrivial = isTrivial and n[i].kind in {nkEmpty, nkCommentStmt}
  468. if isTrivial:
  469. result = rec n[^1]
  470. else:
  471. notImplemented(n)
  472. of nkHiddenDeref:
  473. result = rec n[0]
  474. else:
  475. if isLoc(n, c.assumeUniqueness):
  476. let key = stableName(c.up, n, scope)
  477. result = c.mapping.getOrDefault(key)
  478. if pointer(result) == nil:
  479. let name = Z3_mk_string_symbol(ctx, key)
  480. result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
  481. c.mapping[key] = result
  482. vars.add n
  483. else:
  484. notImplemented(n)
  485. proc addRangeInfo(c: var DrCon, n: PNode; scope: VersionScope, res: var seq[Z3_ast]) =
  486. var cmpOp = mLeI
  487. if n.typ != nil:
  488. cmpOp =
  489. case n.typ.skipTypes(abstractInst).kind
  490. of tyFloat..tyFloat128: mLeF64
  491. of tyChar, tyUInt..tyUInt64: mLeU
  492. else: mLeI
  493. var lowBound, highBound: PNode
  494. if n.kind == nkSym:
  495. let v = n.sym
  496. let t = v.typ.skipTypes(abstractInst - {tyRange})
  497. case t.kind
  498. of tyRange:
  499. lowBound = t.n[0]
  500. highBound = t.n[1]
  501. of tyFloat..tyFloat128:
  502. # no range information for non-range'd floats
  503. return
  504. of tyUInt..tyUInt64, tyChar:
  505. lowBound = newIntNode(nkUInt64Lit, firstOrd(nil, v.typ))
  506. lowBound.typ = v.typ
  507. highBound = newIntNode(nkUInt64Lit, lastOrd(nil, v.typ))
  508. highBound.typ = v.typ
  509. of tyInt..tyInt64, tyEnum:
  510. lowBound = newIntNode(nkInt64Lit, firstOrd(nil, v.typ))
  511. highBound = newIntNode(nkInt64Lit, lastOrd(nil, v.typ))
  512. else:
  513. # no range information available:
  514. return
  515. elif n.kind in nkCallKinds and n.len == 2 and n[0].kind == nkSym and
  516. n[0].sym.magic in {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}:
  517. # we know it's a 'len(x)' expression and we seek to teach
  518. # Z3 that the result is >= 0 and <= high(int).
  519. doAssert n.kind in nkCallKinds
  520. doAssert n[0].kind == nkSym
  521. doAssert n.len == 2
  522. lowBound = newIntNode(nkInt64Lit, 0)
  523. if n.typ != nil:
  524. highBound = newIntNode(nkInt64Lit, lastOrd(nil, n.typ))
  525. else:
  526. highBound = newIntNode(nkInt64Lit, high(int64))
  527. else:
  528. let op = n[0].typ
  529. if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
  530. ensuresEffects < op.n[0].len:
  531. let ensures = op.n[0][ensuresEffects]
  532. if ensures != nil and ensures.kind != nkEmpty:
  533. var dummy: seq[PNode]
  534. res.add nodeToZ3(c, translateEnsures(ensures, n), scope, dummy)
  535. return
  536. let x = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), lowBound, n)
  537. let y = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), n, highBound)
  538. var dummy: seq[PNode]
  539. res.add nodeToZ3(c, x, scope, dummy)
  540. res.add nodeToZ3(c, y, scope, dummy)
  541. proc on_err(ctx: Z3_context, e: Z3_error_code) {.nimcall.} =
  542. #writeStackTrace()
  543. let msg = $Z3_get_error_msg(ctx, e)
  544. raise newException(Z3Exception, msg)
  545. proc forall(ctx: Z3_context; vars: seq[Z3_ast]; assumption, body: Z3_ast): Z3_ast =
  546. let x = Z3_mk_implies(ctx, assumption, body)
  547. if vars.len > 0:
  548. var bound: seq[Z3_app]
  549. for v in vars: bound.add Z3_to_app(ctx, v)
  550. result = Z3_mk_forall_const(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
  551. else:
  552. result = x
  553. proc conj(ctx: Z3_context; conds: seq[Z3_ast]): Z3_ast =
  554. if conds.len > 0:
  555. result = Z3_mk_and(ctx, cuint(conds.len), unsafeAddr conds[0])
  556. else:
  557. result = Z3_mk_true(ctx)
  558. proc setupZ3(): Z3_context =
  559. let cfg = Z3_mk_config()
  560. when false:
  561. Z3_set_param_value(cfg, "timeout", "1000")
  562. Z3_set_param_value(cfg, "model", "true")
  563. result = Z3_mk_context(cfg)
  564. Z3_del_config(cfg)
  565. Z3_set_error_handler(result, on_err)
  566. proc proofEngineAux(c: var DrCon; assumptions: seq[(PNode, VersionScope)];
  567. toProve: (PNode, VersionScope)): (bool, string) =
  568. c.mapping = initTable[string, Z3_ast]()
  569. try:
  570. #[
  571. For example, let's have these facts:
  572. i < 10
  573. i > 0
  574. Question:
  575. i + 3 < 13
  576. What we need to produce:
  577. forall(i, (i < 10) & (i > 0) -> (i + 3 < 13))
  578. ]#
  579. var collectedVars: seq[PNode]
  580. template ctx(): untyped = c.up.z3
  581. let solver = Z3_mk_solver(ctx)
  582. var lhs: seq[Z3_ast]
  583. for assumption in items(assumptions):
  584. try:
  585. let za = nodeToZ3(c, assumption[0], assumption[1], collectedVars)
  586. #Z3_solver_assert ctx, solver, za
  587. lhs.add za
  588. except CannotMapToZ3Error:
  589. discard "ignore a fact we cannot map to Z3"
  590. let z3toProve = nodeToZ3(c, toProve[0], toProve[1], collectedVars)
  591. for v in collectedVars:
  592. addRangeInfo(c, v, toProve[1], lhs)
  593. # to make Z3 produce nice counterexamples, we try to prove the
  594. # negation of our conjecture and see if it's Z3_L_FALSE
  595. let fa = Z3_mk_not(ctx, Z3_mk_implies(ctx, conj(ctx, lhs), z3toProve))
  596. #Z3_mk_not(ctx, forall(ctx, collectedVars, conj(ctx, lhs), z3toProve))
  597. when defined(dz3):
  598. echo "toProve: ", Z3_ast_to_string(ctx, fa), " ", c.graph.config $ toProve[0].info, " ", int(toProve[1])
  599. Z3_solver_assert ctx, solver, fa
  600. let z3res = Z3_solver_check(ctx, solver)
  601. result[0] = z3res == Z3_L_FALSE
  602. result[1] = ""
  603. if not result[0]:
  604. let counterex = strip($Z3_model_to_string(ctx, Z3_solver_get_model(ctx, solver)))
  605. if counterex.len > 0:
  606. result[1].add "; counter example: " & counterex
  607. except ValueError:
  608. result[0] = false
  609. result[1] = getCurrentExceptionMsg()
  610. proc proofEngine(ctx: DrnimContext; assumptions: seq[(PNode, VersionScope)];
  611. toProve: (PNode, VersionScope)): (bool, string) =
  612. var c: DrCon
  613. c.graph = ctx.graph
  614. c.idgen = ctx.idgen
  615. c.assumeUniqueness = assumeUniqueness
  616. c.up = ctx
  617. result = proofEngineAux(c, assumptions, toProve)
  618. proc skipAddr(n: PNode): PNode {.inline.} =
  619. (if n.kind == nkHiddenAddr: n[0] else: n)
  620. proc translateReq(r, call: PNode): PNode =
  621. if r.kind == nkSym and r.sym.kind == skParam:
  622. if r.sym.position+1 < call.len:
  623. result = call[r.sym.position+1].skipAddr
  624. else:
  625. notImplemented("no argument given for formal parameter: " & r.sym.name.s)
  626. else:
  627. result = shallowCopy(r)
  628. for i in 0 ..< safeLen(r):
  629. result[i] = translateReq(r[i], call)
  630. proc requirementsCheck(ctx: DrnimContext; assumptions: seq[(PNode, VersionScope)];
  631. call, requirement: PNode): (bool, string) =
  632. try:
  633. let r = translateReq(requirement, call)
  634. result = proofEngine(ctx, assumptions, (r, ctx.currentScope))
  635. except ValueError:
  636. result[0] = false
  637. result[1] = getCurrentExceptionMsg()
  638. proc compatibleProps(graph: ModuleGraph; formal, actual: PType): bool {.nimcall.} =
  639. #[
  640. Thoughts on subtyping rules for 'proc' types:
  641. proc a(y: int) {.requires: y > 0.} # a is 'weaker' than F
  642. # 'requires' must be weaker (or equal)
  643. # 'ensures' must be stronger (or equal)
  644. # a 'is weaker than' b iff b -> a
  645. # a 'is stronger than' b iff a -> b
  646. # --> We can use Z3 to compute whether 'var x: T = q' is valid
  647. type
  648. F = proc (y: int) {.requires: y > 5.}
  649. var
  650. x: F = a # valid?
  651. ]#
  652. proc isEmpty(n: PNode): bool {.inline.} = n == nil or n.safeLen == 0
  653. result = true
  654. if formal.n != nil and formal.n.len > 0 and formal.n[0].kind == nkEffectList and
  655. ensuresEffects < formal.n[0].len:
  656. let frequires = formal.n[0][requiresEffects]
  657. let fensures = formal.n[0][ensuresEffects]
  658. if actual.n != nil and actual.n.len > 0 and actual.n[0].kind == nkEffectList and
  659. ensuresEffects < actual.n[0].len:
  660. let arequires = actual.n[0][requiresEffects]
  661. let aensures = actual.n[0][ensuresEffects]
  662. var c: DrCon
  663. c.graph = graph
  664. c.idgen = graph.idgen
  665. c.canonParameterNames = true
  666. try:
  667. c.up = DrnimContext(z3: setupZ3(), o: initOperators(graph), graph: graph, owner: nil,
  668. opImplies: createMagic(graph, c.idgen, "->", mImplies))
  669. template zero: untyped = VersionScope(0)
  670. if not frequires.isEmpty:
  671. result = not arequires.isEmpty and proofEngineAux(c, @[(frequires, zero)], (arequires, zero))[0]
  672. if result:
  673. if not fensures.isEmpty:
  674. result = not aensures.isEmpty and proofEngineAux(c, @[(aensures, zero)], (fensures, zero))[0]
  675. finally:
  676. Z3_del_context(c.up.z3)
  677. else:
  678. # formal has requirements but 'actual' has none, so make it
  679. # incompatible. XXX What if the requirement only mentions that
  680. # we already know from the type system?
  681. result = frequires.isEmpty and fensures.isEmpty
  682. template config(c: typed): untyped = c.graph.config
  683. proc addFact(c: DrnimContext; n: PNode) =
  684. let v = c.currentScope
  685. if n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic in {mOr, mAnd}:
  686. c.addFact(n[1])
  687. c.facts.add((n, v))
  688. proc neg(c: DrnimContext; n: PNode): PNode =
  689. result = newNodeI(nkCall, n.info, 2)
  690. result[0] = newSymNode(c.o.opNot)
  691. result[1] = n
  692. proc addFactNeg(c: DrnimContext; n: PNode) =
  693. addFact(c, neg(c, n))
  694. proc combineFacts(c: DrnimContext; a, b: PNode): PNode =
  695. if a == nil:
  696. result = b
  697. else:
  698. result = buildCall(c.o.opAnd, a, b)
  699. proc prove(c: DrnimContext; prop: PNode): bool =
  700. let (success, m) = proofEngine(c, c.facts, (prop, c.currentScope))
  701. if not success:
  702. message(c.config, prop.info, warnStaticIndexCheck, "cannot prove: " & $prop & m)
  703. result = success
  704. proc traversePragmaStmt(c: DrnimContext, n: PNode) =
  705. for it in n:
  706. if it.kind == nkExprColonExpr:
  707. let pragma = whichPragma(it)
  708. if pragma == wAssume:
  709. addFact(c, it[1])
  710. elif pragma == wInvariant or pragma == wAssert:
  711. if prove(c, it[1]):
  712. addFact(c, it[1])
  713. else:
  714. echoFacts(c)
  715. proc requiresCheck(c: DrnimContext, call: PNode; op: PType) =
  716. assert op.n[0].kind == nkEffectList
  717. if requiresEffects < op.n[0].len:
  718. let requires = op.n[0][requiresEffects]
  719. if requires != nil and requires.kind != nkEmpty:
  720. # we need to map the call arguments to the formal parameters used inside
  721. # 'requires':
  722. let (success, m) = requirementsCheck(c, c.facts, call, requires)
  723. if not success:
  724. message(c.config, call.info, warnStaticIndexCheck, "cannot prove: " & $requires & m)
  725. proc freshVersion(c: DrnimContext; arg: PNode) =
  726. let v = getRoot(arg)
  727. if v != nil:
  728. c.varVersions.add v.id
  729. c.varSyms.add v
  730. proc translateEnsuresFromCall(c: DrnimContext, e, call: PNode): PNode =
  731. if e.kind in nkCallKinds and e[0].kind == nkSym and e[0].sym.magic == mOld:
  732. assert e[1].kind == nkSym and e[1].sym.kind == skParam
  733. let param = e[1].sym
  734. let arg = call[param.position+1].skipAddr
  735. result = buildCall(e[0].sym, arg)
  736. elif e.kind == nkSym and e.sym.kind == skParam:
  737. let param = e.sym
  738. let arg = call[param.position+1].skipAddr
  739. result = arg
  740. else:
  741. result = shallowCopy(e)
  742. for i in 0 ..< safeLen(e): result[i] = translateEnsuresFromCall(c, e[i], call)
  743. proc collectEnsuredFacts(c: DrnimContext, call: PNode; op: PType) =
  744. assert op.n[0].kind == nkEffectList
  745. for i in 1 ..< min(call.len, op.len):
  746. if op[i].kind == tyVar:
  747. freshVersion(c, call[i].skipAddr)
  748. if ensuresEffects < op.n[0].len:
  749. let ensures = op.n[0][ensuresEffects]
  750. if ensures != nil and ensures.kind != nkEmpty:
  751. addFact(c, translateEnsuresFromCall(c, ensures, call))
  752. proc checkLe(c: DrnimContext, a, b: PNode) =
  753. var cmpOp = mLeI
  754. if a.typ != nil:
  755. case a.typ.skipTypes(abstractInst).kind
  756. of tyFloat..tyFloat128: cmpOp = mLeF64
  757. of tyChar, tyUInt..tyUInt64: cmpOp = mLeU
  758. else: discard
  759. let cmp = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), a, b)
  760. cmp.info = a.info
  761. discard prove(c, cmp)
  762. proc checkBounds(c: DrnimContext; arr, idx: PNode) =
  763. checkLe(c, lowBound(c.config, arr), idx)
  764. checkLe(c, idx, highBound(c.config, arr, c.o))
  765. proc checkRange(c: DrnimContext; value: PNode; typ: PType) =
  766. let t = typ.skipTypes(abstractInst - {tyRange})
  767. if t.kind == tyRange:
  768. let lowBound = copyTree(t.n[0])
  769. lowBound.info = value.info
  770. let highBound = copyTree(t.n[1])
  771. highBound.info = value.info
  772. checkLe(c, lowBound, value)
  773. checkLe(c, value, highBound)
  774. proc addAsgnFact*(c: DrnimContext, key, value: PNode) =
  775. var fact = newNodeI(nkCall, key.info, 3)
  776. fact[0] = newSymNode(c.o.opEq)
  777. fact[1] = key
  778. fact[2] = value
  779. c.facts.add((fact, c.currentScope))
  780. proc traverse(c: DrnimContext; n: PNode)
  781. proc traverseTryStmt(c: DrnimContext; n: PNode) =
  782. traverse(c, n[0])
  783. let oldFacts = c.facts.len
  784. for i in 1 ..< n.len:
  785. traverse(c, n[i].lastSon)
  786. setLen(c.facts, oldFacts)
  787. proc traverseCase(c: DrnimContext; n: PNode) =
  788. traverse(c, n[0])
  789. let oldFacts = c.facts.len
  790. for i in 1 ..< n.len:
  791. traverse(c, n[i].lastSon)
  792. # XXX make this as smart as 'if elif'
  793. setLen(c.facts, oldFacts)
  794. proc disableVarVersions(c: DrnimContext; until: int) =
  795. for i in until..<c.varVersions.len:
  796. c.varVersions[i] = - abs(c.varVersions[i])
  797. proc varOfVersion(c: DrnimContext; x: PSym; scope: int): PNode =
  798. let version = currentVarVersion(c, x, VersionScope(scope))
  799. result = newTree(nkBindStmt, newSymNode(x), newIntNode(nkIntLit, version))
  800. proc traverseIf(c: DrnimContext; n: PNode) =
  801. #[ Consider this example::
  802. var x = y # x'0
  803. if a:
  804. inc x # x'1 == x'0 + 1
  805. elif b:
  806. inc x, 2 # x'2 == x'0 + 2
  807. afterwards we know this is fact::
  808. x'3 = Phi(x'0, x'1, x'2)
  809. So a Phi node from SSA representation is an 'or' formula like::
  810. x'3 == x'1 or x'3 == x'2 or x'3 == x'0
  811. However, this loses some information. The formula that doesn't
  812. lose information is::
  813. (a -> (x'3 == x'1)) and
  814. ((not a and b) -> (x'3 == x'2)) and
  815. ((not a and not b) -> (x'3 == x'0))
  816. (Where ``->`` is the logical implication.)
  817. In addition to the Phi information we also know the 'facts'
  818. computed by the branches, for example::
  819. if a:
  820. factA
  821. elif b:
  822. factB
  823. else:
  824. factC
  825. (a -> factA) and
  826. ((not a and b) -> factB) and
  827. ((not a and not b) -> factC)
  828. We can combine these two aspects by producing the following facts
  829. after each branch::
  830. var x = y # x'0
  831. if a:
  832. inc x # x'1 == x'0 + 1
  833. # also: x'1 == x'final
  834. elif b:
  835. inc x, 2 # x'2 == x'0 + 2
  836. # also: x'2 == x'final
  837. else:
  838. # also: x'0 == x'final
  839. ]#
  840. let oldFacts = c.facts.len
  841. let oldVars = c.varVersions.len
  842. var newFacts: seq[PNode]
  843. var branches = newSeq[(PNode, int)](n.len) # (cond, newVars) pairs
  844. template condVersion(): untyped = VersionScope(oldVars)
  845. for i in 0..<n.len:
  846. let branch = n[i]
  847. setLen(c.facts, oldFacts)
  848. var cond = PNode(nil)
  849. for j in 0..i-1:
  850. addFactNeg(c, n[j][0])
  851. cond = combineFacts(c, cond, neg(c, n[j][0]))
  852. if branch.len > 1:
  853. addFact(c, branch[0])
  854. cond = combineFacts(c, cond, branch[0])
  855. for i in 0..<branch.len:
  856. traverse(c, branch[i])
  857. assert cond != nil
  858. branches[i] = (cond, c.varVersions.len)
  859. var newInfo = PNode(nil)
  860. for f in oldFacts..<c.facts.len:
  861. newInfo = combineFacts(c, newInfo, c.facts[f][0])
  862. if newInfo != nil:
  863. newFacts.add buildCall(c.opImplies, cond, newInfo)
  864. disableVarVersions(c, oldVars)
  865. setLen(c.facts, oldFacts)
  866. for f in newFacts: c.facts.add((f, condVersion))
  867. # build the 'Phi' information:
  868. let varsWithoutFinals = c.varVersions.len
  869. var mutatedVars = initIntSet()
  870. for i in oldVars ..< varsWithoutFinals:
  871. let vv = c.varVersions[i]
  872. if not mutatedVars.containsOrIncl(vv):
  873. c.varVersions.add vv
  874. c.varSyms.add c.varSyms[i]
  875. var prevIdx = oldVars
  876. for i in 0 ..< branches.len:
  877. for v in prevIdx .. branches[i][1] - 1:
  878. c.facts.add((buildCall(c.opImplies, branches[i][0],
  879. buildCall(c.o.opEq, varOfVersion(c, c.varSyms[v], branches[i][1]), newSymNode(c.varSyms[v]))),
  880. condVersion))
  881. prevIdx = branches[i][1]
  882. proc traverseBlock(c: DrnimContext; n: PNode) =
  883. traverse(c, n)
  884. proc addFactLe(c: DrnimContext; a, b: PNode) =
  885. c.addFact c.o.opLe.buildCall(a, b)
  886. proc addFactLt(c: DrnimContext; a, b: PNode) =
  887. c.addFact c.o.opLt.buildCall(a, b)
  888. proc ensuresCheck(c: DrnimContext; owner: PSym) =
  889. if owner.typ != nil and owner.typ.kind == tyProc and owner.typ.n != nil:
  890. let n = owner.typ.n
  891. if n.len > 0 and n[0].kind == nkEffectList and ensuresEffects < n[0].len:
  892. let ensures = n[0][ensuresEffects]
  893. if ensures != nil and ensures.kind != nkEmpty:
  894. discard prove(c, ensures)
  895. proc traverseAsgn(c: DrnimContext; n: PNode) =
  896. traverse(c, n[0])
  897. traverse(c, n[1])
  898. proc replaceByOldParams(fact, le: PNode): PNode =
  899. if guards.sameTree(fact, le):
  900. result = newNodeIT(nkCall, fact.info, fact.typ)
  901. result.add newSymNode createMagic(c.graph, c.idgen, "old", mOld)
  902. result.add fact
  903. else:
  904. result = shallowCopy(fact)
  905. for i in 0 ..< safeLen(fact):
  906. result[i] = replaceByOldParams(fact[i], le)
  907. freshVersion(c, n[0])
  908. addAsgnFact(c, n[0], replaceByOldParams(n[1], n[0]))
  909. when defined(debug):
  910. echoFacts(c)
  911. proc traverse(c: DrnimContext; n: PNode) =
  912. case n.kind
  913. of nkEmpty..nkNilLit:
  914. discard "nothing to do"
  915. of nkRaiseStmt, nkBreakStmt, nkContinueStmt:
  916. inc c.hasUnstructedCf
  917. for i in 0..<n.safeLen:
  918. traverse(c, n[i])
  919. of nkReturnStmt:
  920. for i in 0 ..< n.safeLen:
  921. traverse(c, n[i])
  922. ensuresCheck(c, c.owner)
  923. of nkCallKinds:
  924. # p's effects are ours too:
  925. var a = n[0]
  926. let op = a.typ
  927. if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
  928. requiresCheck(c, n, op)
  929. collectEnsuredFacts(c, n, op)
  930. if a.kind == nkSym:
  931. case a.sym.magic
  932. of mNew, mNewFinalize, mNewSeq:
  933. # may not look like an assignment, but it is:
  934. let arg = n[1]
  935. freshVersion(c, arg)
  936. traverse(c, arg)
  937. let x = newNodeIT(nkObjConstr, arg.info, arg.typ)
  938. x.add arg
  939. addAsgnFact(c, arg, x)
  940. of mArrGet, mArrPut:
  941. #if optStaticBoundsCheck in c.currOptions: checkBounds(c, n[1], n[2])
  942. discard
  943. else:
  944. discard
  945. for i in 0..<n.safeLen:
  946. traverse(c, n[i])
  947. of nkDotExpr:
  948. #guardDotAccess(c, n)
  949. for i in 0..<n.len: traverse(c, n[i])
  950. of nkCheckedFieldExpr:
  951. traverse(c, n[0])
  952. #checkFieldAccess(c.facts, n, c.config)
  953. of nkTryStmt: traverseTryStmt(c, n)
  954. of nkPragma: traversePragmaStmt(c, n)
  955. of nkAsgn, nkFastAsgn: traverseAsgn(c, n)
  956. of nkVarSection, nkLetSection:
  957. for child in n:
  958. let last = lastSon(child)
  959. if last.kind != nkEmpty: traverse(c, last)
  960. if child.kind == nkIdentDefs and last.kind != nkEmpty:
  961. for i in 0..<child.len-2:
  962. addAsgnFact(c, child[i], last)
  963. elif child.kind == nkVarTuple and last.kind != nkEmpty:
  964. for i in 0..<child.len-1:
  965. if child[i].kind == nkEmpty or
  966. child[i].kind == nkSym and child[i].sym.name.s == "_":
  967. discard "anon variable"
  968. elif last.kind in {nkPar, nkTupleConstr}:
  969. addAsgnFact(c, child[i], last[i])
  970. of nkConstSection:
  971. for child in n:
  972. let last = lastSon(child)
  973. traverse(c, last)
  974. of nkCaseStmt: traverseCase(c, n)
  975. of nkWhen, nkIfStmt, nkIfExpr: traverseIf(c, n)
  976. of nkBlockStmt, nkBlockExpr: traverseBlock(c, n[1])
  977. of nkWhileStmt:
  978. # 'while true' loop?
  979. if isTrue(n[0]):
  980. traverseBlock(c, n[1])
  981. else:
  982. let oldFacts = c.facts.len
  983. addFact(c, n[0])
  984. traverse(c, n[0])
  985. traverse(c, n[1])
  986. setLen(c.facts, oldFacts)
  987. of nkForStmt, nkParForStmt:
  988. # we are very conservative here and assume the loop is never executed:
  989. let oldFacts = c.facts.len
  990. let iterCall = n[n.len-2]
  991. if optStaticBoundsCheck in c.currOptions and iterCall.kind in nkCallKinds:
  992. let op = iterCall[0]
  993. if op.kind == nkSym and fromSystem(op.sym):
  994. let iterVar = n[0]
  995. case op.sym.name.s
  996. of "..", "countup", "countdown":
  997. let lower = iterCall[1]
  998. let upper = iterCall[2]
  999. # for i in 0..n means 0 <= i and i <= n. Countdown is
  1000. # the same since only the iteration direction changes.
  1001. addFactLe(c, lower, iterVar)
  1002. addFactLe(c, iterVar, upper)
  1003. of "..<":
  1004. let lower = iterCall[1]
  1005. let upper = iterCall[2]
  1006. addFactLe(c, lower, iterVar)
  1007. addFactLt(c, iterVar, upper)
  1008. else: discard
  1009. for i in 0..<n.len-2:
  1010. let it = n[i]
  1011. traverse(c, it)
  1012. let loopBody = n[^1]
  1013. traverse(c, iterCall)
  1014. traverse(c, loopBody)
  1015. setLen(c.facts, oldFacts)
  1016. of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
  1017. nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
  1018. discard
  1019. of nkCast:
  1020. if n.len == 2:
  1021. traverse(c, n[1])
  1022. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  1023. if n.len == 2:
  1024. traverse(c, n[1])
  1025. if optStaticBoundsCheck in c.currOptions:
  1026. checkRange(c, n[1], n.typ)
  1027. of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
  1028. if n.len == 1:
  1029. traverse(c, n[0])
  1030. if optStaticBoundsCheck in c.currOptions:
  1031. checkRange(c, n[0], n.typ)
  1032. of nkBracketExpr:
  1033. if optStaticBoundsCheck in c.currOptions and n.len == 2:
  1034. if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
  1035. checkBounds(c, n[0], n[1])
  1036. for i in 0 ..< n.len: traverse(c, n[i])
  1037. else:
  1038. for i in 0 ..< n.len: traverse(c, n[i])
  1039. proc strongSemCheck(graph: ModuleGraph; owner: PSym; n: PNode) =
  1040. var c = DrnimContext()
  1041. c.currOptions = graph.config.options + owner.options
  1042. if optStaticBoundsCheck in c.currOptions:
  1043. c.z3 = setupZ3()
  1044. c.o = initOperators(graph)
  1045. c.graph = graph
  1046. c.idgen = graph.idgen
  1047. c.owner = owner
  1048. c.opImplies = createMagic(c.graph, c.idgen, "->", mImplies)
  1049. try:
  1050. traverse(c, n)
  1051. ensuresCheck(c, owner)
  1052. finally:
  1053. Z3_del_context(c.z3)
  1054. proc mainCommand(graph: ModuleGraph) =
  1055. let conf = graph.config
  1056. conf.lastCmdTime = epochTime()
  1057. graph.strongSemCheck = strongSemCheck
  1058. graph.compatibleProps = compatibleProps
  1059. graph.config.setErrorMaxHighMaybe
  1060. defineSymbol(graph.config.symbols, "nimcheck")
  1061. defineSymbol(graph.config.symbols, "nimDrNim")
  1062. registerPass graph, verbosePass
  1063. registerPass graph, semPass
  1064. compileProject(graph)
  1065. if conf.errorCounter == 0:
  1066. genSuccessX(graph.config)
  1067. proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
  1068. var p = parseopt.initOptParser(cmd)
  1069. var argsCount = 1
  1070. config.commandLine.setLen 0
  1071. config.setCmd cmdCheck
  1072. while true:
  1073. parseopt.next(p)
  1074. case p.kind
  1075. of cmdEnd: break
  1076. of cmdLongOption, cmdShortOption:
  1077. config.commandLine.add " "
  1078. config.commandLine.addCmdPrefix p.kind
  1079. config.commandLine.add p.key.quoteShell # quoteShell to be future proof
  1080. if p.val.len > 0:
  1081. config.commandLine.add ':'
  1082. config.commandLine.add p.val.quoteShell
  1083. if p.key == " ":
  1084. p.key = "-"
  1085. if processArgument(pass, p, argsCount, config): break
  1086. else:
  1087. case p.key.normalize
  1088. of "assumeunique":
  1089. assumeUniqueness = true
  1090. else:
  1091. processSwitch(pass, p, config)
  1092. of cmdArgument:
  1093. config.commandLine.add " "
  1094. config.commandLine.add p.key.quoteShell
  1095. if processArgument(pass, p, argsCount, config): break
  1096. if pass == passCmd2:
  1097. if {optRun, optWasNimscript} * config.globalOptions == {} and
  1098. config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript}:
  1099. rawMessage(config, errGenerated, errArgsNeedRunOption)
  1100. proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
  1101. incl conf.options, optStaticBoundsCheck
  1102. let self = NimProg(
  1103. supportsStdinFile: true,
  1104. processCmdLine: processCmdLine
  1105. )
  1106. self.initDefinesProg(conf, "drnim")
  1107. if paramCount() == 0:
  1108. helpOnError(conf)
  1109. return
  1110. self.processCmdLineAndProjectPath(conf)
  1111. var graph = newModuleGraph(cache, conf)
  1112. if not self.loadConfigsAndProcessCmdLine(cache, conf, graph): return
  1113. mainCommand(graph)
  1114. if conf.hasHint(hintGCStats): echo(GC_getStatistics())
  1115. when compileOption("gc", "refc"):
  1116. # the new correct mark&sweep collector is too slow :-/
  1117. GC_disableMarkAndSweep()
  1118. when not defined(selftest):
  1119. let conf = newConfigRef()
  1120. handleCmdLine(newIdentCache(), conf)
  1121. when declared(GC_setMaxPause):
  1122. echo GC_getStatistics()
  1123. msgQuit(int8(conf.errorCounter > 0))