drnim.nim 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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. - Most important bug:
  11. while i < x.len and use(s[i]): inc i # is safe
  12. - We need to map arrays to Z3 and test for something like 'forall(i, (i in 3..4) -> (a[i] > 3))'
  13. - forall/exists need syntactic sugar as the manual
  14. - We need teach DrNim what 'inc', 'dec' and 'swap' mean, for example
  15. 'x in n..m; inc x' implies 'x in n+1..m+1'
  16. - We need an ``old`` annotation:
  17. proc f(x: var int; y: var int) {.ensures: x == old(x)+1 and y == old(y)+1 .} =
  18. inc x
  19. inc y
  20. var x = 3
  21. var y: range[N..M]
  22. f(x, y)
  23. {.assume: y in N+1 .. M+1.}
  24. # --> y in N+1..M+1
  25. proc myinc(x: var int) {.ensures: x-1 == old(x).} =
  26. inc x
  27. facts(x) # x < 3
  28. myinc x
  29. facts(x+1)
  30. We handle state transitions in this way:
  31. for every f in facts:
  32. replace 'x' by 'old(x)'
  33. facts.add ensuresClause
  34. # then we know: old(x) < 3; x-1 == old(x)
  35. # we can conclude: x-1 < 3 but leave this task to Z3
  36. ]#
  37. import std / [
  38. parseopt, strutils, os, tables, times
  39. ]
  40. import ".." / compiler / [
  41. ast, types, renderer,
  42. commands, options, msgs,
  43. platform,
  44. idents, lineinfos, cmdlinehelper, modulegraphs, condsyms,
  45. pathutils, passes, passaux, sem, modules
  46. ]
  47. import z3 / z3_api
  48. when not defined(windows):
  49. # on UNIX we use static linking because UNIX's lib*.so system is broken
  50. # beyond repair and the neckbeards don't understand software development.
  51. {.passL: "dist/z3/build/libz3.a".}
  52. const
  53. HelpMessage = "DrNim Version $1 [$2: $3]\n" &
  54. "Compiled at $4\n" &
  55. "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
  56. const
  57. Usage = """
  58. drnim [options] [projectfile]
  59. Options: Same options that the Nim compiler supports.
  60. """
  61. proc getCommandLineDesc(conf: ConfigRef): string =
  62. result = (HelpMessage % [system.NimVersion, platform.OS[conf.target.hostOS].name,
  63. CPU[conf.target.hostCPU].name, CompileDate]) &
  64. Usage
  65. proc helpOnError(conf: ConfigRef) =
  66. msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
  67. msgQuit(0)
  68. type
  69. CannotMapToZ3Error = object of ValueError
  70. Z3Exception = object of ValueError
  71. DrCon = object
  72. z3: Z3_context
  73. graph: ModuleGraph
  74. mapping: Table[string, Z3_ast]
  75. canonParameterNames: bool
  76. proc stableName(result: var string; n: PNode) =
  77. # we can map full Nim expressions like 'f(a, b, c)' to Z3 variables.
  78. # We must be carefult to select a unique, stable name for these expressions
  79. # based on structural equality. 'stableName' helps us with this problem.
  80. case n.kind
  81. of nkEmpty, nkNilLit, nkType: discard
  82. of nkIdent:
  83. result.add n.ident.s
  84. of nkSym:
  85. result.add n.sym.name.s
  86. result.add '_'
  87. result.addInt n.sym.id
  88. of nkCharLit..nkUInt64Lit:
  89. result.addInt n.intVal
  90. of nkFloatLit..nkFloat64Lit:
  91. result.addFloat n.floatVal
  92. of nkStrLit..nkTripleStrLit:
  93. result.add strutils.escape n.strVal
  94. else:
  95. result.add $n.kind
  96. result.add '('
  97. for i in 0..<n.len:
  98. if i > 0: result.add ", "
  99. stableName(result, n[i])
  100. result.add ')'
  101. proc stableName(n: PNode): string = stableName(result, n)
  102. proc notImplemented(msg: string) {.noinline.} =
  103. raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & msg)
  104. proc translateEnsures(e, x: PNode): PNode =
  105. if e.kind == nkSym and e.sym.kind == skResult:
  106. result = x
  107. else:
  108. result = shallowCopy(e)
  109. for i in 0 ..< safeLen(e):
  110. result[i] = translateEnsures(e[i], x)
  111. proc typeToZ3(c: DrCon; t: PType): Z3_sort =
  112. template ctx: untyped = c.z3
  113. case t.skipTypes(abstractInst+{tyVar}).kind
  114. of tyEnum, tyInt..tyInt64:
  115. result = Z3_mk_int_sort(ctx)
  116. of tyBool:
  117. result = Z3_mk_bool_sort(ctx)
  118. of tyFloat..tyFloat128:
  119. result = Z3_mk_fpa_sort_double(ctx)
  120. of tyChar, tyUInt..tyUInt64:
  121. result = Z3_mk_bv_sort(ctx, 64)
  122. #cuint(getSize(c.graph.config, t) * 8))
  123. else:
  124. notImplemented(typeToString(t))
  125. template binary(op, a, b): untyped =
  126. var arr = [a, b]
  127. op(ctx, cuint(2), addr(arr[0]))
  128. proc nodeToZ3(c: var DrCon; n: PNode; vars: var seq[PNode]): Z3_ast
  129. template quantorToZ3(fn) {.dirty.} =
  130. template ctx: untyped = c.z3
  131. var bound = newSeq[Z3_app](n.len-1)
  132. for i in 0..n.len-2:
  133. doAssert n[i].kind == nkSym
  134. let v = n[i].sym
  135. let name = Z3_mk_string_symbol(ctx, v.name.s)
  136. let vz3 = Z3_mk_const(ctx, name, typeToZ3(c, v.typ))
  137. c.mapping[stableName(n[i])] = vz3
  138. bound[i] = Z3_to_app(ctx, vz3)
  139. var dummy: seq[PNode]
  140. let x = nodeToZ3(c, n[^1], dummy)
  141. result = fn(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
  142. proc forallToZ3(c: var DrCon; n: PNode): Z3_ast = quantorToZ3(Z3_mk_forall_const)
  143. proc existsToZ3(c: var DrCon; n: PNode): Z3_ast = quantorToZ3(Z3_mk_exists_const)
  144. proc paramName(n: PNode): string =
  145. case n.sym.kind
  146. of skParam: result = "arg" & $n.sym.position
  147. of skResult: result = "result"
  148. else: result = stableName(n)
  149. proc nodeToZ3(c: var DrCon; n: PNode; vars: var seq[PNode]): Z3_ast =
  150. template ctx: untyped = c.z3
  151. template rec(n): untyped = nodeToZ3(c, n, vars)
  152. case n.kind
  153. of nkSym:
  154. let key = if c.canonParameterNames: paramName(n) else: stableName(n)
  155. result = c.mapping.getOrDefault(key)
  156. if pointer(result) == nil:
  157. let name = Z3_mk_string_symbol(ctx, n.sym.name.s)
  158. result = Z3_mk_const(ctx, name, typeToZ3(c, n.sym.typ))
  159. c.mapping[key] = result
  160. vars.add n
  161. of nkCharLit..nkUInt64Lit:
  162. if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyInt..tyInt64}:
  163. # optimized for the common case
  164. result = Z3_mk_int64(ctx, clonglong(n.intval), Z3_mk_int_sort(ctx))
  165. elif n.typ != nil and n.typ.kind == tyBool:
  166. result = if n.intval != 0: Z3_mk_true(ctx) else: Z3_mk_false(ctx)
  167. elif n.typ != nil and isUnsigned(n.typ):
  168. result = Z3_mk_unsigned_int64(ctx, cast[uint64](n.intVal), typeToZ3(c, n.typ))
  169. else:
  170. let zt = if n.typ == nil: Z3_mk_int_sort(ctx) else: typeToZ3(c, n.typ)
  171. result = Z3_mk_numeral(ctx, $getOrdValue(n), zt)
  172. of nkFloatLit..nkFloat64Lit:
  173. result = Z3_mk_fpa_numeral_double(ctx, n.floatVal, Z3_mk_fpa_sort_double(ctx))
  174. of nkCallKinds:
  175. assert n.len > 0
  176. assert n[0].kind == nkSym
  177. let operator = n[0].sym.magic
  178. case operator
  179. of mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
  180. mEqStr, mEqSet, mEqCString:
  181. result = Z3_mk_eq(ctx, rec n[1], rec n[2])
  182. of mLeI, mLeEnum, mLeCh, mLeB, mLePtr, mLeStr:
  183. result = Z3_mk_le(ctx, rec n[1], rec n[2])
  184. of mLtI, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr:
  185. result = Z3_mk_lt(ctx, rec n[1], rec n[2])
  186. of mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq:
  187. # len(x) needs the same logic as 'x' itself
  188. if n[1].kind == nkSym:
  189. let key = stableName(n)
  190. let sym = n[1].sym
  191. result = c.mapping.getOrDefault(key)
  192. if pointer(result) == nil:
  193. let name = Z3_mk_string_symbol(ctx, sym.name.s & ".len")
  194. result = Z3_mk_const(ctx, name, Z3_mk_int_sort(ctx))
  195. c.mapping[key] = result
  196. vars.add n
  197. else:
  198. notImplemented(renderTree(n))
  199. of mAddI, mSucc:
  200. result = binary(Z3_mk_add, rec n[1], rec n[2])
  201. of mSubI, mPred:
  202. result = binary(Z3_mk_sub, rec n[1], rec n[2])
  203. of mMulI:
  204. result = binary(Z3_mk_mul, rec n[1], rec n[2])
  205. of mDivI:
  206. result = Z3_mk_div(ctx, rec n[1], rec n[2])
  207. of mModI:
  208. result = Z3_mk_mod(ctx, rec n[1], rec n[2])
  209. of mMaxI:
  210. # max(a, b) <=> ite(a < b, b, a)
  211. result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
  212. rec n[2], rec n[1])
  213. of mMinI:
  214. # min(a, b) <=> ite(a < b, a, b)
  215. result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
  216. rec n[1], rec n[2])
  217. of mLeU:
  218. result = Z3_mk_bvule(ctx, rec n[1], rec n[2])
  219. of mLtU:
  220. result = Z3_mk_bvult(ctx, rec n[1], rec n[2])
  221. of mAnd:
  222. result = binary(Z3_mk_and, rec n[1], rec n[2])
  223. of mOr:
  224. result = binary(Z3_mk_or, rec n[1], rec n[2])
  225. of mXor:
  226. result = Z3_mk_xor(ctx, rec n[1], rec n[2])
  227. of mNot:
  228. result = Z3_mk_not(ctx, rec n[1])
  229. of mImplies:
  230. result = Z3_mk_implies(ctx, rec n[1], rec n[2])
  231. of mIff:
  232. result = Z3_mk_iff(ctx, rec n[1], rec n[2])
  233. of mForall:
  234. result = forallToZ3(c, n)
  235. of mExists:
  236. result = existsToZ3(c, n)
  237. of mLeF64:
  238. result = Z3_mk_fpa_leq(ctx, rec n[1], rec n[2])
  239. of mLtF64:
  240. result = Z3_mk_fpa_lt(ctx, rec n[1], rec n[2])
  241. of mAddF64:
  242. result = Z3_mk_fpa_add(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  243. of mSubF64:
  244. result = Z3_mk_fpa_sub(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  245. of mMulF64:
  246. result = Z3_mk_fpa_mul(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  247. of mDivF64:
  248. result = Z3_mk_fpa_div(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
  249. of mShrI:
  250. # XXX handle conversions from int to uint here somehow
  251. result = Z3_mk_bvlshr(ctx, rec n[1], rec n[2])
  252. of mAshrI:
  253. result = Z3_mk_bvashr(ctx, rec n[1], rec n[2])
  254. of mShlI:
  255. result = Z3_mk_bvshl(ctx, rec n[1], rec n[2])
  256. of mBitandI:
  257. result = Z3_mk_bvand(ctx, rec n[1], rec n[2])
  258. of mBitorI:
  259. result = Z3_mk_bvor(ctx, rec n[1], rec n[2])
  260. of mBitxorI:
  261. result = Z3_mk_bvxor(ctx, rec n[1], rec n[2])
  262. of mOrd, mChr:
  263. result = rec n[1]
  264. of mOld:
  265. let key = (if c.canonParameterNames: paramName(n[1]) else: stableName(n[1])) & ".old"
  266. result = c.mapping.getOrDefault(key)
  267. if pointer(result) == nil:
  268. let name = Z3_mk_string_symbol(ctx, $n)
  269. result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
  270. c.mapping[key] = result
  271. # XXX change the logic in `addRangeInfo` for this
  272. #vars.add n
  273. else:
  274. # sempass2 adds some 'fact' like 'x = f(a, b)' (see addAsgnFact)
  275. # 'f(a, b)' can have an .ensures annotation and we need to make use
  276. # of this information.
  277. # we need to map 'f(a, b)' to a Z3 variable of this name
  278. let op = n[0].typ
  279. if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
  280. ensuresEffects < op.n[0].len:
  281. let ensures = op.n[0][ensuresEffects]
  282. if ensures != nil and ensures.kind != nkEmpty:
  283. let key = stableName(n)
  284. result = c.mapping.getOrDefault(key)
  285. if pointer(result) == nil:
  286. let name = Z3_mk_string_symbol(ctx, $n)
  287. result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
  288. c.mapping[key] = result
  289. vars.add n
  290. if pointer(result) == nil:
  291. notImplemented(renderTree(n))
  292. of nkStmtListExpr, nkPar:
  293. var isTrivial = true
  294. for i in 0..n.len-2:
  295. isTrivial = isTrivial and n[i].kind in {nkEmpty, nkCommentStmt}
  296. if isTrivial:
  297. result = nodeToZ3(c, n[^1], vars)
  298. else:
  299. notImplemented(renderTree(n))
  300. of nkHiddenDeref:
  301. result = rec n[0]
  302. else:
  303. notImplemented(renderTree(n))
  304. proc addRangeInfo(c: var DrCon, n: PNode, res: var seq[Z3_ast]) =
  305. var cmpOp = mLeI
  306. if n.typ != nil:
  307. cmpOp =
  308. case n.typ.skipTypes(abstractInst).kind
  309. of tyFloat..tyFloat128: mLeF64
  310. of tyChar, tyUInt..tyUInt64: mLeU
  311. else: mLeI
  312. var lowBound, highBound: PNode
  313. if n.kind == nkSym:
  314. let v = n.sym
  315. let t = v.typ.skipTypes(abstractInst - {tyRange})
  316. case t.kind
  317. of tyRange:
  318. lowBound = t.n[0]
  319. highBound = t.n[1]
  320. of tyFloat..tyFloat128:
  321. # no range information for non-range'd floats
  322. return
  323. of tyUInt..tyUInt64, tyChar:
  324. lowBound = newIntNode(nkUInt64Lit, firstOrd(nil, v.typ))
  325. lowBound.typ = v.typ
  326. highBound = newIntNode(nkUInt64Lit, lastOrd(nil, v.typ))
  327. highBound.typ = v.typ
  328. of tyInt..tyInt64, tyEnum:
  329. lowBound = newIntNode(nkInt64Lit, firstOrd(nil, v.typ))
  330. highBound = newIntNode(nkInt64Lit, lastOrd(nil, v.typ))
  331. else:
  332. # no range information available:
  333. return
  334. elif n.kind in nkCallKinds and n.len == 2 and n[0].kind == nkSym and
  335. n[0].sym.magic in {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}:
  336. # we know it's a 'len(x)' expression and we seek to teach
  337. # Z3 that the result is >= 0 and <= high(int).
  338. doAssert n.kind in nkCallKinds
  339. doAssert n[0].kind == nkSym
  340. doAssert n.len == 2
  341. lowBound = newIntNode(nkInt64Lit, 0)
  342. if n.typ != nil:
  343. highBound = newIntNode(nkInt64Lit, lastOrd(nil, n.typ))
  344. else:
  345. highBound = newIntNode(nkInt64Lit, high(int64))
  346. else:
  347. let op = n[0].typ
  348. if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
  349. ensuresEffects < op.n[0].len:
  350. let ensures = op.n[0][ensuresEffects]
  351. if ensures != nil and ensures.kind != nkEmpty:
  352. var dummy: seq[PNode]
  353. res.add nodeToZ3(c, translateEnsures(ensures, n), dummy)
  354. return
  355. let x = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), lowBound, n)
  356. let y = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), n, highBound)
  357. var dummy: seq[PNode]
  358. res.add nodeToZ3(c, x, dummy)
  359. res.add nodeToZ3(c, y, dummy)
  360. proc on_err(ctx: Z3_context, e: Z3_error_code) {.nimcall.} =
  361. #writeStackTrace()
  362. let msg = $Z3_get_error_msg(ctx, e)
  363. raise newException(Z3Exception, msg)
  364. proc forall(ctx: Z3_context; vars: seq[Z3_ast]; assumption, body: Z3_ast): Z3_ast =
  365. let x = Z3_mk_implies(ctx, assumption, body)
  366. if vars.len > 0:
  367. var bound: seq[Z3_app]
  368. for v in vars: bound.add Z3_to_app(ctx, v)
  369. result = Z3_mk_forall_const(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
  370. else:
  371. result = x
  372. proc conj(ctx: Z3_context; conds: seq[Z3_ast]): Z3_ast =
  373. if conds.len > 0:
  374. result = Z3_mk_and(ctx, cuint(conds.len), unsafeAddr conds[0])
  375. else:
  376. result = Z3_mk_true(ctx)
  377. proc proofEngineAux(c: var DrCon; assumptions: seq[PNode]; toProve: PNode): (bool, string) =
  378. c.mapping = initTable[string, Z3_ast]()
  379. let cfg = Z3_mk_config()
  380. Z3_set_param_value(cfg, "model", "true");
  381. let ctx = Z3_mk_context(cfg)
  382. c.z3 = ctx
  383. Z3_del_config(cfg)
  384. Z3_set_error_handler(ctx, on_err)
  385. when false:
  386. Z3_set_param_value(cfg, "timeout", "1000")
  387. try:
  388. #[
  389. For example, let's have these facts:
  390. i < 10
  391. i > 0
  392. Question:
  393. i + 3 < 13
  394. What we need to produce:
  395. forall(i, (i < 10) & (i > 0) -> (i + 3 < 13))
  396. ]#
  397. var collectedVars: seq[PNode]
  398. let solver = Z3_mk_solver(ctx)
  399. var lhs: seq[Z3_ast]
  400. for assumption in assumptions:
  401. if assumption != nil:
  402. try:
  403. let za = nodeToZ3(c, assumption, collectedVars)
  404. #Z3_solver_assert ctx, solver, za
  405. lhs.add za
  406. except CannotMapToZ3Error:
  407. discard "ignore a fact we cannot map to Z3"
  408. let z3toProve = nodeToZ3(c, toProve, collectedVars)
  409. for v in collectedVars:
  410. addRangeInfo(c, v, lhs)
  411. # to make Z3 produce nice counterexamples, we try to prove the
  412. # negation of our conjecture and see if it's Z3_L_FALSE
  413. let fa = Z3_mk_not(ctx, Z3_mk_implies(ctx, conj(ctx, lhs), z3toProve))
  414. #Z3_mk_not(ctx, forall(ctx, collectedVars, conj(ctx, lhs), z3toProve))
  415. #echo "toProve: ", Z3_ast_to_string(ctx, fa), " ", c.graph.config $ toProve.info
  416. Z3_solver_assert ctx, solver, fa
  417. let z3res = Z3_solver_check(ctx, solver)
  418. result[0] = z3res == Z3_L_FALSE
  419. result[1] = ""
  420. if not result[0]:
  421. let counterex = strip($Z3_model_to_string(ctx, Z3_solver_get_model(ctx, solver)))
  422. if counterex.len > 0:
  423. result[1].add "; counter example: " & counterex
  424. except ValueError:
  425. result[0] = false
  426. result[1] = getCurrentExceptionMsg()
  427. finally:
  428. Z3_del_context(ctx)
  429. proc proofEngine(graph: ModuleGraph; assumptions: seq[PNode]; toProve: PNode): (bool, string) =
  430. var c: DrCon
  431. c.graph = graph
  432. result = proofEngineAux(c, assumptions, toProve)
  433. proc translateReq(r, call: PNode): PNode =
  434. if r.kind == nkSym and r.sym.kind == skParam:
  435. if r.sym.position+1 < call.len:
  436. result = call[r.sym.position+1]
  437. else:
  438. notImplemented("no argument given for formal parameter: " & r.sym.name.s)
  439. else:
  440. result = shallowCopy(r)
  441. for i in 0 ..< safeLen(r):
  442. result[i] = translateReq(r[i], call)
  443. proc requirementsCheck(graph: ModuleGraph; assumptions: seq[PNode];
  444. call, requirement: PNode): (bool, string) {.nimcall.} =
  445. try:
  446. let r = translateReq(requirement, call)
  447. result = proofEngine(graph, assumptions, r)
  448. except ValueError:
  449. result[0] = false
  450. result[1] = getCurrentExceptionMsg()
  451. proc compatibleProps(graph: ModuleGraph; formal, actual: PType): bool {.nimcall.} =
  452. #[
  453. Thoughts on subtyping rules for 'proc' types:
  454. proc a(y: int) {.requires: y > 0.} # a is 'weaker' than F
  455. # 'requires' must be weaker (or equal)
  456. # 'ensures' must be stronger (or equal)
  457. # a 'is weaker than' b iff b -> a
  458. # a 'is stronger than' b iff a -> b
  459. # --> We can use Z3 to compute whether 'var x: T = q' is valid
  460. type
  461. F = proc (y: int) {.requires: y > 5.}
  462. var
  463. x: F = a # valid?
  464. ]#
  465. proc isEmpty(n: PNode): bool {.inline.} = n == nil or n.safeLen == 0
  466. result = true
  467. if formal.n != nil and formal.n.len > 0 and formal.n[0].kind == nkEffectList and
  468. ensuresEffects < formal.n[0].len:
  469. let frequires = formal.n[0][requiresEffects]
  470. let fensures = formal.n[0][ensuresEffects]
  471. if actual.n != nil and actual.n.len > 0 and actual.n[0].kind == nkEffectList and
  472. ensuresEffects < actual.n[0].len:
  473. let arequires = actual.n[0][requiresEffects]
  474. let aensures = actual.n[0][ensuresEffects]
  475. var c: DrCon
  476. c.graph = graph
  477. c.canonParameterNames = true
  478. if not frequires.isEmpty:
  479. result = not arequires.isEmpty and proofEngineAux(c, @[frequires], arequires)[0]
  480. if result:
  481. if not fensures.isEmpty:
  482. result = not aensures.isEmpty and proofEngineAux(c, @[aensures], fensures)[0]
  483. else:
  484. # formal has requirements but 'actual' has none, so make it
  485. # incompatible. XXX What if the requirement only mentions that
  486. # we already know from the type system?
  487. result = frequires.isEmpty and fensures.isEmpty
  488. proc mainCommand(graph: ModuleGraph) =
  489. let conf = graph.config
  490. conf.lastCmdTime = epochTime()
  491. graph.proofEngine = proofEngine
  492. graph.requirementsCheck = requirementsCheck
  493. graph.compatibleProps = compatibleProps
  494. graph.config.errorMax = high(int) # do not stop after first error
  495. defineSymbol(graph.config.symbols, "nimcheck")
  496. defineSymbol(graph.config.symbols, "nimDrNim")
  497. registerPass graph, verbosePass
  498. registerPass graph, semPass
  499. compileProject(graph)
  500. if conf.errorCounter == 0:
  501. let mem =
  502. when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
  503. else: formatSize(getTotalMem()) & " totmem"
  504. let loc = $conf.linesCompiled
  505. let build = if isDefined(conf, "danger"): "Dangerous Release"
  506. elif isDefined(conf, "release"): "Release"
  507. else: "Debug"
  508. let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
  509. let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName
  510. var output = $conf.absOutFile
  511. if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename
  512. rawMessage(conf, hintSuccessX, [
  513. "loc", loc,
  514. "sec", sec,
  515. "mem", mem,
  516. "build", build,
  517. "project", project,
  518. "output", output,
  519. ])
  520. proc prependCurDir(f: AbsoluteFile): AbsoluteFile =
  521. when defined(unix):
  522. if os.isAbsolute(f.string): result = f
  523. else: result = AbsoluteFile("./" & f.string)
  524. else:
  525. result = f
  526. proc addCmdPrefix(result: var string, kind: CmdLineKind) =
  527. # consider moving this to std/parseopt
  528. case kind
  529. of cmdLongOption: result.add "--"
  530. of cmdShortOption: result.add "-"
  531. of cmdArgument, cmdEnd: discard
  532. proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
  533. var p = parseopt.initOptParser(cmd)
  534. var argsCount = 1
  535. config.commandLine.setLen 0
  536. config.command = "check"
  537. config.cmd = cmdCheck
  538. while true:
  539. parseopt.next(p)
  540. case p.kind
  541. of cmdEnd: break
  542. of cmdLongOption, cmdShortOption:
  543. config.commandLine.add " "
  544. config.commandLine.addCmdPrefix p.kind
  545. config.commandLine.add p.key.quoteShell # quoteShell to be future proof
  546. if p.val.len > 0:
  547. config.commandLine.add ':'
  548. config.commandLine.add p.val.quoteShell
  549. if p.key == " ":
  550. p.key = "-"
  551. if processArgument(pass, p, argsCount, config): break
  552. else:
  553. processSwitch(pass, p, config)
  554. of cmdArgument:
  555. config.commandLine.add " "
  556. config.commandLine.add p.key.quoteShell
  557. if processArgument(pass, p, argsCount, config): break
  558. if pass == passCmd2:
  559. if {optRun, optWasNimscript} * config.globalOptions == {} and
  560. config.arguments.len > 0 and config.command.normalize notin ["run", "e"]:
  561. rawMessage(config, errGenerated, errArgsNeedRunOption)
  562. proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
  563. let self = NimProg(
  564. supportsStdinFile: true,
  565. processCmdLine: processCmdLine,
  566. mainCommand: mainCommand
  567. )
  568. self.initDefinesProg(conf, "drnim")
  569. if paramCount() == 0:
  570. helpOnError(conf)
  571. return
  572. self.processCmdLineAndProjectPath(conf)
  573. if not self.loadConfigsAndRunMainCommand(cache, conf): return
  574. if conf.hasHint(hintGCStats): echo(GC_getStatistics())
  575. when compileOption("gc", "v2") or compileOption("gc", "refc"):
  576. # the new correct mark&sweep collector is too slow :-/
  577. GC_disableMarkAndSweep()
  578. when not defined(selftest):
  579. let conf = newConfigRef()
  580. handleCmdLine(newIdentCache(), conf)
  581. when declared(GC_setMaxPause):
  582. echo GC_getStatistics()
  583. msgQuit(int8(conf.errorCounter > 0))