package mc2
A mcsat-based SMT solver in pure OCaml
Install
Dune Dependency
Authors
Maintainers
Sources
v0.1.tar.gz
md5=92de696251ec76fbf3eba6ee917fd80f
sha512=e88ba0cfc23186570a52172a0bd7c56053273941eaf3cda0b80fb6752e05d1b75986b01a4e4d46d9711124318e57cba1cd92d302e81d34f9f1ae8b49f39114f0
doc/src/mc2.smtlib/Typecheck.ml.html
Source file Typecheck.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
(* This file is free software. See file "license" for more details. *) (** {1 Preprocessing AST} *) open Mc2_core module Loc = Smtlib_utils.V_2_6.Loc module PA = Smtlib_utils.V_2_6.Ast module SReg = Service.Registry module Ty = Mc2_core.Type module T = Mc2_core.Term module F = Mc2_propositional.F module RLE = Mc2_lra.LE module Stmt = Mc2_core.Statement type 'a or_error = ('a, string) CCResult.t let pp_loc_opt = Loc.pp_opt module StrTbl = CCHashtbl.Make(CCString) module Make(ARG : sig val solver : Mc2_core.Solver.t end) = struct let solver = ARG.solver let reg = Solver.services solver let decl = Solver.get_service_exn solver Mc2_uf.k_decl module BV = Bound_var type term_or_form = | T of term | F of F.t | Rat of RLE.t (* rational linear expression *) module Ctx = struct type t = { tys: (ID.t * Type.t) StrTbl.t; terms: ID.t StrTbl.t; def_funs: (PA.typed_var list * PA.term) StrTbl.t; (* defined functions *) def_consts: term_or_form StrTbl.t; (* defined constants *) mutable loc: Loc.t option; (* current loc *) } let t : t = { terms=StrTbl.create 64; tys=StrTbl.create 64; def_funs=StrTbl.create 64; def_consts=StrTbl.create 32; loc=None; } let loc t = t.loc let set_loc ?loc () = t.loc <- loc let add_term_fun_ (s:string) (id:ID.t) : unit = StrTbl.replace t.terms s id; () let add_ty_ (s:string) (id:ID.t) (ty:Ty.t) : unit = StrTbl.replace t.tys s (id,ty); () let add_def_const s rhs : unit = StrTbl.add t.def_consts s rhs let add_def_fun s vars rhs : unit = StrTbl.add t.def_funs s (vars,rhs) let find_ty (s:string) : ty = match StrTbl.get t.tys s with | Some (_, ty) -> ty | _ -> Error.errorf "expected %s to be an atomic type" s let find_term_fun (s:string) : ID.t = match StrTbl.get t.terms s with | Some f -> f | _ -> Error.errorf "expected %s to be a function symbol" s end let error_loc () : string = Fmt.sprintf "at %a: " pp_loc_opt (Ctx.loc Ctx.t) let errorf_ctx msg = Error.errorf ("at %a:@ " ^^ msg) pp_loc_opt (Ctx.loc Ctx.t) let ill_typed fmt = errorf_ctx ("ill-typed: " ^^ fmt) (* parse a type *) let conv_ty (t:PA.ty) : Ty.t = match t with | PA.Ty_bool -> Ty.bool | PA.Ty_real -> SReg.find_exn reg Mc2_lra.k_rat | PA.Ty_app ("Rat",[]) -> ill_typed "cannot handle reals for now" | PA.Ty_app ("Int",[]) -> ill_typed "cannot handle ints for now" (* TODO: A.Ty.int , Ctx.K_ty Ctx.K_other *) | PA.Ty_app (f, []) -> Ctx.find_ty f | PA.Ty_app (_f, _l) -> ill_typed "cannot handle parametric types" | PA.Ty_arrow _ -> ill_typed "cannot handle arrow types" module Subst = struct module M = Util.Str_map type 'a t = 'a M.t let empty = M.empty let mem subst v = M.mem v subst let add subst v t = if mem subst v then ( Error.errorf "%a already bound" Fmt.string v; ); M.add v t subst let find subst v = M.get v subst end let is_num s = let is_digit_or_dot = function '0' .. '9' | '.' -> true | _ -> false in if s.[0] = '-' then CCString.for_all is_digit_or_dot (String.sub s 1 (String.length s-1)) else CCString.for_all is_digit_or_dot s let mk_ite_id = let n = ref 0 in fun () -> ID.makef "ite_%d" (CCRef.incr_then_get n) let mk_sub_form = let n = ref 0 in fun () -> ID.makef "sub_form_%d" (CCRef.incr_then_get n) let mk_lra_id = let n = ref 0 in fun () -> ID.makef "lra_%d" (CCRef.incr_then_get n) let[@inline] ret_t t = T t let[@inline] ret_f f = F f let[@inline] ret_rat t = Rat t let ret_any t = if Term.is_bool t then F (F.atom (Term.Bool.pa t)) else T t let pp_tof out = function | T t -> Fmt.fprintf out "(@[T %a@])" Term.pp t | F f -> Fmt.fprintf out "(@[F %a@])" F.pp f | Rat lre -> Fmt.fprintf out "(@[RLE %a@])" RLE.pp_no_paren lre let parse_num ~where (s:string) : [`Q of Q.t | `Z of Z.t] = let fail() = Error.errorf "%sexpected number, got `%s`" (Lazy.force where) s in begin match Z.of_string s with | n -> `Z n | exception _ -> begin match Q.of_string s with | n -> `Q n | exception _ -> if String.contains s '.' then ( let p1, p2 = CCString.Split.left_exn ~by:"." s in let n1, n2 = try Z.of_string p1, Z.of_string p2 with _ -> fail() in let factor_10 = Z.pow (Z.of_int 10) (String.length p2) in (* [(p1·10^{length p2}+p2) / 10^{length p2}] *) let n = Q.div (Q.of_bigint (Z.add n2 (Z.mul n1 factor_10))) (Q.of_bigint factor_10) in `Q n ) else fail() end end let side_clauses = ref [] let mk_const = SReg.find_exn reg Mc2_uf.k_const let mk_lra_pred = SReg.find_exn reg Mc2_lra.k_make_pred let mk_lra_eq t u = mk_lra_pred Mc2_lra.Eq0 (RLE.diff t u) |> Term.Bool.pa let[@inline] mk_eq_ t u = Term.mk_eq t u let mk_eq t u = Term.Bool.pa (mk_eq_ t u) let mk_neq t u = Term.Bool.na (mk_eq_ t u) module LRA_tbl = CCHashtbl.Make(RLE) let _lra_names = LRA_tbl.create 16 (* introduce intermediate variable for LRA sub-expression *) let mk_lra_expr (e:RLE.t): term = match RLE.as_const e, RLE.as_singleton e with | Some n, _ -> SReg.find_exn reg Mc2_lra.k_make_const n | None, Some (n,t) when Q.equal n Q.one -> t | _ -> begin match LRA_tbl.find _lra_names e with | t -> t | exception Not_found -> let id = mk_lra_id() in Log.debugf 30 (fun k->k"(@[smtlib.name_lra@ %a@ :as %a@])" RLE.pp e ID.pp id); decl id [] (SReg.find_exn reg Mc2_lra.k_rat); let t = mk_const id in side_clauses := [mk_lra_eq (RLE.singleton1 t) e] :: !side_clauses; LRA_tbl.add _lra_names e t; (* cache *) t end (* adaptative equality *) let mk_eq_t_tf (t:term) (u:term_or_form) : F.t = match u with | F u -> F.equiv (F.atom (Term.Bool.pa t)) u | T u when Term.is_bool u -> F.equiv (F.atom (Term.Bool.pa t)) (F.atom (Term.Bool.pa u)) | T u -> mk_eq t u |> F.atom | Rat u -> mk_lra_eq (RLE.singleton1 t) u |> F.atom let mk_eq_tf_tf (t:term_or_form) (u:term_or_form) = match t, u with | T t, T u when Term.is_bool t -> F.equiv (F.atom (Term.Bool.pa t)) (F.atom (Term.Bool.pa u)) | T t, T u -> mk_eq t u |> F.atom | T t, Rat u | Rat u, T t -> mk_lra_eq (RLE.singleton1 t) u |> F.atom | Rat t, Rat u -> mk_lra_eq t u |> F.atom | F t, F u -> F.equiv t u | F t, T u -> F.equiv t (F.atom @@ Term.Bool.pa u) | T t, F u -> F.equiv (F.atom @@ Term.Bool.pa t) u | _ -> Log.debugf 1 (fun k->k "eq %a %a" pp_tof t pp_tof u); assert false (* convert term *) let rec conv_t_or_f_ (subst:term_or_form Subst.t) (t:PA.term) : term_or_form = (* polymorphic equality *) let mk_app = SReg.find_exn reg Mc2_uf.k_app in let mk_const = SReg.find_exn reg Mc2_uf.k_const in let conv_const v = begin match Subst.find subst v with | Some t -> t | None when is_num v -> (* numeral *) begin match parse_num ~where:(lazy (error_loc ())) v with | `Q n -> Mc2_lra.LE.const n |> ret_rat | `Z n -> Mc2_lra.LE.const (Q.of_bigint n) |> ret_rat end | _ -> (* look for definitions *) match StrTbl.find Ctx.t.Ctx.def_consts v with | rhs -> rhs | exception Not_found -> match Ctx.find_term_fun v with | f -> mk_app f [] |> ret_any | exception Not_found -> Error.errorf "variable %S not bound" v end in begin match t with | PA.Const v -> conv_const v | PA.App ("xor", [a;b]) -> F.xor (conv_form subst a) (conv_form subst b) |> ret_f | PA.App (f, []) -> conv_const f | PA.App (f, l) -> (* see if it's a defined function *) begin match StrTbl.find Ctx.t.Ctx.def_funs f with | (vars,rhs) -> (* TODO: also check types *) if List.length vars <> List.length l then ( errorf_ctx "invalid function call to %s" f ); let l = List.map (conv_t_or_f_ subst) l in let subst = List.fold_left2 (fun s (v,_) t -> Subst.add s v t) Subst.empty vars l in conv_t_or_f_ subst rhs | exception Not_found -> let id = Ctx.find_term_fun f in let l = List.map (conv_term_ subst) l in mk_app id l |> ret_any end | PA.If (a,b,c) -> let a = conv_form subst a in let b = conv_t_or_f_ subst b in let c = conv_t_or_f_ subst c in let ty_b = match b with | F _ -> Type.bool | T t -> Term.ty t | Rat _ -> SReg.find_exn reg Mc2_lra.k_rat in let placeholder_id = mk_ite_id () in Log.debugf 30 (fun k->k"(@[smtlib.name_term@ %a@ :as %a@])" PA.pp_term t ID.pp placeholder_id); decl placeholder_id [] ty_b; let placeholder = mk_const placeholder_id in (* add [f_a => placeholder=b] and [¬f_a => placeholder=c] *) let form = F.and_ [ F.imply a (mk_eq_t_tf placeholder b); F.imply (F.not_ a) (mk_eq_t_tf placeholder c); ] in let cs = mk_cnf form in side_clauses := List.rev_append cs !side_clauses; ret_any placeholder | PA.Let (bs,body) -> (* convert all bindings in the same surrounding [subst], then convert [body] with all bindings active *) let subst = List.fold_left (fun subst' (v,t) -> Subst.add subst' v (conv_t_or_f_ subst t)) subst bs in conv_t_or_f_ subst body | PA.And l -> F.and_ (List.map (conv_form subst) l) |> ret_f | PA.Or l -> F.or_ (List.map (conv_form subst) l) |> ret_f | PA.Imply (a,b) -> ret_f @@ F.imply (conv_form subst a) (conv_form subst b) | PA.Eq (a,b) -> let a = conv_t_or_f_ subst a in let b = conv_t_or_f_ subst b in mk_eq_tf_tf a b |> ret_f | PA.Distinct l -> (* TODO: more efficient "distinct"? *) List.map (conv_term_ subst) l |> CCList.diagonal |> List.map (fun (t,u) -> mk_neq t u |> F.atom) |> F.and_ |> ret_f | PA.Not f -> F.not_ (conv_form subst f) |> ret_f | PA.True -> ret_f F.true_ | PA.False -> ret_f F.false_ | PA.Arith (op, l) -> let l = List.map (conv_rat subst) l in begin match op, l with | PA.Minus, [a] -> RLE.neg a |> ret_rat | PA.Leq, [a;b] -> let e = RLE.diff a b in mk_lra_pred Mc2_lra.Leq0 e |> ret_any | PA.Geq, [a;b] -> let e = RLE.diff b a in mk_lra_pred Mc2_lra.Leq0 e |> ret_any | PA.Lt, [a;b] -> let e = RLE.diff a b in mk_lra_pred Mc2_lra.Lt0 e |> ret_any | PA.Gt, [a;b] -> let e = RLE.diff b a in mk_lra_pred Mc2_lra.Lt0 e |> ret_any | (PA.Leq | PA.Lt | PA.Geq | PA.Gt), _ -> Error.errorf "ill-formed arith expr:@ %a@ (binary operator)" PA.pp_term t | PA.Add, _ -> let e = List.fold_left (fun n t -> RLE.add t n) RLE.empty l in mk_lra_expr e |> ret_t | PA.Minus, a::tail -> let e = List.fold_left (fun n t -> RLE.diff n t) a tail in mk_lra_expr e |> ret_t | PA.Mult, _::_::_ -> let coeffs, terms = CCList.partition_map (fun t -> match RLE.as_const t with | None -> `Right t | Some c -> `Left c) l in begin match coeffs, terms with | c::c_tail, [] -> List.fold_right RLE.mult c_tail (RLE.const c) |> ret_rat | _, [t] -> List.fold_right RLE.mult coeffs t |> ret_rat | _ -> Error.errorf "non-linear expr:@ `%a`" PA.pp_term t end | PA.Div, (first::l) -> (* support t/a/b/c where only [t] is a rational *) let coeffs = List.map (fun c -> match RLE.as_const c with | None -> Error.errorf "non-linear expr:@ `%a`" PA.pp_term t | Some c -> Q.inv c) l in List.fold_right RLE.mult coeffs first |> ret_rat | (PA.Minus | PA.Div | PA.Mult), ([] | [_]) -> Error.errorf "ill-formed arith expr:@ %a@ (binary operator)" PA.pp_term t end | PA.Attr (t,_) -> conv_t_or_f_ subst t | PA.Cast (t, ty_expect) -> let t = conv_term_ subst t in let ty_expect = conv_ty ty_expect in if not (Ty.equal (Term.ty t) ty_expect) then ( ill_typed "term `%a`@ should have type `%a`" Term.pp t Ty.pp ty_expect ); ret_any t | PA.HO_app _ -> errorf_ctx "cannot handle HO application" | PA.Fun _ -> errorf_ctx "cannot handle lambdas" | PA.Match (_lhs, _l) -> errorf_ctx "cannot handle match" | PA.Is_a _ -> errorf_ctx "cannot handle is-a" (* TODO *) | PA.Forall _ | PA.Exists _ -> errorf_ctx "cannot handle quantifiers" (* TODO *) end (* expect a term *) and conv_term_ subst (t:PA.term) : term = match conv_t_or_f_ subst t with | T t -> t | Rat e -> mk_lra_expr e | F {F.view=F.Lit a;_} when Atom.is_pos a -> Atom.term a | F ({F.view=F.Lit _;_} as f) -> (* name [¬a] *) let placeholder_id = mk_sub_form() in decl placeholder_id [] Type.bool; let placeholder = mk_const placeholder_id in Log.debugf 30 (fun k->k"(@[smtlib.name_atom@ %a@ :as %a@])" F.pp f ID.pp placeholder_id); (* add [placeholder <=> ¬a] *) let form = F.equiv (F.atom (Term.Bool.na placeholder)) f in side_clauses := List.rev_append (mk_cnf form) !side_clauses; placeholder | F f -> (* name the sub-formula and add CNF *) let placeholder_id = mk_sub_form() in decl placeholder_id [] Type.bool; Log.debugf 30 (fun k->k"(@[smtlib.name_subform@ %a@ :as %a@])" F.pp f ID.pp placeholder_id); let placeholder = mk_const placeholder_id in (* add [placeholder <=> f] *) let form = F.equiv (F.atom (Term.Bool.pa placeholder)) f in side_clauses := List.rev_append (mk_cnf form) !side_clauses; placeholder and mk_cnf (f:F.t) : atom list list = let fresh = SReg.find_exn reg Mc2_propositional.k_fresh in F.cnf ~fresh f (* convert formula *) and conv_form subst (t:PA.term): F.t = match conv_t_or_f_ subst t with | T t -> F.atom (Term.Bool.pa t) | F f -> f | Rat _ -> Error.errorf "expected proposition,@ got %a" PA.pp_term t (* expect a rational expr *) and conv_rat subst (t:PA.term) : RLE.t = match conv_t_or_f_ subst t with | Rat e -> e | T t -> RLE.singleton1 t | F _ -> assert false let wrap_side_ (f:unit -> 'a) : atom list list * 'a = assert (!side_clauses = []); let x = f() in let side = !side_clauses in side_clauses := []; side, x let conv_term_or_form (t:PA.term) = wrap_side_ (fun () -> conv_t_or_f_ Subst.empty t) let conv_bool_term (t:PA.term): atom list list = let side, cs = wrap_side_ (fun () -> conv_form Subst.empty t |> mk_cnf) in List.rev_append side cs let conv_fun_decl f : string * Ty.t list * Ty.t = if f.PA.fun_ty_vars <> [] then ( errorf_ctx "cannot convert polymorphic function@ %a" (PA.pp_fun_decl PA.pp_ty) f ); let args = List.map conv_ty f.PA.fun_args in let ret = conv_ty f.PA.fun_ret in f.PA.fun_name, args, ret let conv_term t = conv_term_ Subst.empty t let rec conv_statement (s:PA.statement): Stmt.t list = Log.debugf 4 (fun k->k "(@[<1>statement_of_ast@ %a@])" PA.pp_stmt s); Ctx.set_loc ?loc:(PA.loc s) (); conv_statement_aux s and conv_statement_aux (stmt:PA.statement) : Stmt.t list = match PA.view stmt with | PA.Stmt_set_logic s -> [Stmt.Stmt_set_logic s] | PA.Stmt_set_option l -> [Stmt.Stmt_set_option l] | PA.Stmt_set_info (a,b) -> [Stmt.Stmt_set_info (a,b)] | PA.Stmt_exit -> [Stmt.Stmt_exit] | PA.Stmt_decl_sort (s,n) -> if n > 0 then ( errorf_ctx "cannot handle polymorphic type %s" s (* TODO *) ); let id = ID.make s in (* declare type, and save it *) (* TODO: when handling polymorphic types, will have to kill ctx.types and use the function Mc2_unin_sort.k_make directly *) SReg.find_exn reg Mc2_unin_sort.k_decl_sort id n; let ty = SReg.find_exn reg Mc2_unin_sort.k_make id [] in Ctx.add_ty_ s id ty; [Stmt.Stmt_ty_decl (id, n)] | PA.Stmt_decl fr -> let f, args, ret = conv_fun_decl fr in let id = ID.make f in decl id args ret; Ctx.add_term_fun_ f id; [Stmt.Stmt_decl (id, args,ret)] | PA.Stmt_data _l -> errorf_ctx "cannot handle datatype in %a" PA.pp_stmt stmt (* FIXME [Stmt.Stmt_data l] *) | PA.Stmt_funs_rec _defs -> errorf_ctx "not implemented: definitions in %a" PA.pp_stmt stmt (* TODO let {PA.fsr_decls=decls; fsr_bodies=bodies} = defs in if List.length decls <> List.length bodies then ( errorf_ctx ctx "declarations and bodies should have same length"; ); let defs = conv_fun_defs ctx decls bodies in [A.Define defs] *) | PA.Stmt_fun_def {PA.fr_decl={PA.fun_ty_vars=[]; fun_args=[]; fun_name; fun_ret=_}; fr_body} -> (* substitute on the fly *) let cs, rhs = conv_term_or_form fr_body in Ctx.add_def_const fun_name rhs; if cs=[] then [] else [Stmt.Stmt_assert_clauses cs] (* side conditions *) | PA.Stmt_fun_def {PA.fr_decl={PA.fun_ty_vars=[]; fun_args; fun_name; fun_ret=_}; fr_body} -> (* will substitute on the fly *) Ctx.add_def_fun fun_name fun_args fr_body; [] | PA.Stmt_fun_def _ | PA.Stmt_fun_rec _ -> errorf_ctx "unsupported definition: %a" PA.pp_stmt stmt | PA.Stmt_assert t -> Log.debugf 50 (fun k->k ">>> conv assert %a" PA.pp_term t); let cs = conv_bool_term t in Log.debugf 60 (fun k->k ">>> assert clauses %a" Fmt.(Dump.(list @@ list @@ Atom.pp)) cs); [Stmt.Stmt_assert_clauses cs] | PA.Stmt_check_sat -> [Stmt.Stmt_check_sat] | PA.Stmt_check_sat_assuming _ | PA.Stmt_get_assertions | PA.Stmt_get_option _ | PA.Stmt_get_info _ | PA.Stmt_get_model | PA.Stmt_get_proof | PA.Stmt_get_unsat_core | PA.Stmt_get_unsat_assumptions | PA.Stmt_get_assignment | PA.Stmt_reset | PA.Stmt_reset_assertions | PA.Stmt_push _ | PA.Stmt_pop _ | PA.Stmt_get_value _ -> (* TODO: handle more of these *) errorf_ctx "cannot handle statement %a" PA.pp_stmt stmt end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>