drnim.nim 37 KB

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