drnim.nim 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  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. let mem =
  1067. when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
  1068. else: formatSize(getTotalMem()) & " totmem"
  1069. let loc = $conf.linesCompiled
  1070. let build = if isDefined(conf, "danger"): "Dangerous Release"
  1071. elif isDefined(conf, "release"): "Release"
  1072. else: "Debug"
  1073. let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
  1074. let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName
  1075. rawMessage(conf, hintSuccessX, [
  1076. "loc", loc,
  1077. "sec", sec,
  1078. "mem", mem,
  1079. "build", build,
  1080. "project", project,
  1081. "output", ""
  1082. ])
  1083. proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
  1084. var p = parseopt.initOptParser(cmd)
  1085. var argsCount = 1
  1086. config.commandLine.setLen 0
  1087. config.setCmd cmdCheck
  1088. while true:
  1089. parseopt.next(p)
  1090. case p.kind
  1091. of cmdEnd: break
  1092. of cmdLongOption, cmdShortOption:
  1093. config.commandLine.add " "
  1094. config.commandLine.addCmdPrefix p.kind
  1095. config.commandLine.add p.key.quoteShell # quoteShell to be future proof
  1096. if p.val.len > 0:
  1097. config.commandLine.add ':'
  1098. config.commandLine.add p.val.quoteShell
  1099. if p.key == " ":
  1100. p.key = "-"
  1101. if processArgument(pass, p, argsCount, config): break
  1102. else:
  1103. case p.key.normalize
  1104. of "assumeunique":
  1105. assumeUniqueness = true
  1106. else:
  1107. processSwitch(pass, p, config)
  1108. of cmdArgument:
  1109. config.commandLine.add " "
  1110. config.commandLine.add p.key.quoteShell
  1111. if processArgument(pass, p, argsCount, config): break
  1112. if pass == passCmd2:
  1113. if {optRun, optWasNimscript} * config.globalOptions == {} and
  1114. config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript}:
  1115. rawMessage(config, errGenerated, errArgsNeedRunOption)
  1116. proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
  1117. incl conf.options, optStaticBoundsCheck
  1118. let self = NimProg(
  1119. supportsStdinFile: true,
  1120. processCmdLine: processCmdLine
  1121. )
  1122. self.initDefinesProg(conf, "drnim")
  1123. if paramCount() == 0:
  1124. helpOnError(conf)
  1125. return
  1126. self.processCmdLineAndProjectPath(conf)
  1127. var graph = newModuleGraph(cache, conf)
  1128. if not self.loadConfigsAndProcessCmdLine(cache, conf, graph): return
  1129. mainCommand(graph)
  1130. if conf.hasHint(hintGCStats): echo(GC_getStatistics())
  1131. when compileOption("gc", "refc"):
  1132. # the new correct mark&sweep collector is too slow :-/
  1133. GC_disableMarkAndSweep()
  1134. when not defined(selftest):
  1135. let conf = newConfigRef()
  1136. handleCmdLine(newIdentCache(), conf)
  1137. when declared(GC_setMaxPause):
  1138. echo GC_getStatistics()
  1139. msgQuit(int8(conf.errorCounter > 0))