drnim.nim 41 KB

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