From fb538ce174c3872767c9ac49d8624a91ae389081 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 23 Mar 2018 00:04:14 -0400 Subject: [PATCH 001/118] bump version number to 0.5 pre --- README.md | 2 +- src/main/config.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67e98848..ec4c7c8e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ GRASShopper ======= -![Version 0.4](https://img.shields.io/badge/version-0.4-green.svg) +![Version 0.5 pre](https://img.shields.io/badge/version-0.5_pre-green.svg) [![BSD licensed](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/wies/grasshopper/master/LICENSE) [![Build Status](https://travis-ci.org/wies/grasshopper.svg?branch=master)](https://travis-ci.org/wies/grasshopper) diff --git a/src/main/config.ml b/src/main/config.ml index 426b5871..9743e4c6 100644 --- a/src/main/config.ml +++ b/src/main/config.ml @@ -1,5 +1,5 @@ (* Version string *) -let version = "0.4" +let version = "0.5 pre" (* Base directory for includes *) let base_dir = ref "" From 1966f69aee6dc8ab88cecf2a44d61a18ffdc5356 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 23 Mar 2018 17:49:29 -0400 Subject: [PATCH 002/118] minor fix in front-end pretty printer --- src/frontends/spl/splSyntax.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 01df8fe8..dd71dbf3 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -616,7 +616,7 @@ let prio_of_expr = function | Binder _ -> 17 | Annot _ -> 18 -let is_left_assoc = function _ -> true +let is_left_assoc = function OpImpl -> false | _ -> true let rec pr_expr ppf = function @@ -710,8 +710,8 @@ let rec pr_expr ppf = (prio_of_expr e < prio_of_expr e1, prio_of_expr e <= prio_of_expr e2) else - (prio_of_expr e < prio_of_expr e1, - prio_of_expr e <= prio_of_expr e2) + (prio_of_expr e <= prio_of_expr e1, + prio_of_expr e < prio_of_expr e2) in let pr_paran paran ppf e = if paran From 8da1351a9f66f29f7d823cf2fa60dd6bae2e703c Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 23 Mar 2018 17:49:49 -0400 Subject: [PATCH 003/118] improve GRASS pretty printer --- src/backends/smtlib/smtLibSolver.ml | 11 ++ src/formulas/grass.ml | 176 ++++++++++++++-------------- src/formulas/grassUtil.ml | 20 +++- 3 files changed, 115 insertions(+), 92 deletions(-) diff --git a/src/backends/smtlib/smtLibSolver.ml b/src/backends/smtlib/smtLibSolver.ml index 06985ead..be777ad6 100644 --- a/src/backends/smtlib/smtLibSolver.ml +++ b/src/backends/smtlib/smtLibSolver.ml @@ -661,6 +661,17 @@ let push session = let grass_smtlib_prefix = "grass_" +let string_of_symbol = function + | Div -> "div" + | Mod -> "mod" + | Empty -> "emptyset" + | Elem -> "member" + | SubsetEq -> "subset" + | Union -> "union" + | Inter -> "intersection" + | Diff -> "setminus" + | sym -> Grass.string_of_symbol sym + let smtlib_symbol_of_grass_symbol_no_bv solver_info sym = match sym with | FreeSym id -> SmtLibSyntax.Ident id | Plus -> SmtLibSyntax.Plus diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index afa26574..0f9989ff 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -239,13 +239,13 @@ let string_of_symbol = function | Plus -> "+" | Minus -> "-" | Mult -> "*" - | Div -> "div" - | Mod -> "mod" - | Empty -> "emptyset" + | Div -> "/" + | Mod -> "%" + | Empty -> "{}" | SetEnum -> "singleton" - | Union -> "union" - | Inter -> "intersection" - | Diff -> "setminus" + | Union -> "++" + | Inter -> "**" + | Diff -> "--" | BitAnd -> "&" | BitOr -> "|" | BitNot -> "!" @@ -255,14 +255,14 @@ let string_of_symbol = function | IntToByte -> "toByte" | Ite -> "ite" (* predicate symbols *) - | Eq -> "=" + | Eq -> "==" | LtEq -> "<=" | GtEq -> ">=" | Lt -> "<" | Gt -> ">" | Btwn -> "Btwn" - | Elem -> "member" - | SubsetEq -> "subset" + | Elem -> "in" + | SubsetEq -> "subsetof" | Disjoint -> "Disjoint" | Frame -> "Frame" (* constructors and destructors *) @@ -274,6 +274,39 @@ let string_of_symbol = function | Old -> "old" (* patterns *) | Known -> "known" + +let string_of_bop = function + | And -> "&&" + | Or -> "||" + | Not -> "!" + +let prio_of_symbol = function + | Null | Empty | IntConst _ | BoolConst _ -> 0 + | Read | Write | Constructor _ | Destructor _ | Old | SetEnum + | Length | IndexOfCell | ArrayOfCell | ArrayCells | EntPnt | ByteToInt | IntToByte + | Btwn | Frame | Disjoint | Known | FreeSym _ -> 1 + | UMinus | BitNot -> 2 + | Mult | Div | Mod -> 3 + | Minus | Plus -> 4 + | ShiftLeft | ShiftRight -> 5 + | Diff | Union | Inter -> 6 + | Gt | Lt | GtEq | LtEq | Elem | SubsetEq -> 7 + | Eq -> 8 + | BitAnd -> 9 + | BitOr -> 10 + | Ite -> 11 + +let prio_of_term = function + | App (sym, _, _) -> prio_of_symbol sym + | Var _ -> 0 + +let prio_of_form = function + | Atom (t, []) -> prio_of_term t + | Atom (_, _) -> 0 + | BoolOp (Not, _) -> 2 + | BoolOp (And, _) -> 12 + | BoolOp (Or, _) -> 16 + | Binder _ -> 18 let pr_ident ppf id = fprintf ppf "%s" (string_of_ident id) @@ -319,17 +352,6 @@ let rec pr_sort ppf srt = match srt with | Adt (id, cnsts) -> fprintf ppf "%a" pr_ident id | FreeSrt id -> pr_ident ppf id | Map (ds, r) -> fprintf ppf "%s<@[%a,@ %a@]>" map_sort_string pr_sorts ds pr_sort r - -(*and pr_adt_constrs ppf = function - | [] -> () - | [c] -> pr_adt_constr ppf c - | c :: cs -> fprintf ppf "%a | %a" pr_adt_constr c pr_adt_constrs cs - -and pr_adt_constr ppf = function - | (id, args) -> fprintf ppf "%s(%a)" (string_of_ident id) (pr_list_comma pr_adt_arg) args - -and pr_adt_arg ppf = function - | (id, srt) -> fprintf ppf "%s: %a" (string_of_ident id) pr_ident id*) and pr_sorts ppf = function | [srt] -> fprintf ppf "%a" pr_sort srt @@ -340,18 +362,10 @@ let pr_var ppf (x, srt) = let rec pr_vars = pr_list_comma pr_var -let rec pr_term0 ppf t = - match t with - | App (sym, _, _) -> - (match sym with - | Diff | Union | Inter | Plus | Minus | Mult | Div -> - fprintf ppf "(%a)" pr_term t - | _ -> pr_term ppf t) - | _ -> pr_term ppf t - -and pr_term ppf = function +let rec pr_term ppf = function | Var (id, _) -> fprintf ppf "%a" pr_ident id - | App (Empty, _, _) -> fprintf ppf "{}" + | App (Union, [], _) -> fprintf ppf "{}" + | App (Inter, [], _) -> fprintf ppf "Univ" | App (sym, [], _) -> fprintf ppf "%a" pr_sym sym | App (Read, [map; t], _) -> (match sort_of t with @@ -361,50 +375,26 @@ and pr_term ppf = function fprintf ppf "%a[%a].%a" pr_term t pr_term_list ts pr_term map | App (Write, [map; t1; t2], _) -> fprintf ppf "%a[%a := %a]" pr_term map pr_term t1 pr_term t2 - | App (Minus, [t1; t2], _) -> fprintf ppf "%a - @[<2>%a@]" pr_term0 t1 pr_term0 t2 - | App (Plus, [t1; t2], _) -> fprintf ppf "%a + @[<2>%a@]" pr_term0 t1 pr_term0 t2 - | App (Mult, [t1; t2], _) -> fprintf ppf "%a * @[<2>%a@]" pr_term0 t1 pr_term0 t2 - | App (Div, [t1; t2], _) -> fprintf ppf "%a / @[<2>%a@]" pr_term0 t1 pr_term0 t2 - | App (Mod, [t1; t2], _) -> fprintf ppf "%a % @[<2>%a@]" pr_term0 t1 pr_term0 t2 - | App (Diff, [t1; t2], _) -> fprintf ppf "%a -- @[<2>%a@]" pr_term0 t1 pr_term0 t2 + | App ((Minus | Plus | Mult | Div | Mod | Diff | Inter | Union | Eq | SubsetEq | LtEq | GtEq | Lt | Gt | Elem as sym), [t1; t2], _) -> + let pr_t1 = + if prio_of_symbol sym < prio_of_term t1 + then pr_term_paran + else pr_term + in + let pr_t2 = + if prio_of_symbol sym <= prio_of_term t2 + then pr_term_paran + else pr_term + in + fprintf ppf "@[<2>%a %a@ %a@]" pr_t1 t1 pr_sym sym pr_t2 t2 | App (Length, [t], _) -> fprintf ppf "%a.%s" pr_term t (string_of_symbol Length) | App (ArrayCells, [t], _) -> fprintf ppf "%a.%s" pr_term t (string_of_symbol ArrayCells) - | App (Inter, ss, _) -> pr_inter ppf ss - | App (Union, ss, _) -> pr_union ppf ss - | App (Eq, [t1; t2], _) -> fprintf ppf "@[%a@] == @[<2>%a@]" pr_term t1 pr_term t2 - | App (SubsetEq, [t1; t2], _) -> fprintf ppf "@[%a@] subsetof @[<2>%a@]" pr_term t1 pr_term t2 - | App (LtEq, [t1; t2], _) -> fprintf ppf "%a <= @[<2>%a@]" pr_term t1 pr_term t2 - | App (GtEq, [t1; t2], _) -> fprintf ppf "%a >= @[<2>%a@]" pr_term t1 pr_term t2 - | App (Lt, [t1; t2], _) -> fprintf ppf "%a < @[<2>%a@]" pr_term t1 pr_term t2 - | App (Gt, [t1; t2], _) -> fprintf ppf "%a > @[<2>%a@]" pr_term t1 pr_term t2 - | App (Elem, [t1; t2], _) -> fprintf ppf "@[%a@] in @[<2>%a@]" pr_term t1 pr_term t2 | App (SetEnum, ts, _) -> fprintf ppf "{@[%a@]}" pr_term_list ts | App (sym, ts, _) -> fprintf ppf "%a(@[%a@])" pr_sym sym pr_term_list ts -and pr_term_list ppf = pr_list_comma pr_term ppf +and pr_term_paran ppf t = fprintf ppf "(%a)" pr_term t -and pr_inter ppf = function - | [] -> fprintf ppf "Univ" - | [App (Diff, _, _) as s] -> - fprintf ppf "(@[%a@])" pr_term s - | [s] -> pr_term ppf s - | App (Inter, ss1, _) :: ss2 -> - pr_inter ppf (ss1 @ ss2) - | (App (Union, _, _) as s) :: ss - | (App (Diff, _, _) as s) :: ss -> - fprintf ppf "(@[<2>%a@]) ** %a" pr_term s pr_inter ss - | s :: ss -> - fprintf ppf "@[<2>%a@] ** %a" pr_term s pr_inter ss - -and pr_union ppf = function - | [] -> fprintf ppf "{}" - | [App (Diff, _, _) as s] -> - fprintf ppf "(@[%a@])" pr_term s - | [s] -> pr_term ppf s - | (App (Diff, _, _) as s) :: ss -> - fprintf ppf "(@[<2>%a@]) ++ %a" pr_term s pr_union ss - | s :: ss -> - fprintf ppf "@[<2>%a@] ++ %a" pr_term s pr_union ss +and pr_term_list ppf = pr_list_comma pr_term ppf let extract_name ann = let names = Util.filter_map @@ -445,16 +435,33 @@ let pr_binder ppf b = let rec pr_form ppf = function | Binder (b, vs, f, a) -> - fprintf ppf "@[(%a%a)@]" pr_quantifier (b, vs, f) pr_annot a + fprintf ppf "@[%a%a@]" pr_quantifier (b, vs, f) pr_annot a + | BoolOp (And, []) -> fprintf ppf "true" + | BoolOp (Or, []) -> fprintf ppf "false" | BoolOp (And, [f]) | BoolOp (Or, [f]) -> pr_form ppf f - | BoolOp (And, fs) -> pr_ands ppf fs - | BoolOp (Or, fs) -> pr_ors ppf fs + | BoolOp ((And | Or as bop), f1 :: f2 :: fs) as f -> + let pr_f1 = + if prio_of_form f < prio_of_form f1 + then pr_form_paran + else pr_form + in + let pr_f2 ppf = function + | f2, [] -> + if prio_of_form f <= prio_of_form f2 + then pr_form_paran ppf f2 + else pr_form ppf f2 + | f2, fs -> + pr_form ppf (BoolOp (And, f2 :: fs)) + in + fprintf ppf "@[<2>%a@] %s@ %a" pr_f1 f1 (string_of_bop bop) pr_f2 (f2, fs) | BoolOp (Not, [f]) -> pr_not ppf f | BoolOp (_, _) -> () | Atom (t, []) -> fprintf ppf "@[%a@]" pr_term t | Atom (t, a) -> fprintf ppf "@[(%a%a)@]" pr_term t pr_annot a - + +and pr_form_paran ppf f = fprintf ppf "(%a)" pr_form f + and pr_annot ppf a = let gen = extract_gens a in let name = extract_name a in @@ -511,26 +518,19 @@ and pr_annot ppf a = in fprintf ppf "%a%a%a" pr_generators gen pr_patterns pat pr_comment (name, pos, lbl) - -and pr_ands ppf = function - | [] -> fprintf ppf "%s" "true" - | [f] -> fprintf ppf "(@[<2>%a@])" pr_form f - | (BoolOp (Or, _) as f) :: fs -> fprintf ppf "(@[<2>%a@]) &&@ %a" pr_form f pr_ands fs - | f :: fs -> fprintf ppf "@[<2>%a@] &&@ %a" pr_form f pr_ands fs - -and pr_ors ppf = function - | [] -> fprintf ppf "%s" "false" - | [f] -> fprintf ppf "@[<2>%a@]" pr_form f - | f :: fs -> fprintf ppf "@[<2>%a@] ||@ %a" pr_form f pr_ors fs - and pr_not ppf = function + | Atom (App (Eq, [t1; t2], _), []) -> + fprintf ppf "@[%a@]@ !=@ @[<2>%a@]" pr_term t1 pr_term t2 + | Atom (App (Elem, [t1; t2], _), []) -> + fprintf ppf "@[%a@]@ !in@ @[<2>%a@]" pr_term t1 pr_term t2 | Atom (App (Eq, [t1; t2], _), a) -> fprintf ppf "@[(@[%a@]@ !=@ @[<2>%a@]%a)@]" pr_term t1 pr_term t2 pr_annot a | Atom (App (Elem, [t1; t2], _), a) -> fprintf ppf "@[(@[%a@]@ !in@ @[<2>%a@]%a)@]" pr_term t1 pr_term t2 pr_annot a - | Atom (App (_, [], _), _) as f -> - fprintf ppf "!@[%a@]" pr_form f - | f -> fprintf ppf "!(@[%a@])" pr_form f + | f -> + if prio_of_form (BoolOp (Not, [f])) < prio_of_form f + then fprintf ppf "!@[%a@]" pr_form f + else fprintf ppf "!(@[%a@])" pr_form f and pr_quantifier ppf = function | (_, [], f) -> fprintf ppf "%a" pr_form f diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index eaffe47e..d29f4a7c 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -410,7 +410,15 @@ let mk_setenum ts = let mk_inter sets = if List.exists (function App (Empty, [], _) -> true | _ -> false) sets then mk_empty (sort_ofs sets) - else mk_app (sort_ofs sets) Inter sets + else + let rec mi = function + | [] -> mk_app (sort_ofs sets) Inter [] + | [s] -> s + | s1 :: sets -> + let s2 = mi sets in + mk_app (sort_of s1) Inter [s1; s2] + in + mi sets (** Constructor for set union.*) let mk_union sets = @@ -419,11 +427,15 @@ let mk_union sets = (function App (Empty, [], _) -> false | _ -> true) sets in - match sets1 with + let rec mu = function | [] -> mk_empty (sort_ofs sets) | [s] -> s - | _ -> mk_app (sort_ofs sets) Union sets1 - + | s1 :: sets -> + let s2 = mu sets in + mk_app (sort_of s1) Union [s1; s2] + in + mu sets1 + (** Construtor for set difference.*) let mk_diff s t = mk_app (sort_of s) Diff [s; t] From 34921a73a9f2841609c810dc2166ffd36fdc9fbb Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 26 Mar 2018 18:50:39 -0400 Subject: [PATCH 004/118] add preciseness checks for footprint functions --- src/formulas/grass.ml | 4 +- src/programs/prog.ml | 48 +++++++++++++-------- src/verifier/grassifier.ml | 88 ++++++++++++++++++++++++++++++-------- src/verifier/verifier.ml | 5 ++- 4 files changed, 105 insertions(+), 40 deletions(-) diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index 0f9989ff..acd2f566 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -529,8 +529,8 @@ and pr_not ppf = function fprintf ppf "@[(@[%a@]@ !in@ @[<2>%a@]%a)@]" pr_term t1 pr_term t2 pr_annot a | f -> if prio_of_form (BoolOp (Not, [f])) < prio_of_form f - then fprintf ppf "!@[%a@]" pr_form f - else fprintf ppf "!(@[%a@])" pr_form f + then fprintf ppf "!(@[%a@])" pr_form f + else fprintf ppf "!@[%a@]" pr_form f and pr_quantifier ppf = function | (_, [], f) -> fprintf ppf "%a" pr_form f diff --git a/src/programs/prog.ml b/src/programs/prog.ml index efa4efaa..97fcfb29 100644 --- a/src/programs/prog.ml +++ b/src/programs/prog.ml @@ -369,6 +369,8 @@ let struct_sorts prog = collect_srts (locals_of_proc proc) struct_srts) struct_srts prog +let add_proc prog proc = + { prog with prog_procs = IdMap.add (name_of_proc proc) proc prog.prog_procs } (** Auxiliary functions for specifications *) @@ -1038,6 +1040,11 @@ let pr_term_list = Util.pr_list_comma pr_term let pr_sl_form ppf f = Sl.pr_form ppf (old_to_fun_sl_form f) let pr_form ppf f = Grass.pr_form ppf (old_to_fun_form f) + +let pr_comment ppf c = + match c with + | "" -> () + | _ -> fprintf ppf "/* %s */@\n" c let pr_spec_form ppf sf = match sf.spec_form with @@ -1065,18 +1072,18 @@ let pr_basic_cmd ppf = function | Dispose dc -> fprintf ppf "@[<2>free@ %a@]" pr_term dc.dispose_arg | Assume sf -> - fprintf ppf "/* %s */@\n@[<2>%sassume@ %a@]" - sf.spec_name + fprintf ppf "%a@[<2>%sassume@ %a@]" + pr_comment sf.spec_name (fold_spec_form (fun _ -> "pure ") (fun _ -> "") sf) pr_spec_form sf | Assert sf -> - fprintf ppf "/* %s */@\n@[<2>%sassert@ %a@]" - sf.spec_name + fprintf ppf "%a@[<2>%sassert@ %a@]" + pr_comment sf.spec_name (fold_spec_form (fun _ -> "pure ") (fun _ -> "") sf) pr_spec_form sf | Split sf -> - fprintf ppf "/* %s */@\n@[<2>split@ %a@]" - sf.spec_name + fprintf ppf "%a@[<2>split@ %a@]" + pr_comment sf.spec_name pr_spec_form sf | Return rc -> fprintf ppf "@[<2>return@ %a@]" pr_term_list rc.return_args @@ -1136,8 +1143,8 @@ let pr_spec_kind ppf = function let rec pr_precond ppf = function | [] -> () | sf :: sfs -> - fprintf ppf "@\n/* %s */@\n@[<2>%a%srequires@ %a@]%a" - sf.spec_name + fprintf ppf "@\n%a@[<2>%a%srequires@ %a@]%a" + pr_comment sf.spec_name pr_spec_kind sf.spec_kind (fold_spec_form (fun _ -> "pure ") (fun _ -> "") sf) pr_spec_form sf @@ -1146,8 +1153,8 @@ let rec pr_precond ppf = function let rec pr_postcond ppf = function | [] -> () | sf :: sfs -> - fprintf ppf "@\n/* %s */@\n@[<2>%a%sensures@ %a@]%a" - sf.spec_name + fprintf ppf "@\n%a@[<2>%a%sensures@ %a@]%a" + pr_comment sf.spec_name pr_spec_kind sf.spec_kind (fold_spec_form (fun _ -> "pure ") (fun _ -> "") sf) pr_spec_form sf @@ -1210,11 +1217,16 @@ let pr_proc ppf proc = then locals else (id, decl) :: locals) (locals_of_proc proc) [] in - fprintf ppf "@[<2>%s %a(@[<0>%a@])@\nreturns (@[<0>%a@])%a@]@\n%a@\n" - "procedure" + let intro = if proc.proc_is_lemma then "lemma" else "procedure" in + let pr_returns ppf returns = + if returns = [] then () + else fprintf ppf "@\nreturns (@[<0>%a@])" pr_id_srt_list (add_srts returns) + in + fprintf ppf "@\n@[<2>%s %a(@[<0>%a@])%a%a@]@\n%a" + intro pr_ident (name_of_proc proc) pr_id_srt_list (add_srts (formals_of_proc proc)) - pr_id_srt_list (add_srts (returns_of_proc proc)) + pr_returns (returns_of_proc proc) pr_contract proc.proc_contract (pr_body locals) proc.proc_body @@ -1231,12 +1243,12 @@ let pr_pred ppf pred = let pr_header ppf pred = match returns_of_pred pred with | [] -> - fprintf ppf "@[<2>predicate %a(@[<0>%a@])%a@]@ " + fprintf ppf "@\n@[<2>predicate %a(@[<0>%a@])%a@]@ " pr_ident (name_of_pred pred) pr_id_srt_list (add_srts (formals_of_pred pred)) pr_contract pred.pred_contract | _ -> - fprintf ppf "@[<2>function %a(@[<0>%a@])@\nreturns (@[<0>%a@])%a@]@\n" + fprintf ppf "@\n@[<2>function %a(@[<0>%a@])@\nreturns (@[<0>%a@])%a@]@\n" pr_ident (name_of_pred pred) pr_id_srt_list (add_srts (formals_of_pred pred)) pr_id_srt_list (add_srts (returns_of_pred pred)) @@ -1245,7 +1257,7 @@ let pr_pred ppf pred = let pr_body ppf pred = match pred.pred_body with | Some sf -> - fprintf ppf "{@[<1>@\n%a@]@\n}@\n@\n" pr_spec_form sf + fprintf ppf "{@[<1>@\n%a@]@\n}" pr_spec_form sf | None -> fprintf ppf "@\n@\n" in fprintf ppf "%a%a" pr_header pred pr_body pred @@ -1256,8 +1268,8 @@ let rec pr_preds ppf = function fprintf ppf "%a@\n%a" pr_pred pred pr_preds preds let pr_axiom ppf sf = - fprintf ppf "/* %s */@\n@[<2>axiom@ %a@];@\n" - sf.spec_name + fprintf ppf "%a@[<2>axiom@ %a@];@\n" + pr_comment sf.spec_name pr_spec_form sf diff --git a/src/verifier/grassifier.ml b/src/verifier/grassifier.ml index b71a89fb..a9560fe8 100644 --- a/src/verifier/grassifier.ml +++ b/src/verifier/grassifier.ml @@ -1014,7 +1014,7 @@ let elim_sl prog = contr1, footprint_sets, footprint_context in (* translate the predicates from SL to GRASS *) - let translate_pred preds pred = + let translate_pred (preds, aux_lemmas) pred = let is_pure = is_pure_pred pred in let pname = name_of_pred pred in (*print_endline @@ "translating predicate " ^ string_of_ident pname;*) @@ -1040,7 +1040,10 @@ let elim_sl prog = let make_for_sort sort = let func_name = footprint_fun_id pname sort in let ret_var_id = mk_ident @@ "FP_" ^ (string_of_sort sort) in + let ret_var_id1 = (name ret_var_id, 1) in let ret_var = mk_loc_set_decl sort ret_var_id dummy_position in + let ret_var1 = mk_loc_set_decl sort ret_var_id1 dummy_position in + let ssort = Set (Loc sort) in let fp_func_term = let formals = List.map @@ -1111,12 +1114,12 @@ let elim_sl prog = when equals_fp_func t1 || equals_fp_func t2 -> let t1 = if t1 = fp_func_term - then mk_free_const sort ret_var_id + then mk_free_const ssort ret_var_id else t1 in let t2 = if t2 = fp_func_term - then mk_free_const sort ret_var_id + then mk_free_const ssort ret_var_id else t2 in subsets, defs, mk_eq ~ann:a t1 t2 @@ -1125,7 +1128,7 @@ let elim_sl prog = when equals_fp_func t1 -> let t1 = if t1 = fp_func_term - then mk_free_const sort ret_var_id + then mk_free_const ssort ret_var_id else t1 in if List.mem (t2, t1) subsets @@ -1136,7 +1139,7 @@ let elim_sl prog = when equals_fp_func t2 -> let t2 = if t2 = fp_func_term - then mk_free_const sort ret_var_id + then mk_free_const ssort ret_var_id else t2 in (t1, t2) :: subsets, defs, @@ -1170,19 +1173,23 @@ let elim_sl prog = | [] -> subsets1, defs1, Binder (b, vs, f1, annots) | _ -> subsets, defs, Binder (b, vs, f1, annots) in - let spec_form = + let func_body = match spec.spec_form with | SL _ -> failwith "Expected SL to be eliminated already" | FOL f -> - FOL (f |> nnf |> nnf |> process_form [] [] |> function (_, _, f) -> post_process_form f) + let f1 = f |> nnf |> process_form [] [] |> function (_, _, f) -> post_process_form f in + FOL f1 in - Some { spec with spec_form = spec_form} + Some { spec with spec_form = func_body} + in + let fp_func_locals = + IdMap.add ret_var_id ret_var pred.pred_contract.contr_locals in let fp_func_contract = { contr_name = func_name; contr_formals = pred.pred_contract.contr_formals; contr_returns = [ret_var_id]; - contr_locals = IdMap.add ret_var_id ret_var pred.pred_contract.contr_locals; (* TODO find the set of locals used here *) + contr_locals = fp_func_locals; contr_precond = []; contr_postcond = []; contr_footprint_sorts = SortSet.empty; @@ -1192,12 +1199,52 @@ let elim_sl prog = let footprint_func = { pred_contract = fp_func_contract; pred_body = pred_body; - pred_accesses = IdSet.empty; (* TODO confirm *) + pred_accesses = IdSet.empty; (* access sets will be recomputed after grassification *) pred_is_self_framing = true; pred_was_sl_pred = false; } in - func_name, footprint_func + (* generate lemma for checking that footprint function is precise *) + let fp_precise_lemma_opt = + match pred_body with + | Some sf -> + let f = form_of_spec sf in + let check_preconds = [f; subst_id (IdMap.singleton ret_var_id ret_var_id1) f] in + let fp_check_locals = + IdMap.add ret_var_id1 ret_var1 fp_func_locals + in + let check_postcond = + FOL (mk_eq (mk_free_const ssort ret_var_id) (mk_free_const ssort ret_var_id1)) + in + let msg _ = + "Footprint of " ^ (string_of_ident @@ name_of_pred pred) ^ " may not be precise for type " ^ (string_of_sort sort), "" + in + let pos = pos_of_pred pred in + let check_precond_specs = List.map (fun f -> mk_spec_form (FOL f) "" None pos) check_preconds in + let check_postcond_spec = mk_spec_form check_postcond "preciseness" (Some msg) pos in + let contract = + { contr_name = fresh_ident ("wf_" ^ name func_name); + contr_formals = ret_var_id :: ret_var_id1 :: pred.pred_contract.contr_formals; + contr_returns = []; + contr_locals = fp_check_locals; + contr_precond = check_precond_specs; + contr_postcond = [check_postcond_spec]; + contr_footprint_sorts = SortSet.empty; + contr_pos = pred.pred_contract.contr_pos; + } + in + let fp_precise_lemma = + { proc_contract = contract; + proc_body = Some (Seq ([], mk_ppoint contract.contr_pos)); + proc_is_lemma = true; + proc_is_tailrec = false; + proc_deps = []; + } + in + Some fp_precise_lemma + | _ -> None + in + footprint_func, fp_precise_lemma_opt in (if is_sl_pred pred then pred.pred_contract.contr_footprint_sorts else SortSet.empty) |> SortSet.elements @@ -1211,21 +1258,22 @@ let elim_sl prog = } in (* Add functions for the footprints *) - let preds = + let preds, aux_lemmas = if not (is_pure_pred pred) then (* Only add for predicates, not for functions *) make_fp_funcs pred pred1 |> List.fold_left - (fun preds (func_name, func) -> - IdMap.add func_name func preds) - preds - else preds + (fun (preds, aux_lemmas) (func, check) -> + IdMap.add (name_of_pred func) func preds, + Opt.to_list check @ aux_lemmas) + (preds, aux_lemmas) + else preds, aux_lemmas in - IdMap.add pname pred1 preds + IdMap.add pname pred1 preds, aux_lemmas in let axioms = List.map (map_spec_fol_form post_process_form) prog.prog_axioms in - let preds = fold_preds translate_pred IdMap.empty prog in + let preds, aux_lemmas = fold_preds translate_pred (IdMap.empty, []) prog in let prog = { prog with prog_preds = preds; prog_axioms = axioms } in let compile_proc proc = let proc_footprints = footprint_sorts_proc proc in @@ -1350,7 +1398,9 @@ let elim_sl prog = proc_body = body; } in - add_ghost_field_invariants (map_procs compile_proc prog) + let prog = map_procs compile_proc prog in + let prog = List.fold_left add_proc prog aux_lemmas in + add_ghost_field_invariants prog (** Annotate safety checks for heap accesses *) let annotate_heap_checks prog = diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 6c22a207..1cbbb52d 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -544,7 +544,10 @@ let check_proc prog proc = end in check_one vc0 in - let _ = Debug.info (fun () -> "Checking procedure " ^ string_of_ident (name_of_proc proc) ^ "...\n") in + let _ = Debug.info (fun () -> + "Checking " ^ (if proc.proc_is_lemma then "lemma " else "procedure ") ^ + string_of_ident (name_of_proc proc) ^ "...\n") + in let vcs = vcgen prog proc in List.fold_left check_vc [] vcs From 3b4c0dd006e0bfa1f5e113657b96e5231f6c7a0f Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 26 Mar 2018 18:51:08 -0400 Subject: [PATCH 005/118] syntax highlighting for '^' in identifiers --- emacs-mode/spl-mode.el | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/emacs-mode/spl-mode.el b/emacs-mode/spl-mode.el index 0b1e961f..3a5638d8 100644 --- a/emacs-mode/spl-mode.el +++ b/emacs-mode/spl-mode.el @@ -52,28 +52,28 @@ '("\\<\\(emp\\|false\\|null\\|true\\)\\>" 1 font-lock-constant-face) - '("\\(\\<[a-zA-Z_][a-zA-Z0-9_']*[ \t]*\\>\\)(" 1 + '("\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*[ \t]*\\>\\)(" 1 font-lock-function-name-face) - '("[^:]:[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_']*\\>\\)" 1 + '("[^:]:[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)" 1 font-lock-type-face) - '("<[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_']*\\>\\)[ \t]*>" 1 + '("<[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)[ \t]*>" 1 font-lock-type-face) - '("<[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_']*\\>\\)[ \t]*<" 1 + '("<[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)[ \t]*<" 1 font-lock-type-face) - '("new[ \t]+\\(\\<[a-zA-Z_][a-zA-Z0-9_']*\\>\\)" 1 + '("new[ \t]+\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)" 1 font-lock-type-face) - '("\\(struct\\|type\\)[ \t]+\\(\\<[a-zA-Z_][a-zA-Z0-9_']*\\>\\)" 2 + '("\\(struct\\|type\\)[ \t]+\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)" 2 font-lock-type-face) - '("\\<\\(forall\\|exists\\)[ \t]*\\([a-zA-Z_][a-zA-Z0-9_']*\\)\\>" 2 + '("\\<\\(forall\\|exists\\)[ \t]*\\([a-zA-Z_][a-zA-Z0-9_^']*\\)\\>" 2 font-lock-variable-name-face) - '("\\(\\<[a-zA-Z_][a-zA-Z0-9_']*[ \t]*\\>\\):[^:=]" 1 + '("\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*[ \t]*\\>\\):[^:=]" 1 font-lock-variable-name-face) )) From 011a424c27ac3f70e53a2081b0089361f953d910 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 7 Apr 2018 14:40:41 -0400 Subject: [PATCH 006/118] minor fixes related to error reporting --- src/frontends/spl/splParser.mly | 2 +- src/frontends/spl/splSyntax.ml | 2 ++ src/frontends/spl/splTranslator.ml | 8 ++++++++ src/verifier/grassifier.ml | 8 ++++---- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 03d7dc3d..19ed1ba7 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -283,7 +283,7 @@ function_header: pred_impl: | LBRACE expr RBRACE { - Some $2 + Some (Annot ($2, Position, mk_position 1 3)) } | /* empty */ { None } ; diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index dd71dbf3..92c20104 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -201,6 +201,7 @@ and binder_kind = | Forall | Exists | SetComp and annotation = + | Position | GeneratorAnnot of (expr * ident list) list * expr | PatternAnnot of expr | CommentAnnot of string @@ -934,3 +935,4 @@ let print_cu out_ch prog = Util.print_of_format pr_cu prog out_ch let string_of_type t = Util.string_of_format pr_type t +let string_of_expr e = Util.string_of_format pr_expr e diff --git a/src/frontends/spl/splTranslator.ml b/src/frontends/spl/splTranslator.ml index aee7ade9..316b2a6d 100644 --- a/src/frontends/spl/splTranslator.ml +++ b/src/frontends/spl/splTranslator.ml @@ -341,6 +341,8 @@ let convert cu = GrassUtil.mk_btwn_term tfld tx ty tz | Binder (SetComp, _, _, pos) -> failwith ("set comprehension should have been desugared at " ^ string_of_src_pos pos) + | Annot (e, Position, pos) -> + convert_term locals e | e -> failwith ("unexpected expression at " ^ string_of_src_pos (pos_of_expr e)) in @@ -438,6 +440,9 @@ let convert cu = let ge1 = convert_term locals ge in let matches = List.map (fun (e, filters) -> Match (e, filters)) es1 in GrassUtil.annotate f [TermGenerator (matches, [ge1])] + | Annot (e, Position, pos) -> + let f = convert_grass_form locals e in + GrassUtil.annotate f [SrcPos pos] | e -> let t = convert_term locals e in Grass.Atom (t, [SrcPos (pos_of_expr e)]) @@ -514,6 +519,9 @@ let convert cu = let f1 = convert_sl_form locals1 f in let f2 = SlUtil.subst_consts subst f1 in mk_quant ~pos:pos vars f2 + | Annot (e, Position, pos) -> + let f = convert_sl_form locals e in + SlUtil.mk_exists ~pos:pos [] f | e -> let f = convert_grass_form locals e in Pure (f, Some (pos_of_expr e)) diff --git a/src/verifier/grassifier.ml b/src/verifier/grassifier.ml index a9560fe8..19345bfe 100644 --- a/src/verifier/grassifier.ml +++ b/src/verifier/grassifier.ml @@ -1185,6 +1185,7 @@ let elim_sl prog = let fp_func_locals = IdMap.add ret_var_id ret_var pred.pred_contract.contr_locals in + let pos = pred.pred_body |> Opt.map (fun sf -> sf.spec_pos) |> Opt.get_or_else (pos_of_pred pred) in let fp_func_contract = { contr_name = func_name; contr_formals = pred.pred_contract.contr_formals; @@ -1193,7 +1194,7 @@ let elim_sl prog = contr_precond = []; contr_postcond = []; contr_footprint_sorts = SortSet.empty; - contr_pos = dummy_position; + contr_pos = pos; } in let footprint_func = @@ -1219,7 +1220,6 @@ let elim_sl prog = let msg _ = "Footprint of " ^ (string_of_ident @@ name_of_pred pred) ^ " may not be precise for type " ^ (string_of_sort sort), "" in - let pos = pos_of_pred pred in let check_precond_specs = List.map (fun f -> mk_spec_form (FOL f) "" None pos) check_preconds in let check_postcond_spec = mk_spec_form check_postcond "preciseness" (Some msg) pos in let contract = @@ -1230,12 +1230,12 @@ let elim_sl prog = contr_precond = check_precond_specs; contr_postcond = [check_postcond_spec]; contr_footprint_sorts = SortSet.empty; - contr_pos = pred.pred_contract.contr_pos; + contr_pos = pos; } in let fp_precise_lemma = { proc_contract = contract; - proc_body = Some (Seq ([], mk_ppoint contract.contr_pos)); + proc_body = Some (Seq ([], mk_ppoint pos)); proc_is_lemma = true; proc_is_tailrec = false; proc_deps = []; From bb699d976a68bd9aeef8caa57492ae11ee43eb3b Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 7 Apr 2018 16:03:13 -0400 Subject: [PATCH 007/118] make semicolon after datatype declarations optional --- src/frontends/spl/splParser.mly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 19ed1ba7..ee43e315 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -366,7 +366,7 @@ proc_returns: ; datatype_decl: -| DATATYPE IDENT EQ constr_decls SEMICOLON { +| DATATYPE IDENT EQ constr_decls semicolon_opt { { t_name = $2; t_def = ADTypeDef $4; t_pos = mk_position 1 5; From 5ae31bbdce9da84052597e11437b1b362bf340b4 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 7 Apr 2018 16:04:11 -0400 Subject: [PATCH 008/118] fixes #21 --- src/frontends/spl/splChecker.ml | 13 ++++++++----- src/frontends/spl/splTypeChecker.ml | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index c1324434..656ac5c9 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -944,9 +944,10 @@ let infer_types cu = in Return (es1, pos) in - let preds = + (* infer types of predicates *) + let cu = IdMap.fold - (fun id pred preds -> + (fun id pred cu -> let rtype = match pred.pr_outputs with | [res_id] -> @@ -969,9 +970,11 @@ let infer_types cu = pr_body = body } in - IdMap.add id pred1 preds) - cu.pred_decls IdMap.empty + let pred_decls1 = IdMap.add id pred1 cu.pred_decls in + { cu with pred_decls = pred_decls1 }) + cu.pred_decls cu in + (* infer types of procedures/lemmas *) let procs = IdMap.fold (fun _ proc procs -> @@ -992,7 +995,7 @@ let infer_types cu = (fun (e, pos) -> (check_spec IdMap.empty true e, pos) ) cu.background_theory in - { cu with pred_decls = preds; proc_decls = procs; background_theory = bg_theory; } + { cu with proc_decls = procs; background_theory = bg_theory; } (** Check compilation unit [cu]. *) let check cu = diff --git a/src/frontends/spl/splTypeChecker.ml b/src/frontends/spl/splTypeChecker.ml index 22743f1f..7511e453 100644 --- a/src/frontends/spl/splTypeChecker.ml +++ b/src/frontends/spl/splTypeChecker.ml @@ -153,7 +153,7 @@ let type_of_expr cu locals e = | PredApp (Pred id, _, _) -> let decl = IdMap.find id cu.pred_decls in (match decl.pr_outputs with - | [] -> decl.pr_body |> Opt.map te |> Opt.get_or_else BoolType + | [] -> decl.pr_body |> Opt.map te |> Opt.get_or_else PermType | [rid] -> let rdecl = IdMap.find rid decl.pr_locals in rdecl.v_type @@ -531,7 +531,7 @@ let infer_types cu locals ty e = let body_ty = decl.pr_body |> Opt.map (type_of_expr cu locals) |> - Opt.get_or_else BoolType + Opt.get_or_else PermType in match_types pos ty body_ty in From be32c3319cf00c895ce804a2753694f4bf14081d Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Sat, 7 Apr 2018 17:08:25 -0400 Subject: [PATCH 009/118] macros: also substitute inside annotations --- src/frontends/spl/splSyntax.ml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index d016315e..24ebbf8b 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -324,8 +324,8 @@ let subst sm = | UnaryOp (op, e, pos) -> UnaryOp (op, s bv e, pos) | Annot (e, a, pos) -> - Annot (s bv e, a, pos) (* TODO: substitute in a *) - | Read (e1, e2, pos) -> + Annot (s bv e, s_annot bv a, pos) + | Read (e1, e2, pos) -> (* TODO Thomas, why no substitution for Write? *) Read (s bv e1, s bv e2, pos) | Write (e1, e2, e3, pos) -> Write (s bv e1, s bv e2, s bv e3, pos) @@ -342,6 +342,13 @@ let subst sm = in Binder (b, vs, s bv e, pos) | (Null _ | Emp _ | IntVal _ | BoolVal _) as e -> e + and s_annot bv = function + | GeneratorAnnot (ms, e) -> + let ms = List.map (fun (e, is) -> (s bv e, is)) ms in + GeneratorAnnot (ms, s bv e) + | PatternAnnot e -> PatternAnnot (s bv e) + | CommentAnnot _ as a -> a + | Position -> Position in s IdSet.empty let pos_of_stmt = function From edb6c8de5eb200b1e1a96a6142e8dc1f17eb24d0 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Sat, 7 Apr 2018 17:11:39 -0400 Subject: [PATCH 010/118] splSyntax: fix TODOs in substitution functions --- src/frontends/spl/splSyntax.ml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 24ebbf8b..2415a564 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -282,7 +282,7 @@ let subst_id sm = | UnaryOp (op, e, pos) -> UnaryOp (op, s bv e, pos) | Annot (e, a, pos) -> - Annot (s bv e, a, pos) (* TODO: substitute in a *) + Annot (s bv e, s_annot bv a, pos) | Read (e1, e2, pos) -> Read (s bv e1, s bv e2, pos) | Write (e1, e2, e3, pos) -> @@ -300,6 +300,13 @@ let subst_id sm = in Binder (b, vs, s bv e, pos) | (Null _ | Emp _ | IntVal _ | BoolVal _ as e) -> e + and s_annot bv = function + | GeneratorAnnot (ms, e) -> + let ms = List.map (fun (e, is) -> (s bv e, is)) ms in + GeneratorAnnot (ms, s bv e) + | PatternAnnot e -> PatternAnnot (s bv e) + | CommentAnnot _ as a -> a + | Position -> Position in s IdSet.empty (** General (id -> expr) substitution for expressions (not capture avoiding) *) @@ -325,7 +332,7 @@ let subst sm = UnaryOp (op, s bv e, pos) | Annot (e, a, pos) -> Annot (s bv e, s_annot bv a, pos) - | Read (e1, e2, pos) -> (* TODO Thomas, why no substitution for Write? *) + | Read (e1, e2, pos) -> Read (s bv e1, s bv e2, pos) | Write (e1, e2, e3, pos) -> Write (s bv e1, s bv e2, s bv e3, pos) From 790938e3f1706b8f23cd9d61f7b4853182b744df Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 7 Apr 2018 18:13:25 -0400 Subject: [PATCH 011/118] make semicolon after field declaration optional --- src/frontends/spl/splParser.mly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index ee43e315..bbc7d475 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -415,7 +415,7 @@ field_decls: ; field_decl: -| VAR var_decl SEMICOLON { $2 } +| VAR var_decl semicolon_opt { $2 } ; block: From fb8fbb14268eb7038c58f3689f87edcfdd3c6a52 Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 8 Apr 2018 14:46:12 -0400 Subject: [PATCH 012/118] fix divergence issue in GrassUtil.foralls_to_exists --- src/formulas/grassUtil.ml | 109 ++------------------------------------ 1 file changed, 3 insertions(+), 106 deletions(-) diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index d29f4a7c..ba6c6014 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -1308,11 +1308,11 @@ let propagate_binder_up b f = let vars = fv f in let vs0 = List.filter (fun (v, _) -> IdSet.mem v vars) vs in let sm, vs1 = - List.fold_left - (fun (sm, vs1) (v, srt) -> + List.fold_right + (fun (v, srt) (sm, vs1) -> let v1 = fresh_ident (name v) in IdMap.add v (mk_var srt v1) sm, (v1, srt) :: vs1) - (IdMap.empty, tvs) vs0 + vs0 (IdMap.empty, tvs) in let f1, vs2 = prop vs1 (subst sm f) in (match a with @@ -1506,109 +1506,6 @@ let foralls_to_exists f = in cf f -(** Convert universal quantifiers in formula [f] into existentials where possible. *) -(** Assumes that [f] is in negation normal form. *) -(* BROKEN!! - -let foralls_to_exists f = - let rec find_defs bvs defs f = - let rec find nodefs defs = function - | BoolOp (Not, [Atom (App (Eq, [Var (x, _) as xt; Var _ as yt], _), a)]) - when IdSet.mem x nodefs -> - IdSet.remove x nodefs, mk_eq xt yt :: defs, mk_false - | BoolOp (Not, [Atom (App (Eq, [Var (x, _) as xt; t], _), a)]) - when IdSet.mem x nodefs && - IdSet.is_empty (IdSet.inter nodefs (fv_term t)) -> - IdSet.remove x nodefs, mk_eq xt t :: defs, mk_false - | BoolOp (Not, [Atom (App (Eq, [t; Var (x, srt) as xt], _), a)]) - when IdSet.mem x nodefs && - IdSet.is_empty (IdSet.inter nodefs (fv_term t)) -> - IdSet.remove x nodefs, mk_eq xt t :: defs, mk_false - | BoolOp (Or, fs) -> - let nodefs, defs, gs = - List.fold_right - (fun f (nodefs, defs, gs) -> - let nodefs, defs, g = find nodefs defs f in - nodefs, defs, g :: gs) - fs (nodefs, defs, []) - in - nodefs, defs, mk_or gs - | Binder (b, [], f, a) -> - let nodefs, defs, g = find nodefs defs f in - nodefs, defs, Binder (b, [], g, a) - | f -> - nodefs, defs, f - in - let nodefs, defs, g = find bvs defs f in - if IdSet.subset bvs nodefs - then begin - let defs, sm = - List.fold_right (fun f (defs, sm) -> - match f with - | Atom (App (Eq, [Var (v, _); t], _), a) -> - let t1 = subst_term sm t in - let smv = IdMap.singleton v t1 in - let sm = IdMap.fold (fun w tw -> IdMap.add w (subst_term smv tw)) sm IdMap.empty in - defs, IdMap.add v t1 sm - | f -> f :: defs, sm) - defs ([], IdMap.empty) - in - nodefs, List.map (subst sm) defs, subst sm g, sm - end - else find_defs nodefs defs g - in - let rec distribute_and bvs a gs = function - | BoolOp (And, fs) :: gs1 -> - let fs1 = List.map (fun f -> smk_or (List.rev_append gs (f :: gs1))) fs in - cf (mk_forall ~ann:a bvs (smk_and fs1)) - | Binder (_, [], BoolOp (And, fs), a1) :: gs1 -> - let fs1 = List.map (fun f -> smk_or (List.rev_append gs (f :: gs1))) fs in - cf (mk_forall ~ann:(a @ a1) bvs (smk_and fs1)) - | BoolOp (Or, gs2) :: gs1 -> - distribute_and bvs a gs (gs2 @ gs1) - | Binder (_, [], BoolOp (Or, gs2), a1) :: gs1 -> - distribute_and bvs a gs (List.map (fun f -> annotate f a1) gs2 @ gs1) - | [Binder (b, [], g, a1)] -> - distribute_and bvs (a @ a1) gs [g] - (*| Binder (_, [], g, a1) :: gs1 -> - (*assert (List.for_all (function TermGenerator _ -> false | _ -> true) a);*) - distribute_and bvs a gs (g :: gs1)*) - | g :: gs1 -> distribute_and bvs a (g :: gs) gs1 - | [] -> smk_forall ~ann:a bvs (mk_or (List.rev gs)) - and cf = function - | Binder (b, [], f, a) -> - Binder (b, [], cf f, a) - | Binder (Forall, bvs, BoolOp (And, fs), a) -> - let fs1 = List.map (fun f -> cf (Binder (Forall, bvs, f, a))) fs in - smk_and fs1 - | Binder (Forall, _, BoolOp (Or, _), _) as f -> - (match propagate_forall_up f with - | Binder (Forall, bvs, (BoolOp (Or, fs) as f), a) -> - let bvs_set = id_set_of_list (List.map fst bvs) in - let nodefs, defs, g, sm = find_defs bvs_set [] f in - let a = List.map (subst_annot sm) a in - let ubvs, ebvs = List.partition (fun (x, _) -> IdSet.mem x nodefs) bvs in - (match ebvs with - | [] -> - (*assert (List.for_all (function TermGenerator _ -> false | _ -> true) a);*) - distribute_and bvs a [] [g] - | _ -> - let g1 = cf (mk_forall ubvs g) in - smk_exists ~ann:a ebvs (mk_and (defs @ [g1])) - ) - | _ -> f) - | Binder (Forall, bvs1, Binder (Forall, bvs2, f2, a2), a1) -> - cf (Binder (Forall, bvs1 @ bvs2, f2, a1 @ a2)) - | Binder (Exists, bvs, f, a) -> - smk_exists ~ann:a bvs (cf f) - | BoolOp (And as op, fs) - | BoolOp (Or as op, fs) -> - let fs1 = List.map cf fs in - BoolOp (op, fs1) - | f -> f - in - cf f - *) (** Skolemize formula [f]. ** Assumes that [f] is in negation normal form. *) From 87fc903c013da51533380311444c739fc389182b Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 10 Apr 2018 00:53:40 -0400 Subject: [PATCH 013/118] add rudimentary support for if-then-else expressions --- src/backends/c/removeGhost.ml | 1 + src/backends/c/splCompiler.ml | 6 ++++-- src/frontends/spl/splChecker.ml | 7 +++++++ src/frontends/spl/splLexer.mll | 1 + src/frontends/spl/splParser.mly | 9 +++++++-- src/frontends/spl/splSyntax.ml | 18 +++++++++++++++--- src/frontends/spl/splTranslator.ml | 8 ++++++++ src/frontends/spl/splTypeChecker.ml | 14 +++++++++++--- tests/spl/adt/lists.spl | 23 ++++++++++++----------- 9 files changed, 66 insertions(+), 21 deletions(-) diff --git a/src/backends/c/removeGhost.ml b/src/backends/c/removeGhost.ml index 2f506257..04d828eb 100644 --- a/src/backends/c/removeGhost.ml +++ b/src/backends/c/removeGhost.ml @@ -60,6 +60,7 @@ let removeGhost cu = | New (t, exprs, p) -> New (t, (List.map (process_expr scope) exprs), p) | Read (fld, idx, p) -> Read ((process_expr scope fld), (process_expr scope idx), p) (*TODO ghost fields*) | Write (fld, idx, v, p) -> Write ((process_expr scope fld), (process_expr scope idx), (process_expr scope v), p) (*TODO ghost fields*) + | Ite (cond, t, e, p) -> Ite ((process_expr scope cond), (process_expr scope t), (process_expr scope e), p) | ConstrApp (id, args, p) -> ConstrApp (id, List.map (process_expr scope) args, p) | DestrApp (id, e, p) -> DestrApp (id, process_expr scope e, p) | ProcCall (id, args, p) -> diff --git a/src/backends/c/splCompiler.ml b/src/backends/c/splCompiler.ml index 7a096345..97432114 100644 --- a/src/backends/c/splCompiler.ml +++ b/src/backends/c/splCompiler.ml @@ -199,7 +199,9 @@ let convert oc cu = array_len_field | (UnaryOp (op, e, _), cur_proc) -> pr_un_op ppf (op, (e, cur_proc)) | (BinaryOp (e1, op1, e2, _, _), cur_proc) -> - pr_bin_op ppf ((e1, cur_proc), op1, (e2, cur_proc)) + pr_bin_op ppf ((e1, cur_proc), op1, (e2, cur_proc)) + | (Ite (cond, t, e, _), curr_proc) -> + fprintf ppf "(%a ? %a : %a)" pr_c_expr (cond, curr_proc) pr_c_expr (t, curr_proc) pr_c_expr (e, curr_proc) | (Ident (id, _), {p_returns=p_returns}) -> if ((List.length p_returns) == 1) then fprintf ppf "%s" (c_string_of_ident id) @@ -340,7 +342,7 @@ let convert oc cu = ) | BinaryOp _ -> fprintf ppf "/* ERROR: freeing the result of binary operation will possibly be implemented in the future for freeing Sets. */" | (Null _ | Emp _ | Setenum _ | IntVal _ | BoolVal _ | PredApp _ | Binder _ - | UnaryOp _ | Annot _ | Write _ | ConstrApp _ | DestrApp _) -> + | UnaryOp _ | Annot _ | Write _ | Ite _ | ConstrApp _ | DestrApp _) -> fprintf ppf "/* ERROR: expression cannot be disposed */" in (** Because SPL allows multiple return variables but C does not, yet in diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 656ac5c9..39d6cee9 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -214,6 +214,8 @@ let resolve_names cu = Read (re locals tbl map, re locals tbl idx, pos) | Write (map, idx, upd, pos) -> Write (re locals tbl map, re locals tbl idx, re locals tbl upd, pos) + | Ite (cond, t, e, pos) -> + Ite (re locals tbl cond, re locals tbl t, re locals tbl e, pos) | ConstrApp (id, es, pos) -> ConstrApp (id, List.map (re locals tbl) es, pos) | DestrApp (id, e, pos) -> @@ -535,6 +537,11 @@ let flatten_exprs cu = let idx1, aux2, locals = flatten_expr scope aux1 locals idx in let upd1, aux3, locals = flatten_expr scope aux2 locals upd in Write (map1, idx1, upd1, pos), aux3, locals + | Ite (cond, t, e, pos) -> + let cond1, aux1, locals = flatten_expr scope aux locals cond in + let t1, aux2, locals = flatten_expr scope aux1 locals t in + let e1, aux3, locals = flatten_expr scope aux2 locals e in + Ite (cond1, t1, e1, pos), aux3, locals | ConstrApp (id, args, pos) -> let args1, aux1, locals = flatten_expr_list scope aux locals args in ConstrApp (id, args1, pos), aux1, locals diff --git a/src/frontends/spl/splLexer.mll b/src/frontends/spl/splLexer.mll index 397eefad..308075c1 100644 --- a/src/frontends/spl/splLexer.mll +++ b/src/frontends/spl/splLexer.mll @@ -168,6 +168,7 @@ rule token = parse with Not_found -> IDENT (kw, 0) } +| "?" { QMARK } | "0x" (['A'-'F''a'-'f''0'-'9']+ as num) { INTVAL (hexa_to_int num) } | digits as num { INTVAL (Int64.of_string num) } | "'" (_ as c) "'" { CHARVAL c } diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index bbc7d475..9b792e35 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -53,7 +53,7 @@ type rhs_string_maybe = %token BOOLVAL %token STRINGVAL %token LPAREN RPAREN LBRACE RBRACE LBRACKET RBRACKET -%token COR, CHOOSE COLON COLONEQ COLONCOLON SEMICOLON DOT PIPE +%token COR, CHOOSE COLON COLONEQ COLONCOLON SEMICOLON DOT PIPE QMARK %token UMINUS PLUS MINUS DIV TIMES MOD %token UNION INTER DIFF %token EQ NEQ LEQ GEQ LT GT IN NOTIN AT @@ -789,8 +789,13 @@ iff_expr: | iff_expr IFF iff_expr { BinaryOp ($1, OpEq, $3, BoolType, mk_position 1 3) } ; -annot_expr: +ite_expr: | iff_expr { $1 } +| ite_expr QMARK iff_expr COLON iff_expr { Ite ($1, $3, $5, mk_position 1 5) } +; + +annot_expr: +| ite_expr { $1 } | annot_expr AT LPAREN annot RPAREN { Annot ($1, $4, mk_position 1 5) } diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 2415a564..860e805f 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -166,6 +166,7 @@ and expr = | New of typ * exprs * pos | Read of expr * expr * pos | Write of expr * expr * expr * pos + | Ite of expr * expr * expr * pos | ConstrApp of ident * exprs * pos | DestrApp of ident * expr * pos | ProcCall of ident * exprs * pos @@ -217,6 +218,7 @@ let pos_of_expr = function | New (_, _, p) | Read (_, _, p) | Write (_, _, _, p) + | Ite (_, _, _, p) | ConstrApp (_, _, p) | DestrApp (_, _, p) | Binder (_, _, _, p) @@ -244,7 +246,8 @@ let free_vars e = | Read (e1, e2, _) | BinaryOp (e1, _, e2, _, _) -> fv bv (fv bv acc e1) e2 - | Write (e1, e2, e3, _) -> + | Write (e1, e2, e3, _) + | Ite (e1, e2, e3, _) -> fv bv (fv bv (fv bv acc e1) e2) e3 | Binder (_, vs, e, _) -> let bv, acc = @@ -287,6 +290,8 @@ let subst_id sm = Read (s bv e1, s bv e2, pos) | Write (e1, e2, e3, pos) -> Write (s bv e1, s bv e2, s bv e3, pos) + | Ite (e1, e2, e3, pos) -> + Ite (s bv e1, s bv e2, s bv e3, pos) | BinaryOp (e1, op, e2, ty, pos) -> BinaryOp (s bv e1, op, s bv e2, ty, pos) | Binder (b, vs, e, pos) -> @@ -336,6 +341,8 @@ let subst sm = Read (s bv e1, s bv e2, pos) | Write (e1, e2, e3, pos) -> Write (s bv e1, s bv e2, s bv e3, pos) + | Ite (e1, e2, e3, pos) -> + Ite (s bv e1, s bv e2, s bv e3, pos) | BinaryOp (e1, op, e2, ty, pos) -> BinaryOp (s bv e1, op, s bv e2, ty, pos) | Binder (b, vs, e, pos) -> @@ -489,6 +496,8 @@ let replace_macros prog = Read (repl_expr e1, repl_expr e2, pos) | Write (e1, e2, e3, pos) -> Write (repl_expr e1, repl_expr e2, repl_expr e3, pos) + | Ite (e1, e2, e3, pos) -> + Ite (repl_expr e1, repl_expr e2, repl_expr e3, pos) | ConstrApp (c, es, pos) -> ConstrApp(c, List.map (repl_expr) es, pos) | DestrApp (d, e, pos) -> @@ -632,8 +641,9 @@ let prio_of_expr = function | BinaryOp (_, OpSepIncl, _, _, _) -> 14 | BinaryOp (_, OpOr, _, _, _) -> 15 | BinaryOp (_, OpImpl, _, _, _) -> 16 - | Binder _ -> 17 - | Annot _ -> 18 + | Ite (_, _, _, _) -> 17 + | Binder _ -> 18 + | Annot _ -> 19 let is_left_assoc = function OpImpl -> false | _ -> true @@ -658,6 +668,8 @@ let rec pr_expr ppf = fprintf ppf "%a.%a" pr_expr e2 pr_expr e1 | Write (e1, e2, e3, _) -> fprintf ppf "%a[%a := %a]" pr_expr e1 pr_expr e2 pr_expr e3 + | Ite (e1, e2, e3, _) -> + fprintf ppf "%a ? %a : %a" pr_expr e1 pr_expr e2 pr_expr e3 | DestrApp (id, e, _) -> fprintf ppf "%a.%a" pr_expr e pr_ident id | ConstrApp (id, es, _) diff --git a/src/frontends/spl/splTranslator.ml b/src/frontends/spl/splTranslator.ml index eb986ea7..b12a304d 100644 --- a/src/frontends/spl/splTranslator.ml +++ b/src/frontends/spl/splTranslator.ml @@ -718,6 +718,14 @@ let convert cu = in let v = Ident (v_decl.v_name, v_decl.v_pos) in Binder (Forall, [UnguardedVar v_decl], BinaryOp (BinaryOp (v, OpIn, r, BoolType, pos), OpEq, f, BoolType, pos), pos) + | Annot (Ite (cond, t, e, pos), a, pos2) -> + let body = + BinaryOp (BinaryOp (cond, OpImpl, BinaryOp (r, OpEq, t, BoolType, pos), BoolType, pos), + OpAnd, + BinaryOp (UnaryOp (OpNot, cond, pos), OpImpl, BinaryOp (r, OpEq, e, BoolType, pos), BoolType, pos), + BoolType, pos) + in + Annot (body, a, pos2) | e -> BinaryOp (r, OpEq, e, BoolType, pos_of_expr e)) decl.pr_body diff --git a/src/frontends/spl/splTypeChecker.ml b/src/frontends/spl/splTypeChecker.ml index 7511e453..c34d77a6 100644 --- a/src/frontends/spl/splTypeChecker.ml +++ b/src/frontends/spl/splTypeChecker.ml @@ -127,9 +127,6 @@ let type_of_expr cu locals e = | ArrayType ty -> ty | _ -> AnyType) | Write (map, _, _, _) -> te map - | ConstrApp (id, _, _) | DestrApp (id, _, _) -> - let decl = IdMap.find id cu.fun_decls in - decl.f_res | UnaryOp ((OpOld | OpKnown), e, _) -> te e | UnaryOp (OpLength, map, _) -> IntType | UnaryOp (OpArrayOfCell, c, _) -> @@ -141,6 +138,12 @@ let type_of_expr cu locals e = (match te map with | ArrayType srt -> MapType (IntType, ArrayCellType srt) | _ -> AnyType) + (* Algebraic data types *) + | ConstrApp (id, _, _) | DestrApp (id, _, _) -> + let decl = IdMap.find id cu.fun_decls in + decl.f_res + (* If-then-else *) + | Ite (_, e, _, _) -> te e (* Other stuff *) | Null (ty, _) -> ty | ProcCall (id, _, _) -> @@ -253,6 +256,11 @@ let infer_types cu locals ty e = (* Boolean constants *) | BoolVal (_, pos) as e -> e, match_types pos ty BoolType + (* If-then-else *) + | Ite (e1, e2, e3, pos) -> + let e1, _ = it locals BoolType e1 in + let e2, e3, ty = itp locals ty e2 e3 in + Ite (e1, e2, e3, pos), ty (* Permissions *) | Emp pos as e -> e, match_types pos ty PermType diff --git a/tests/spl/adt/lists.spl b/tests/spl/adt/lists.spl index caea6cd7..3071ae34 100644 --- a/tests/spl/adt/lists.spl +++ b/tests/spl/adt/lists.spl @@ -1,22 +1,23 @@ datatype List = cons(hd: Int, tl: List) | nil; -function append(l1 : List, l2 : List) returns (res : List) - ensures l1 == nil ==> res == l2 - ensures l1 != nil ==> res == cons(l1.hd, append(l1.tl, l2)) +function append(l1 : List, l2 : List) returns (res : List) { + l1 == nil ? l2 : cons(l1.hd, append(l1.tl, l2)) +} -function length(l: List) returns (res: Int) - ensures l == nil ==> res == 0 - ensures l != nil ==> res == 1 + length(l.tl) +function length(l: List) returns (res: Int) { + l == nil ? 0 : 1 + length(l.tl) +} -function reverse(l: List) returns (r: List) - ensures l == nil ==> r == nil - ensures l != nil ==> r == append(reverse(l.tl), cons(l.hd, nil)) +function reverse(l: List) returns (r: List) { + l == nil ? nil : append(reverse(l.tl), cons(l.hd, nil)) +} function reverse_tailrec_helper(l1: List, l2: List, ghost l3: List) returns (res: List) requires append(reverse(l1), l2) == reverse(l3) ensures res == reverse(l3) - ensures l2 == nil ==> res == l1 - ensures l2 != nil ==> res == reverse_tailrec_helper(cons(l2.hd, l1), l2.tl, l3) +{ + l2 == nil ? l1 : reverse_tailrec_helper(cons(l2.hd, l1), l2.tl, l3) +} function reverse_tailrec(l: List) returns (res: List) ensures res == reverse(l) From f12bb85ea3cb16d7f1041597480ca0019d8aa7fb Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 10 Apr 2018 00:55:04 -0400 Subject: [PATCH 014/118] rename function --- src/verifier/grassifier.ml | 4 ++-- src/verifier/verifier.ml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/verifier/grassifier.ml b/src/verifier/grassifier.ml index 19345bfe..8fe5d92b 100644 --- a/src/verifier/grassifier.ml +++ b/src/verifier/grassifier.ml @@ -1402,8 +1402,8 @@ let elim_sl prog = let prog = List.fold_left add_proc prog aux_lemmas in add_ghost_field_invariants prog -(** Annotate safety checks for heap accesses *) -let annotate_heap_checks prog = +(** Annotate safety checks for runtime errors (memory safety, divisiton by zero, etc.) *) +let annotate_runtime_checks prog = let rec checks acc = function | App (Read, map :: idx :: _, _) -> let acc1 = diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 1cbbb52d..aa7d430c 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -25,7 +25,7 @@ let simplify proc prog = dump_if 0 |> info "Inferring accesses, eliminating loops, arrays, new/dispose, and global dependencies.\n" |> elim_arrays |> - annotate_heap_checks |> + annotate_runtime_checks |> elim_new_dispose |> Analyzer.infer_accesses |> Simplifier.prune_uncalled init_procs |> From 02cce5bca655df30aa71fa4246e660c8460d6e02 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 10 Apr 2018 00:55:52 -0400 Subject: [PATCH 015/118] support read over write axioms for arbitrary maps --- src/prover/axioms.ml | 12 +++---- src/prover/reduction.ml | 77 +++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/prover/axioms.ml b/src/prover/axioms.ml index 3b1b0948..cd33f667 100644 --- a/src/prover/axioms.ml +++ b/src/prover/axioms.ml @@ -129,10 +129,10 @@ let isFunVar f = (** Array read over write axioms *) let read_write_axioms fld1 = - let struct_srt, ind_srts, res_srt = + let ind_srts, res_srt = match sort_of fld1 with - | Map (Loc sid :: ind_srts, srt) -> (sid, ind_srts, srt) - | _ -> failwith "expected field in read_write_axioms" + | Map (ind_srts, srt) -> (ind_srts, srt) + | _ -> failwith "expected map in read_write_axioms" in let srt_string = string_of_sort res_srt in let d = fresh_ident "?d" in @@ -140,9 +140,9 @@ let read_write_axioms fld1 = (*let g = fresh_ident "?g" in let g1 = g, Fld res_srt in*) let mk_inds () = List.map (fun srt -> mk_var srt (fresh_ident "?i")) ind_srts in - let loc1 = loc1 struct_srt :: mk_inds () in - let loc2 = loc2 struct_srt :: mk_inds () in - let loc3 = loc3 struct_srt :: mk_inds () in + let loc1 = mk_inds () in + let loc2 = mk_inds () in + let loc3 = mk_inds () in let new_fld1 = mk_write fld1 loc1 dvar in let f x = mk_read fld1 x in let g x = mk_read new_fld1 x in diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 87e4f3ea..8f3019a4 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -24,6 +24,10 @@ let elim_exists = let ve = mk_var srt e in mk_exists [(e, srt)] (smk_or [smk_and [smk_elem ~ann:a ve s1; mk_not (smk_elem ~ann:a ve s2)]; smk_and [smk_elem ~ann:a ve s2; mk_not (smk_elem ~ann:a ve s1)]]) + | Map (dsrts, rsrt) -> + let vs = List.map (fun srt -> fresh_ident "?i", srt) dsrts in + let vts = List.map (fun (v, srt) -> mk_var srt v) vs in + mk_exists vs (mk_neq (mk_read s1 vts) (mk_read s2 vts)) | _ -> f) | BoolOp (Not, [Atom (App (Disjoint, [s1; s2], _), a)]) when bvs = [] -> let srt = element_sort_of_set s1 in @@ -264,7 +268,7 @@ let add_read_write_axioms fs = let field_sorts = TermSet.fold (fun t srts -> match sort_of t with | Loc (ArrayCell _) as srt -> SortSet.add srt srts - | Map (Loc _ :: _, _) as srt -> SortSet.add srt srts + | Map (_ :: _, _) as srt -> SortSet.add srt srts | _ -> srts) gts SortSet.empty in @@ -293,62 +297,75 @@ let add_read_write_axioms fs = let b = Axioms.loc2 (Array srt) in let i = fresh_ident "?i" in let idx = mk_var Int i in - (* a = b, a[i].f -> b[i].f *) + (* a = b, a.f[i] -> b.f[i] *) ([Match (mk_eq_term a b, []); Match (mk_read fld [a; idx], [])], [mk_read fld [b; idx]]) :: + (* a = b, b.cells[i].f -> a.cells[i].f *) ([Match (mk_eq_term a b, []); - (* a = b, b.cells[i].f -> a.cells[i].f *) Match (mk_read fld [mk_read (mk_array_cells b) [idx]], [])], [mk_read fld [mk_read (mk_array_cells a) [idx]]]) :: propagators - | Map (Loc ssrt :: dsrts, srt) as fldsrt -> fun propagators -> + | Map (dsrts, srt) as fldsrt -> fun propagators -> let f1 = fresh_ident "?f", fldsrt in let fld1 = mk_var (snd f1) (fst f1) in let f2 = fresh_ident "?g", fldsrt in let fld2 = mk_var (snd f2) (fst f2) in let d = fresh_ident "?d" in let dvar = mk_var srt d in - let loc1 = Axioms.loc1 ssrt in - let loc2 = Axioms.loc2 ssrt in - let set1 = Axioms.set1 ssrt in - let set2 = Axioms.set2 ssrt in + let ssrt_opt = match dsrts with + | Loc ssrt :: _ -> Some ssrt + | _ -> None + in let ivars1 = List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts in let ivars2 = List.map (fun srt -> mk_var srt (fresh_ident "?j")) dsrts in + let match_ivar1 = + ssrt_opt |> + Opt.map (fun _ -> Match (List.hd ivars1, [FilterNotNull])) |> + Opt.to_list + in + let gen_frame wrap = + ssrt_opt |> + Opt.map (fun ssrt -> + let set1 = Axioms.set1 ssrt in + let set2 = Axioms.set2 ssrt in + [(* Frame (x, a, f, g), y.g -> y.f *) + ([Match (mk_frame_term set1 set2 fld1 fld2, []); + Match (wrap (mk_read fld2 (ivars1)), [])] @ + match_ivar1, + [wrap (mk_read fld1 (ivars1))]); + (* Frame (x, a, f, g), y.f -> y.g *) + ([Match (mk_frame_term set1 set2 fld1 fld2, []); + Match (wrap (mk_read fld1 (ivars1)), [])], + [wrap (mk_read fld2 (ivars1))]) + ]) |> + Opt.get_or_else [] + in let mk_generators wrap = ([Match (mk_eq_term fld1 fld2, []); - Match (wrap (mk_read fld1 (loc1 :: ivars1)), []); + Match (wrap (mk_read fld1 (ivars1)), []); ], - [wrap (mk_read fld2 (loc1 :: ivars1))]) :: + [wrap (mk_read fld2 (ivars1))]) :: ([Match (mk_eq_term fld1 fld2, []); - Match (wrap (mk_read fld1 (loc1 :: ivars1)), []); + Match (wrap (mk_read fld1 (ivars1)), []) ], - [wrap (mk_read fld2 (loc1 :: ivars1))]) :: + [wrap (mk_read fld2 (ivars1))]) :: (* f = g, x.g -> x.f *) ([Match (mk_eq_term fld1 fld2, []); - Match (wrap (mk_read fld2 (loc1 :: ivars1)), []); - Match (loc1, [FilterNotNull])], - [wrap (mk_read fld1 (loc1 :: ivars1))]) :: + Match (wrap (mk_read fld2 (ivars1)), [])] @ match_ivar1, + [wrap (mk_read fld1 (ivars1))]) :: (* f [x := d], y.(f [x := d]) -> y.f *) - ([Match (mk_write fld1 (loc1 :: ivars1) dvar, []); - Match (wrap (mk_read (mk_write fld1 (loc1 :: ivars1) dvar) (loc2 :: ivars2)), []); + ([Match (mk_write fld1 ivars1 dvar, []); + Match (wrap (mk_read (mk_write fld1 ivars1 dvar) ivars2), []); (*Match (loc1, [FilterNotNull]);*) (*Match (loc2, [FilterNotNull])*)], - [wrap (mk_read fld1 (loc2 :: ivars2))]) :: + [wrap (mk_read fld1 ivars2)]) :: (* f [x := d], y.f -> y.(f [x := d]) *) - ([Match (mk_write fld1 (loc1 :: ivars1) dvar, []); - Match (wrap (mk_read fld1 (loc2 :: ivars2)), []); + ([Match (mk_write fld1 ivars1 dvar, []); + Match (wrap (mk_read fld1 (ivars2)), []); (*Match (loc1, [FilterNotNull]);*) (*Match (loc2, [FilterNotNull])*)], - [wrap (mk_read (mk_write fld1 (loc1 :: ivars1) dvar) (loc2 :: ivars2))]) :: - (* Frame (x, a, f, g), y.g -> y.f *) - ([Match (mk_frame_term set1 set2 fld1 fld2, []); - Match (wrap (mk_read fld2 (loc1 :: ivars1)), []); - Match (loc1, [FilterNotNull])], - [wrap (mk_read fld1 (loc1 :: ivars1))]) :: - (* Frame (x, a, f, g), y.f -> y.g *) - [([Match (mk_frame_term set1 set2 fld1 fld2, []); - Match (wrap (mk_read fld1 (loc1 :: ivars1)), [])], - [wrap (mk_read fld2 (loc1 :: ivars1))])] + [wrap (mk_read (mk_write fld1 (ivars1) dvar) (ivars2))]) :: + gen_frame wrap in mk_generators (fun t -> t) (*@ mk_generators (fun t -> mk_known t)*) @ propagators | _ -> fun propagators -> propagators) From c46d2468fe50ba7b3082f81a120ab047bffbd1fb Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 10 Apr 2018 23:09:38 -0400 Subject: [PATCH 016/118] add support for pure predicates and map comprehensions --- src/frontends/spl/splChecker.ml | 41 +++++++++++++++-------- src/frontends/spl/splLexer.mll | 6 ++-- src/frontends/spl/splParser.mly | 8 +++-- src/frontends/spl/splSyntax.ml | 13 +++++--- src/frontends/spl/splTranslator.ml | 51 ++++++++++++++++------------- src/frontends/spl/splTypeChecker.ml | 41 +++++++++++++++++------ src/programs/prog.ml | 8 +++-- src/verifier/analyzer.ml | 6 ++-- src/verifier/grassifier.ml | 3 ++ src/verifier/simplifier.ml | 1 + 10 files changed, 118 insertions(+), 60 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 39d6cee9..783f85af 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -242,7 +242,7 @@ let resolve_names cu = (match args1 with | [arg] -> (match type_of_expr cu locals arg with - | SetType _ -> + | SetType _ | MapType _ -> PredApp (AccessPred, [arg], pos) | ty -> PredApp (AccessPred, [Setenum (resolve_typ types pos tbl ty, [arg], pos)], pos)) @@ -495,7 +495,7 @@ let resolve_names cu = background_theory = bg_theory; } -(** Flatten procedure calls and new expressions in compilation unit [cu].*) +(** Flatten procedure calls, comprehensions, and new expressions in compilation unit [cu].*) let flatten_exprs cu = let decl_aux_var name vtype pos scope locals = let aux_id = GrassUtil.fresh_ident name in @@ -561,18 +561,23 @@ let flatten_exprs cu = let f1, aux, locals = flatten_expr scope aux locals f in (match b with | Exists | Forall -> Binder (b, vars1, f1, pos), aux, locals - | SetComp -> + | Comp -> let v_decl = match vars1 with | [UnguardedVar decl] -> decl | _ -> failwith "unexpected set comprehension" in - let sc_id = GrassUtil.fresh_ident "set_compr" in + let c_id = GrassUtil.fresh_ident "compr" in let fv = IdSet.elements (free_vars e) in let res_id = GrassUtil.fresh_ident "res" in + let res_ty = + match type_of_expr cu locals f1 with + | BoolType -> SetType v_decl.v_type + | rty -> MapType (v_decl.v_type, rty) + in let res_decl = { v_name = res_id; - v_type = SetType v_decl.v_type; + v_type = res_ty; v_ghost = false; v_implicit = false; v_aux = true; @@ -580,30 +585,31 @@ let flatten_exprs cu = v_scope = pos; } in - let sc_locals = + let c_locals = IdMap.add v_decl.v_name v_decl (IdMap.singleton res_id res_decl) in let formals = List.filter (fun id -> IdMap.mem id locals) fv in - let sc_locals = + let c_locals = List.fold_left - (fun sc_locals id -> IdMap.add id (IdMap.find id locals) sc_locals) - sc_locals formals + (fun c_locals id -> IdMap.add id (IdMap.find id locals) c_locals) + c_locals formals in - let sc_decl = - { pr_name = sc_id; + let c_decl = + { pr_name = c_id; pr_formals = formals; pr_outputs = [res_id]; - pr_locals = sc_locals; + pr_locals = c_locals; pr_contracts = []; + pr_is_pure = false; pr_body = Some e; pr_pos = pos; } in let actuals = List.map (fun id -> Ident (id, pos)) formals in - let sc_app = PredApp (Pred sc_id, actuals, pos) in + let c_app = PredApp (Pred c_id, actuals, pos) in let aux_cmds, aux_funs = aux in - sc_app, (aux_cmds, sc_decl :: aux_funs), locals + c_app, (aux_cmds, c_decl :: aux_funs), locals ) | ProcCall (id, args, pos) -> let pdecl = IdMap.find id cu.proc_decls in @@ -820,6 +826,13 @@ let flatten_exprs cu = Some body, aux, locals | None -> None, ([], aux_funs), locals in + (* if auxiliary function comes from top-level expression in body then undo the flattening *) + let body, aux_funs = + match body, aux_funs with + | Some (PredApp (Pred id, _, _) | Annot(PredApp (Pred id, _, _), _, _)), aux_decl :: aux_decls + when id = aux_decl.pr_name -> aux_decl.pr_body, aux_decls + | _ -> body, aux_funs + in match aux_cmds with | cmd :: _ -> illegal_side_effect_error (pos_of_stmt cmd) "function and procedure bodies" diff --git a/src/frontends/spl/splLexer.mll b/src/frontends/spl/splLexer.mll index 308075c1..fcb8cb7f 100644 --- a/src/frontends/spl/splLexer.mll +++ b/src/frontends/spl/splLexer.mll @@ -29,7 +29,7 @@ let _ = ("forall", QUANT(SplSyntax.Forall)); ("exists", QUANT(SplSyntax.Exists)); ("free", FREE); - ("function", FUNCTION); + ("function", FUNCTION false); ("ghost", GHOST); ("havoc", HAVOC); ("if", IF); @@ -47,7 +47,7 @@ let _ = ("or", COR); ("outputs", OUTPUTS); ("pattern", PATTERN); - ("predicate", PREDICATE); + ("predicate", PREDICATE false); ("procedure", PROCEDURE); ("pure", PURE); ("requires", REQUIRES); @@ -161,6 +161,8 @@ rule token = parse | '~' { TILDE } | "<-<" { BSL } | ">->" { BSR } +| "pure" [' ' '\t' '\n'] "function" { FUNCTION true } (* What a hack... *) +| "pure" [' ' '\t' '\n'] "predicate" { PREDICATE true } (* What a hack... *) | ident as name '^' (digitchar+ as num) { IDENT(name, int_of_string num) } | ident as kw { try diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 9b792e35..740a4ade 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -63,7 +63,9 @@ type rhs_string_maybe = %token QUANT %token ASSUME ASSERT SPLIT CALL FREE HAVOC NEW RETURN %token IF ELSE WHILE -%token GHOST IMPLICIT VAR STRUCT PURE LEMMA PROCEDURE PREDICATE FUNCTION INCLUDE AXIOM TYPE +%token FUNCTION +%token PREDICATE +%token GHOST IMPLICIT VAR STRUCT PURE LEMMA PROCEDURE INCLUDE AXIOM TYPE %token DEFINE DATATYPE OUTPUTS RETURNS REQUIRES ENSURES INVARIANT %token LOC INT BOOL BYTE SET MAP ARRAY ARRAYCELL %token MATCHING YIELDS WITHOUT COMMENT PATTERN @@ -243,6 +245,7 @@ pred_decl: pr_outputs = []; pr_locals = locals; pr_contracts = $6; + pr_is_pure = $1; pr_body = $7; pr_pos = mk_position 2 2; } @@ -274,6 +277,7 @@ function_header: pr_outputs = outputs; pr_locals = locals; pr_contracts = contracts; + pr_is_pure = $1; pr_body = None; pr_pos = mk_position 2 2; } @@ -614,7 +618,7 @@ primary_no_set: set_expr: | LBRACE expr_list_opt RBRACE { Setenum (AnyType, $2, mk_position 1 3) } -| LBRACE simple_bound_var COLONCOLON expr RBRACE { Binder (SetComp, [$2], $4, mk_position 1 5) } +| LBRACE simple_bound_var COLONCOLON expr RBRACE { Binder (Comp, [$2], $4, mk_position 1 5) } ; alloc: diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 860e805f..6a88368c 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -69,6 +69,7 @@ and pred = pr_outputs: idents; pr_locals: vars; pr_contracts: contracts; + pr_is_pure: bool; pr_body: expr option; pr_pos: pos; } @@ -199,7 +200,7 @@ and pred_sym = | AccessPred | BtwnPred | DisjointPred | FramePred | ReachPred | Pred of ident and binder_kind = - | Forall | Exists | SetComp + | Forall | Exists | Comp and annotation = | Position @@ -623,7 +624,7 @@ let string_of_pred = function let prio_of_expr = function | Null _ | Emp _ | IntVal _ | BoolVal _ | Ident _ -> 0 | Read _ | Write _ | ProcCall _ | PredApp _ | ConstrApp _ | DestrApp _ | New _ | Setenum _ | - Binder (SetComp, _, _, _) -> 1 + Binder (Comp, _, _, _) -> 1 | UnaryOp ((OpArrayCells | OpIndexOfCell | OpArrayOfCell | OpLength | OpToInt | OpToByte | OpOld | OpKnown), _, _) -> 1 | UnaryOp ((OpUMinus | OpUPlus | OpBvNot | OpNot), _, _) -> 2 @@ -762,7 +763,7 @@ let rec pr_expr ppf = pr_var ppf v in (match b with - | SetComp -> + | Comp -> fprintf ppf "{ @[<2>%a ::@ %a@] }" (Util.pr_list_comma pr_bound_var) vs pr_expr e | _ -> @@ -825,12 +826,14 @@ let pr_pred_decl ppf pred = let pr_header ppf pred = match pred.pr_outputs with | [] -> - fprintf ppf "@[<2>predicate %a(@[<0>%a@])%a@]@ " + fprintf ppf "@[<2>%spredicate %a(@[<0>%a@])%a@]@ " + (if pred.pr_is_pure then "pure " else "") pr_ident pred.pr_name pr_var_list (lookup pred.pr_formals) pr_contracts pred.pr_contracts | _ -> - fprintf ppf "@[<2>function %a(@[<0>%a@])@\nreturns (@[<0>%a@])%a@]@\n" + fprintf ppf "@[<2>%sfunction %a(@[<0>%a@])@\nreturns (@[<0>%a@])%a@]@\n" + (if pred.pr_is_pure then "pure " else "") pr_ident pred.pr_name pr_var_list (lookup pred.pr_formals) pr_var_list (lookup pred.pr_outputs) diff --git a/src/frontends/spl/splTranslator.ml b/src/frontends/spl/splTranslator.ml index b12a304d..e1f33cbb 100644 --- a/src/frontends/spl/splTranslator.ml +++ b/src/frontends/spl/splTranslator.ml @@ -229,7 +229,7 @@ let convert cu = let res_ty = decl.pr_body |> Opt.map (type_of_expr cu decl.pr_locals) |> - Opt.get_or_else PermType + Opt.get_or_else (if decl.pr_is_pure then BoolType else PermType) in let res_srt = convert_type res_ty pos in GrassUtil.mk_free_fun res_srt id ts @@ -338,8 +338,8 @@ let convert cu = let ty = convert_term locals y in let tz = convert_term locals z in GrassUtil.mk_btwn_term tfld tx ty tz - | Binder (SetComp, _, _, pos) -> - failwith ("set comprehension should have been desugared at " ^ string_of_src_pos pos) + | Binder (Comp, _, _, pos) -> + failwith ("comprehension should have been desugared at " ^ string_of_src_pos pos) | Annot (e, Position, pos) -> convert_term locals e | e -> @@ -351,7 +351,7 @@ let convert cu = let mk_guard = match q with | Forall -> GrassUtil.mk_implies | Exists -> fun f g -> GrassUtil.mk_and [f; g] - | SetComp -> failwith "unexpected type" + | Comp -> failwith "unexpected binder" in let mk_quant vs f = let f0, ann = match f with @@ -361,7 +361,7 @@ let convert cu = let f1 = match q with | Forall -> GrassUtil.mk_forall vs f0 | Exists -> GrassUtil.mk_exists vs f0 - | SetComp -> failwith "unexpected type" + | Comp -> failwith "unexpected binder" in GrassUtil.annotate f1 ann in @@ -501,7 +501,7 @@ let convert cu = let ty = decl.v_type in (id, convert_type ty pos) :: vars, IdMap.add id decl locals1 - | GuardedVar _ -> failwith "unexpected Guarded variable" + | GuardedVar _ -> failwith "unexpected guarded variable" ) decls ([], locals) in @@ -513,7 +513,7 @@ let convert cu = let mk_quant = match q with | Forall -> SlUtil.mk_forall | Exists -> SlUtil.mk_exists - | SetComp -> failwith "unexpected type" + | Comp -> failwith "unexpected binder" in let f1 = convert_sl_form locals1 f in let f2 = SlUtil.subst_consts subst f1 in @@ -678,7 +678,7 @@ let convert cu = | [r] -> (IdMap.find r decl.pr_locals).v_type | _ -> decl.pr_body |> Opt.map (type_of_expr cu decl.pr_locals) |> - Opt.get_or_else PermType + Opt.get_or_else (if decl.pr_is_pure then BoolType else PermType) in let opt_body, locals, outputs, contracts = match rtype, decl.pr_outputs with @@ -709,25 +709,30 @@ let convert cu = res_id, IdMap.add res_id rdecl decl.pr_locals in let r = Ident (ret_id, decl.pr_pos) in + let rec desugar_ite r = function + | Annot (e, a, pos2) -> + Annot (desugar_ite r e, a, pos2) + | Ite (cond, t, e, pos) -> + BinaryOp (BinaryOp (cond, OpImpl, BinaryOp (r, OpEq, t, BoolType, pos), BoolType, pos), + OpAnd, + BinaryOp (UnaryOp (OpNot, cond, pos), OpImpl, BinaryOp (r, OpEq, e, BoolType, pos), BoolType, pos), + BoolType, pos) + | e -> BinaryOp (r, OpEq, e, BoolType, pos_of_expr e) + in let opt_body = Opt.map (function - | Binder (SetComp, vs, f, pos) -> + | Binder (Comp, vs, e, pos) -> let v_decl = match vs with | [UnguardedVar decl] -> decl - | _ -> failwith "unexpected set comprehension" + | _ -> failwith "unexpected comprehension" in let v = Ident (v_decl.v_name, v_decl.v_pos) in - Binder (Forall, [UnguardedVar v_decl], BinaryOp (BinaryOp (v, OpIn, r, BoolType, pos), OpEq, f, BoolType, pos), pos) - | Annot (Ite (cond, t, e, pos), a, pos2) -> - let body = - BinaryOp (BinaryOp (cond, OpImpl, BinaryOp (r, OpEq, t, BoolType, pos), BoolType, pos), - OpAnd, - BinaryOp (UnaryOp (OpNot, cond, pos), OpImpl, BinaryOp (r, OpEq, e, BoolType, pos), BoolType, pos), - BoolType, pos) + let rv = match rtype with + | SetType _ -> BinaryOp (v, OpIn, r, BoolType, pos) + | _ -> Read (r, v, pos) in - Annot (body, a, pos2) - | e -> - BinaryOp (r, OpEq, e, BoolType, pos_of_expr e)) + Binder (Forall, [UnguardedVar v_decl], desugar_ite rv e, pos) + | e -> desugar_ite r e) decl.pr_body in let contracts = match rtype with @@ -747,8 +752,8 @@ let convert cu = Opt.map pos_of_expr decl.pr_body |> Opt.get_or_else decl.pr_pos in let footprint_sorts = match decl.pr_body with - | None -> struct_sorts - | Some _ -> SortSet.empty + | None when not decl.pr_is_pure -> struct_sorts + | _ -> SortSet.empty in let contract = { contr_name = id; @@ -758,6 +763,7 @@ let convert cu = contr_locals = locals; contr_precond = pre; contr_postcond = post; + contr_is_pure = decl.pr_is_pure; contr_pos = decl.pr_pos; } in @@ -789,6 +795,7 @@ let convert cu = contr_footprint_sorts = SortSet.empty; contr_returns = decl.p_returns; contr_locals = IdMap.map convert_var_decl decl.p_locals; + contr_is_pure = false; contr_precond = pre; contr_postcond = post; contr_pos = decl.p_pos; diff --git a/src/frontends/spl/splTypeChecker.ml b/src/frontends/spl/splTypeChecker.ml index c34d77a6..eaa49f66 100644 --- a/src/frontends/spl/splTypeChecker.ml +++ b/src/frontends/spl/splTypeChecker.ml @@ -21,6 +21,8 @@ let match_types pos oty1 oty2 = ArrayType (mt ty1 ty2) | ArrayCellType ty1, ArrayCellType ty2 -> ArrayCellType (mt ty1 ty2) + | MapType (ty1, (BoolType | AnyType)), SetType ty2 + | SetType ty1, MapType (ty2, (BoolType | AnyType)) | SetType ty1, SetType ty2 -> SetType (mt ty1 ty2) | MapType (dty1, rty1), MapType (dty2, rty2) -> @@ -99,11 +101,16 @@ let type_of_expr cu locals e = | BinaryOp (e, OpUn, _, _, _) | BinaryOp (e, OpInt, _, _, _) -> te e - | Binder (SetComp, [v], _, _) -> - (match v with - | GuardedVar (_, e) -> SetType (te e) - | UnguardedVar v -> SetType (v.v_type)) - | Binder (SetComp, _, _, _) -> failwith "invalid set comprehension" + | Binder (Comp, [v], e, _) -> + let dty = + match v with + | GuardedVar (_, e1) -> te e1 + | UnguardedVar v -> v.v_type + in + (match te e with + | BoolType -> SetType dty + | rty -> MapType (dty, rty)) + | Binder (Comp, _, _, _) -> failwith "invalid comprehension" | Setenum (ty, _, _) -> SetType ty (* Permission types *) @@ -156,7 +163,7 @@ let type_of_expr cu locals e = | PredApp (Pred id, _, _) -> let decl = IdMap.find id cu.pred_decls in (match decl.pr_outputs with - | [] -> decl.pr_body |> Opt.map te |> Opt.get_or_else PermType + | [] -> decl.pr_body |> Opt.map te |> Opt.get_or_else (if decl.pr_is_pure then BoolType else PermType) | [rid] -> let rdecl = IdMap.find rid decl.pr_locals in rdecl.v_type @@ -320,9 +327,23 @@ let infer_types cu locals ty e = match b with | Exists | Forall -> it locals1 ty f - | SetComp -> - fst (it locals1 BoolType f), - match_types pos ty (SetType (Opt.get vty_opt)) + | Comp -> + match ty with + | SetType _ -> + fst (it locals1 BoolType f), + match_types pos ty (SetType (Opt.get vty_opt)) + | _ -> + let ty = match_types pos ty (MapType (Opt.get vty_opt, AnyType)) in + let dty, rty = match ty with + | MapType (dty, rty) -> dty, rty + | _ -> failwith "impossible" + in + let f1, rty = it locals1 rty f in + let ty1 = match rty with + | BoolType -> SetType dty + | _ -> MapType (dty, rty) + in + f1, ty1 in Binder (b, decls1, f1, pos), ty (* Reference and array types *) @@ -539,7 +560,7 @@ let infer_types cu locals ty e = let body_ty = decl.pr_body |> Opt.map (type_of_expr cu locals) |> - Opt.get_or_else PermType + Opt.get_or_else (if decl.pr_is_pure then BoolType else PermType) in match_types pos ty body_ty in diff --git a/src/programs/prog.ml b/src/programs/prog.ml index 97fcfb29..367e916a 100644 --- a/src/programs/prog.ml +++ b/src/programs/prog.ml @@ -133,6 +133,7 @@ type contract = { contr_precond: spec list; (** precondition *) contr_postcond: spec list; (** postcondition *) contr_footprint_sorts: SortSet.t; (** footprint sorts *) + contr_is_pure: bool; (** has no state dependencies *) contr_pos: source_position; (** position of declaration *) } @@ -230,6 +231,7 @@ let trivial_contract name = contr_pos = dummy_position; contr_precond = []; contr_postcond = []; + contr_is_pure = false; contr_footprint_sorts = SortSet.empty; } @@ -1243,12 +1245,14 @@ let pr_pred ppf pred = let pr_header ppf pred = match returns_of_pred pred with | [] -> - fprintf ppf "@\n@[<2>predicate %a(@[<0>%a@])%a@]@ " + fprintf ppf "@\n@[<2>%spredicate %a(@[<0>%a@])%a@]@ " + (if pred.pred_contract.contr_is_pure then "pure " else "") pr_ident (name_of_pred pred) pr_id_srt_list (add_srts (formals_of_pred pred)) pr_contract pred.pred_contract | _ -> - fprintf ppf "@\n@[<2>function %a(@[<0>%a@])@\nreturns (@[<0>%a@])%a@]@\n" + fprintf ppf "@\n@[<2>%sfunction %a(@[<0>%a@])@\nreturns (@[<0>%a@])%a@]@\n" + (if pred.pred_contract.contr_is_pure then "pure " else "") pr_ident (name_of_pred pred) pr_id_srt_list (add_srts (formals_of_pred pred)) pr_id_srt_list (add_srts (returns_of_pred pred)) diff --git a/src/verifier/analyzer.ml b/src/verifier/analyzer.ml index 75f9b032..1f1eff4a 100644 --- a/src/verifier/analyzer.ml +++ b/src/verifier/analyzer.ml @@ -107,17 +107,17 @@ let infer_accesses prog = (Opt.to_list pred.pred_body @ pred.pred_contract.contr_precond @ pred.pred_contract.contr_postcond) in let fps = SortSet.union fps (footprint_sorts_pred pred) in - (* missing body? assume worst case *) + (* missing body and not pure? assume worst case *) let accs = match pred.pred_body with - | None -> + | None when not pred.pred_contract.contr_is_pure -> IdMap.fold (fun id decl accs -> match decl.var_sort with | Map (Loc _ :: _, _) -> IdSet.add id accs | _ -> accs) prog.prog_vars accs - | Some _ -> accs + | _ -> accs in let global_accs = IdSet.filter diff --git a/src/verifier/grassifier.ml b/src/verifier/grassifier.ml index 8fe5d92b..fddf4f79 100644 --- a/src/verifier/grassifier.ml +++ b/src/verifier/grassifier.ml @@ -96,6 +96,7 @@ let add_ghost_field_invariants prog = contr_locals = IdMap.add decl.var_name decl locals; contr_precond = []; contr_postcond = []; + contr_is_pure = false; contr_footprint_sorts = SortSet.empty; contr_pos = decl.var_pos; } @@ -1193,6 +1194,7 @@ let elim_sl prog = contr_locals = fp_func_locals; contr_precond = []; contr_postcond = []; + contr_is_pure = false; contr_footprint_sorts = SortSet.empty; contr_pos = pos; } @@ -1230,6 +1232,7 @@ let elim_sl prog = contr_precond = check_precond_specs; contr_postcond = [check_postcond_spec]; contr_footprint_sorts = SortSet.empty; + contr_is_pure = false; contr_pos = pos; } in diff --git a/src/verifier/simplifier.ml b/src/verifier/simplifier.ml index 92b18bf6..78a0c259 100644 --- a/src/verifier/simplifier.ml +++ b/src/verifier/simplifier.ml @@ -178,6 +178,7 @@ let elim_loops (prog : program) = subst_id_spec subst_returns |> map_terms_spec subst_old) postcond; + contr_is_pure = false; contr_pos = pp.pp_pos; } in From 896ffc239e884affdcaa7451d3d91be040e485c3 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 10 Apr 2018 23:14:05 -0400 Subject: [PATCH 017/118] array utils --- tests/spl/include/ordered_type.spl | 9 ++ tests/spl/sorted_array_util/array_util.spl | 168 +++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 tests/spl/include/ordered_type.spl create mode 100644 tests/spl/sorted_array_util/array_util.spl diff --git a/tests/spl/include/ordered_type.spl b/tests/spl/include/ordered_type.spl new file mode 100644 index 00000000..5d8acfab --- /dev/null +++ b/tests/spl/include/ordered_type.spl @@ -0,0 +1,9 @@ +// An ordered type +type K + +pure predicate lt(x: K, y: K) + +axiom forall a: K :: !lt(a, a); +axiom forall a: K, b: K :: !lt(a, b) || !lt(b, a); +axiom forall a: K, b: K :: lt(a, b) || lt(b, a) || a == b; +axiom forall a: K, b: K, c: K :: lt(a, b) && lt(b, c) ==> lt(a, c); diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl new file mode 100644 index 00000000..b4daaf42 --- /dev/null +++ b/tests/spl/sorted_array_util/array_util.spl @@ -0,0 +1,168 @@ +include "../include/ordered_type.spl" + +// project map segment m[i..j] to set of its elements +function set_of_map(m: Map, i: Int, j: Int) + returns (res: Set) + requires 0 <= i <= j +{ + i < j ? {m[i]} ++ set_of_map(m, i + 1, j) : {} +} + +lemma extend_right(m: Map, i: Int, j: Int) + requires i <= j + pure ensures set_of_map(m, i, j) ++ {m[j]} == set_of_map(m, i, j + 1) +{ + if (i == j) { + } else { + extend_right(m, i + 1, j); + } +} + +// +lemma in_set_of_map(m: Map, i: Int, j: Int) + requires 0 <= i <= j + ensures forall k: Int :: i <= k < j ==> m[k] in set_of_map(m, i, j) +{ + if (i == j) { + } else { + in_set_of_map(m, i + 1, j); + } +} + +lemma set_of_map_split(m: Map, i: Int, j: Int, k: Int) + requires i <= j <= k + ensures set_of_map(m, i, k) == set_of_map(m, i, j) ++ set_of_map(m, j, k) +{ + if (j == k) { + } else { + extend_right(m, i, j); + set_of_map_split(m, i, j + 1, k); + } +} + +lemma frame_set_of_map(m: Map, i: Int, j: Int) + requires i <= j + ensures forall i1: Int, k: K :: i1 < i || j <= i1 ==> set_of_map(m, i, j) == set_of_map(m[i1 := k], i, j) +{ + if (i == j) { + } else { + frame_set_of_map(m, i + 1, j); + } +} + +lemma not_in_sorted_seg(a: Array, i: Int, len: Int, k: K) + requires acc(a) &*& sorted_array_seg(a, i, len) + requires 0 <= i <= len + requires i < len ==> lt(k, a[i]) + ensures acc(a) &*& sorted_array_seg(a, i, len) + ensures k !in set_of_array(a, i, len) +{ + if (i == len) { + } else { + not_in_sorted_seg(a, i + 1, len, k); + } +} + +function map_of_array(a: Array) returns (res: Map) + requires acc(a) +{ + { i: Int :: a[i] @(matching res[i] yields a[i]) @(matching a[i] yields res[i]) } +} + + +predicate sorted_array_seg(a: Array, i: Int, j: Int) + requires acc(a) +{ + 0 <= i <= j <= a.length &*& + (forall i1: Int, i2: Int :: i <= i1 < i2 < j ==> lt(a[i1], a[i2])) +} + +function set_of_array(a: Array, i: Int, j: Int) returns (res: Set) + requires acc(a) &*& 0 <= i <= j <= a.length +{ + set_of_map(map_of_array(a), i, j) +} + +predicate array_seg_with_content(a: Array, i: Int, j: Int, C: Set) + requires acc(a) &*& 0 <= i <= j <= a.length +{ + sorted_array_seg(a, i, j) &*& C == set_of_map(map_of_array(a), i, j) +} + +predicate array_with_content(a: Array, len: Int, C: Set) +{ + acc(a) &*& array_seg_with_content(a, 0, len, C) +} + +procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) + returns (new_len: Int) + requires array_with_content(a, len, C) + requires len < a.length + ensures array_with_content(a, new_len, C ++ {k}) + ensures k in C ==> new_len == len + ensures k !in C ==> new_len == len + 1 +{ + var i := 0; + + // find position for insertion + while (i < len && lt(a[i], k)) + invariant array_with_content(a, len, C) + invariant 0 <= i <= len < a.length + invariant 0 < i < len ==> lt(a[i - 1], a[i]) + invariant 0 < i ==> lt(a[i - 1], k) + invariant k !in set_of_array(a, 0, i) + { + extend_right(map_of_array(a), 0, i); + i := i + 1; + } + + // k already in C? + if (i < len && a[i] == k) { + var ghost m := map_of_array(a); + in_set_of_map(m, 0, len); + return len; + } + + // prove k !in C + not_in_sorted_seg(a, i, len, k); + set_of_map_split(map_of_array(a), 0, i, len); + + // shift array entries a[j..len] by 1 entry to the right + var j := len; + + while (j > i) + invariant acc(a) &*& sorted_array_seg(a, 0, j) &*& sorted_array_seg(a, j + 1, len + 1) + invariant 0 <= i <= j <= len + invariant i < len ==> lt(k, a[i]) + invariant 0 < i ==> lt(a[i - 1], k) + invariant i <= j < len ==> a[j] == a[j + 1] + invariant 0 < j < len ==> lt(a[j - 1], a[j]) + invariant C == set_of_array(a, 0, j) ++ set_of_array(a, j + 1, len + 1) + { + var ghost m := map_of_array(a); + + var tmp := a[j - 1]; + a[j] := tmp; + + // prove C == set_of_array(a, 0, j - 1) ++ set_of_array(a, j, len + 1) + pure assert map_of_array(a) == m[j := tmp]; + frame_set_of_map(m, 0, j - 1); + frame_set_of_map(m, j + 1, len + 1); + extend_right(m, 0, j - 1); + + j := j - 1; + } + + var ghost m := map_of_array(a); + + a[i] := k; + + // prove C == set_of_array(a, 0, i) ++ {k} ++ set_of_array(a, i + 1, len + 1) + pure assert map_of_array(a) == m[i := k]; + frame_set_of_map(m, 0, i); + frame_set_of_map(m, i + 1, len + 1); + set_of_map_split(map_of_array(a), 0, i, len + 1); + + return len + 1; +} + From 6568878b4d79cf9d2b3ba9d63d8f91226ad7bf52 Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 11 Apr 2018 02:00:12 -0400 Subject: [PATCH 018/118] cosmetics --- tests/spl/simple_arrays/copy.spl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/spl/simple_arrays/copy.spl b/tests/spl/simple_arrays/copy.spl index eedd0cd0..cc368ead 100644 --- a/tests/spl/simple_arrays/copy.spl +++ b/tests/spl/simple_arrays/copy.spl @@ -5,16 +5,16 @@ procedure copy(a: Array) requires array(a) ensures array(a) &*& array(b) ensures a.length == b.length - ensures forall i: Int :: 0 <= i && i < a.length ==> a[i] == b[i] + ensures forall i: Int :: 0 <= i < a.length ==> a[i] == b[i] { b := new Array(a.length); var i := 0; while (i < a.length) - invariant 0 <= i && i <= a.length && a.length == b.length + invariant 0 <= i <= a.length && a.length == b.length invariant array(a) &*& array(b) - invariant forall j: Int :: 0 <= j && j < i ==> a[j] == b[j] + invariant forall j: Int :: 0 <= j < i ==> a[j] == b[j] { b[i] := a[i]; i := i + 1; From 77c434b6c995392f5bb8ccff84da776fcbd74050 Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 11 Apr 2018 02:00:34 -0400 Subject: [PATCH 019/118] use binary search in insert --- tests/spl/sorted_array_util/array_util.spl | 119 ++++++++++++++------- 1 file changed, 82 insertions(+), 37 deletions(-) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index b4daaf42..58c35c53 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -18,12 +18,11 @@ lemma extend_right(m: Map, i: Int, j: Int) } } -// lemma in_set_of_map(m: Map, i: Int, j: Int) requires 0 <= i <= j ensures forall k: Int :: i <= k < j ==> m[k] in set_of_map(m, i, j) { - if (i == j) { + if (i >= j) { } else { in_set_of_map(m, i + 1, j); } @@ -50,16 +49,22 @@ lemma frame_set_of_map(m: Map, i: Int, j: Int) } } -lemma not_in_sorted_seg(a: Array, i: Int, len: Int, k: K) - requires acc(a) &*& sorted_array_seg(a, i, len) - requires 0 <= i <= len - requires i < len ==> lt(k, a[i]) - ensures acc(a) &*& sorted_array_seg(a, i, len) - ensures k !in set_of_array(a, i, len) +lemma not_in_sorted_seg(a: Array, i: Int, j: Int, k: K) + requires acc(a) &*& sorted_array_seg(a, i, j) + ensures acc(a) &*& sorted_array_seg(a, i, j) + ensures i >= j || lt(k, a[i]) || lt(a[j-1], k) ==> k !in set_of_array(a, i, j) { - if (i == len) { - } else { - not_in_sorted_seg(a, i + 1, len, k); + if (i >= j) return; + + if (lt(a[j - 1], k)) { + extend_right(map_of_array(a), i, j - 1); + not_in_sorted_seg(a, i, j - 1, k); + return; + } + + if (lt(k, a[i])) { + not_in_sorted_seg(a, i + 1, j, k); + return; } } @@ -73,27 +78,83 @@ function map_of_array(a: Array) returns (res: Map) predicate sorted_array_seg(a: Array, i: Int, j: Int) requires acc(a) { - 0 <= i <= j <= a.length &*& + 0 <= i && j <= a.length &*& (forall i1: Int, i2: Int :: i <= i1 < i2 < j ==> lt(a[i1], a[i2])) } function set_of_array(a: Array, i: Int, j: Int) returns (res: Set) - requires acc(a) &*& 0 <= i <= j <= a.length + requires acc(a) &*& 0 <= i && j <= a.length { set_of_map(map_of_array(a), i, j) } predicate array_seg_with_content(a: Array, i: Int, j: Int, C: Set) - requires acc(a) &*& 0 <= i <= j <= a.length + requires acc(a) { - sorted_array_seg(a, i, j) &*& C == set_of_map(map_of_array(a), i, j) + sorted_array_seg(a, i, j) &*& C == set_of_array(a, i, j) } predicate array_with_content(a: Array, len: Int, C: Set) { + 0 <= len &*& acc(a) &*& array_seg_with_content(a, 0, len, C) } - + + +procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) + returns (found: Bool, idx: Int) + requires array_with_content(a, len, C) + requires 0 <= len <= a.length + ensures array_with_content(a, len, C) + // what we actually care about + ensures 0 <= idx <= len + ensures found == (k in C) + ensures found ==> a[idx] == k + ensures !found ==> idx == len || lt(k, a[idx]) + ensures 0 < idx ==> lt(a[idx - 1], k) + ensures k !in set_of_array(a, 0, idx) + ensures k !in set_of_array(a, idx + 1, len) +{ + var lo := 0; + var hi := len; + + while (hi != lo) + invariant array_with_content(a, len, C) + // what we actually care about + invariant 0 <= lo <= hi <= len <= a.length + invariant hi == len || a[lo] == k || lt(k, a[hi]) + invariant 0 < lo ==> lt(a[lo - 1], k) + invariant hi < len - 1 ==> lt(k, a[hi + 1]) + { + var m := (hi + lo) / 2; + var cmp: Int; + cmp := compare(k, a[m]); + if (cmp < 0) { + hi := m; // look in first half + } else if (cmp > 0) { + lo := m+1; // look in second half + } else { + // found it + hi := m; + lo := m; + } + } + + idx := lo; + if (idx == len || lt(k, a[lo])) { + found := false; + } else { + found := true; + } + // prove k !in set_of_array(a, 0, idx); + not_in_sorted_seg(a, 0, idx, k); + // prove k !in set_of_array(a, idx + 1, len); + not_in_sorted_seg(a, idx + 1, len, k); + // prove found == (k in C) + set_of_map_split(map_of_array(a), 0, idx, len); +} + + procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) returns (new_len: Int) requires array_with_content(a, len, C) @@ -102,31 +163,15 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) ensures k in C ==> new_len == len ensures k !in C ==> new_len == len + 1 { - var i := 0; - // find position for insertion - while (i < len && lt(a[i], k)) - invariant array_with_content(a, len, C) - invariant 0 <= i <= len < a.length - invariant 0 < i < len ==> lt(a[i - 1], a[i]) - invariant 0 < i ==> lt(a[i - 1], k) - invariant k !in set_of_array(a, 0, i) - { - extend_right(map_of_array(a), 0, i); - i := i + 1; - } - + var i: Int, found: Bool; + found, i := find(a, len, k); + // k already in C? - if (i < len && a[i] == k) { - var ghost m := map_of_array(a); - in_set_of_map(m, 0, len); + if (found) { return len; } - // prove k !in C - not_in_sorted_seg(a, i, len, k); - set_of_map_split(map_of_array(a), 0, i, len); - // shift array entries a[j..len] by 1 entry to the right var j := len; @@ -163,6 +208,6 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) frame_set_of_map(m, i + 1, len + 1); set_of_map_split(map_of_array(a), 0, i, len + 1); - return len + 1; + return len + 1; } From dc13a86f833648a61aba31a42415ef11963878dc Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 11 Apr 2018 02:04:20 -0400 Subject: [PATCH 020/118] use binary search in insert --- tests/spl/include/ordered_type.spl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/spl/include/ordered_type.spl b/tests/spl/include/ordered_type.spl index 5d8acfab..dcc69bc7 100644 --- a/tests/spl/include/ordered_type.spl +++ b/tests/spl/include/ordered_type.spl @@ -7,3 +7,8 @@ axiom forall a: K :: !lt(a, a); axiom forall a: K, b: K :: !lt(a, b) || !lt(b, a); axiom forall a: K, b: K :: lt(a, b) || lt(b, a) || a == b; axiom forall a: K, b: K, c: K :: lt(a, b) && lt(b, c) ==> lt(a, c); +pure function compare(x: K, y: K) returns (res: Int) + ensures (x == y) == (res == 0) + ensures lt(x, y) == (res < 0) + ensures lt(y, x) == (res > 0) + From 5b5e57cfc6523a8bb69e34ae6f428460cb6be796 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 13 Apr 2018 13:54:06 -0400 Subject: [PATCH 021/118] array utils --- tests/spl/include/ordered_type.spl | 13 +- tests/spl/sorted_array_util/array_util.spl | 200 +++++++++++++++------ 2 files changed, 154 insertions(+), 59 deletions(-) diff --git a/tests/spl/include/ordered_type.spl b/tests/spl/include/ordered_type.spl index dcc69bc7..e0b2fa3b 100644 --- a/tests/spl/include/ordered_type.spl +++ b/tests/spl/include/ordered_type.spl @@ -3,12 +3,17 @@ type K pure predicate lt(x: K, y: K) -axiom forall a: K :: !lt(a, a); -axiom forall a: K, b: K :: !lt(a, b) || !lt(b, a); -axiom forall a: K, b: K :: lt(a, b) || lt(b, a) || a == b; -axiom forall a: K, b: K, c: K :: lt(a, b) && lt(b, c) ==> lt(a, c); +axiom forall a: K :: !lt(a, a) +axiom forall a: K, b: K :: !lt(a, b) || !lt(b, a) +axiom forall a: K, b: K :: lt(a, b) || lt(b, a) || a == b +axiom forall a: K, b: K, c: K :: lt(a, b) && lt(b, c) ==> lt(a, c) pure function compare(x: K, y: K) returns (res: Int) ensures (x == y) == (res == 0) ensures lt(x, y) == (res < 0) ensures lt(y, x) == (res > 0) +var bottom: K; +var top: K; + +axiom forall a: K :: !lt(a, bottom) +axiom forall a: K :: !lt(top, a) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index 58c35c53..86f44aea 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -39,6 +39,17 @@ lemma set_of_map_split(m: Map, i: Int, j: Int, k: Int) } } +lemma set_of_map_equal(m1: Map, m2: Map, i1: Int, i2: Int, len: Int) + requires forall j: Int :: i1 <= j < i1 + len ==> m1[j] == m2[j + (i2 - i1)] @(matching m1[j] yields m2[j + (i2 - i1)]) + ensures set_of_map(m1, i1, i1 + len) == set_of_map(m2, i2, i2 + len) +{ + if (len <= 0) { + } else { + assume len > 0; + set_of_map_equal(m1, m2, i1 + 1, i2 + 1, len - 1); + } +} + lemma frame_set_of_map(m: Map, i: Int, j: Int) requires i <= j ensures forall i1: Int, k: K :: i1 < i || j <= i1 ==> set_of_map(m, i, j) == set_of_map(m[i1 := k], i, j) @@ -49,37 +60,52 @@ lemma frame_set_of_map(m: Map, i: Int, j: Int) } } -lemma not_in_sorted_seg(a: Array, i: Int, j: Int, k: K) - requires acc(a) &*& sorted_array_seg(a, i, j) - ensures acc(a) &*& sorted_array_seg(a, i, j) - ensures i >= j || lt(k, a[i]) || lt(a[j-1], k) ==> k !in set_of_array(a, i, j) +lemma not_in_sorted_seg(m: Map, i: Int, j: Int, k: K, implicit ghost C: Set) + requires sorted_map_seg(m, i, j) && C == set_of_map(m, i, j) + ensures i >= j || lt(k, m[i]) || lt(m[j-1], k) ==> k !in C { if (i >= j) return; - if (lt(a[j - 1], k)) { - extend_right(map_of_array(a), i, j - 1); - not_in_sorted_seg(a, i, j - 1, k); + if (lt(m[j - 1], k)) { + extend_right(m, i, j - 1); + not_in_sorted_seg(m, i, j - 1, k); return; } - if (lt(k, a[i])) { - not_in_sorted_seg(a, i + 1, j, k); + if (lt(k, m[i])) { + not_in_sorted_seg(m, i + 1, j, k); return; } + } +function shift_map(m: Map, src: Int, dst: Int, len: Int) + returns (res: Map) + requires 0 <= len +{ + { i: Int :: i < dst || dst + len <= i ? m[i] : m[src + (i - dst)] + @(matching res[i] yields m[src + (i - dst)]) + @(matching res[i] yields m[i])} +} + + function map_of_array(a: Array) returns (res: Map) requires acc(a) { - { i: Int :: a[i] @(matching res[i] yields a[i]) @(matching a[i] yields res[i]) } + { i: Int :: 0 <= i < a.length ? a[i] : bottom + @(matching res[i] yields a[i]) + @(matching a[i] yields res[i]) } } +predicate sorted_map_seg(m: Map, i: Int, j: Int) +{ + forall i1: Int, i2: Int :: i <= i1 < i2 < j ==> lt(m[i1], m[i2]) +} predicate sorted_array_seg(a: Array, i: Int, j: Int) requires acc(a) { - 0 <= i && j <= a.length &*& - (forall i1: Int, i2: Int :: i <= i1 < i2 < j ==> lt(a[i1], a[i2])) + 0 <= i && j <= a.length && sorted_map_seg(map_of_array(a), i, j) } function set_of_array(a: Array, i: Int, j: Int) returns (res: Set) @@ -88,24 +114,70 @@ function set_of_array(a: Array, i: Int, j: Int) returns (res: Set) set_of_map(map_of_array(a), i, j) } -predicate array_seg_with_content(a: Array, i: Int, j: Int, C: Set) +predicate sorted_array_seg_with_content(a: Array, i: Int, j: Int, C: Set) requires acc(a) { - sorted_array_seg(a, i, j) &*& C == set_of_array(a, i, j) + + sorted_array_seg(a, i, j) && C == set_of_array(a, i, j) } -predicate array_with_content(a: Array, len: Int, C: Set) +predicate sorted_array_with_content(a: Array, len: Int, C: Set) { 0 <= len &*& - acc(a) &*& array_seg_with_content(a, 0, len, C) + acc(a) &*& sorted_array_seg_with_content(a, 0, len, C) } +procedure shift(a: Array, src: Int, dst: Int, len: Int) + requires acc(a) + requires 0 <= src <= src + len <= a.length && 0 <= dst <= dst + len <= a.length + ensures acc(a) + ensures map_of_array(a) == shift_map(old(map_of_array(a)), src, dst, len) +{ + var ghost m := map_of_array(a); + + if (src < dst) { + var i := len - 1; + + while (i >= 0) + invariant acc(a) + invariant src < dst + invariant 0 <= src <= src + len <= a.length + invariant 0 <= dst <= dst + len <= a.length + invariant -1 <= i < len + invariant shift_map(m, src + i + 1, dst + i + 1, len - i - 1) == map_of_array(a) + { + var ghost m1 := map_of_array(a); + var tmp := a[src + i]; + a[dst + i] := tmp; + pure assert map_of_array(a) == m1[dst + i := tmp]; + i := i - 1; + } + } else if (src > dst) { + var i := 0; + while (i < len) + invariant acc(a) + invariant src > dst + invariant 0 <= src <= src + len <= a.length + invariant 0 <= dst <= dst + len <= a.length + invariant 0 <= i <= len + invariant shift_map(m, src, dst, i) == map_of_array(a) + { + var ghost m1 := map_of_array(a); + var tmp := a[src + i]; + a[dst + i] := a[src + i]; + pure assert map_of_array(a) == m1[dst + i := tmp]; + i := i + 1; + } + } +} + +// Find key `k` in sorted array segment `a[0..len]` using binary search procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) returns (found: Bool, idx: Int) - requires array_with_content(a, len, C) + requires sorted_array_with_content(a, len, C) requires 0 <= len <= a.length - ensures array_with_content(a, len, C) + ensures sorted_array_with_content(a, len, C) // what we actually care about ensures 0 <= idx <= len ensures found == (k in C) @@ -119,7 +191,7 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) var hi := len; while (hi != lo) - invariant array_with_content(a, len, C) + invariant sorted_array_with_content(a, len, C) // what we actually care about invariant 0 <= lo <= hi <= len <= a.length invariant hi == len || a[lo] == k || lt(k, a[hi]) @@ -141,25 +213,29 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) } idx := lo; + if (idx == len || lt(k, a[lo])) { found := false; } else { found := true; } + // prove k !in set_of_array(a, 0, idx); - not_in_sorted_seg(a, 0, idx, k); + not_in_sorted_seg(map_of_array(a), 0, idx, k); // prove k !in set_of_array(a, idx + 1, len); - not_in_sorted_seg(a, idx + 1, len, k); + not_in_sorted_seg(map_of_array(a), idx + 1, len, k); // prove found == (k in C) set_of_map_split(map_of_array(a), 0, idx, len); } - +// Given a sorted array segment `a[0..len]`, +// insert `k` into `a[0..len+1]` while preserving sortedness. +// If `k` is already contained in `a[0..len]`, then do not modify `a`. procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) returns (new_len: Int) - requires array_with_content(a, len, C) + requires sorted_array_with_content(a, len, C) requires len < a.length - ensures array_with_content(a, new_len, C ++ {k}) + ensures sorted_array_with_content(a, new_len, C ++ {k}) ensures k in C ==> new_len == len ensures k !in C ==> new_len == len + 1 { @@ -168,46 +244,60 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) found, i := find(a, len, k); // k already in C? - if (found) { - return len; - } - - // shift array entries a[j..len] by 1 entry to the right - var j := len; - - while (j > i) - invariant acc(a) &*& sorted_array_seg(a, 0, j) &*& sorted_array_seg(a, j + 1, len + 1) - invariant 0 <= i <= j <= len - invariant i < len ==> lt(k, a[i]) - invariant 0 < i ==> lt(a[i - 1], k) - invariant i <= j < len ==> a[j] == a[j + 1] - invariant 0 < j < len ==> lt(a[j - 1], a[j]) - invariant C == set_of_array(a, 0, j) ++ set_of_array(a, j + 1, len + 1) - { - var ghost m := map_of_array(a); + if (found) return len; - var tmp := a[j - 1]; - a[j] := tmp; - - // prove C == set_of_array(a, 0, j - 1) ++ set_of_array(a, j, len + 1) - pure assert map_of_array(a) == m[j := tmp]; - frame_set_of_map(m, 0, j - 1); - frame_set_of_map(m, j + 1, len + 1); - extend_right(m, 0, j - 1); + // shift array entries a[i..len] by 1 entry to the right + var ghost m := map_of_array(a); + shift(a, i, i + 1, len - i); - j := j - 1; - } + // prove C == set_of_array(a, 0, i) ++ set_of_array(a, i + 1, len + 1); + set_of_map_split(m, 0, i, len); + set_of_map_equal(m, map_of_array(a), 0, 0, i); + set_of_map_equal(m, map_of_array(a), i, i + 1, len - i); - var ghost m := map_of_array(a); + var ghost m1 := map_of_array(a); a[i] := k; // prove C == set_of_array(a, 0, i) ++ {k} ++ set_of_array(a, i + 1, len + 1) - pure assert map_of_array(a) == m[i := k]; - frame_set_of_map(m, 0, i); - frame_set_of_map(m, i + 1, len + 1); + pure assert map_of_array(a) == m1[i := k]; + frame_set_of_map(m1, 0, i); + frame_set_of_map(m1, i + 1, len + 1); set_of_map_split(map_of_array(a), 0, i, len + 1); return len + 1; } +procedure delete(a: Array, k: K, len: Int, implicit ghost C: Set) + returns (new_len: Int) + requires sorted_array_with_content(a, len, C) + ensures sorted_array_with_content(a, new_len, C -- {k}) + ensures k !in C ==> new_len == len + ensures k in C ==> new_len == len - 1 +{ + // find position for insertion + var i: Int, found: Bool; + found, i := find(a, len, k); + + // k !in C? + if (!found) return len; + + // shift array entries a[i+1..len] by 1 entry to the left + var ghost m := map_of_array(a); + shift(a, i + 1, i, len - (i + 1)); + + // prove C -- {k} == set_of_array(a, 0, i - 1) ++ set_of_array(a, i - 1, len); + if (i > 0) { + set_of_map_split(m, 0, i, len); + } + + set_of_map_equal(m, map_of_array(a), 0, 0, i); + set_of_map_equal(m, map_of_array(a), i + 1, i, len - (i + 1)); + + not_in_sorted_seg(m, 0, i - 1, k); + not_in_sorted_seg(m, i + 1, len, k); + + set_of_map_split(map_of_array(a), 0, i, len - 1); + + return len - 1; +} From 70846dcc98cb9661c3f63cec558f18891e73e0bb Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 13 Apr 2018 15:36:42 -0400 Subject: [PATCH 022/118] do not drop annotations when skolemizing negated map equalities --- src/prover/reduction.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 8f3019a4..29cdb801 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -27,7 +27,7 @@ let elim_exists = | Map (dsrts, rsrt) -> let vs = List.map (fun srt -> fresh_ident "?i", srt) dsrts in let vts = List.map (fun (v, srt) -> mk_var srt v) vs in - mk_exists vs (mk_neq (mk_read s1 vts) (mk_read s2 vts)) + mk_exists vs (annotate (mk_neq (mk_read s1 vts) (mk_read s2 vts)) a) | _ -> f) | BoolOp (Not, [Atom (App (Disjoint, [s1; s2], _), a)]) when bvs = [] -> let srt = element_sort_of_set s1 in From 0b65ac6fd4e02eb61eea2065c18ef5df0575871e Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 13 Apr 2018 15:37:03 -0400 Subject: [PATCH 023/118] array util --- tests/spl/sorted_array_util/array_util.spl | 63 ++++++++++++++++------ 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index 86f44aea..fc361874 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -12,8 +12,7 @@ lemma extend_right(m: Map, i: Int, j: Int) requires i <= j pure ensures set_of_map(m, i, j) ++ {m[j]} == set_of_map(m, i, j + 1) { - if (i == j) { - } else { + if (i < j) { extend_right(m, i + 1, j); } } @@ -22,8 +21,7 @@ lemma in_set_of_map(m: Map, i: Int, j: Int) requires 0 <= i <= j ensures forall k: Int :: i <= k < j ==> m[k] in set_of_map(m, i, j) { - if (i >= j) { - } else { + if (i < j) { in_set_of_map(m, i + 1, j); } } @@ -32,8 +30,7 @@ lemma set_of_map_split(m: Map, i: Int, j: Int, k: Int) requires i <= j <= k ensures set_of_map(m, i, k) == set_of_map(m, i, j) ++ set_of_map(m, j, k) { - if (j == k) { - } else { + if (j < k) { extend_right(m, i, j); set_of_map_split(m, i, j + 1, k); } @@ -43,9 +40,7 @@ lemma set_of_map_equal(m1: Map, m2: Map, i1: Int, i2: Int, len: requires forall j: Int :: i1 <= j < i1 + len ==> m1[j] == m2[j + (i2 - i1)] @(matching m1[j] yields m2[j + (i2 - i1)]) ensures set_of_map(m1, i1, i1 + len) == set_of_map(m2, i2, i2 + len) { - if (len <= 0) { - } else { - assume len > 0; + if (0 < len) { set_of_map_equal(m1, m2, i1 + 1, i2 + 1, len - 1); } } @@ -54,8 +49,7 @@ lemma frame_set_of_map(m: Map, i: Int, j: Int) requires i <= j ensures forall i1: Int, k: K :: i1 < i || j <= i1 ==> set_of_map(m, i, j) == set_of_map(m[i1 := k], i, j) { - if (i == j) { - } else { + if (i < j) { frame_set_of_map(m, i + 1, j); } } @@ -85,9 +79,15 @@ function shift_map(m: Map, src: Int, dst: Int, len: Int) { { i: Int :: i < dst || dst + len <= i ? m[i] : m[src + (i - dst)] @(matching res[i] yields m[src + (i - dst)]) - @(matching res[i] yields m[i])} + @(matching res[i] yields m[i]) } } +function copy_map(m1: Map, m2: Map, src: Int, dst: Int, len: Int) returns (res: Map) +{ + { i: Int :: dst <= i < dst + len ? m2[src + (i - dst)] : m1[i] + @(matching res[i] yields m2[src + (i - dst)]) + @(matching res[i] yields m1[i]) } +} function map_of_array(a: Array) returns (res: Map) requires acc(a) @@ -128,6 +128,7 @@ predicate sorted_array_with_content(a: Array, len: Int, C: Set) } +// Shift a[src..src+len] to a[dst..dst+len] procedure shift(a: Array, src: Int, dst: Int, len: Int) requires acc(a) requires 0 <= src <= src + len <= a.length && 0 <= dst <= dst + len <= a.length @@ -172,6 +173,35 @@ procedure shift(a: Array, src: Int, dst: Int, len: Int) } } +// Copy a[src..src+len] to b[dst..dst+len] +procedure copy(a: Array, b: Array, src: Int, dst: Int, len: Int) + requires acc(a) &*& acc(b) + requires 0 <= src <= src + len <= a.length + requires 0 <= dst <= dst + len <= b.length + ensures acc(a) &*& acc(b) + ensures map_of_array(a) == old(map_of_array(a)) + ensures map_of_array(b) == copy_map(old(map_of_array(b)), map_of_array(a), src, dst, len) +{ + var ghost mb := map_of_array(b); + var ghost ma := map_of_array(a); + var i := 0; + + while (i < len) + invariant acc(a) &*& acc(b) + invariant 0 <= i <= len + invariant 0 <= src <= src + len <= a.length + invariant 0 <= dst <= dst + len <= b.length + invariant map_of_array(a) == ma + invariant map_of_array(b) == copy_map(mb, ma, src, dst, i) + { + var ghost m1 := map_of_array(b); + var tmp := a[src + i]; + b[dst + i] := tmp; + pure assert map_of_array(b) == m1[dst + i := tmp]; + i := i + 1; + } +} + // Find key `k` in sorted array segment `a[0..len]` using binary search procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) returns (found: Bool, idx: Int) @@ -220,9 +250,9 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) found := true; } - // prove k !in set_of_array(a, 0, idx); + // prove k !in set_of_array(a, 0, idx) not_in_sorted_seg(map_of_array(a), 0, idx, k); - // prove k !in set_of_array(a, idx + 1, len); + // prove k !in set_of_array(a, idx + 1, len) not_in_sorted_seg(map_of_array(a), idx + 1, len, k); // prove found == (k in C) set_of_map_split(map_of_array(a), 0, idx, len); @@ -268,6 +298,9 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) return len + 1; } +// Given a sorted array segment `a[0..len]`, +// delete `k` from the segment while preserving sortedness. +// If `k` is already contained in `a[0..len]`, then do not modify `a`. procedure delete(a: Array, k: K, len: Int, implicit ghost C: Set) returns (new_len: Int) requires sorted_array_with_content(a, len, C) @@ -286,7 +319,7 @@ procedure delete(a: Array, k: K, len: Int, implicit ghost C: Set) var ghost m := map_of_array(a); shift(a, i + 1, i, len - (i + 1)); - // prove C -- {k} == set_of_array(a, 0, i - 1) ++ set_of_array(a, i - 1, len); + // prove C -- {k} == set_of_array(a, 0, i - 1) ++ set_of_array(a, i - 1, len) if (i > 0) { set_of_map_split(m, 0, i, len); } From 2ac4ec211c7ae8b262c76e93c4a6fe590aba045f Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Thu, 12 Apr 2018 18:02:01 -0400 Subject: [PATCH 024/118] grass: make pretty printer consistent with input syntax --- src/formulas/grass.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index acd2f566..206cc87b 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -368,8 +368,8 @@ let rec pr_term ppf = function | App (Inter, [], _) -> fprintf ppf "Univ" | App (sym, [], _) -> fprintf ppf "%a" pr_sym sym | App (Read, [map; t], _) -> - (match sort_of t with - | Loc _ -> fprintf ppf "%a.%a" pr_term t pr_term map + (match map, sort_of t with + | App (FreeSym _, [], _), Loc _ -> fprintf ppf "%a.%a" pr_term t pr_term map | _ -> fprintf ppf "%a[%a]" pr_term map pr_term t) | App (Read, map :: t :: ts, _) -> fprintf ppf "%a[%a].%a" pr_term t pr_term_list ts pr_term map @@ -390,6 +390,7 @@ let rec pr_term ppf = function | App (Length, [t], _) -> fprintf ppf "%a.%s" pr_term t (string_of_symbol Length) | App (ArrayCells, [t], _) -> fprintf ppf "%a.%s" pr_term t (string_of_symbol ArrayCells) | App (SetEnum, ts, _) -> fprintf ppf "{@[%a@]}" pr_term_list ts + | App (Destructor d, [t], _) -> fprintf ppf "%a.%a" pr_term t pr_ident d | App (sym, ts, _) -> fprintf ppf "%a(@[%a@])" pr_sym sym pr_term_list ts and pr_term_paran ppf t = fprintf ppf "(%a)" pr_term t From 5dc0dd7a284b9e95cacae052223e2d568a820725 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Sat, 14 Apr 2018 12:24:40 -0400 Subject: [PATCH 025/118] add a model REPL (use -model-repl flag) --- src/backends/smtlib/smtLibSolver.ml | 8 ++- src/frontends/spl/splParser.mly | 2 + src/main/config.ml | 6 +- src/main/grasshopper.ml | 12 ++-- src/prover/model.ml | 91 ++++++++++++++++++++++++++- src/prover/modelPrinting.ml | 56 ++--------------- src/prover/modelRepl.ml | 96 +++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 62 deletions(-) create mode 100644 src/prover/modelRepl.ml diff --git a/src/backends/smtlib/smtLibSolver.ml b/src/backends/smtlib/smtLibSolver.ml index be777ad6..4a3ca4f9 100644 --- a/src/backends/smtlib/smtLibSolver.ml +++ b/src/backends/smtlib/smtLibSolver.ml @@ -1308,7 +1308,13 @@ let convert_model session smtModel = | _ -> model) model0 smtModel in - Model.finalize_values model1 + let model2 = + match session.signature with + | Some signature -> + { model1 with sign = signature } + | None -> failwith "convert_model: expected session to have a signature" + in + (Model.finalize_values model2) let rec get_model session = let gm state = diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 740a4ade..a54e3521 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -96,6 +96,8 @@ type rhs_string_maybe = %start main %type main +%start expr +%type expr %% main: diff --git a/src/main/config.ml b/src/main/config.ml index 9743e4c6..c246fb11 100644 --- a/src/main/config.ml +++ b/src/main/config.ml @@ -12,7 +12,10 @@ let model_file = ref "" (* Display the edges going to null in the model *) let model_null_edge = ref false -(* File name where counterexample trace is saved. *) +(** Enable model REPL after failing VC? *) +let model_repl = ref false + +(** File name where counterexample trace is saved. *) let trace_file = ref "" (* Flags controlling the axioms generation *) @@ -82,6 +85,7 @@ let cmd_options = ("-stats", Arg.Set print_stats, " Print statistics"); ("-lint", Arg.Set flycheck_mode, " Print single line error messages for on-the-fly checking"); ("-model", Arg.Set_string model_file, " Produce counterexample model for the first failing verification condition"); + ("-model-repl", Arg.Set model_repl, "Enter interactive mode to query the counterexample model for the first failing verification condition"); ("-nulledges", Arg.Set model_null_edge, " Show the edges going to null in the model"); ("-trace", Arg.Set_string trace_file, " Produce counterexample trace for the first failing verification condition"); ("-dumpghp", Arg.Set_int dump_ghp, " Print intermediate program after specified simplification stage (num=0,1,2,3)"); diff --git a/src/main/grasshopper.ml b/src/main/grasshopper.ml index c6d7fcbb..7423f6f8 100644 --- a/src/main/grasshopper.ml +++ b/src/main/grasshopper.ml @@ -127,12 +127,12 @@ let check_spl_program spl_prog proc = (fun first (pp, error_msg, model) -> output_trace simple_prog proc (pp, model); let _ = - if !Config.robust - then begin - (if not first then print_newline ()); - ProgError.print_error pp error_msg - end - else ProgError.error pp error_msg + if !Config.robust || !Config.model_repl + then ((if not first then print_newline ()); ProgError.print_error pp error_msg); + if !Config.model_repl then ModelRepl.repl model; + if not !Config.robust + then ProgError.error pp error_msg + else () in false ) diff --git a/src/prover/model.ml b/src/prover/model.ml index 57a2d8d0..a0589f40 100644 --- a/src/prover/model.ml +++ b/src/prover/model.ml @@ -40,7 +40,6 @@ module SortedValueListMap = type t = (value * sort) list let compare = compare end) - module SortedValueSet = Set.Make(struct @@ -78,6 +77,21 @@ let rec string_of_value = function (List.map string_of_value vs |> String.concat ", ") +let string_of_sorted_value srt v = + let rec elim_loc = function + | Loc srt -> elim_loc srt + | Set srt -> Set (elim_loc srt) + | Array srt -> Array (elim_loc srt) + | ArrayCell srt -> ArrayCell (elim_loc srt) + | Map (dsrts, rsrt) -> Map (List.map elim_loc dsrts, elim_loc rsrt) + | srt -> srt + in + match srt with + | Int | Bool | Adt _ -> string_of_value v + | _ -> + let srt_str = string_of_sort (elim_loc srt) in + srt_str ^ "!" ^ string_of_value v + type interpretation = (value ValueListMap.t * ext_value) SortedSymbolMap.t type ext_interpretation = ext_value SortedValueMap.t @@ -86,12 +100,14 @@ type model = { mutable card: int SortMap.t; mutable intp: interpretation; mutable vals: ext_interpretation; + sign: arity list SymbolMap.t; } let empty : model = { card = SortMap.empty; intp = SortedSymbolMap.empty; - vals = SortedValueMap.empty + vals = SortedValueMap.empty; + sign = SymbolMap.empty; } (*let get_arity model sym = SymbolMap.find sym m*) @@ -337,6 +353,27 @@ let rec eval model = function and interp_symbol model sym arity args = match sym with | Constructor id -> ADT (id, args) + | Destructor id -> + (match args with + | [ADT (cons, args)] -> + (* Find the defn of constructor cons *) + let adt_defs = + (match arity with + | ([Adt(_, adt_defs)], _) -> adt_defs + | _ -> failwith "Destructor has unexpected sort"); + in + let cons_def = + find_map (fun (a, adt_def) -> + find_map (fun (id, def) -> + if id = cons then Some def else None) adt_def) + adt_defs + |> Opt.lazy_get_or_else (fun () -> failwith "Couldn't find constructor") + in + (* Look for the position corresponding to destructor id *) + List.combine cons_def args + |> List.find (fun ((des_id, _), v) -> des_id = id) + |> snd + | _ -> failwith "Destructor applied to non-ADT args") | _ -> SortedSymbolMap.find_opt (sym, arity) model.intp |> Opt.map (fun (m, d) -> fun_app model (MapVal (m, d)) args) |> @@ -857,3 +894,53 @@ let finalize_values model = SortSet.iter generate_fields (get_map_sorts model); model + +(** Utils to pretty print sets and maps *) + +let rec pr_sorted_value model ppf (term, srt) = + try + (match srt with + | Set s -> + let cnt = find_set_value model term s in + pr_set model s ppf cnt + | Map (arg_s, res_s) -> + let map_val, def_val = find_map_value model term arg_s res_s in + pr_map model (arg_s, res_s) ppf (map_val, def_val) + | _ -> + Format.fprintf ppf "%s" (string_of_sorted_value srt term)) + with Undefined -> + Format.fprintf ppf "%s" (string_of_sorted_value srt term) + +and pr_sorted_value_list model ppf svals = + pr_list_comma (pr_sorted_value model) ppf svals + +and pr_sorted_ext_value model srt ppf = function + | BaseVal v -> pr_sorted_value model ppf (v, srt) + | MapVal (m, d) -> + (match srt with + | Map (s1, s2) -> pr_map model (s1, s2) ppf (m, d) + | _ -> failwith "Got map value with non-map sort") + | SetVal v -> + (match srt with + | Set srt -> pr_set model srt ppf v + | _ -> failwith "Got set value with non-set sort") + | TermVal _ + | FormVal _ + | Undef -> Format.fprintf ppf "Undef" + +and pr_map model (arg_s, res_s) ppf (map, def_val) = + let pr_map_elem ppf (args, v) = + Format.fprintf ppf "%a: %a" + (pr_sorted_value_list model) (List.combine args arg_s) + (pr_sorted_value model) (v, res_s) + in + Format.fprintf ppf "{@[%a@]}(__default: %a)" + (pr_list_comma pr_map_elem) (ValueListMap.bindings map) + (pr_sorted_ext_value model res_s) def_val + +and pr_set model srt ppf vs = + Format.fprintf ppf "{@[%a@]}" (pr_sorted_value_list model) + (ValueSet.elements vs |> List.map (fun v -> (v, srt))) + +let string_of_eval model srt v = + string_of_format (pr_sorted_value model) (v, srt) \ No newline at end of file diff --git a/src/prover/modelPrinting.ml b/src/prover/modelPrinting.ml index c5c3acff..45d0411d 100644 --- a/src/prover/modelPrinting.ml +++ b/src/prover/modelPrinting.ml @@ -4,21 +4,6 @@ open Grass open GrassUtil open Model -let string_of_sorted_value srt v = - let rec elim_loc = function - | Loc srt -> elim_loc srt - | Set srt -> Set (elim_loc srt) - | Array srt -> Array (elim_loc srt) - | ArrayCell srt -> ArrayCell (elim_loc srt) - | Map (dsrts, rsrt) -> Map (List.map elim_loc dsrts, elim_loc rsrt) - | srt -> srt - in - match srt with - | Int | Bool | Adt _ -> string_of_value v - | _ -> - let srt_str = string_of_sort (elim_loc srt) in - srt_str ^ "!" ^ string_of_value v - let replace_lt_gt str = Str.global_replace (Str.regexp "<") "<" (Str.global_replace (Str.regexp ">") ">" str) @@ -481,40 +466,6 @@ let print_graph output chan model terms = let ep_colors = Util.fold_left2 (fun acc fld color -> (fld, color)::acc) [] all_flds colors2 in let get_color fsrt fld = try List.assoc (fsrt, fld) fld_colors with Not_found -> "black" in let out_tbl = output.table chan in - (* Utils to pretty print sets and maps *) - let rec pr_ext_val ppf ext_val = - match ext_val with - | BaseVal v -> Format.fprintf ppf "%s" (string_of_value v) - | MapVal (m, d) -> Format.fprintf ppf "Map" - | SetVal s -> Format.fprintf ppf "Set" - | TermVal (vs, t) -> Grass.pr_term ppf t - | FormVal (vs, t) -> Format.fprintf ppf "Form" - | Undef -> Format.fprintf ppf "Undef" - and pr_sorted_value ppf (term, srt) = - try - (match srt with - | Set s -> - let cnt = find_set_value model term s in - Format.fprintf ppf "{@[%a@]}" pr_sorted_value_list - (ValueSet.elements cnt |> List.map (fun v -> (v, s))); - | Map (arg_s, res_s) -> - let map_val, def_val = find_map_value model term arg_s res_s in - Format.fprintf ppf "%a" (pr_map (arg_s, res_s)) (map_val, def_val) - | _ -> - Format.fprintf ppf "%s" (string_of_sorted_value srt term)) - with Undefined -> - Format.fprintf ppf "%s" (string_of_sorted_value srt term) - and pr_sorted_value_list ppf svals = - pr_list_comma pr_sorted_value ppf svals - and pr_map (arg_s, res_s) ppf (map, def_val) = - let pr_map_elem ppf (args, v) = - Format.fprintf ppf "%a: %a" pr_sorted_value_list (List.combine args arg_s) - pr_sorted_value (v, res_s) - in - Format.fprintf ppf "{@[%a@]}(__default: %a)" (pr_list_comma pr_map_elem) - (ValueListMap.bindings map) pr_ext_val def_val - in - let str_of_t srt term = string_of_format pr_sorted_value (term, srt) in let output_constants () = let sorts = SortSet.filter @@ -557,7 +508,8 @@ let print_graph output chan model terms = Adt (id, adt_defs) else FreeSrt id in - Printf.sprintf "%s: %s" (string_of_ident destr) (string_of_format pr_sorted_value (v, convert srt)) + Printf.sprintf "%s: %s" (string_of_ident destr) + (string_of_eval model (convert srt) v) in Printf.sprintf "%s(%s)" (string_of_ident cons) (List.map str_one_arg (List.combine args cons_def) |> String.concat ", ") @@ -624,7 +576,7 @@ let print_graph output chan model terms = | App (FreeSym _, _, Set srt1) as set_t when srt = srt1 -> (try let set = eval model set_t in - let set_rep = str_of_t (Set srt1) set in + let set_rep = string_of_eval model (Set srt1) set in ((string_of_term set_t), set_rep) :: acc with Failure _ | Undefined -> acc) | _ -> acc) @@ -648,7 +600,7 @@ let print_graph output chan model terms = let rows = let row_of_func (func, arity) = let m, d = SortedSymbolMap.find (func, arity) model.intp in - let val_str = string_of_format (pr_map arity) (m, d) in + let val_str = string_of_format (pr_map model arity) (m, d) in (string_of_symbol func, val_str) in SortedSymbolSet.fold (fun func acc -> (row_of_func func) :: acc) funs [] diff --git a/src/prover/modelRepl.ml b/src/prover/modelRepl.ml new file mode 100644 index 00000000..8fd8fb6e --- /dev/null +++ b/src/prover/modelRepl.ml @@ -0,0 +1,96 @@ +(** {5 Model read-eval-print-loop} *) + +open Util +open Grass +open GrassUtil +open SplSyntax +open Model +open Printf + + +exception Unsupported of string +let fail str = raise (Unsupported str) + + +let rec term_of_expr model e = + let get_sign sym = match SymbolMap.find_opt sym model.sign with + | Some [arity] -> arity + | None | Some [] -> + fail (sprintf "Couldn't find %s in signature" (string_of_symbol sym)) + | Some arities -> + fail (sprintf "Symbol %s has multiple arities: %s" + (string_of_symbol sym) + (arities |> List.map string_of_arity |> String.concat ", ")) + in + + match e with + | IntVal (i, _) -> mk_int64 i + | BoolVal (b, _) -> mk_bool_term b + | Ident (id, _) -> + let sym = FreeSym id in + (match get_sign sym with + | ([], srt) -> mk_const srt sym + | arity -> + fail (sprintf "Got Ident %s with non-const sort %s" (string_of_ident id) + (string_of_arity arity))) + | Read (Ident (id, _), t, _) when SymbolMap.mem (Destructor id) model.sign -> + let t = term_of_expr model t in + (match get_sign (Destructor id) with + | ([_], res_s) -> + App (Destructor id, [t], res_s) + | arity -> + fail (sprintf "Symbol %s has arity %s but was applied to %s: %s" + (string_of_ident id) (string_of_arity arity) + (string_of_term t) (sort_of t |> string_of_sort))) + | Read (map, t, _) -> + let map = term_of_expr model map in + let t = term_of_expr model t in + mk_read map [t] + | ProcCall (id, ts, _) + | PredApp (Pred id, ts, _) -> + let ts = List.map (term_of_expr model ) ts in + let sym = FreeSym id in + let (arg_srts, res_srt) = get_sign sym in + mk_app res_srt sym ts + | e -> fail ("TODO: can't yet handle: " ^ string_of_expr e) + +let repl model = + printf "\nModel:\nintp:\n"; + SortedSymbolMap.iter (fun (sym, ar) (m, d) -> + printf " %s: %s = {%s} | def: ??\n" (string_of_symbol sym) (string_of_arity ar) + (ValueListMap.bindings m + |> List.map (fun (vs, v) -> + (List.map string_of_value vs |> String.concat ", ") ^ " : " ^ (string_of_value v)) + |> String.concat ", ") + ) model.intp; + (* printf "vals:\n"; + SortedValueMap.iter (fun (v, s) ev -> + printf " %s: %s = ??\n" (string_of_value v) (string_of_sort s)) model.vals; *) + + print_endline "\nModel REPL. Press Ctrl-D to quit."; + let rec repl_loop () = + try + print_string " > " ; flush stdout; + let lexbuf = Lexing.from_string (read_line ()) in + let e = SplParser.expr SplLexer.token lexbuf in + let t = term_of_expr model e in + let v = Model.eval model t in + print_endline @@ string_of_eval model (sort_of t) v; + repl_loop () + with + | Parsing.Parse_error -> + print_string "Syntax error.\n"; + Parsing.clear_parser (); + repl_loop () + | Unsupported str + | Failure str -> + print_string @@ "Error: " ^ str ^ "\n"; + Parsing.clear_parser (); + repl_loop () + | Undefined -> + print_string "Model: Undefined.\n"; + Parsing.clear_parser (); + repl_loop () + | End_of_file -> print_endline "\n"; + in + repl_loop () From e9360c7cef0e4d0bccd5154070d2df859f76acdb Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 14 Apr 2018 15:47:53 -0400 Subject: [PATCH 026/118] fix bug in pretty printer --- src/formulas/grass.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index 206cc87b..da38d08e 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -453,7 +453,7 @@ let rec pr_form ppf = function then pr_form_paran ppf f2 else pr_form ppf f2 | f2, fs -> - pr_form ppf (BoolOp (And, f2 :: fs)) + pr_form ppf (BoolOp (bop, f2 :: fs)) in fprintf ppf "@[<2>%a@] %s@ %a" pr_f1 f1 (string_of_bop bop) pr_f2 (f2, fs) | BoolOp (Not, [f]) -> pr_not ppf f From 70762207bf958f995874dc21efc900f86fec53aa Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 14 Apr 2018 20:14:16 -0400 Subject: [PATCH 027/118] fix indentation function in spl-mode --- LICENSE | 34 +++++++ emacs-mode/spl-mode.el | 214 +++++++++++++++++++++++------------------ 2 files changed, 153 insertions(+), 95 deletions(-) diff --git a/LICENSE b/LICENSE index 1da2b4cd..efcdc0c2 100644 --- a/LICENSE +++ b/LICENSE @@ -141,3 +141,37 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Andrea Leofreddi. + +---- + +GRASShopper's Emacs major mode integrates a modified version of the +line indentation function from the Dafny major mode provided by +boogie-friends. See file emacs-mode/spl-mode.el. + +Its copyright: + +Copyright (C) 2015 Clément Pit--Claudel +Author: Clément Pit--Claudel +URL: https://github.com/boogie-org/boogie-friends/ + +Keywords: convenience, languages + +This file is not part of GNU Emacs. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/emacs-mode/spl-mode.el b/emacs-mode/spl-mode.el index 3a5638d8..8702ae99 100644 --- a/emacs-mode/spl-mode.el +++ b/emacs-mode/spl-mode.el @@ -1,10 +1,10 @@ ;;; spl-mode.el -- Emacs mode for GRASShopper programs. -;; Copyright (c) 2013-2016 Thomas Wies > +;; Copyright (c) 2013-2018 Thomas Wies > ;; ;; Author: Thomas Wies ;; URL: http://cs.nyu.edu/wies/software/grasshopper -;; Version: 0.4 +;; Version: 0.5 ;;; Commentary: @@ -35,39 +35,62 @@ )) +(defconst spl-defuns '("define" "function" "predicate" "lemma" "procedure" "struct" "type" "datatype" + "pure function" "pure predicate" "include" "var")) + +(defconst spl-specifiers '("axiom" "ensures" "free" "invariant" "requires" "pure" "assert" "assume" "split" "returns")) + +(defconst spl-modifiers '("implicit" "ghost")) + +(defconst spl-constants '("emp" "null" "true" "false")) + +(defconst spl-builtins '("import" "yields" "exists" "forall" "Btwn" "Reach" "Disjoint" "Frame" "in" "old" "subsetof")) + +(defconst spl-keywords '("havoc" "free" "choose" "else" "if" "new" "return" "while" "matching" "yields" "without" "pattern" "known")) + +;(defconst dafny-all-keywords (cl-loop for source in '(dafny-defuns dafny-specifiers dafny-modifiers +; dafny-builtins dafny-keywords dafny-types) +; append (mapcar (lambda (kwd) (propertize kwd 'source source)) (symbol-value source)))) + +(defconst spl-defuns-regexp (regexp-opt spl-defuns 'symbols)) +(defconst spl-specifiers-regexp (regexp-opt spl-specifiers 'symbols)) +(defconst spl-modifiers-regexp (regexp-opt spl-modifiers 'symbols)) +(defconst spl-constants-regexp (regexp-opt spl-constants 'symbols)) +(defconst spl-builtins-regexp (regexp-opt spl-builtins 'symbols)) +(defconst spl-keywords-regexp (regexp-opt spl-keywords 'symbols)) + +(defconst spl-block-heads '("else" "if" "while")) +(defconst spl-extended-block-head-regexp (concat "\\s-*" (regexp-opt (append spl-block-heads spl-defuns) 'symbols))) +(defconst spl-extended-defun-regexp (concat "\\s-*" spl-defuns-regexp)) + + (defconst spl-mode-font-lock-keywords (list '("\\(//[^\n]*\\)" 1 font-lock-comment-face) - '("\\<\\(i\\(f\\|nclude\\)\\|c\\(hoose\\|omment\\)\\|define\\|else\\|f\\(ree\\|unction\\)\\|havoc\\|lemma\\|matching\\|new\\|or\\|p\\(attern\\|r\\(ocedure\\|edicate\\)\\)\\|return\\(s\\|\\)\\|struct\\|\\(data\\)?type\\|var\\|w\\(ithout\\|hile\\)\\|yields\\)\\>" - 1 font-lock-keyword-face) - - '("\\<\\(a\\(xiom\\|ss\\(ert\\|ume\\)\\)\\|ensures\\|i\\(mplicit\\|nvariant\\)\\|pure\\|requires\\|ghost\\|footprint\\|split\\)\\>" - 1 font-lock-keyword-face) - - '("\\<\\(Btwn\\|Disjoint\\|exists\\|forall\\|Frame\\|in\\|old\\|Reach\\|subsetof\\|&\\|!\\||\\|*\\|-\\|=\\|:\\|+\\)\\>" - 1 font-lock-builtin-face) - - '("\\<\\(emp\\|false\\|null\\|true\\)\\>" - 1 font-lock-constant-face) + (list spl-defuns-regexp + '(1 font-lock-keyword-face)) - '("\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*[ \t]*\\>\\)(" 1 - font-lock-function-name-face) + (list spl-specifiers-regexp + '(1 font-lock-keyword-face)) - '("[^:]:[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)" 1 - font-lock-type-face) + (list spl-modifiers-regexp + '(1 font-lock-keyword-face)) - '("<[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)[ \t]*>" 1 - font-lock-type-face) + (list spl-keywords-regexp + '(1 font-lock-keyword-face)) - '("<[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)[ \t]*<" 1 - font-lock-type-face) + (list spl-builtins-regexp + '(1 font-lock-builtin-face)) - '("new[ \t]+\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)" 1 - font-lock-type-face) + (list spl-constants-regexp + '(1 font-lock-constant-face)) + + '("\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)[ \t]*(" 1 + font-lock-function-name-face) - '("\\(struct\\|type\\)[ \t]+\\(\\<[a-zA-Z_][a-zA-Z0-9_^']*\\>\\)" 2 + '("\\(\\<[A-Z_][a-zA-Z0-9_^']*\\>\\)" 1 font-lock-type-face) '("\\<\\(forall\\|exists\\)[ \t]*\\([a-zA-Z_][a-zA-Z0-9_^']*\\)\\>" 2 @@ -100,7 +123,7 @@ :syntax-table spl-mode-syntax-table (setq-local comment-start "// ") (setq-local font-lock-defaults '(spl-mode-font-lock-keywords)) - (setq-local indent-line-function 'c-indent-line) + (setq-local indent-line-function 'spl-indent-keep-position) ) (setq font-lock-defaults-alist (cons (cons 'spl-mode @@ -120,73 +143,78 @@ (set-syntax-table spl-mode-syntax-table) (run-hooks 'spl-mode-hook))) -(defun spl-set-indent () +(defun spl-line-props () + "Classifies the current line (for indentation)." + (save-excursion + (beginning-of-line) + (cons (cond ((or (comment-beginning) (looking-at-p "\\s-*/[/*]")) 'comment) + ((looking-at-p "\\s-*\\(case\\|else\\)") 'case) + ((looking-at-p ".*{\\s-*\\(//.*\\)?$") 'open) + ((looking-at-p ".*}\\s-*\\(//.*\\)?$") 'close) + ((looking-at-p ".*;\\s-*\\(//.*\\)?$") 'semicol) + ((looking-at-p spl-extended-defun-regexp) 'defun) + (t 'none)) + (current-indentation)))) + +(defun spl-backward-line () + "Jump one line backwards, and then skip over blank lines." + (forward-line 0) + (/= 0 (skip-chars-backward "\r\n\t "))) + +(defun spl-indent () + "Indent current line." (interactive) - (defun spl-lineup-statement-cont (langelem) - ;; lineup loop invariants - (save-excursion - (beginning-of-line) - (if (looking-at "[ \t]*\\(invariant\\|while\\|//\\)") - 0 - (if (progn (goto-char (cdr langelem)) - (looking-at "[ \t]*\\(function\\|predicate\\|{\\)")) - 0 - (if (and (not (looking-at "[ \t]*\\(invariant\\|assert\\|assume\\|pure\\|free\\|ensures\\|requires\\)")) - (looking-at ".*\\(&&\\|||\\)[ \t]*$")) - 0 - c-basic-offset))))) - (defun spl-lineup-statement (langelem) - ;; lineup loop invariants - (save-excursion - (beginning-of-line) - (if (and (looking-at "[ \t]*invariant") - (progn (goto-char (cdr langelem)) - (not (looking-at "[ \t]*invariant")))) - c-basic-offset - 0))) - (defun spl-lineup-topmost-intro (langelem) - ;; lineup procedure contracts - (save-excursion - (beginning-of-line) - (if (looking-at "[ \t]*\\(requires\\|ensures\\)") - (- c-basic-offset (c-langelem-col c-syntactic-element)) - 0))) - (defun spl-lineup-defun-open (langelem) - ;; lineup block start after specs - (save-excursion - (goto-char (cdr langelem)) - (beginning-of-line) - (if (looking-at "[ \t]*\\(invariant\\|ensures\\|requires\\)") - (- 0 c-basic-offset) - 0))) - (defun spl-lineup-block-open (langelem) - ;; lineup block start after specs - (save-excursion - (goto-char (c-langelem-pos c-syntactic-element)) - (beginning-of-line) - (if (looking-at "[ \t]*\\(invariant\\|ensures\\|requires\\)") - (- 0 c-basic-offset) - 0))) - (defun spl-lineup-topmost (langelem) - (save-excursion - (beginning-of-line) - (if (looking-at "[ \t]*\\(axiom\\|lemma\\|procedure\\|function\\|predicate\\|struct\\|\\(data\\)?type\\)") - 0 - c-basic-offset))) - (c-set-offset 'statement-cont 'spl-lineup-statement-cont) - (c-set-offset 'statement 'spl-lineup-statement) - (c-set-offset 'topmost-intro 'spl-lineup-topmost-intro) - (c-set-offset 'defun-open 'spl-lineup-defun-open) - (c-set-offset 'substatement-open 'spl-lineup-defun-open) - (c-set-offset 'block-open 'spl-lineup-block-open) - ;;(c-set-offset 'substatement-open 0) - (c-set-offset 'knr-argdecl-intro 'spl-lineup-topmost) - (c-set-offset 'topmost-intro-cont 'spl-lineup-topmost) - (c-set-offset 'func-decl-cont 'spl-lineup-topmost) - (c-set-offset 'knr-argdecl 'spl-lineup-topmost-intro) - (c-set-offset 'label '+)) - -(add-hook 'spl-mode-hook 'spl-set-indent) + (beginning-of-line) + (let* ((pprev-type (car-safe (save-excursion (and (spl-backward-line) (spl-backward-line) (spl-line-props))))) + (prev-props (save-excursion (and (spl-backward-line) (spl-line-props)))) + (prev-type (car-safe prev-props)) + (prev-offset (or (cdr-safe prev-props) 0)) + (is-defun (looking-at-p spl-extended-defun-regexp)) + (is-close (looking-at-p "[^{\n]*}")) + (is-lonely-open (looking-at-p "[ \t]*{")) + (is-else (looking-at-p "[ \t]*else")) + (comment-beg (save-excursion (comment-beginning)))) + (indent-line-to + (cond (comment-beg (if (< comment-beg (point-at-bol)) ;; Multiline comment; indent to '*' or beginning of text + (let ((incr (if (looking-at-p "\\s-*\\*") 1 3))) + (save-excursion (goto-char comment-beg) (+ (current-indentation) incr))) + prev-offset)) + ((or is-close is-lonely-open) + (save-excursion + (when is-close + (backward-up-list)) + ;; Find beginning of block head (the head can span multiple lines) + (let ((bound (save-excursion (ignore-errors (backward-up-list) (point))))) + ;; The bound ensures that brackets headerless blocks are indented properly + (re-search-backward (concat "^\\s-*}?" spl-extended-block-head-regexp) bound t)) + (current-indentation))) + (is-defun (if (memq prev-type '(open)) (+ prev-offset c-basic-offset) prev-offset)) + ;(is-case (-if-let (parent (save-excursion (when (re-search-backward "^\\s-*match" nil t) (current-indentation)))) + ; (indent-next-tab-stop parent) + ; prev-offset)) + (is-else (or (save-excursion (when (re-search-backward "^\\s-*if" nil t) (current-indentation))) + prev-offset)) + (t (pcase prev-type + (`comment prev-offset) + (`case (+ prev-offset c-basic-offset)) + (`open (+ prev-offset c-basic-offset)) + (`close prev-offset) + (`semicol prev-offset) + (`defun (+ prev-offset c-basic-offset)) + (`none (if (memq pprev-type '(none defun comment case)) prev-offset (+ prev-offset c-basic-offset))) + (_ prev-offset)))))) + (skip-chars-forward " ")) + + +(defun spl-indent-keep-position () + "Indent current line, minimally moving point. +That is, leaves the point in place if it is already beyond the +first non-blank character of that line, and moves it to the first +character in the line otherwise." + (interactive) + (let ((position (save-excursion (spl-indent) (point)))) + (when (> position (point)) + (goto-char position)))) (or (assoc "\\.spl$" auto-mode-alist) (setq auto-mode-alist (cons '("\\.spl$" . spl-mode) @@ -198,21 +226,17 @@ (push '("==>" . ?⟹) prettify-symbols-alist) (push '("|->" . ?↦) prettify-symbols-alist) (push '("->" . ?⟶) prettify-symbols-alist) - ;(push '("(&*&)" . ?✹) prettify-symbols-alist) (push '("&*&" . ?✶) prettify-symbols-alist) (push '("&+&" . ?⊕) prettify-symbols-alist) (push '("&&" . ?∧) prettify-symbols-alist) (push '("||" . ?∨) prettify-symbols-alist) (push '("**" . ?∩) prettify-symbols-alist) - ;(push '("(+*)" . ?⨄) prettify-symbols-alist) - ;(push '("(++)" . ?⋃) prettify-symbols-alist) (push '("++" . ?∪) prettify-symbols-alist) (push '("--" . ?—) prettify-symbols-alist) (push '("in" . ?∈) prettify-symbols-alist) (push '("!in" . ?∉) prettify-symbols-alist) (push '("subsetof" . ?⊆) prettify-symbols-alist) (push '("!=" . ?≠) prettify-symbols-alist) - ;(push '("==" . ?=) prettify-symbols-alist) (push '("!" . ?¬) prettify-symbols-alist) (push '("forall" . ?∀) prettify-symbols-alist) (push '("exists" . ?∃) prettify-symbols-alist) From 4e9bc7050be7a69005b564d07c51588482254dbf Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 14 Apr 2018 20:35:59 -0400 Subject: [PATCH 028/118] remove ghp emacs mode --- README.md | 23 +++------ emacs-mode/ghp-mode.el | 111 ----------------------------------------- 2 files changed, 8 insertions(+), 126 deletions(-) delete mode 100644 emacs-mode/ghp-mode.el diff --git a/README.md b/README.md index ec4c7c8e..a990e463 100644 --- a/README.md +++ b/README.md @@ -65,26 +65,19 @@ To see the available command line options, run ./grasshopper.native -help ``` -Emacs Modes +Emacs Major Mode ------------------------- -GRASShopper provides two emacs modes for GRASShopper programs: -- SPL mode: this mode provides syntax highlighting and automatic - indentation for the GRASShopper input programs (see tests/spl for - examples). +GRASShopper provides an Emacs major mode for GRASShopper programs. +The mode provides syntax highlighting and automatic indentation for +the GRASShopper input programs (see tests/spl for examples). -- GHP mode: this mode provides syntax highlighting for the intermediate - representation of programs inside GRASShopper. Such programs can be - generated by running GRASShopper with the option `-dumpghp n`. - Here, n=0,1,2,3 refers to the n-th simplification stage of - verification condition generation. - -To use the emacs modes, copy the files in the directory emacs-mode to -your site-lisp directory and add the following lines to your `.emacs` file: +To use the Emacs mode, copy the files in the directory emacs-mode to +your site-lisp directory and add the following line to your `.emacs` +file: ```elisp -(load "spl-mode") -(load "ghp-mode") +(load "spl-mode") ``` Optional: Flycheck minor mode diff --git a/emacs-mode/ghp-mode.el b/emacs-mode/ghp-mode.el deleted file mode 100644 index 6b387c0f..00000000 --- a/emacs-mode/ghp-mode.el +++ /dev/null @@ -1,111 +0,0 @@ -;;; ghp-mode.el -- Emacs GHP mode - - -(require 'font-lock) -(if (<= 20 emacs-major-version) - (defun make-regexp (a b c) (regexp-opt a b)) - (require 'make-regexp)) - -(cond - ((x-display-color-p) - (make-face 'Firebrick) - (set-face-foreground 'Firebrick "Firebrick") - (make-face 'RosyBrown) - (set-face-foreground 'RosyBrown "RosyBrown") - (make-face 'Purple) - (set-face-foreground 'Purple "Purple") - (make-face 'MidnightBlue) - (set-face-foreground 'MidnightBlue "MidnightBlue") - (make-face 'DarkGoldenRod) - (set-face-foreground 'DarkGoldenRod "DarkGoldenRod") - (make-face 'Spec) - (set-face-foreground 'Spec "magenta4") - (make-face 'DarkOliveGreen) - (set-face-foreground 'DarkOliveGreen "DarkOliveGreen4") - (make-face 'CadetBlue) - (set-face-foreground 'CadetBlue "CadetBlue") - (make-face 'Stop) - (set-face-foreground 'Stop "White") - (set-face-background 'Stop "Red") - (setq font-lock-reference-face 'CadetBlue) - (setq font-lock-spec-face 'Spec) - (setq font-lock-stop-face 'Stop) - (setq font-lock-doc-face 'CadetBlue) -)) - -(defconst ghp-mode-font-lock-keywords - (list - '("\\(//[^\n]*\\)" 1 - font-lock-comment-face) - - '("\\<\\(check\\|for\\(all\\|\\)\\|free ensures\\|free requires\\|e\\(xists\\|nsures\\)\\|ghost\\|i\\(mplicit\\|nvariant\\)\\|old\\|pure\\|requires\\)\\>" - 1 font-lock-spec-face) - - '("\\<\\(ass\\(ert\\|ume\\)\\|c\\(all\\|hoose\\)\\|f\\(unction\\|ree\\)\\|havoc\\|locals\\|new\\|pr\\(ocedure\\|edicate\\)\\|or\\|return\\(s\\|\\)\\|var\\|while\\)\\>" - 1 font-lock-keyword-face) - - '("\\<\\(\\)\\>" - 1 font-lock-builtin-face) - - '("\\<\\(Frame\\|Btwn\\|false\\|in\\|null\\|true\\|Univ\\)\\>" - 1 font-lock-constant-face) - - '("\\(\\<[a-zA-Z_][a-zA-Z0-9_']*[ \t]*\\>\\)(" 1 - font-lock-function-name-face) - - '("[^:]:[ \t]*\\(\\<[a-zA-Z_][a-zA-Z0-9_']*\\>\\)" 1 - font-lock-type-face) - - '("\\(\\<[a-zA-Z_?][a-zA-Z0-9_'?]*[ \t]*\\>\\):[^:=]" 1 - font-lock-variable-name-face) - )) - - -(defvar ghp-mode-syntax-table nil - "Syntax table in use in ghp-mode buffers.") - -(if ghp-mode-syntax-table - () - (setq ghp-mode-syntax-table (make-syntax-table)) - (modify-syntax-entry ?/ ". 14b" ghp-mode-syntax-table) - (modify-syntax-entry ?* ". 23b" ghp-mode-syntax-table) - (modify-syntax-entry ?\n ">" ghp-mode-syntax-table) - (modify-syntax-entry ?\f ">" ghp-mode-syntax-table) - (modify-syntax-entry ?' "w" ghp-mode-syntax-table) - (modify-syntax-entry ?_ "w" ghp-mode-syntax-table) - (modify-syntax-entry ?@ "w" ghp-mode-syntax-table) - (modify-syntax-entry ?$ "w" ghp-mode-syntax-table) -) - -(if (< 23 emacs-major-version) - (define-derived-mode ghp-mode c-mode "GHP" - "Major mode for editing Grasshopper program files." - :syntax-table ghp-mode-syntax-table - (setq-local comment-start "// ") - (setq-local font-lock-defaults '(ghp-mode-font-lock-keywords)) - ;(setq-local indent-line-function 'c-indent-line) - ) - (setq font-lock-defaults-alist - (cons (cons 'ghp-mode - '(ghp-mode-font-lock-keywords - nil nil nil backward-paragraph - (font-lock-comment-start-regexp . "/[*]"))) - font-lock-defaults-alist)) - (defun ghp-mode () - "Major mode for editing Grasshopper program files" - - (interactive) - - (kill-all-local-variables) - - (setq mode-name "GHP") - (setq major-mode 'ghp-mode) - (set-syntax-table ghp-mode-syntax-table) - (run-hooks 'ghp-mode-hook))) - - -(or (assoc "\\.ghp$" auto-mode-alist) - (setq auto-mode-alist (cons '("\\.ghp$" . ghp-mode) - auto-mode-alist))) - -(provide 'ghp-mode) From 84256bfa863b50a896a7ea6da7ec6364daebea43 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 14 Apr 2018 21:40:23 -0400 Subject: [PATCH 029/118] var ghost -> ghost var --- src/frontends/spl/splParser.mly | 80 +++++++++++++------ tests/spl/array/quicksort.spl | 8 +- tests/spl/counter/counter_simple.spl | 2 +- tests/spl/counter/counter_two_cells.spl | 2 +- tests/spl/counter/counter_very_simple.spl | 2 +- tests/spl/include/bstree.spl | 2 +- tests/spl/include/ordered_type.spl | 4 +- tests/spl/sorted_array_util/array_util.spl | 51 ++++++------ tests/spl/tree/tree_to_list.spl | 8 +- tests/spl/tree/union_find.spl | 2 +- .../btree_simple_array/array_utils.spl | 2 +- 11 files changed, 100 insertions(+), 63 deletions(-) diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index a54e3521..cfc27044 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -119,8 +119,8 @@ declarations: { (fst3 $2, PredDecl $1 :: snd3 $2, trd3 $2) } | macro_decl declarations { (fst3 $2, MacroDecl $1 :: snd3 $2, trd3 $2) } -| VAR var_decl SEMICOLON declarations - { (fst3 $4, VarDecl $2 :: snd3 $4, trd3 $4) } +| ghost_modifier VAR var_decl semicolon_opt declarations + { (fst3 $5, VarDecl { $3 with v_ghost = $1 } :: snd3 $5, trd3 $5) } | /* empty */ { ([], [], []) } | error { ProgError.syntax_error (mk_position 1 1) None } ; @@ -199,10 +199,10 @@ proc_header: ; proc_head: -| PROCEDURE IDENT LPAREN var_decls RPAREN proc_returns contracts { +| PROCEDURE IDENT LPAREN var_decls_with_modifiers RPAREN proc_returns contracts { ($2, $4, $6, $7, mk_position 2 2, false) } -| LEMMA IDENT LPAREN var_decls RPAREN proc_returns contracts { +| LEMMA IDENT LPAREN var_decls_with_modifiers RPAREN proc_returns contracts { ($2, $4, $6, $7, mk_position 2 2, true) } ; @@ -235,7 +235,7 @@ proc_impl: ; pred_decl: -| PREDICATE IDENT LPAREN var_decls RPAREN contracts pred_impl { +| PREDICATE IDENT LPAREN var_decls_with_modifiers RPAREN contracts pred_impl { let formals, locals = List.fold_right (fun decl (formals, locals) -> decl.v_name :: formals, IdMap.add decl.v_name decl locals) @@ -259,7 +259,7 @@ pred_decl: ; function_header: -| FUNCTION IDENT LPAREN var_decls RPAREN RETURNS LPAREN var_decls RPAREN contracts { +| FUNCTION IDENT LPAREN var_decls_with_modifiers RPAREN RETURNS LPAREN var_decls_with_modifiers RPAREN contracts { let formals, locals = List.fold_right (fun decl (formals, locals) -> decl.v_name :: formals, IdMap.add decl.v_name decl locals) @@ -294,17 +294,17 @@ pred_impl: | /* empty */ { None } ; -var_decls: -| var_decl var_decl_list { $1 :: $2 } +var_decls_with_modifiers: +| var_decl_with_modifiers var_decl_with_modifiers_list { $1 :: $2 } | /* empty */ { [] } ; -var_decl_list: -| COMMA var_decl var_decl_list { $2 :: $3 } +var_decl_with_modifiers_list: +| COMMA var_decl_with_modifiers var_decl_with_modifiers_list { $2 :: $3 } | /* empty */ { [] } ; -var_decl: +var_decl_with_modifiers: | var_modifier IDENT COLON var_type { let decl = { v_name = $2; @@ -320,10 +320,34 @@ var_decl: } ; +var_decl: +| IDENT COLON var_type { + let decl = + { v_name = $1; + v_type = $3; + v_ghost = false; + v_implicit = false; + v_aux = false; + v_pos = mk_position 1 1; + v_scope = GrassUtil.global_scope; (* scope is fixed later *) + } + in + decl +} +; + +var_decls: +| var_decl var_decl_list { $1 :: $2 } + + +var_decl_list: +| COMMA var_decl var_decl_list { $2 :: $3 } +| /* empty */ { [] } +; + var_decls_opt_type: | var_decl var_decl_opt_type_list { $1 :: $2 } | var_decl_opt_type var_decl_opt_type_list { $1 :: $2 } -| /* empty */ { [] } ; var_decl_opt_type_list: @@ -333,14 +357,14 @@ var_decl_opt_type_list: ; var_decl_opt_type: -| var_modifier IDENT { +| IDENT { let decl = - { v_name = $2; + { v_name = $1; v_type = AnyType; - v_ghost = snd $1; - v_implicit = fst $1; + v_ghost = false; + v_implicit = false; v_aux = false; - v_pos = mk_position 2 2; + v_pos = mk_position 1 1; v_scope = GrassUtil.global_scope; (* scope is fixed later *) } in @@ -348,10 +372,14 @@ var_decl_opt_type: } ; +ghost_modifier: +| GHOST { true } +| /* empty */ { false } +; + var_modifier: | IMPLICIT GHOST { true, true } -| GHOST { false, true } -| /* empty */ { false, false } +| ghost_modifier { false, $1 } ; var_type: @@ -367,7 +395,7 @@ var_type: ; proc_returns: -| RETURNS LPAREN var_decls RPAREN { $3 } +| RETURNS LPAREN var_decls_with_modifiers RPAREN { $3 } | /* empty */ { [] } ; @@ -421,7 +449,7 @@ field_decls: ; field_decl: -| VAR var_decl semicolon_opt { $2 } +| ghost_modifier VAR var_decl semicolon_opt { { $3 with v_ghost = $1 } } ; block: @@ -446,8 +474,14 @@ stmt_no_short_if: stmt_wo_trailing_substmt: /* variable declaration */ -| VAR var_decls SEMICOLON { LocalVars ($2, None, mk_position 1 3) } -| VAR var_decls_opt_type COLONEQ expr_list SEMICOLON { LocalVars ($2, Some $4, mk_position 1 5) } +| ghost_modifier VAR var_decls SEMICOLON { + let decls = List.map (fun decl -> { decl with v_ghost = $1 }) $3 in + LocalVars (decls, None, mk_position 1 4) +} +| ghost_modifier VAR var_decls_opt_type COLONEQ expr_list SEMICOLON { + let decls = List.map (fun decl -> { decl with v_ghost = $1 }) $3 in + LocalVars (decls, Some $5, mk_position 1 6) +} /* nested block */ | LBRACE block RBRACE { Block ($2, mk_position 1 3) diff --git a/tests/spl/array/quicksort.spl b/tests/spl/array/quicksort.spl index bb9e8b97..c7d44d3e 100644 --- a/tests/spl/array/quicksort.spl +++ b/tests/spl/array/quicksort.spl @@ -26,10 +26,10 @@ procedure quicksort1(a: Array, lower: Int, upper: Int, ghost content: Set(); - var ghost C2 := Set(); + ghost var C1 := Set(); + ghost var C2 := Set(); //var C3 := content -- Set(a[upper]); //only true for multiset - var ghost C3: Set; + ghost var C3: Set; pure assume(C3 == content || C3 == content -- Set(a[upper])); while (i < upper) invariant idx >= lower && i <= upper && i >= idx @@ -49,7 +49,7 @@ procedure quicksort1(a: Array, lower: Int, upper: Int, ghost content: Set 0) -var bottom: K; -var top: K; +var bottom: K +var top: K axiom forall a: K :: !lt(a, bottom) axiom forall a: K :: !lt(top, a) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index fc361874..6dd0711c 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -59,7 +59,7 @@ lemma not_in_sorted_seg(m: Map, i: Int, j: Int, k: K, implicit ghost C: ensures i >= j || lt(k, m[i]) || lt(m[j-1], k) ==> k !in C { if (i >= j) return; - + if (lt(m[j - 1], k)) { extend_right(m, i, j - 1); not_in_sorted_seg(m, i, j - 1, k); @@ -79,14 +79,17 @@ function shift_map(m: Map, src: Int, dst: Int, len: Int) { { i: Int :: i < dst || dst + len <= i ? m[i] : m[src + (i - dst)] @(matching res[i] yields m[src + (i - dst)]) - @(matching res[i] yields m[i]) } + @(matching res[i] yields m[i]) + } } -function copy_map(m1: Map, m2: Map, src: Int, dst: Int, len: Int) returns (res: Map) +function copy_map(m1: Map, m2: Map, src: Int, dst: Int, len: Int) + returns (res: Map) { { i: Int :: dst <= i < dst + len ? m2[src + (i - dst)] : m1[i] @(matching res[i] yields m2[src + (i - dst)]) - @(matching res[i] yields m1[i]) } + @(matching res[i] yields m1[i]) + } } function map_of_array(a: Array) returns (res: Map) @@ -94,14 +97,15 @@ function map_of_array(a: Array) returns (res: Map) { { i: Int :: 0 <= i < a.length ? a[i] : bottom @(matching res[i] yields a[i]) - @(matching a[i] yields res[i]) } + @(matching a[i] yields res[i]) + } } predicate sorted_map_seg(m: Map, i: Int, j: Int) { forall i1: Int, i2: Int :: i <= i1 < i2 < j ==> lt(m[i1], m[i2]) } - + predicate sorted_array_seg(a: Array, i: Int, j: Int) requires acc(a) { @@ -117,14 +121,13 @@ function set_of_array(a: Array, i: Int, j: Int) returns (res: Set) predicate sorted_array_seg_with_content(a: Array, i: Int, j: Int, C: Set) requires acc(a) { - sorted_array_seg(a, i, j) && C == set_of_array(a, i, j) } predicate sorted_array_with_content(a: Array, len: Int, C: Set) { 0 <= len &*& - acc(a) &*& sorted_array_seg_with_content(a, 0, len, C) + acc(a) &*& sorted_array_seg_with_content(a, 0, len, C) } @@ -135,11 +138,11 @@ procedure shift(a: Array, src: Int, dst: Int, len: Int) ensures acc(a) ensures map_of_array(a) == shift_map(old(map_of_array(a)), src, dst, len) { - var ghost m := map_of_array(a); - + ghost var m := map_of_array(a); + if (src < dst) { var i := len - 1; - + while (i >= 0) invariant acc(a) invariant src < dst @@ -148,7 +151,7 @@ procedure shift(a: Array, src: Int, dst: Int, len: Int) invariant -1 <= i < len invariant shift_map(m, src + i + 1, dst + i + 1, len - i - 1) == map_of_array(a) { - var ghost m1 := map_of_array(a); + ghost var m1 := map_of_array(a); var tmp := a[src + i]; a[dst + i] := tmp; pure assert map_of_array(a) == m1[dst + i := tmp]; @@ -164,7 +167,7 @@ procedure shift(a: Array, src: Int, dst: Int, len: Int) invariant 0 <= i <= len invariant shift_map(m, src, dst, i) == map_of_array(a) { - var ghost m1 := map_of_array(a); + ghost var m1 := map_of_array(a); var tmp := a[src + i]; a[dst + i] := a[src + i]; pure assert map_of_array(a) == m1[dst + i := tmp]; @@ -182,10 +185,10 @@ procedure copy(a: Array, b: Array, src: Int, dst: Int, len: Int) ensures map_of_array(a) == old(map_of_array(a)) ensures map_of_array(b) == copy_map(old(map_of_array(b)), map_of_array(a), src, dst, len) { - var ghost mb := map_of_array(b); - var ghost ma := map_of_array(a); + ghost var mb := map_of_array(b); + ghost var ma := map_of_array(a); var i := 0; - + while (i < len) invariant acc(a) &*& acc(b) invariant 0 <= i <= len @@ -194,7 +197,7 @@ procedure copy(a: Array, b: Array, src: Int, dst: Int, len: Int) invariant map_of_array(a) == ma invariant map_of_array(b) == copy_map(mb, ma, src, dst, i) { - var ghost m1 := map_of_array(b); + ghost var m1 := map_of_array(b); var tmp := a[src + i]; b[dst + i] := tmp; pure assert map_of_array(b) == m1[dst + i := tmp]; @@ -219,7 +222,7 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) { var lo := 0; var hi := len; - + while (hi != lo) invariant sorted_array_with_content(a, len, C) // what we actually care about @@ -241,15 +244,15 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) lo := m; } } - + idx := lo; - + if (idx == len || lt(k, a[lo])) { found := false; } else { found := true; } - + // prove k !in set_of_array(a, 0, idx) not_in_sorted_seg(map_of_array(a), 0, idx, k); // prove k !in set_of_array(a, idx + 1, len) @@ -277,7 +280,7 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) if (found) return len; // shift array entries a[i..len] by 1 entry to the right - var ghost m := map_of_array(a); + ghost var m := map_of_array(a); shift(a, i, i + 1, len - i); // prove C == set_of_array(a, 0, i) ++ set_of_array(a, i + 1, len + 1); @@ -285,7 +288,7 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) set_of_map_equal(m, map_of_array(a), 0, 0, i); set_of_map_equal(m, map_of_array(a), i, i + 1, len - i); - var ghost m1 := map_of_array(a); + ghost var m1 := map_of_array(a); a[i] := k; @@ -316,7 +319,7 @@ procedure delete(a: Array, k: K, len: Int, implicit ghost C: Set) if (!found) return len; // shift array entries a[i+1..len] by 1 entry to the left - var ghost m := map_of_array(a); + ghost var m := map_of_array(a); shift(a, i + 1, i, len - (i + 1)); // prove C -- {k} == set_of_array(a, 0, i - 1) ++ set_of_array(a, i - 1, len) diff --git a/tests/spl/tree/tree_to_list.spl b/tests/spl/tree/tree_to_list.spl index 544b8eac..5dcdfc00 100644 --- a/tests/spl/tree/tree_to_list.spl +++ b/tests/spl/tree/tree_to_list.spl @@ -4,7 +4,7 @@ struct LNode { var ldata: Int; } -footprint function lseg_footprint(x: LNode, y: LNode) returns (FP: Set) { +function lseg_footprint(x: LNode, y: LNode) returns (FP: Set) { forall z: LNode :: z in FP == (Btwn(next, x, z, y) && z != y) } @@ -29,7 +29,7 @@ predicate list_set(x: LNode, y: LNode, C: Set)(FP: Set) { struct TNode { var left: TNode; var right: TNode; - var ghost parent: TNode; + ghost var parent: TNode; var tdata: Int; } @@ -121,8 +121,8 @@ procedure insertL(lst: LNode, val: Int, var curr: LNode, prev: LNode; curr := lst; prev := null; - var ghost c1 := content; - var ghost c2 := Set(); + ghost var c1 := content; + ghost var c2 := Set(); while (curr != null && curr.ldata < val) invariant prev == null && curr == lst && c2 == Set() || diff --git a/tests/spl/tree/union_find.spl b/tests/spl/tree/union_find.spl index d03aba10..7de9e157 100644 --- a/tests/spl/tree/union_find.spl +++ b/tests/spl/tree/union_find.spl @@ -1,6 +1,6 @@ struct Node { var next: Node; - var ghost gnext: Node; + ghost var gnext: Node; } function rep(x: Node) returns (res: Node) diff --git a/tests/spl/work_in_progress/link_and_array/btree_simple_array/array_utils.spl b/tests/spl/work_in_progress/link_and_array/btree_simple_array/array_utils.spl index 53fb73a1..442e1e9f 100644 --- a/tests/spl/work_in_progress/link_and_array/btree_simple_array/array_utils.spl +++ b/tests/spl/work_in_progress/link_and_array/btree_simple_array/array_utils.spl @@ -25,7 +25,7 @@ procedure key_arraymove(a: Array, srcPos: Int, dstPos: Int, length: Int) a[dstPos + i] := a[srcPos + i]; i := i - 1; } - } else if (srcPos > dstPos){ + } else if (srcPos > dstPos) { var i := 0; while (i < length) invariant srcPos > dstPos && 0 <= srcPos && srcPos + length <= a.length && 0 <= dstPos && dstPos + length <= a.length From 5a1786b3c76a93e38db5d0284e76c3d6efd92ced Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 14 Apr 2018 21:55:23 -0400 Subject: [PATCH 030/118] insert now returns index where k was inserted --- tests/spl/sorted_array_util/array_util.spl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index 6dd0711c..a59ed615 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -265,19 +265,20 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) // insert `k` into `a[0..len+1]` while preserving sortedness. // If `k` is already contained in `a[0..len]`, then do not modify `a`. procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) - returns (new_len: Int) + returns (idx: Int, new_len: Int) requires sorted_array_with_content(a, len, C) requires len < a.length ensures sorted_array_with_content(a, new_len, C ++ {k}) ensures k in C ==> new_len == len ensures k !in C ==> new_len == len + 1 + ensures 0 <= idx < new_len && a[idx] == k { // find position for insertion var i: Int, found: Bool; found, i := find(a, len, k); // k already in C? - if (found) return len; + if (found) return i, len; // shift array entries a[i..len] by 1 entry to the right ghost var m := map_of_array(a); @@ -298,7 +299,7 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) frame_set_of_map(m1, i + 1, len + 1); set_of_map_split(map_of_array(a), 0, i, len + 1); - return len + 1; + return i, len + 1; } // Given a sorted array segment `a[0..len]`, From d438438ff7d8999ad6e612d9b7109a4049835ef2 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 14 Apr 2018 23:28:04 -0400 Subject: [PATCH 031/118] support options command to set command line options from within spl files --- emacs-mode/spl-mode.el | 2 +- src/frontends/spl/splLexer.mll | 1 + src/frontends/spl/splParser.mly | 19 ++++++++++++++++++- src/main/config.ml | 11 +++++++++-- src/main/grasshopper.ml | 4 ++-- src/main/vizmodel.ml | 4 ++-- tests/spl/sorted_array_util/array_util.spl | 6 ++++-- 7 files changed, 37 insertions(+), 10 deletions(-) diff --git a/emacs-mode/spl-mode.el b/emacs-mode/spl-mode.el index 8702ae99..82311129 100644 --- a/emacs-mode/spl-mode.el +++ b/emacs-mode/spl-mode.el @@ -36,7 +36,7 @@ (defconst spl-defuns '("define" "function" "predicate" "lemma" "procedure" "struct" "type" "datatype" - "pure function" "pure predicate" "include" "var")) + "pure function" "pure predicate" "include" "options" "var")) (defconst spl-specifiers '("axiom" "ensures" "free" "invariant" "requires" "pure" "assert" "assume" "split" "returns")) diff --git a/src/frontends/spl/splLexer.mll b/src/frontends/spl/splLexer.mll index fcb8cb7f..e4747702 100644 --- a/src/frontends/spl/splLexer.mll +++ b/src/frontends/spl/splLexer.mll @@ -44,6 +44,7 @@ let _ = ("matching", MATCHING); ("new", NEW); ("null", NULL); + ("options", OPTIONS); ("or", COR); ("outputs", OUTPUTS); ("pattern", PATTERN); diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index cfc27044..36ad5aad 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -65,7 +65,7 @@ type rhs_string_maybe = %token IF ELSE WHILE %token FUNCTION %token PREDICATE -%token GHOST IMPLICIT VAR STRUCT PURE LEMMA PROCEDURE INCLUDE AXIOM TYPE +%token GHOST IMPLICIT VAR STRUCT PURE LEMMA PROCEDURE INCLUDE OPTIONS AXIOM TYPE %token DEFINE DATATYPE OUTPUTS RETURNS REQUIRES ENSURES INVARIANT %token LOC INT BOOL BYTE SET MAP ARRAY ARRAYCELL %token MATCHING YIELDS WITHOUT COMMENT PATTERN @@ -109,6 +109,8 @@ main: declarations: | background_th declarations { (fst3 $2, snd3 $2, ($1, mk_position 1 1) :: trd3 $2) } +| options_cmd declarations + { fst3 $2, snd3 $2, trd3 $2 } | include_cmd declarations { (($1, mk_position 1 1) :: fst3 $2, snd3 $2, trd3 $2) } | type_decl declarations @@ -128,6 +130,21 @@ declarations: include_cmd: | INCLUDE STRINGVAL semicolon_opt { $2 } ; + +options_cmd: +| OPTIONS STRINGVAL semicolon_opt { + try Config.parse_options $2 + with Arg.Bad full_msg -> + let regexp = Sys.argv.(0) ^ ": \\([^\\.]*\\)" in + let matched = Str.string_match (Str.regexp regexp) full_msg 0 in + let msg = + if matched then Str.matched_group 1 full_msg + else "invalid option" + in + ProgError.error (mk_position 2 2) msg +} +; + background_th: | AXIOM expr semicolon_opt { $2 } diff --git a/src/main/config.ml b/src/main/config.ml index c246fb11..7681e032 100644 --- a/src/main/config.ml +++ b/src/main/config.ml @@ -1,6 +1,6 @@ (* Version string *) let version = "0.5 pre" - + (* Base directory for includes *) let base_dir = ref "" @@ -78,7 +78,7 @@ let ccFixedPoint = ref true (* maximal number of term generation rounds *) let term_gen_max_rounds = ref 2 -let cmd_options = +let cmd_options_spec = [("-basedir", Arg.Set_string base_dir, " Base directory for resolving include directives. Default: current working directory\n\nOptions for controlling error reporting and debug output:"); ("-v", Arg.Unit Debug.more_verbose, " Display more messages"); ("-q", Arg.Unit Debug.less_verbose, " Display fewer messages"); @@ -115,3 +115,10 @@ let cmd_options = ("-bitvector", Arg.Set use_bitvector, " Use bitvector theory for integers\n\nOptions for compiler:"); ("-compile", Arg.Set_string compile_to, " Compile SPL program to a C program outputed as a file with the given name.\n\nOptions for help:"); ] + +(* Parse auxiliary 'command line options' that are set during parsing of the input file *) +let parse_options options = + Debug.info (fun () -> "Setting options: " ^ options ^ "\n"); + let options = Sys.argv.(0) :: Str.split_delim (Str.regexp "[ \t\n]+") options |> Array.of_list in + let current = ref 0 in + Arg.parse_argv ~current:current options cmd_options_spec (fun _ -> ()) "invalid option" diff --git a/src/main/grasshopper.ml b/src/main/grasshopper.ml index 7423f6f8..f6988a63 100644 --- a/src/main/grasshopper.ml +++ b/src/main/grasshopper.ml @@ -12,7 +12,7 @@ let usage_message = " [options]\n" let cmd_line_error msg = - Arg.usage (Arg.align Config.cmd_options) usage_message; + Arg.usage (Arg.align Config.cmd_options_spec) usage_message; failwith ("Command line option error: " ^ msg) (** Output JSON file with error trace *) @@ -191,7 +191,7 @@ let _ = in let start_time = current_time () in try - Arg.parse Config.cmd_options set_main_file usage_message; + Arg.parse Config.cmd_options_spec set_main_file usage_message; if !Config.unsat_cores then Config.named_assertions := true; Debug.info (fun () -> greeting); SmtLibSolver.select_solver (String.uppercase_ascii !Config.smtsolver); diff --git a/src/main/vizmodel.ml b/src/main/vizmodel.ml index 932a7847..22511b19 100644 --- a/src/main/vizmodel.ml +++ b/src/main/vizmodel.ml @@ -9,7 +9,7 @@ let usage_message = " [options]\n" let cmd_line_error msg = - Arg.usage Config.cmd_options usage_message; + Arg.usage Config.cmd_options_spec usage_message; failwith ("Command line option error: " ^ msg) let parse_input file = @@ -39,7 +39,7 @@ let vizmodel file = let _ = try - Arg.parse Config.cmd_options (fun s -> input_file := s) usage_message; + Arg.parse Config.cmd_options_spec (fun s -> input_file := s) usage_message; SmtLibSolver.select_solver (String.uppercase_ascii !Config.smtsolver); if !input_file = "" then cmd_line_error "input file missing" diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index a59ed615..d2cb79ad 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -1,3 +1,5 @@ +options "-simplearrays" + include "../include/ordered_type.spl" // project map segment m[i..j] to set of its elements @@ -263,7 +265,7 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) // Given a sorted array segment `a[0..len]`, // insert `k` into `a[0..len+1]` while preserving sortedness. -// If `k` is already contained in `a[0..len]`, then do not modify `a`. +// If `k` is already contained in `a[0..len]`, then do not modify `a`. procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) returns (idx: Int, new_len: Int) requires sorted_array_with_content(a, len, C) @@ -304,7 +306,7 @@ procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) // Given a sorted array segment `a[0..len]`, // delete `k` from the segment while preserving sortedness. -// If `k` is already contained in `a[0..len]`, then do not modify `a`. +// If `k` is already contained in `a[0..len]`, then do not modify `a`. procedure delete(a: Array, k: K, len: Int, implicit ghost C: Set) returns (new_len: Int) requires sorted_array_with_content(a, len, C) From b2a2b3d0a6ba87a2a5c9281cdb741c29a088bfd5 Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 15 Apr 2018 11:15:23 -0400 Subject: [PATCH 032/118] some refactoring --- src/frontends/spl/splParser.mly | 10 ++-------- src/main/config.ml | 10 +++++++++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 36ad5aad..86138b7b 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -134,14 +134,8 @@ include_cmd: options_cmd: | OPTIONS STRINGVAL semicolon_opt { try Config.parse_options $2 - with Arg.Bad full_msg -> - let regexp = Sys.argv.(0) ^ ": \\([^\\.]*\\)" in - let matched = Str.string_match (Str.regexp regexp) full_msg 0 in - let msg = - if matched then Str.matched_group 1 full_msg - else "invalid option" - in - ProgError.error (mk_position 2 2) msg + with Invalid_argument msg -> + ProgError.error (mk_position 2 2) msg } ; diff --git a/src/main/config.ml b/src/main/config.ml index 7681e032..2295de07 100644 --- a/src/main/config.ml +++ b/src/main/config.ml @@ -121,4 +121,12 @@ let parse_options options = Debug.info (fun () -> "Setting options: " ^ options ^ "\n"); let options = Sys.argv.(0) :: Str.split_delim (Str.regexp "[ \t\n]+") options |> Array.of_list in let current = ref 0 in - Arg.parse_argv ~current:current options cmd_options_spec (fun _ -> ()) "invalid option" + try Arg.parse_argv ~current:current options cmd_options_spec (fun _ -> ()) "" + with Arg.Bad full_msg -> + let regexp = Sys.argv.(0) ^ ": \\([^\\.]*\\)" in + let matched = Str.string_match (Str.regexp regexp) full_msg 0 in + let msg = + if matched then Str.matched_group 1 full_msg + else "invalid option" + in + raise (Invalid_argument msg) From 6f1e61e98d8faa2d8d83d644a9776cc51cf80e65 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 16 Apr 2018 19:19:40 -0400 Subject: [PATCH 033/118] allow general expressions below field writes in SPL --- src/frontends/spl/splParser.mly | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 86138b7b..767399d6 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -698,6 +698,9 @@ field_write: | ident LBRACKET expr COLONEQ expr RBRACKET { Write ($1, $3, $5, mk_position 1 6) } +| primary LBRACKET expr COLONEQ expr RBRACKET { + Write ($1, $3, $5, mk_position 1 6) +} ; array_access: From ef4bbb9ab3e2506b82a57d27e7f24ef006e83186 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 16 Apr 2018 19:20:01 -0400 Subject: [PATCH 034/118] more precise specs --- tests/spl/sorted_array_util/array_util.spl | 218 +++++++++++++-------- 1 file changed, 131 insertions(+), 87 deletions(-) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index d2cb79ad..2454694f 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -75,7 +75,7 @@ lemma not_in_sorted_seg(m: Map, i: Int, j: Int, k: K, implicit ghost C: } -function shift_map(m: Map, src: Int, dst: Int, len: Int) +function map_shift(m: Map, src: Int, dst: Int, len: Int) returns (res: Map) requires 0 <= len { @@ -85,7 +85,7 @@ function shift_map(m: Map, src: Int, dst: Int, len: Int) } } -function copy_map(m1: Map, m2: Map, src: Int, dst: Int, len: Int) +function map_copy(m1: Map, m2: Map, src: Int, dst: Int, len: Int) returns (res: Map) { { i: Int :: dst <= i < dst + len ? m2[src + (i - dst)] : m1[i] @@ -126,19 +126,19 @@ predicate sorted_array_seg_with_content(a: Array, i: Int, j: Int, C: Set) sorted_array_seg(a, i, j) && C == set_of_array(a, i, j) } -predicate sorted_array_with_content(a: Array, len: Int, C: Set) +predicate sorted_array_with_content(a: Array, len: Int, m: Map) { 0 <= len &*& - acc(a) &*& sorted_array_seg_with_content(a, 0, len, C) + acc(a) &*& sorted_array_seg(a, 0, len) &*& m == map_of_array(a) } // Shift a[src..src+len] to a[dst..dst+len] -procedure shift(a: Array, src: Int, dst: Int, len: Int) +procedure arr_shift(a: Array, src: Int, dst: Int, len: Int) requires acc(a) requires 0 <= src <= src + len <= a.length && 0 <= dst <= dst + len <= a.length ensures acc(a) - ensures map_of_array(a) == shift_map(old(map_of_array(a)), src, dst, len) + ensures map_of_array(a) == map_shift(old(map_of_array(a)), src, dst, len) { ghost var m := map_of_array(a); @@ -151,7 +151,7 @@ procedure shift(a: Array, src: Int, dst: Int, len: Int) invariant 0 <= src <= src + len <= a.length invariant 0 <= dst <= dst + len <= a.length invariant -1 <= i < len - invariant shift_map(m, src + i + 1, dst + i + 1, len - i - 1) == map_of_array(a) + invariant map_shift(m, src + i + 1, dst + i + 1, len - i - 1) == map_of_array(a) { ghost var m1 := map_of_array(a); var tmp := a[src + i]; @@ -167,7 +167,7 @@ procedure shift(a: Array, src: Int, dst: Int, len: Int) invariant 0 <= src <= src + len <= a.length invariant 0 <= dst <= dst + len <= a.length invariant 0 <= i <= len - invariant shift_map(m, src, dst, i) == map_of_array(a) + invariant map_shift(m, src, dst, i) == map_of_array(a) { ghost var m1 := map_of_array(a); var tmp := a[src + i]; @@ -179,13 +179,13 @@ procedure shift(a: Array, src: Int, dst: Int, len: Int) } // Copy a[src..src+len] to b[dst..dst+len] -procedure copy(a: Array, b: Array, src: Int, dst: Int, len: Int) +procedure arr_copy(a: Array, b: Array, src: Int, dst: Int, len: Int) requires acc(a) &*& acc(b) requires 0 <= src <= src + len <= a.length requires 0 <= dst <= dst + len <= b.length ensures acc(a) &*& acc(b) ensures map_of_array(a) == old(map_of_array(a)) - ensures map_of_array(b) == copy_map(old(map_of_array(b)), map_of_array(a), src, dst, len) + ensures map_of_array(b) == map_copy(old(map_of_array(b)), map_of_array(a), src, dst, len) { ghost var mb := map_of_array(b); ghost var ma := map_of_array(a); @@ -197,7 +197,7 @@ procedure copy(a: Array, b: Array, src: Int, dst: Int, len: Int) invariant 0 <= src <= src + len <= a.length invariant 0 <= dst <= dst + len <= b.length invariant map_of_array(a) == ma - invariant map_of_array(b) == copy_map(mb, ma, src, dst, i) + invariant map_of_array(b) == map_copy(mb, ma, src, dst, i) { ghost var m1 := map_of_array(b); var tmp := a[src + i]; @@ -207,43 +207,66 @@ procedure copy(a: Array, b: Array, src: Int, dst: Int, len: Int) } } +function map_find(m: Map, i: Int, j: Int, k: K) returns (idx: Int) + requires i <= j + requires sorted_map_seg(m, i, j) + ensures i <= idx <= j + ensures m[idx] == k || idx == j || lt(k, m[idx]) + ensures 0 < idx ==> lt(m[idx - 1], k) +{ + i < j && lt(m[i], k) ? map_find(m, i + 1, j, k) : i +} + +lemma arr_find_content_set(m: Map, len: Int, idx: Int, k: K) + requires sorted_map_seg(m, 0, len) + requires idx == map_find(m, 0, len, k) + //requires 0 <= idx <= len + //requires m[idx] == k || idx == len || lt(k, m[idx]) + //requires 0 < idx ==> lt(m[idx - 1], k) + ensures k !in set_of_map(m, 0, idx) + ensures k !in set_of_map(m, idx + 1, len) +{ + // prove k !in set_of_array(a, 0, idx) + not_in_sorted_seg(m, 0, idx, k); + // prove k !in set_of_array(a, idx + 1, len) + not_in_sorted_seg(m, idx + 1, len, k); + // prove found == (k in C) + set_of_map_split(m, 0, idx, len); +} + // Find key `k` in sorted array segment `a[0..len]` using binary search -procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) +procedure arr_find(a: Array, len: Int, k: K, implicit ghost m: Map) returns (found: Bool, idx: Int) - requires sorted_array_with_content(a, len, C) + requires sorted_array_with_content(a, len, m) requires 0 <= len <= a.length - ensures sorted_array_with_content(a, len, C) + ensures sorted_array_with_content(a, len, m) // what we actually care about - ensures 0 <= idx <= len - ensures found == (k in C) - ensures found ==> a[idx] == k - ensures !found ==> idx == len || lt(k, a[idx]) - ensures 0 < idx ==> lt(a[idx - 1], k) - ensures k !in set_of_array(a, 0, idx) - ensures k !in set_of_array(a, idx + 1, len) + ensures idx == map_find(m, 0, len, k) + ensures found ==> idx < len && m[idx] == k + ensures !found ==> idx == len || lt(k, m[idx]) { var lo := 0; var hi := len; while (hi != lo) - invariant sorted_array_with_content(a, len, C) + invariant sorted_array_with_content(a, len, m) // what we actually care about invariant 0 <= lo <= hi <= len <= a.length - invariant hi == len || a[lo] == k || lt(k, a[hi]) - invariant 0 < lo ==> lt(a[lo - 1], k) - invariant hi < len - 1 ==> lt(k, a[hi + 1]) + invariant hi == len || m[lo] == k || lt(k, m[hi]) + invariant 0 < lo ==> lt(m[lo - 1], k) + invariant hi < len - 1 ==> lt(k, m[hi + 1]) { - var m := (hi + lo) / 2; + var mid := (hi + lo) / 2; var cmp: Int; - cmp := compare(k, a[m]); + cmp := compare(k, a[mid]); if (cmp < 0) { - hi := m; // look in first half + hi := mid; // look in first half } else if (cmp > 0) { - lo := m+1; // look in second half + lo := mid + 1; // look in second half } else { // found it - hi := m; - lo := m; + hi := mid; + lo := mid; } } @@ -253,90 +276,111 @@ procedure find(a: Array, len: Int, k: K, implicit ghost C: Set) found := false; } else { found := true; + } +} + +lemma arr_insert_content_set(m: Map, m1: Map, idx: Int, k: K, len: Int, new_len: Int) + requires sorted_map_seg(m, 0, len) + requires idx == map_find(m, 0, len, k) + requires m[idx] == k && idx < len ==> new_len == len && m1 == m + requires m[idx] != k || idx == len ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] + ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) ++ {k} +{ + if (m[idx] == k && idx < len) { + in_set_of_map(m, 0, len); + return; } - // prove k !in set_of_array(a, 0, idx) - not_in_sorted_seg(map_of_array(a), 0, idx, k); - // prove k !in set_of_array(a, idx + 1, len) - not_in_sorted_seg(map_of_array(a), idx + 1, len, k); - // prove found == (k in C) - set_of_map_split(map_of_array(a), 0, idx, len); + var ms := map_shift(m, idx, idx + 1, len - idx); + + // prove set_of_map(m, 0, len) == set_of_map(ms, 0, idx) ++ set_of_array(ms, idx + 1, len + 1); + set_of_map_split(m, 0, idx, len); + set_of_map_equal(m, ms, 0, 0, idx); + set_of_map_equal(m, ms, idx, idx + 1, len - idx); + + // prove set_of_map(m1, 0, new_len) == set_of_array(ms, 0, idx) ++ {k} ++ set_of_map(ms, idx + 1, len + 1) + frame_set_of_map(ms, 0, idx); + frame_set_of_map(ms, idx + 1, len + 1); + set_of_map_split(m1, 0, idx, len + 1); } // Given a sorted array segment `a[0..len]`, // insert `k` into `a[0..len+1]` while preserving sortedness. // If `k` is already contained in `a[0..len]`, then do not modify `a`. -procedure insert(a: Array, k: K, len: Int, implicit ghost C: Set) - returns (idx: Int, new_len: Int) - requires sorted_array_with_content(a, len, C) +procedure arr_insert(a: Array, k: K, len: Int, implicit ghost m: Map) + returns (idx: Int, new_len: Int, implicit ghost m1: Map) + requires sorted_array_with_content(a, len, m) requires len < a.length - ensures sorted_array_with_content(a, new_len, C ++ {k}) - ensures k in C ==> new_len == len - ensures k !in C ==> new_len == len + 1 - ensures 0 <= idx < new_len && a[idx] == k + ensures sorted_array_with_content(a, new_len, m1) + ensures idx == map_find(m, 0, len, k) + ensures m[idx] == k && idx < len ==> new_len == len && m1 == m + ensures m[idx] != k || idx == len ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] { // find position for insertion var i: Int, found: Bool; - found, i := find(a, len, k); + found, i := arr_find(a, len, k); // k already in C? - if (found) return i, len; - - // shift array entries a[i..len] by 1 entry to the right - ghost var m := map_of_array(a); - shift(a, i, i + 1, len - i); - - // prove C == set_of_array(a, 0, i) ++ set_of_array(a, i + 1, len + 1); - set_of_map_split(m, 0, i, len); - set_of_map_equal(m, map_of_array(a), 0, 0, i); - set_of_map_equal(m, map_of_array(a), i, i + 1, len - i); + if (found) return i, len, map_of_array(a); + + arr_shift(a, i, i + 1, len - i); - ghost var m1 := map_of_array(a); + ghost var ms := map_of_array(a); a[i] := k; - // prove C == set_of_array(a, 0, i) ++ {k} ++ set_of_array(a, i + 1, len + 1) - pure assert map_of_array(a) == m1[i := k]; - frame_set_of_map(m1, 0, i); - frame_set_of_map(m1, i + 1, len + 1); - set_of_map_split(map_of_array(a), 0, i, len + 1); + pure assert map_of_array(a) == ms[i := k]; + + return i, len + 1, map_of_array(a); +} + +lemma arr_delete_content_set(m: Map, m1: Map, len: Int, new_len: Int, idx: Int, k: K) + requires sorted_map_seg(m, 0, len) + requires idx == map_find(m, 0, len, k) + requires idx == len || m[idx] != k ==> new_len == len && m1 == m + requires idx < len && m[idx] == k ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) + ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) -- {k} +{ + arr_find_content_set(m, len, idx, k); + set_of_map_split(m, 0, idx, len); + + if (idx == len || m[idx] != k) { + return; + } + + // prove: set_of_map(m, 0, len) -- {k} == set_of_map(m1, 0, idx - 1) ++ set_of_map(m1, idx - 1, len - 1) + set_of_map_equal(m, m1, 0, 0, idx); + set_of_map_equal(m, m1, idx + 1, idx, len - (idx + 1)); - return i, len + 1; + not_in_sorted_seg(m, 0, idx - 1, k); + not_in_sorted_seg(m, idx + 1, len, k); + + set_of_map_split(m1, 0, idx, len - 1); } // Given a sorted array segment `a[0..len]`, // delete `k` from the segment while preserving sortedness. // If `k` is already contained in `a[0..len]`, then do not modify `a`. -procedure delete(a: Array, k: K, len: Int, implicit ghost C: Set) - returns (new_len: Int) - requires sorted_array_with_content(a, len, C) - ensures sorted_array_with_content(a, new_len, C -- {k}) - ensures k !in C ==> new_len == len - ensures k in C ==> new_len == len - 1 +procedure arr_delete(a: Array, k: K, len: Int, implicit ghost m: Map) + returns (new_len: Int, idx: Int, implicit ghost m1: Map) + requires sorted_array_with_content(a, len, m) + ensures sorted_array_with_content(a, new_len, m1) + ensures idx == map_find(m, 0, len, k) + ensures idx == len || m[idx] != k ==> new_len == len && m1 == m + ensures idx < len && m[idx] == k ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) { // find position for insertion - var i: Int, found: Bool; - found, i := find(a, len, k); + var found: Bool; + found, idx := arr_find(a, len, k); // k !in C? - if (!found) return len; - - // shift array entries a[i+1..len] by 1 entry to the left - ghost var m := map_of_array(a); - shift(a, i + 1, i, len - (i + 1)); - - // prove C -- {k} == set_of_array(a, 0, i - 1) ++ set_of_array(a, i - 1, len) - if (i > 0) { - set_of_map_split(m, 0, i, len); + if (!found) { + return len, idx, m; } - set_of_map_equal(m, map_of_array(a), 0, 0, i); - set_of_map_equal(m, map_of_array(a), i + 1, i, len - (i + 1)); - - not_in_sorted_seg(m, 0, i - 1, k); - not_in_sorted_seg(m, i + 1, len, k); - - set_of_map_split(map_of_array(a), 0, i, len - 1); + // shift array entries a[i+1..len] by 1 entry to the left + //ghost var m := map_of_array(a); + arr_shift(a, idx + 1, idx, len - (idx + 1)); - return len - 1; + return len - 1, idx, map_of_array(a); } From aa8c519a5cf22eeeaa35cef141bcefd0ff8c810b Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 16 Apr 2018 20:06:51 -0400 Subject: [PATCH 035/118] clean up specs --- tests/spl/sorted_array_util/array_util.spl | 72 +++++++++++++--------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index 2454694f..4a513982 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -5,7 +5,7 @@ include "../include/ordered_type.spl" // project map segment m[i..j] to set of its elements function set_of_map(m: Map, i: Int, j: Int) returns (res: Set) - requires 0 <= i <= j + requires i <= j { i < j ? {m[i]} ++ set_of_map(m, i + 1, j) : {} } @@ -20,7 +20,7 @@ lemma extend_right(m: Map, i: Int, j: Int) } lemma in_set_of_map(m: Map, i: Int, j: Int) - requires 0 <= i <= j + requires i <= j ensures forall k: Int :: i <= k < j ==> m[k] in set_of_map(m, i, j) { if (i < j) { @@ -212,26 +212,33 @@ function map_find(m: Map, i: Int, j: Int, k: K) returns (idx: Int) requires sorted_map_seg(m, i, j) ensures i <= idx <= j ensures m[idx] == k || idx == j || lt(k, m[idx]) - ensures 0 < idx ==> lt(m[idx - 1], k) + ensures i < idx ==> lt(m[idx - 1], k) { i < j && lt(m[i], k) ? map_find(m, i + 1, j, k) : i } -lemma arr_find_content_set(m: Map, len: Int, idx: Int, k: K) - requires sorted_map_seg(m, 0, len) - requires idx == map_find(m, 0, len, k) - //requires 0 <= idx <= len - //requires m[idx] == k || idx == len || lt(k, m[idx]) - //requires 0 < idx ==> lt(m[idx - 1], k) - ensures k !in set_of_map(m, 0, idx) - ensures k !in set_of_map(m, idx + 1, len) +lemma map_find_in_set(m: Map, i: Int, j: Int, k: K) + requires i <= j + requires sorted_map_seg(m, i, j) + ensures k in set_of_map(m, i, j) ==> map_find(m, i, j, k) < j && m[map_find(m, i, j, k)] == k + ensures k !in set_of_map(m, i, j) ==> map_find(m, i, j, k) == j || lt(k, m[map_find(m, i, j, k)]) { - // prove k !in set_of_array(a, 0, idx) - not_in_sorted_seg(m, 0, idx, k); - // prove k !in set_of_array(a, idx + 1, len) - not_in_sorted_seg(m, idx + 1, len, k); - // prove found == (k in C) - set_of_map_split(m, 0, idx, len); + var idx := map_find(m, i, j, k); + map_find_content_set(m, i, j, idx, k); + set_of_map_split(m, i, idx, j); +} + +lemma map_find_content_set(m: Map, i: Int, j: Int, idx: Int, k: K) + requires i <= j + requires sorted_map_seg(m, i, j) + requires idx == map_find(m, i, j, k) + ensures k !in set_of_map(m, i, idx) + ensures k !in set_of_map(m, idx + 1, j) +{ + // prove k !in set_of_map(a, 0, idx) + not_in_sorted_seg(m, i, idx, k); + // prove: k !in set_of_map(a, idx + 1, len) + not_in_sorted_seg(m, idx + 1, j, k); } // Find key `k` in sorted array segment `a[0..len]` using binary search @@ -242,8 +249,7 @@ procedure arr_find(a: Array, len: Int, k: K, implicit ghost m: Map) ensures sorted_array_with_content(a, len, m) // what we actually care about ensures idx == map_find(m, 0, len, k) - ensures found ==> idx < len && m[idx] == k - ensures !found ==> idx == len || lt(k, m[idx]) + ensures found == (k in set_of_map(m, 0, len)) { var lo := 0; var hi := len; @@ -276,14 +282,16 @@ procedure arr_find(a: Array, len: Int, k: K, implicit ghost m: Map) found := false; } else { found := true; - } + } + + map_find_in_set(m, 0, len, k); } -lemma arr_insert_content_set(m: Map, m1: Map, idx: Int, k: K, len: Int, new_len: Int) +lemma map_insert_content_set(m: Map, m1: Map, idx: Int, k: K, len: Int, new_len: Int) requires sorted_map_seg(m, 0, len) requires idx == map_find(m, 0, len, k) - requires m[idx] == k && idx < len ==> new_len == len && m1 == m - requires m[idx] != k || idx == len ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] + requires k in set_of_map(m, 0, len) ==> new_len == len && m1 == m + requires k !in set_of_map(m, 0, len) ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) ++ {k} { if (m[idx] == k && idx < len) { @@ -313,12 +321,14 @@ procedure arr_insert(a: Array, k: K, len: Int, implicit ghost m: Map) requires len < a.length ensures sorted_array_with_content(a, new_len, m1) ensures idx == map_find(m, 0, len, k) - ensures m[idx] == k && idx < len ==> new_len == len && m1 == m - ensures m[idx] != k || idx == len ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] + ensures k in set_of_map(m, 0, len) ==> new_len == len && m1 == m + ensures k !in set_of_map(m, 0, len) ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] { // find position for insertion var i: Int, found: Bool; found, i := arr_find(a, len, k); + + map_find_in_set(m, 0, len, k); // k already in C? if (found) return i, len, map_of_array(a); @@ -337,11 +347,11 @@ procedure arr_insert(a: Array, k: K, len: Int, implicit ghost m: Map) lemma arr_delete_content_set(m: Map, m1: Map, len: Int, new_len: Int, idx: Int, k: K) requires sorted_map_seg(m, 0, len) requires idx == map_find(m, 0, len, k) - requires idx == len || m[idx] != k ==> new_len == len && m1 == m - requires idx < len && m[idx] == k ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) + requires k !in set_of_map(m, 0, len) ==> new_len == len && m1 == m + requires k in set_of_map(m, 0, len) ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) -- {k} { - arr_find_content_set(m, len, idx, k); + map_find_content_set(m, 0, len, idx, k); set_of_map_split(m, 0, idx, len); if (idx == len || m[idx] != k) { @@ -366,12 +376,14 @@ procedure arr_delete(a: Array, k: K, len: Int, implicit ghost m: Map) requires sorted_array_with_content(a, len, m) ensures sorted_array_with_content(a, new_len, m1) ensures idx == map_find(m, 0, len, k) - ensures idx == len || m[idx] != k ==> new_len == len && m1 == m - ensures idx < len && m[idx] == k ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) + ensures k !in set_of_map(m, 0, len) ==> new_len == len && m1 == m + ensures k in set_of_map(m, 0, len) ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) { // find position for insertion var found: Bool; found, idx := arr_find(a, len, k); + + map_find_in_set(m, 0, len, k); // k !in C? if (!found) { From cfe5717e0040094680dfc3d6207329a74e1e94d6 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 17 Apr 2018 00:53:54 -0400 Subject: [PATCH 036/118] fixes #9 --- src/frontends/spl/splChecker.ml | 8 +++++++- src/frontends/spl/splParser.mly | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 783f85af..34e291fd 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -335,6 +335,12 @@ let resolve_names cu = | LocalVars (vars, es_opt, pos) -> let es_opt1, tys = match es_opt with + | Some [ProcCall (init_id, _, _) as e] -> + let e1 = resolve_expr locals tbl e in + let id = lookup_id init_id tbl pos in + let decl = IdMap.find id cu.proc_decls in + let tys = List.map (fun v -> (IdMap.find v decl.p_locals).v_type) decl.p_returns in + Some [e1], tys | Some es -> let es1, tys = Util.map_split (fun e -> @@ -663,7 +669,7 @@ let flatten_exprs cu = (fun (stmts, locals, aux_funs) stmt0 -> let stmt, locals, aux_funs = flatten pos locals aux_funs returns stmt0 in stmt :: stmts, locals, aux_funs - ) + ) ([], locals, aux_funs) stmts0 in Block (List.rev stmts, pos), locals, aux_funs | LocalVars (_, _, pos) -> diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 767399d6..4c03541f 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -487,11 +487,11 @@ stmt_wo_trailing_substmt: /* variable declaration */ | ghost_modifier VAR var_decls SEMICOLON { let decls = List.map (fun decl -> { decl with v_ghost = $1 }) $3 in - LocalVars (decls, None, mk_position 1 4) + LocalVars (decls, None, mk_position (if $1 then 1 else 2) 4) } | ghost_modifier VAR var_decls_opt_type COLONEQ expr_list SEMICOLON { let decls = List.map (fun decl -> { decl with v_ghost = $1 }) $3 in - LocalVars (decls, Some $5, mk_position 1 6) + LocalVars (decls, Some $5, mk_position (if $1 then 1 else 2) 6) } /* nested block */ | LBRACE block RBRACE { From cfe40ea67319e0c68bdd2416ed694c0c7cb97f5c Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 17 Apr 2018 01:07:21 -0400 Subject: [PATCH 037/118] add -abspreds option --- tests/spl/recursive_defs/list.spl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/spl/recursive_defs/list.spl b/tests/spl/recursive_defs/list.spl index 7515504d..2421fd53 100644 --- a/tests/spl/recursive_defs/list.spl +++ b/tests/spl/recursive_defs/list.spl @@ -1,4 +1,6 @@ -include "../include/sllist_rec.spl"; +options "-abspreds" + +include "../include/sllist_rec.spl" predicate lseg_grass(x: Node, y: Node) { acc({ z: Node :: Btwn(next, x, z, y) && z != y }) &*& Reach(next, x, y) From 528ed2fe4abae00905a9af85fda6337cc0dcb5e8 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 17 Apr 2018 01:20:51 -0400 Subject: [PATCH 038/118] some comments --- src/frontends/spl/splChecker.ml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 34e291fd..ed85a014 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -20,13 +20,6 @@ let resolve_names cu = if not (IdMap.mem id types) then not_a_type_error id pos in - (*let check_struct id types pos = - check_type id types pos; - let decl = IdMap.find id types in - match decl.t_def with - | StructTypeDef _ -> () - | _ -> not_a_struct_error id pos - in*) let check_proc id procs pos = if not (IdMap.mem id procs) then not_a_proc_error id pos @@ -173,6 +166,7 @@ let resolve_names cu = fun_decls = funs; } in + (* declare and resolve local variables in given expression *) let resolve_expr locals tbl e = let rec re locals tbl = function | Setenum (ty, args, pos) -> @@ -320,6 +314,7 @@ let resolve_names cu = in re locals tbl e in + (* declare and resolve local variables in given statement *) let rec resolve_stmt first_block in_loop locals tbl = function | Skip pos -> Skip pos, locals, tbl | Block (stmts0, pos) -> @@ -335,12 +330,14 @@ let resolve_names cu = | LocalVars (vars, es_opt, pos) -> let es_opt1, tys = match es_opt with + (* var x1, ..., xn := p(e1, ..., em); *) | Some [ProcCall (init_id, _, _) as e] -> let e1 = resolve_expr locals tbl e in let id = lookup_id init_id tbl pos in let decl = IdMap.find id cu.proc_decls in let tys = List.map (fun v -> (IdMap.find v decl.p_locals).v_type) decl.p_returns in Some [e1], tys + (* var x1, ..., xn := e1, ..., en; *) | Some es -> let es1, tys = Util.map_split (fun e -> @@ -348,6 +345,7 @@ let resolve_names cu = e1, type_of_expr cu locals e1) es in Some es1, tys + (* var x1: T1, ..., xn: Tn; *) | None -> None, List.map (fun decl -> decl.v_type) vars in @@ -421,7 +419,7 @@ let resolve_names cu = if in_loop then return_in_loop_error pos; Return (List.map (resolve_expr locals tbl) es, pos), locals, tbl in - (* declare and resolve local variables *) + (* declare and resolve local variables in given contracts *) let resolve_contracts contracts returns locals tbl = let pre_locals, pre_tbl = List.fold_left @@ -438,6 +436,7 @@ let resolve_names cu = ) contracts in + (* declare and resolve local variables in all procedures *) let procs = IdMap.fold (fun _ decl procs -> @@ -467,6 +466,7 @@ let resolve_names cu = ) procs0 IdMap.empty in + (* declare and resolve local variables in all predicates *) let preds = IdMap.fold (fun _ decl preds -> @@ -488,6 +488,7 @@ let resolve_names cu = ) preds0 IdMap.empty in + (* declare and resolve local variables in all axioms *) let bg_theory = List.map (fun (e, pos) -> resolve_expr globals tbl e, pos) @@ -568,6 +569,7 @@ let flatten_exprs cu = (match b with | Exists | Forall -> Binder (b, vars1, f1, pos), aux, locals | Comp -> + (* create auxiliary function for set/map comprehension *) let v_decl = match vars1 with | [UnguardedVar decl] -> decl @@ -862,6 +864,7 @@ let infer_types cu = let ty = if pure then BoolType else PermType in infer_types cu locals ty e in + (* infer types within given statement *) let rec check_stmt proc = let locals = proc.p_locals in function From acba02fe529650f558a38bf11086080a989c7484 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Mon, 16 Apr 2018 15:45:51 -0400 Subject: [PATCH 039/118] modelRepl: add support for a few more things --- src/prover/modelRepl.ml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/prover/modelRepl.ml b/src/prover/modelRepl.ml index 8fd8fb6e..dad8f404 100644 --- a/src/prover/modelRepl.ml +++ b/src/prover/modelRepl.ml @@ -47,11 +47,15 @@ let rec term_of_expr model e = let t = term_of_expr model t in mk_read map [t] | ProcCall (id, ts, _) - | PredApp (Pred id, ts, _) -> + | PredApp (Pred id, ts, _) -> (* Function application *) let ts = List.map (term_of_expr model ) ts in let sym = FreeSym id in let (arg_srts, res_srt) = get_sign sym in mk_app res_srt sym ts + | BinaryOp (e1, OpIn, e2, _, _) -> + mk_elem_term (term_of_expr model e1) (term_of_expr model e2) + | BinaryOp (e1, OpEq, e2, _, _) -> + mk_eq_term (term_of_expr model e1) (term_of_expr model e2) | e -> fail ("TODO: can't yet handle: " ^ string_of_expr e) let repl model = @@ -88,7 +92,11 @@ let repl model = Parsing.clear_parser (); repl_loop () | Undefined -> - print_string "Model: Undefined.\n"; + print_string "Error: Model says undefined.\n"; + Parsing.clear_parser (); + repl_loop () + | Not_found -> + print_string "Error: Model says Not_found.\n"; Parsing.clear_parser (); repl_loop () | End_of_file -> print_endline "\n"; From 67808704a07caac3b0b492357c7b982e6a4a6071 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 17 Apr 2018 15:32:21 -0400 Subject: [PATCH 040/118] fix fix for #9 --- src/frontends/spl/splChecker.ml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index ed85a014..7edec5be 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -331,11 +331,22 @@ let resolve_names cu = let es_opt1, tys = match es_opt with (* var x1, ..., xn := p(e1, ..., em); *) - | Some [ProcCall (init_id, _, _) as e] -> + | Some [ProcCall (_, _, _) as e] -> let e1 = resolve_expr locals tbl e in - let id = lookup_id init_id tbl pos in - let decl = IdMap.find id cu.proc_decls in - let tys = List.map (fun v -> (IdMap.find v decl.p_locals).v_type) decl.p_returns in + let tys = match e1 with + | ProcCall (id, _, _) + | PredApp (Pred id, _, _) -> + let returns, locals = + try + let decl = IdMap.find id cu.proc_decls in + decl.p_returns, decl.p_locals + with Not_found -> + let decl = IdMap.find id cu.pred_decls in + decl.pr_outputs, decl.pr_locals + in + List.map (fun v -> (IdMap.find v locals).v_type) returns + | e -> [type_of_expr cu locals e1] + in Some [e1], tys (* var x1, ..., xn := e1, ..., en; *) | Some es -> From 9152ae8bc9cfc0bb14f878b0291e567ac788be0c Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 23 Apr 2018 19:35:40 -0400 Subject: [PATCH 041/118] add support for implicit return parameters --- src/frontends/spl/splChecker.ml | 16 ++- src/verifier/simplifier.ml | 138 +++++++++++++-------- src/verifier/verifier.ml | 17 ++- tests/spl/sorted_array_util/array_util.spl | 8 +- 4 files changed, 114 insertions(+), 65 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 7edec5be..9701ebd7 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -339,7 +339,8 @@ let resolve_names cu = let returns, locals = try let decl = IdMap.find id cu.proc_decls in - decl.p_returns, decl.p_locals + let returns = List.filter (fun p -> not (IdMap.find p decl.p_locals).v_implicit) decl.p_returns in + returns, decl.p_locals with Not_found -> let decl = IdMap.find id cu.pred_decls in decl.pr_outputs, decl.pr_locals @@ -632,8 +633,9 @@ let flatten_exprs cu = ) | ProcCall (id, args, pos) -> let pdecl = IdMap.find id cu.proc_decls in + let returns = List.filter (fun p -> not (IdMap.find p pdecl.p_locals).v_implicit) pdecl.p_returns in let res_type = - match pdecl.p_returns with + match returns with | [res] -> let rdecl = IdMap.find res pdecl.p_locals in rdecl.v_type @@ -892,17 +894,18 @@ let infer_types cu = Split (check_spec locals false e, pos) | Assign (lhs, [ProcCall (id, args, cpos) as e], pos) -> let decl = IdMap.find id cu.proc_decls in + let returns = List.filter (fun p -> not (IdMap.find p decl.p_locals).v_implicit) decl.p_returns in let rtys = List.map (fun fid -> let vdecl = IdMap.find fid decl.p_locals in vdecl.v_type) - decl.p_returns + returns in let lhs1 = try List.map2 (infer_types cu locals) rtys lhs with Invalid_argument _ -> ProgError.error cpos - (Printf.sprintf "Procedure %s has %d return value(s)" + (Printf.sprintf "Procedure %s has %d explicit return value(s)" (fst id) (List.length rtys)) in let e1 = infer_types cu locals AnyType e in @@ -972,14 +975,15 @@ let infer_types cu = let postb1 = check_stmt proc postb in Loop (inv1, preb1, cond1, postb1, pos) | Return (es, pos) -> + let returns = List.filter (fun x -> not (IdMap.find x proc.p_locals).v_implicit) proc.p_returns in let rtys = - List.map (fun id -> (IdMap.find id locals).v_type) proc.p_returns + List.map (fun id -> (IdMap.find id locals).v_type) returns in let es1 = try List.map2 (infer_types cu locals) rtys es with Invalid_argument _ -> ProgError.error pos - (Printf.sprintf "Procedure %s returns %d values(s), found %d" + (Printf.sprintf "Procedure %s returns %d explicit values(s), found %d" (fst proc.p_name) (List.length rtys) (List.length es)) in Return (es1, pos) diff --git a/src/verifier/simplifier.ml b/src/verifier/simplifier.ml index 78a0c259..019be133 100644 --- a/src/verifier/simplifier.ml +++ b/src/verifier/simplifier.ml @@ -336,6 +336,40 @@ let elim_global_deps prog = let prog2 = map_preds elim_pred prog1 in { prog2 with prog_axioms = List.map elim_spec prog2.prog_axioms } + +(** Helper function that existentially quantifies the variables in [vs] that are implicits + * according to local variable map [locals] in the spec forms [sfs] *) +let quantify_implicits_in_specs locals vs sfs = + let implicits = + List.filter + (fun id -> (IdMap.find id locals).var_is_implicit) + vs + in + let implicits_w_sorts, implicits_var_subst = + List.fold_left + (fun (implicits_w_sorts, implicits_var_subst) id -> + let decl = IdMap.find id locals in + (id, decl.var_sort) :: implicits_w_sorts, + IdMap.add id (mk_var decl.var_sort id) implicits_var_subst) + ([], IdMap.empty) implicits + in + let fs, aux = + List.fold_left (fun (fs, aux) sf -> + let new_aux = + match aux with + | Some (_, _, p) -> + Some (sf.spec_name, sf.spec_msg, merge_src_pos p sf.spec_pos) + | None -> Some (sf.spec_name, sf.spec_msg, sf.spec_pos) + in + form_of_spec sf :: fs, new_aux) + ([], None) sfs + in + List.map + (fun (name, msg, pos) -> + let f_pos = mk_exists implicits_w_sorts (subst_consts implicits_var_subst (mk_and fs)) in + mk_spec_form (FOL f_pos) name msg pos) (Util.Opt.to_list aux) + + (** Eliminate all return commands. ** Assumes that all SL formulas have been desugared. *) let elim_return prog = @@ -362,7 +396,9 @@ let elim_return prog = | FOL f -> oldify_spec (id_set_of_list (formals_of_proc proc)) sf | SL _ -> failwith "elim_return: Found SL formula that should have been desugared.") (postcond_of_proc proc) - in fun pos -> List.map (fun sf -> mk_assert_cmd sf pos) posts + in fun pos -> + let posts = quantify_implicits_in_specs (locals_of_proc proc) (returns_of_proc proc) posts in + List.map (fun sf -> mk_assert_cmd sf pos) posts in let body = (* add final check of postcondition at the end of procedure body *) @@ -381,8 +417,8 @@ let elim_return prog = mk_seq_cmd (body :: mk_postcond_check return_pos) (prog_point body).pp_pos) proc.proc_body in - Util.Opt.map (map_basic_cmds (elim (returns_of_proc proc) mk_postcond_check)) body1 - + let returns = List.filter (fun x -> not (IdMap.find x (locals_of_proc proc)).var_is_implicit) (returns_of_proc proc) in + Util.Opt.map (map_basic_cmds (elim returns mk_postcond_check)) body1 in { proc with proc_body = body } in @@ -402,7 +438,7 @@ let elim_state prog = let decl1 = { decl with var_name = id1; - var_is_implicit = false; + (*var_is_implicit = false;*) (*var_is_aux = true;*) var_pos = pos; } @@ -602,33 +638,8 @@ let elim_state prog = mk_assume_cmd sf1 pp.pp_pos) preconds_w_implicits in - (* skolemize implicits *) - let preconds_w_implicits = - let fs, aux = - List.fold_left (fun (fs, aux) sf -> - let new_aux = - match aux with - | Some (_, _, p) -> - Some (sf.spec_name, sf.spec_msg, merge_src_pos p sf.spec_pos) - | None -> Some (sf.spec_name, sf.spec_msg, sf.spec_pos) - in - form_of_spec sf :: fs, new_aux) - ([], None) preconds_w_implicits - in - List.map - (fun (name, msg, pos) -> - let implicits_w_sorts, implicits_var_subst = - List.fold_left - (fun (implicits_w_sorts, implicits_var_subst) id -> - let decl = IdMap.find id locals in - (id, decl.var_sort) :: implicits_w_sorts, - IdMap.add id (mk_var decl.var_sort id) implicits_var_subst) - ([], subst_old) implicits - in - let f_pos = mk_exists implicits_w_sorts (subst_consts implicits_var_subst (mk_and fs)) in - mk_spec_form (FOL f_pos) name msg pos) - (Util.Opt.to_list aux) - in + (* quantify implicits *) + let preconds_w_implicits = quantify_implicits_in_specs locals implicits preconds_w_implicits in List.map (fun sf -> mk_assert_cmd sf pp.pp_pos) (preconds_wo_implicits @ preconds_w_implicits), assume_precond_implicits in @@ -648,9 +659,9 @@ let elim_state prog = let sf = mk_spec_form (FOL (mk_and eqs)) "havoc" None pp.pp_pos in [mk_assume_cmd sf pp.pp_pos] in - (* compute substitution for postcondition *) - let subst_post = - (* substitute formal parameters to actual parameters *) + (* compute postcondition with all formals substituted by actuals *) + let postconds = + (* substitute formal parameters to actual parameters *) let subst_wo_old_mods_formals = List.fold_left (fun sm id -> @@ -658,39 +669,64 @@ let elim_state prog = subst_pre callee_formals in (* substitute formal return parameters to actual return parameters *) - let subst_wo_old_mods = + let implicit_returns, explicit_returns = + List.partition + (fun id -> (IdMap.find id callee_locals).var_is_implicit) + callee_returns + in + let subst_post_explicit = List.fold_left2 (fun sm id rtn_id -> let decl = IdMap.find rtn_id locals in IdMap.add id (mk_free_const decl.var_sort rtn_id) sm) subst_wo_old_mods_formals - callee_returns + explicit_returns (List.map (fun id -> IdMap.find id sm1) cc.call_lhs) in + (* substitution map for implicit return parameters *) + let subst_implicits, locals = + fresh callee_decl IdMap.empty locals pp.pp_pos implicit_returns + in + (* substitution map for formals to actuals *) + let subst_post = + to_term_subst_merge subst_implicits locals subst_post_explicit + in (* TODO: I currently have no idea what this was supposed to do. Is this redundant? *) let subst_wo_old = List.fold_left (fun sm id -> IdMap.add id (IdMap.find id (to_term_subst sm1 locals)) sm) - subst_wo_old_mods + subst_post mods in (* substitute old versions of global variables *) - IdMap.fold - (fun id decl subst_post -> - let old_id = try IdMap.find id sm with Not_found -> id in - IdMap.add (oldify id) (mk_free_const decl.var_sort old_id) subst_post) - prog.prog_vars subst_wo_old + let subst_post = + IdMap.fold + (fun id decl subst_post -> + let old_id = try IdMap.find id sm with Not_found -> id in + IdMap.add (oldify id) (mk_free_const decl.var_sort old_id) subst_post) + prog.prog_vars subst_wo_old + in + (* apply substitution to post conditions *) + let postconds = + List.map + (fun sf -> + let old_sf = oldify_spec (id_set_of_list callee_formals) sf in + let sf1 = subst_spec subst_post old_sf in + map_spec_fol_form strip_error_msgs sf1) + (postcond_of_proc callee_decl) + in + let implicits = List.map (fun id -> IdMap.find id subst_implicits) implicit_returns in + let implicitss = id_set_of_list implicits in + let postconds_wo_implicits, postconds_w_implicits = + List.partition + (fun sf -> IdSet.is_empty (IdSet.inter (free_consts (form_of_spec sf)) implicitss)) + postconds + in + (* quantify implicit returns *) + postconds_wo_implicits @ quantify_implicits_in_specs locals implicits postconds_w_implicits in (* assume updated postcondition *) - let assume_postcond = - List.map - (fun sf -> - let old_sf = oldify_spec (id_set_of_list callee_formals) sf in - let sf1 = subst_spec subst_post old_sf in - let sf2 = map_spec_fol_form strip_error_msgs sf1 in - mk_assume_cmd sf2 pp.pp_pos) - (postcond_of_proc callee_decl) - in + let assume_postcond = List.map (fun postcond -> mk_assume_cmd postcond pp.pp_pos) postconds in sm1, locals, mk_seq_cmd (assert_precond @ assume_precond_implicits @ mods_havoc @ assume_postcond) pp.pp_pos | _ -> sm, locals, Basic (bc, pp) in diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 57c72c02..5b78e95c 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -23,25 +23,34 @@ let simplify proc prog = let info msg prog = Debug.info (fun () -> msg); prog in prog |> dump_if 0 |> - info "Inferring accesses, eliminating loops, arrays, new/dispose, and global dependencies.\n" |> + info "Encoding arrays.\n" |> elim_arrays |> + info "Adding checks for run-time errors.\n" |> annotate_runtime_checks |> + info "Eliminating new/free.\n" |> elim_new_dispose |> + info "Inferring accesses.\n" |> Analyzer.infer_accesses |> + info "Pruning uncalled procedures and predicates.\n" |> Simplifier.prune_uncalled init_procs |> + info "Eliminating loops.\n" |> elim_loops |> + info "Eliminating dependencies on global state.\n" |> elim_global_deps |> dump_if 1 |> - info "Eliminating SL, adding frame axioms.\n" |> + info "Eliminating SL specifications.\n" |> elim_sl |> Analyzer.infer_accesses |> + info "Eliminating unused formal parameters.\n" |> elim_unused_formals |> + info "Adding frame axioms.\n" |> add_frame_axioms |> (*(fun prog -> if !Config.abstract_preds then annotate_frame_axioms prog else prog) |> *) (*annotate_term_generators |>*) dump_if 2 |> - info "Eliminating return statements and transforming to SSA form.\n" |> + info "Eliminating return statements.\n" |> elim_return |> + info "Transforming to SSA.\n" |> elim_state |> dump_if 3 @@ -507,7 +516,7 @@ let check_proc prog proc = let check_vc errors (vc_name, (vc_msg, pp), vc0, labels) = let check_one vc = if errors <> [] && not !Config.robust then errors else begin - Debug.info (fun () -> "Checking VC: " ^ vc_name ^ ".\n"); + Debug.info (fun () -> "\t" ^ vc_name ^ ".\n"); Debug.debug (fun () -> (string_of_form vc) ^ "\n"); (*IdMap.iter (fun id (pos, c) -> Printf.printf ("%s -> %s: %s\n") (string_of_ident id) (string_of_src_pos pos) c) labels;*) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index 4a513982..44727c13 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -331,7 +331,7 @@ procedure arr_insert(a: Array, k: K, len: Int, implicit ghost m: Map) map_find_in_set(m, 0, len, k); // k already in C? - if (found) return i, len, map_of_array(a); + if (found) return i, len; arr_shift(a, i, i + 1, len - i); @@ -341,7 +341,7 @@ procedure arr_insert(a: Array, k: K, len: Int, implicit ghost m: Map) pure assert map_of_array(a) == ms[i := k]; - return i, len + 1, map_of_array(a); + return i, len + 1; } lemma arr_delete_content_set(m: Map, m1: Map, len: Int, new_len: Int, idx: Int, k: K) @@ -387,12 +387,12 @@ procedure arr_delete(a: Array, k: K, len: Int, implicit ghost m: Map) // k !in C? if (!found) { - return len, idx, m; + return len, idx; } // shift array entries a[i+1..len] by 1 entry to the left //ghost var m := map_of_array(a); arr_shift(a, idx + 1, idx, len - (idx + 1)); - return len - 1, idx, map_of_array(a); + return len - 1, idx; } From f1c8371529df77bd4d445851ca25788f98e1202d Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Tue, 24 Apr 2018 15:17:39 -0400 Subject: [PATCH 042/118] splLexer: allow ? in identifier names --- src/frontends/spl/splLexer.mll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontends/spl/splLexer.mll b/src/frontends/spl/splLexer.mll index e4747702..df331f79 100644 --- a/src/frontends/spl/splLexer.mll +++ b/src/frontends/spl/splLexer.mll @@ -109,7 +109,7 @@ let hexa_to_int num = let digitchar = ['0'-'9'] let idchar = ['A'-'Z''a'-'z''_'] -let ident = ('?' idchar | idchar) (idchar | digitchar)* +let ident = (idchar | digitchar)* ('?' idchar | idchar) (idchar | digitchar)* let digits = digitchar+ rule token = parse From b3286e90310e6992d9aee750b19bb32c11dce27d Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Tue, 24 Apr 2018 15:17:58 -0400 Subject: [PATCH 043/118] modelRepl: incremental support for more terms --- src/prover/modelRepl.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/prover/modelRepl.ml b/src/prover/modelRepl.ml index dad8f404..8fb3251a 100644 --- a/src/prover/modelRepl.ml +++ b/src/prover/modelRepl.ml @@ -42,6 +42,8 @@ let rec term_of_expr model e = fail (sprintf "Symbol %s has arity %s but was applied to %s: %s" (string_of_ident id) (string_of_arity arity) (string_of_term t) (sort_of t |> string_of_sort))) + | Read ((Ident (("length", _), _)), idx, _) -> + mk_length (term_of_expr model idx) | Read (map, t, _) -> let map = term_of_expr model map in let t = term_of_expr model t in From 4736dd77ea067009c715cf07ea680c1a20680333 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Tue, 24 Apr 2018 15:18:32 -0400 Subject: [PATCH 044/118] array_utils: separate specifications and programs for line counting --- tests/spl/sorted_array_util/array_util.spl | 168 +++++++++++---------- 1 file changed, 85 insertions(+), 83 deletions(-) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index 44727c13..409ca976 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -133,6 +133,91 @@ predicate sorted_array_with_content(a: Array, len: Int, m: Map) } +function map_find(m: Map, i: Int, j: Int, k: K) returns (idx: Int) + requires i <= j + requires sorted_map_seg(m, i, j) + ensures i <= idx <= j + ensures m[idx] == k || idx == j || lt(k, m[idx]) + ensures i < idx ==> lt(m[idx - 1], k) +{ + i < j && lt(m[i], k) ? map_find(m, i + 1, j, k) : i +} + +lemma map_find_in_set(m: Map, i: Int, j: Int, k: K) + requires i <= j + requires sorted_map_seg(m, i, j) + ensures k in set_of_map(m, i, j) ==> map_find(m, i, j, k) < j && m[map_find(m, i, j, k)] == k + ensures k !in set_of_map(m, i, j) ==> map_find(m, i, j, k) == j || lt(k, m[map_find(m, i, j, k)]) +{ + var idx := map_find(m, i, j, k); + map_find_content_set(m, i, j, idx, k); + set_of_map_split(m, i, idx, j); +} + +lemma map_find_content_set(m: Map, i: Int, j: Int, idx: Int, k: K) + requires i <= j + requires sorted_map_seg(m, i, j) + requires idx == map_find(m, i, j, k) + ensures k !in set_of_map(m, i, idx) + ensures k !in set_of_map(m, idx + 1, j) +{ + // prove k !in set_of_map(a, 0, idx) + not_in_sorted_seg(m, i, idx, k); + // prove: k !in set_of_map(a, idx + 1, len) + not_in_sorted_seg(m, idx + 1, j, k); +} + +lemma map_insert_content_set(m: Map, m1: Map, idx: Int, k: K, len: Int, new_len: Int) + requires sorted_map_seg(m, 0, len) + requires idx == map_find(m, 0, len, k) + requires k in set_of_map(m, 0, len) ==> new_len == len && m1 == m + requires k !in set_of_map(m, 0, len) ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] + ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) ++ {k} +{ + if (m[idx] == k && idx < len) { + in_set_of_map(m, 0, len); + return; + } + + var ms := map_shift(m, idx, idx + 1, len - idx); + + // prove set_of_map(m, 0, len) == set_of_map(ms, 0, idx) ++ set_of_array(ms, idx + 1, len + 1); + set_of_map_split(m, 0, idx, len); + set_of_map_equal(m, ms, 0, 0, idx); + set_of_map_equal(m, ms, idx, idx + 1, len - idx); + + // prove set_of_map(m1, 0, new_len) == set_of_array(ms, 0, idx) ++ {k} ++ set_of_map(ms, idx + 1, len + 1) + frame_set_of_map(ms, 0, idx); + frame_set_of_map(ms, idx + 1, len + 1); + set_of_map_split(m1, 0, idx, len + 1); +} + +lemma arr_delete_content_set(m: Map, m1: Map, len: Int, new_len: Int, idx: Int, k: K) + requires sorted_map_seg(m, 0, len) + requires idx == map_find(m, 0, len, k) + requires k !in set_of_map(m, 0, len) ==> new_len == len && m1 == m + requires k in set_of_map(m, 0, len) ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) + ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) -- {k} +{ + map_find_content_set(m, 0, len, idx, k); + set_of_map_split(m, 0, idx, len); + + if (idx == len || m[idx] != k) { + return; + } + + // prove: set_of_map(m, 0, len) -- {k} == set_of_map(m1, 0, idx - 1) ++ set_of_map(m1, idx - 1, len - 1) + set_of_map_equal(m, m1, 0, 0, idx); + set_of_map_equal(m, m1, idx + 1, idx, len - (idx + 1)); + + not_in_sorted_seg(m, 0, idx - 1, k); + not_in_sorted_seg(m, idx + 1, len, k); + + set_of_map_split(m1, 0, idx, len - 1); +} + +/** Programs */ + // Shift a[src..src+len] to a[dst..dst+len] procedure arr_shift(a: Array, src: Int, dst: Int, len: Int) requires acc(a) @@ -207,40 +292,6 @@ procedure arr_copy(a: Array, b: Array, src: Int, dst: Int, len: Int) } } -function map_find(m: Map, i: Int, j: Int, k: K) returns (idx: Int) - requires i <= j - requires sorted_map_seg(m, i, j) - ensures i <= idx <= j - ensures m[idx] == k || idx == j || lt(k, m[idx]) - ensures i < idx ==> lt(m[idx - 1], k) -{ - i < j && lt(m[i], k) ? map_find(m, i + 1, j, k) : i -} - -lemma map_find_in_set(m: Map, i: Int, j: Int, k: K) - requires i <= j - requires sorted_map_seg(m, i, j) - ensures k in set_of_map(m, i, j) ==> map_find(m, i, j, k) < j && m[map_find(m, i, j, k)] == k - ensures k !in set_of_map(m, i, j) ==> map_find(m, i, j, k) == j || lt(k, m[map_find(m, i, j, k)]) -{ - var idx := map_find(m, i, j, k); - map_find_content_set(m, i, j, idx, k); - set_of_map_split(m, i, idx, j); -} - -lemma map_find_content_set(m: Map, i: Int, j: Int, idx: Int, k: K) - requires i <= j - requires sorted_map_seg(m, i, j) - requires idx == map_find(m, i, j, k) - ensures k !in set_of_map(m, i, idx) - ensures k !in set_of_map(m, idx + 1, j) -{ - // prove k !in set_of_map(a, 0, idx) - not_in_sorted_seg(m, i, idx, k); - // prove: k !in set_of_map(a, idx + 1, len) - not_in_sorted_seg(m, idx + 1, j, k); -} - // Find key `k` in sorted array segment `a[0..len]` using binary search procedure arr_find(a: Array, len: Int, k: K, implicit ghost m: Map) returns (found: Bool, idx: Int) @@ -287,31 +338,6 @@ procedure arr_find(a: Array, len: Int, k: K, implicit ghost m: Map) map_find_in_set(m, 0, len, k); } -lemma map_insert_content_set(m: Map, m1: Map, idx: Int, k: K, len: Int, new_len: Int) - requires sorted_map_seg(m, 0, len) - requires idx == map_find(m, 0, len, k) - requires k in set_of_map(m, 0, len) ==> new_len == len && m1 == m - requires k !in set_of_map(m, 0, len) ==> new_len == len + 1 && m1 == map_shift(m, idx, idx + 1, len - idx)[idx := k] - ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) ++ {k} -{ - if (m[idx] == k && idx < len) { - in_set_of_map(m, 0, len); - return; - } - - var ms := map_shift(m, idx, idx + 1, len - idx); - - // prove set_of_map(m, 0, len) == set_of_map(ms, 0, idx) ++ set_of_array(ms, idx + 1, len + 1); - set_of_map_split(m, 0, idx, len); - set_of_map_equal(m, ms, 0, 0, idx); - set_of_map_equal(m, ms, idx, idx + 1, len - idx); - - // prove set_of_map(m1, 0, new_len) == set_of_array(ms, 0, idx) ++ {k} ++ set_of_map(ms, idx + 1, len + 1) - frame_set_of_map(ms, 0, idx); - frame_set_of_map(ms, idx + 1, len + 1); - set_of_map_split(m1, 0, idx, len + 1); -} - // Given a sorted array segment `a[0..len]`, // insert `k` into `a[0..len+1]` while preserving sortedness. // If `k` is already contained in `a[0..len]`, then do not modify `a`. @@ -344,30 +370,6 @@ procedure arr_insert(a: Array, k: K, len: Int, implicit ghost m: Map) return i, len + 1; } -lemma arr_delete_content_set(m: Map, m1: Map, len: Int, new_len: Int, idx: Int, k: K) - requires sorted_map_seg(m, 0, len) - requires idx == map_find(m, 0, len, k) - requires k !in set_of_map(m, 0, len) ==> new_len == len && m1 == m - requires k in set_of_map(m, 0, len) ==> new_len == len - 1 && m1 == map_shift(m, idx + 1, idx, len - (idx + 1)) - ensures set_of_map(m1, 0, new_len) == set_of_map(m, 0, len) -- {k} -{ - map_find_content_set(m, 0, len, idx, k); - set_of_map_split(m, 0, idx, len); - - if (idx == len || m[idx] != k) { - return; - } - - // prove: set_of_map(m, 0, len) -- {k} == set_of_map(m1, 0, idx - 1) ++ set_of_map(m1, idx - 1, len - 1) - set_of_map_equal(m, m1, 0, 0, idx); - set_of_map_equal(m, m1, idx + 1, idx, len - (idx + 1)); - - not_in_sorted_seg(m, 0, idx - 1, k); - not_in_sorted_seg(m, idx + 1, len, k); - - set_of_map_split(m1, 0, idx, len - 1); -} - // Given a sorted array segment `a[0..len]`, // delete `k` from the segment while preserving sortedness. // If `k` is already contained in `a[0..len]`, then do not modify `a`. From 8b2c4f91ec10aafe46fd80651a03ea25ce1acecb Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Fri, 11 May 2018 15:57:56 -0400 Subject: [PATCH 045/118] add frontend support for a.map for arrays a --- src/backends/c/splCompiler.ml | 3 ++- src/formulas/grass.ml | 5 +++-- src/formulas/grassUtil.ml | 3 +++ src/frontends/spl/splChecker.ml | 5 +++++ src/frontends/spl/splSyntax.ml | 6 ++++-- src/frontends/spl/splTranslator.ml | 3 +++ src/frontends/spl/splTypeChecker.ml | 21 ++++++++++++++++++++- 7 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/backends/c/splCompiler.ml b/src/backends/c/splCompiler.ml index 97432114..05012b29 100644 --- a/src/backends/c/splCompiler.ml +++ b/src/backends/c/splCompiler.ml @@ -158,7 +158,8 @@ let convert oc cu = | (OpToInt, e1) -> fprintf ppf "((int) %a)" pr_c_expr e1 | (OpToByte, e1) -> fprintf ppf "%a" pr_c_expr e1 | (OpUPlus, e1) -> fprintf ppf "((char) %a)" pr_c_expr e1 - | ((OpLength | OpOld | OpKnown | OpArrayCells | OpArrayOfCell | OpIndexOfCell), _) -> + | ((OpLength | OpOld | OpKnown | OpArrayCells | OpArrayOfCell + | OpIndexOfCell | OpArrayMap), _) -> fprintf ppf "/* ERROR: no such unary operator. */" and pr_bin_op ppf = function | (e1, OpMinus, e2) -> fprintf ppf "(%a - %a)" pr_c_expr e1 pr_c_expr e2 diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index da38d08e..6bdc4465 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -78,7 +78,7 @@ type symbol = | UMinus | Plus | Minus | Mult | Div | Mod (* Int *) | BitAnd | BitOr | BitNot | ShiftLeft | ShiftRight (* Bit Vector *) | Empty | SetEnum | Union | Inter | Diff (* Set *) - | Length | IndexOfCell | ArrayOfCell | ArrayCells + | Length | IndexOfCell | ArrayOfCell | ArrayCells | ArrayMap | ByteToInt | IntToByte (* explicit conversion *) | Ite (* if-then-else *) (* interpreted predicate symbols *) @@ -235,6 +235,7 @@ let string_of_symbol = function | IndexOfCell -> "index" | ArrayOfCell -> "array" | ArrayCells -> "cells" + | ArrayMap -> "map" | UMinus -> "-" | Plus -> "+" | Minus -> "-" @@ -282,7 +283,7 @@ let string_of_bop = function let prio_of_symbol = function | Null | Empty | IntConst _ | BoolConst _ -> 0 - | Read | Write | Constructor _ | Destructor _ | Old | SetEnum + | Read | Write | Constructor _ | Destructor _ | Old | SetEnum | ArrayMap | Length | IndexOfCell | ArrayOfCell | ArrayCells | EntPnt | ByteToInt | IntToByte | Btwn | Frame | Disjoint | Known | FreeSym _ -> 1 | UMinus | BitNot -> 2 diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index ba6c6014..5adb155e 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -366,6 +366,9 @@ let mk_read_form map ind = let mk_length map = mk_app Int Length [map] +let mk_array_map arr = + mk_app (Map ([Int], element_sort_of_array arr)) ArrayMap [arr] + let mk_array_of_cell c = mk_app (Loc (Array (element_sort_of_cell c))) ArrayOfCell [c] diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 9701ebd7..3ad0ac74 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -182,6 +182,11 @@ let resolve_names cu = (match type_of_expr cu locals idx1 with | ArrayType _ | AnyType -> UnaryOp (OpLength, idx1, pos) | ty -> Read (re locals tbl map, idx1, pos)) + | Read ((Ident (("map", _), _) as map), arr, pos) -> + let arr = re locals tbl arr in + (match type_of_expr cu locals arr with + | ArrayType _ | AnyType -> UnaryOp (OpArrayMap, arr, pos) + | ty -> Read (re locals tbl map, arr, pos)) | Read ((Ident (("cells", _), _) as map), idx, pos) -> let idx1 = re locals tbl idx in (match type_of_expr cu locals idx1 with diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 6a88368c..694ee6ef 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -189,7 +189,7 @@ and bin_op = | OpAnd | OpOr | OpImpl and un_op = - | OpArrayCells | OpIndexOfCell | OpArrayOfCell | OpLength + | OpArrayCells | OpIndexOfCell | OpArrayOfCell | OpLength | OpArrayMap | OpUMinus | OpUPlus | OpBvNot | OpToInt | OpToByte | OpNot @@ -625,7 +625,7 @@ let prio_of_expr = function | Null _ | Emp _ | IntVal _ | BoolVal _ | Ident _ -> 0 | Read _ | Write _ | ProcCall _ | PredApp _ | ConstrApp _ | DestrApp _ | New _ | Setenum _ | Binder (Comp, _, _, _) -> 1 - | UnaryOp ((OpArrayCells | OpIndexOfCell | OpArrayOfCell | + | UnaryOp ((OpArrayCells | OpIndexOfCell | OpArrayOfCell | OpArrayMap | OpLength | OpToInt | OpToByte | OpOld | OpKnown), _, _) -> 1 | UnaryOp ((OpUMinus | OpUPlus | OpBvNot | OpNot), _, _) -> 2 | BinaryOp (_, (OpMult | OpDiv | OpMod), _, _, _) -> 3 @@ -688,6 +688,8 @@ let rec pr_expr ppf = fprintf ppf "%a.array" pr_expr e1 | OpLength -> fprintf ppf "%a.length" pr_expr e1 + | OpArrayMap -> + fprintf ppf "%a.map" pr_expr e1 | OpUMinus | OpBvNot | OpUPlus | OpNot -> let op_str = match op with | OpUMinus -> "-" diff --git a/src/frontends/spl/splTranslator.ml b/src/frontends/spl/splTranslator.ml index e1f33cbb..28dac72c 100644 --- a/src/frontends/spl/splTranslator.ml +++ b/src/frontends/spl/splTranslator.ml @@ -208,6 +208,9 @@ let convert cu = | UnaryOp (OpLength, e, pos) -> let t = convert_term locals e in GrassUtil.mk_length t + | UnaryOp (OpArrayMap, e, pos) -> + let t = convert_term locals e in + GrassUtil.mk_array_map t | UnaryOp (OpArrayOfCell, e, pos) -> let t = convert_term locals e in GrassUtil.mk_array_of_cell t diff --git a/src/frontends/spl/splTypeChecker.ml b/src/frontends/spl/splTypeChecker.ml index eaa49f66..590915dd 100644 --- a/src/frontends/spl/splTypeChecker.ml +++ b/src/frontends/spl/splTypeChecker.ml @@ -2,7 +2,9 @@ open Util open Grass open SplSyntax open SplErrors - + +(** Checks if expected type [oty1] is compatible with actual/found type [oty2]. + Returns ?? *) let match_types pos oty1 oty2 = let rec mt ty1 ty2 = match ty1, ty2 with @@ -145,6 +147,10 @@ let type_of_expr cu locals e = (match te map with | ArrayType srt -> MapType (IntType, ArrayCellType srt) | _ -> AnyType) + | UnaryOp (OpArrayMap, arr, _) -> + (match te arr with + | ArrayType srt -> MapType (IntType, srt) + | _ -> MapType (IntType, AnyType)) (* Algebraic data types *) | ConstrApp (id, _, _) | DestrApp (id, _, _) -> let decl = IdMap.find id cu.fun_decls in @@ -398,6 +404,19 @@ let infer_types cu locals ty e = | _ -> ety in UnaryOp (OpArrayCells, map1, pos), match_types pos ty (MapType (IntType, ArrayCellType ety)) + | UnaryOp (OpArrayMap, arr, pos) -> + let arr_ety = + match ty with + | MapType (IntType, ety) -> ety + | _ -> AnyType + in + let arr, arr_ty = it locals (ArrayType arr_ety) arr in + let arr_ety = + match arr_ty with + | MapType (IntType, ety) -> ety + | _ -> AnyType + in + UnaryOp (OpArrayMap, arr, pos), match_types pos ty (MapType (IntType, arr_ety)) (*| Dot (e, id, pos) -> let decl = IdMap.find id cu.var_decls in let dty, rty = From 904fab0857c973e02869081f00f67c168b846db0 Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 16 May 2018 13:08:34 -0400 Subject: [PATCH 046/118] fixes issue #23 --- src/frontends/spl/splChecker.ml | 175 ++++++++++++++++++-------------- src/frontends/spl/splSyntax.ml | 2 +- 2 files changed, 100 insertions(+), 77 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 3ad0ac74..928f67d2 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -7,7 +7,6 @@ open Grass open SplSyntax open SplErrors open SplTypeChecker - (** Resolve names of identifiers in compilation unit [cu] so that all identifiers have unique names.*) let resolve_names cu = @@ -519,6 +518,28 @@ let resolve_names cu = background_theory = bg_theory; } + +(** Utility function for extending local variable map *) +let extend_locals cu vars locals scope = + let extend locals = function + | GuardedVar (id, e) -> + let decl = + { v_name = id; + v_type = type_of_expr cu locals e; + v_ghost = false; + v_implicit = false; + v_aux = false; + v_pos = pos_of_expr e; + v_scope = scope + } + in + IdMap.add id decl locals + | UnguardedVar decl -> + IdMap.add decl.v_name decl locals + in + List.fold_right (fun vars locals -> extend locals vars) vars locals + + (** Flatten procedure calls, comprehensions, and new expressions in compilation unit [cu].*) let flatten_exprs cu = let decl_aux_var name vtype pos scope locals = @@ -536,55 +557,56 @@ let flatten_exprs cu = let locals1 = IdMap.add aux_id decl locals in aux_id, locals1 in - let rec flatten_expr_list scope aux locals es = - List.fold_right (fun e (es1, aux1, locals) -> - let e1, aux1, locals = flatten_expr scope aux1 locals e in - e1 :: es1, aux1, locals) - es ([], aux, locals) - and flatten_expr scope aux locals = function + let rec flatten_expr_list scope aux new_locals locals es = + List.fold_right (fun e (es1, aux1, new_locals) -> + let e1, aux1, new_locals = flatten_expr scope aux1 new_locals locals e in + e1 :: es1, aux1, new_locals) + es ([], aux, new_locals) + and flatten_expr scope aux new_locals locals = function | Setenum (ty, args, pos) -> - let args1, aux1, locals = flatten_expr_list scope aux locals args in - Setenum (ty, args1, pos), aux1, locals + let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in + Setenum (ty, args1, pos), aux1, new_locals | New (ty, args, pos) -> - let aux_id, locals = decl_aux_var "tmp" ty pos scope locals in - let args1, aux1, locals = flatten_expr_list scope aux locals args in + let aux_id, new_locals = decl_aux_var "tmp" ty pos scope new_locals in + let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in let aux_var = Ident (aux_id, pos) in let alloc = Assign ([aux_var], [New (ty, args1, pos)], pos) in let aux_cmds, aux_funs = aux1 in - aux_var, (alloc :: aux_cmds, aux_funs), locals + aux_var, (alloc :: aux_cmds, aux_funs), new_locals | Read (map, idx, pos) -> - let map1, aux1, locals = flatten_expr scope aux locals map in - let idx1, aux2, locals = flatten_expr scope aux1 locals idx in - Read (map1, idx1, pos), aux2, locals + let map1, aux1, new_locals = flatten_expr scope aux new_locals locals map in + let idx1, aux2, new_locals = flatten_expr scope aux1 new_locals locals idx in + Read (map1, idx1, pos), aux2, new_locals | Write (map, idx, upd, pos) -> - let map1, aux1, locals = flatten_expr scope aux locals map in - let idx1, aux2, locals = flatten_expr scope aux1 locals idx in - let upd1, aux3, locals = flatten_expr scope aux2 locals upd in - Write (map1, idx1, upd1, pos), aux3, locals + let map1, aux1, new_locals = flatten_expr scope aux new_locals locals map in + let idx1, aux2, new_locals = flatten_expr scope aux1 new_locals locals idx in + let upd1, aux3, new_locals = flatten_expr scope aux2 new_locals locals upd in + Write (map1, idx1, upd1, pos), aux3, new_locals | Ite (cond, t, e, pos) -> - let cond1, aux1, locals = flatten_expr scope aux locals cond in - let t1, aux2, locals = flatten_expr scope aux1 locals t in - let e1, aux3, locals = flatten_expr scope aux2 locals e in - Ite (cond1, t1, e1, pos), aux3, locals + let cond1, aux1, new_locals = flatten_expr scope aux new_locals locals cond in + let t1, aux2, new_locals = flatten_expr scope aux1 new_locals locals t in + let e1, aux3, new_locals = flatten_expr scope aux2 new_locals locals e in + Ite (cond1, t1, e1, pos), aux3, new_locals | ConstrApp (id, args, pos) -> - let args1, aux1, locals = flatten_expr_list scope aux locals args in - ConstrApp (id, args1, pos), aux1, locals + let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in + ConstrApp (id, args1, pos), aux1, new_locals | DestrApp (id, arg, pos) -> - let arg1, aux1, locals = flatten_expr scope aux locals arg in - DestrApp (id, arg1, pos), aux1, locals + let arg1, aux1, new_locals = flatten_expr scope aux new_locals locals arg in + DestrApp (id, arg1, pos), aux1, new_locals | Binder (b, vars, f, pos) as e -> - let vars1, aux, locals = - List.fold_right (fun v (vars1, aux, locals) -> + let vars1, aux, new_locals = + List.fold_right (fun v (vars1, aux, new_locals) -> match v with | GuardedVar (x, s) -> - let s1, aux1, locals = flatten_expr scope aux locals s in - GuardedVar (x, s) :: vars1, aux1, locals - | _ -> v :: vars1, aux, locals) - vars ([], aux, locals) + let s1, aux1, new_locals = flatten_expr scope aux new_locals locals s in + GuardedVar (x, s) :: vars1, aux1, new_locals + | _ -> v :: vars1, aux, new_locals) + vars ([], aux, new_locals) in - let f1, aux, locals = flatten_expr scope aux locals f in + let locals = extend_locals cu vars1 locals scope in + let f1, aux, new_locals = flatten_expr scope aux new_locals locals f in (match b with - | Exists | Forall -> Binder (b, vars1, f1, pos), aux, locals + | Exists | Forall -> Binder (b, vars1, f1, pos), aux, new_locals | Comp -> (* create auxiliary function for set/map comprehension *) let v_decl = @@ -634,7 +656,7 @@ let flatten_exprs cu = let actuals = List.map (fun id -> Ident (id, pos)) formals in let c_app = PredApp (Pred c_id, actuals, pos) in let aux_cmds, aux_funs = aux in - c_app, (aux_cmds, c_decl :: aux_funs), locals + c_app, (aux_cmds, c_decl :: aux_funs), new_locals ) | ProcCall (id, args, pos) -> let pdecl = IdMap.find id cu.proc_decls in @@ -646,40 +668,40 @@ let flatten_exprs cu = rdecl.v_type | _ -> invalid_nested_proc_call_error pdecl.p_name pos in - let aux_id, locals = decl_aux_var "tmp" res_type pos scope locals in - let args1, aux1, locals = flatten_expr_list scope aux locals args in + let aux_id, new_locals = decl_aux_var "tmp" res_type pos scope new_locals in + let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in let aux_var = Ident (aux_id, pos) in let call = Assign ([aux_var], [ProcCall (id, args1, pos)], pos) in let aux_cmds, aux_funs = aux1 in - aux_var, (aux_cmds @ [call], aux_funs), locals + aux_var, (aux_cmds @ [call], aux_funs), new_locals | PredApp (p, args, pos) -> - let args1, aux1, locals = flatten_expr_list scope aux locals args in - PredApp(p, args1, pos), aux1, locals + let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in + PredApp(p, args1, pos), aux1, new_locals | UnaryOp (op, e, pos) -> - let e1, aux1, locals = flatten_expr scope aux locals e in - UnaryOp (op, e1, pos), aux1, locals + let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in + UnaryOp (op, e1, pos), aux1, new_locals | BinaryOp (e1, op, e2, ty, pos) -> - let e21, aux1, locals = flatten_expr scope aux locals e2 in - let e11, aux2, locals = flatten_expr scope aux1 locals e1 in - BinaryOp (e11, op, e21, ty, pos), aux2, locals + let e21, aux1, new_locals = flatten_expr scope aux new_locals locals e2 in + let e11, aux2, new_locals = flatten_expr scope aux1 new_locals locals e1 in + BinaryOp (e11, op, e21, ty, pos), aux2, new_locals | Annot (e, PatternAnnot p, pos) -> - let e1, aux1, locals = flatten_expr scope aux locals e in - let p1, aux2, locals = flatten_expr scope aux1 locals p in - Annot (e1, PatternAnnot p1, pos), aux2, locals + let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in + let p1, aux2, new_locals = flatten_expr scope aux1 new_locals locals p in + Annot (e1, PatternAnnot p1, pos), aux2, new_locals | Annot (e, GeneratorAnnot (es, ge), pos) -> - let es1, aux1, locals = - List.fold_right (fun (e, id) (es1, aux1, locals) -> - let e1, aux1, locals = flatten_expr scope aux locals e in - (e1, id) :: es1, aux1, locals) - es ([], aux, locals) + let es1, aux1, new_locals = + List.fold_right (fun (e, id) (es1, aux1, new_locals) -> + let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in + (e1, id) :: es1, aux1, new_locals) + es ([], aux, new_locals) in - let ge1, aux2, locals = flatten_expr scope aux1 locals ge in - let e1, aux3, locals = flatten_expr scope aux2 locals e in - Annot (e1, GeneratorAnnot (es1, ge1), pos), aux3, locals + let ge1, aux2, new_locals = flatten_expr scope aux1 new_locals locals ge in + let e1, aux3, new_locals = flatten_expr scope aux2 new_locals locals e in + Annot (e1, GeneratorAnnot (es1, ge1), pos), aux3, new_locals | Annot (e, ann, pos) -> - let e1, aux1, locals = flatten_expr scope aux locals e in - Annot (e1, ann, pos), aux1, locals - | e -> e, aux, locals + let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in + Annot (e1, ann, pos), aux1, new_locals + | e -> e, aux, new_locals in let rec flatten scope locals aux_funs returns = function | Skip pos -> Skip pos, locals, aux_funs @@ -695,16 +717,16 @@ let flatten_exprs cu = | LocalVars (_, _, pos) -> failwith "flatten_exprs: LocalVars should have been eliminated" | Assume (e, pure, pos) -> - let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals e in + let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in Assume (e1, pure, pos), locals, snd aux1 | Assert (e, pure, pos) -> - let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals e in + let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in Assert (e1, pure, pos), locals, snd aux1 | Split (e, pos) -> - let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals e in + let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in Split (e1, pos), locals, snd aux1 | Assign (lhs, [ProcCall (id, args, cpos)], pos) -> - let args1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals args in + let args1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals args in begin match lhs with | [Read (map, idx, opos)] -> @@ -724,7 +746,7 @@ let flatten_exprs cu = let lhs1, aux2, locals = List.fold_right (fun e (es, aux, locals) -> - let e1, aux1, locals = flatten_expr scope aux locals e in + let e1, aux1, locals = flatten_expr scope aux locals locals e in e1 :: es, aux1, locals ) lhs ([], aux1, locals) @@ -733,11 +755,11 @@ let flatten_exprs cu = mk_block pos (aux_cmds @ [Assign (lhs1, [ProcCall (id, args1, cpos)], pos)]), locals, aux_funs end | Assign (lhs, rhs, pos) -> - let rhs1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals rhs in + let rhs1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals rhs in let lhs1, aux2, locals = List.fold_right (fun e (es, aux, locals) -> - let e1, aux1, locals = flatten_expr scope aux locals e in + let e1, aux1, locals = flatten_expr scope aux locals locals e in match e1 with | Read(Ident _, _, _) -> e1 :: es, aux1, locals @@ -758,14 +780,14 @@ let flatten_exprs cu = let aux_cmds, aux_funs = aux2 in mk_block pos (aux_cmds @ [Assign (lhs1, rhs1, pos)]), locals, aux_funs | Dispose (e, pos) -> - let e1, aux, locals = flatten_expr scope ([], aux_funs) locals e in + let e1, aux, locals = flatten_expr scope ([], aux_funs) locals locals e in mk_block pos (fst aux @ [Dispose (e1, pos)]), locals, snd aux | Havoc (es, pos) -> - let es1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals es in + let es1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals es in let aux_cmds, aux_funs = aux1 in mk_block pos (aux_cmds @ [Havoc (es1, pos)]), locals, aux_funs | If (cond, t, e, pos) -> - let cond1, (aux_cmds, aux_funs), locals = flatten_expr scope ([], aux_funs) locals cond in + let cond1, (aux_cmds, aux_funs), locals = flatten_expr scope ([], aux_funs) locals locals cond in let t1, locals, aux_funs = flatten scope locals aux_funs returns t in let e1, locals, aux_funs = flatten scope locals aux_funs returns e in mk_block pos (aux_cmds @ [If (cond1, t1, e1, pos)]), locals, aux_funs @@ -782,23 +804,23 @@ let flatten_exprs cu = let inv1, aux_funs, locals = List.fold_right (function Invariant (e, pos) -> fun (inv1, aux_funs, locals) -> - let e1, (_, aux_funs), locals = flatten_expr scope ([], aux_funs) locals e in + let e1, (_, aux_funs), locals = flatten_expr scope ([], aux_funs) locals locals e in Invariant (e1, pos) :: inv1, aux_funs, locals) inv ([], aux_funs, locals) in let preb1, locals, aux_funs = flatten pos locals aux_funs returns preb in - let cond1, (aux_cmds, aux_funs), locals = flatten_expr pos ([], aux_funs) locals cond in + let cond1, (aux_cmds, aux_funs), locals = flatten_expr pos ([], aux_funs) locals locals cond in let postb1, locals, aux_funs = flatten pos locals aux_funs returns postb in Loop (inv1, mk_block pos ([preb1] @ aux_cmds), cond1, postb1, pos), locals, aux_funs | Return ([ProcCall (id, args, cpos)], pos) -> - let args1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals args in + let args1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals args in let rts = List.map (fun id -> Ident (id, cpos)) returns in let assign = Assign (rts, [ProcCall (id, args1, cpos)], pos) in let ret = Return (rts, pos) in let aux_cmds, aux_funs = aux1 in mk_block pos (aux_cmds @ [assign; ret]), locals, aux_funs | Return (es, pos) -> - let es1, (aux_cmds, aux_funs), locals = flatten_expr_list scope ([], aux_funs) locals es in + let es1, (aux_cmds, aux_funs), locals = flatten_expr_list scope ([], aux_funs) locals locals es in mk_block pos (aux_cmds @ [Return (es1, pos)]), locals, aux_funs in let flatten_contracts aux_funs locals contracts = @@ -806,7 +828,7 @@ let flatten_exprs cu = (fun c (contracts, aux_funs, locals) -> let flatten_spec e = let e1, (aux_cmds, aux_funs), locals = - flatten_expr (pos_of_expr e) ([], aux_funs) locals e + flatten_expr (pos_of_expr e) ([], aux_funs) locals locals e in match aux_cmds with | [] -> e1, aux_funs, locals @@ -848,7 +870,7 @@ let flatten_exprs cu = let body, (aux_cmds, aux_funs), locals = match decl.pr_body with | Some body -> - let body, aux, locals = flatten_expr decl.pr_pos ([], aux_funs) decl.pr_locals body in + let body, aux, locals = flatten_expr decl.pr_pos ([], aux_funs) decl.pr_locals decl.pr_locals body in Some body, aux, locals | None -> None, ([], aux_funs), locals in @@ -876,6 +898,7 @@ let flatten_exprs cu = let preds_all = List.fold_left (fun preds_all decl -> IdMap.add decl.pr_name decl preds_all) preds aux_funs in { cu with proc_decls = procs; pred_decls = preds_all } + (** Type check compilation unit [cu]. Missing type annotations are inferred along the way.*) let infer_types cu = let check_spec locals pure e = diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 694ee6ef..9d502d79 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -263,7 +263,7 @@ let free_vars e = fv bv acc e | Null _ | Emp _ | IntVal _ | BoolVal _ -> acc in fv IdSet.empty IdSet.empty e - + (** Variable substitution for expressions (not capture avoiding) *) let subst_id sm = let rec s bv = function From 09bdac9adb21f9e758e412144a15b7544699242a Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 18 May 2018 12:35:23 -0400 Subject: [PATCH 047/118] fixes #24 --- src/prover/axioms.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prover/axioms.ml b/src/prover/axioms.ml index cd33f667..127c8cdc 100644 --- a/src/prover/axioms.ml +++ b/src/prover/axioms.ml @@ -93,7 +93,7 @@ let open_axioms ?(force=false) open_cond axioms = let rec open_axiom generators = function | Binder (b, [], f, a) -> let f1, generators1 = open_axiom generators f in - let generators2, a1 = extract_generators generators a in + let generators2, a1 = extract_generators generators1 a in Binder (b, [], f1, a1), generators2 | Binder (b, vs, f, a) -> (* extract term generators *) From 4a6016b7dbc20b17476fa32b263e9283db55a90c Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 18 May 2018 15:19:00 -0400 Subject: [PATCH 048/118] add support for array map function in VC back-end --- src/prover/axioms.ml | 23 +++++++++- src/verifier/grassifier.ml | 6 +++ tests/spl/sorted_array_util/array_util.spl | 53 +++++++++------------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/prover/axioms.ml b/src/prover/axioms.ml index 127c8cdc..73ddfb72 100644 --- a/src/prover/axioms.ml +++ b/src/prover/axioms.ml @@ -36,6 +36,8 @@ let i1 = fresh_ident "?i", Int let i2 = fresh_ident "?j", Int let d = fresh_ident "?d" let e = fresh_ident "?e" +let arrs = fresh_ident "?array_state" +let witness = fresh_ident "witness" let loc1 struct_srt = mk_var (snd (l1 struct_srt)) (fst (l1 struct_srt)) let loc2 struct_srt = mk_var (snd (l2 struct_srt)) (fst (l2 struct_srt)) @@ -53,7 +55,8 @@ let set3 struct_srt = mk_var (snd (s3 struct_srt)) (fst (s3 struct_srt)) let intset1 = mk_var (snd is1) (fst is1) let int1 = mk_var (snd i1) (fst i1) let int2 = mk_var (snd i2) (fst i2) - +let arrst1 srt = mk_var (Map ([Loc (Array srt); Int], srt)) arrs + let reachwo_Fld f u v w = mk_or [mk_btwn f u v w; mk_and [mk_reach f u v; mk_not (mk_reach f u w)]] @@ -467,9 +470,19 @@ let array_axioms elem_srt = let a = loc1 (Array elem_srt) in let c = loc2 (ArrayCell elem_srt) in let i = int1 in + let arrstate = arrst1 elem_srt in let array_length = mk_or [mk_eq a (mk_null (Array elem_srt)); mk_leq (mk_int 0) (mk_length a)] in + let array_map_simple1 = + mk_or [mk_lt i (mk_int 0); + mk_geq i (mk_length a); + mk_eq (mk_read arrstate [a; i]) (mk_read (App (ArrayMap, [arrstate; a], Map ([Int], elem_srt))) [i])] + in + let array_map_simple2 = + mk_or [mk_and [mk_geq i (mk_int 0); mk_lt i (mk_length a)]; + mk_eq (mk_read (App (ArrayMap, [arrstate; a], Map ([Int], elem_srt))) [i]) (mk_free_const (elem_srt) witness)] + in let array_cells1 = mk_eq (mk_array_of_cell (mk_read (mk_array_cells a) [i])) a in @@ -487,6 +500,10 @@ let array_axioms elem_srt = mk_sequent [mk_eq (mk_array_of_cell c) a; mk_eq (mk_index_of_cell c) i] [mk_eq (mk_read (mk_array_cells a) i) c] in *) + let array_map_gen = + [([Match (mk_read arrstate [a; i], [])], [mk_read (App (ArrayMap, [arrstate; a], Map ([Int], elem_srt))) [i]]); + ([Match (mk_read (App (ArrayMap, [arrstate; a], Map ([Int], elem_srt))) [i], [])], [mk_read arrstate [a; i]])] + in let array_cell_gen = ([Match (c, [FilterSymbolNotOccurs ArrayOfCell; FilterSymbolNotOccurs IndexOfCell; @@ -510,7 +527,9 @@ let array_axioms elem_srt = ([Match (a, [])], [mk_length a]) in if !Config.simple_arrays then - [mk_axiom ~gen:[array_length_gen] "array-length" array_length] + [mk_axiom ~gen:[array_length_gen] "array-length" array_length; + mk_axiom ~gen:array_map_gen "array-map1" array_map_simple1; + mk_axiom "array-map2" array_map_simple2] else [mk_axiom ~gen:[index_of_cell_gen; array_of_cell_gen; array_cells_gen; array_cell_gen] "array-cells1" array_cells1; mk_axiom "array-cells2" array_cells2; diff --git a/src/verifier/grassifier.ml b/src/verifier/grassifier.ml index fddf4f79..dc318c85 100644 --- a/src/verifier/grassifier.ml +++ b/src/verifier/grassifier.ml @@ -178,6 +178,12 @@ let elim_arrays prog = | Loc (Array srt) -> mk_read (array_state simple srt) [map; idx] | _ -> mk_read map1 [idx1]) + | App (ArrayMap, [arr], msrt) -> + let arr1 = compile_term arr in + (match sort_of arr with + | Loc (Array srt) -> + App (ArrayMap, [array_state simple srt; arr], msrt) + | _ -> mk_array_map arr1) | App (sym, ts, srt) -> let ts1 = List.map compile_term ts in App (sym, ts1, srt) diff --git a/tests/spl/sorted_array_util/array_util.spl b/tests/spl/sorted_array_util/array_util.spl index 409ca976..d2e06dfe 100644 --- a/tests/spl/sorted_array_util/array_util.spl +++ b/tests/spl/sorted_array_util/array_util.spl @@ -94,15 +94,6 @@ function map_copy(m1: Map, m2: Map, src: Int, dst: Int, len: Int } } -function map_of_array(a: Array) returns (res: Map) - requires acc(a) -{ - { i: Int :: 0 <= i < a.length ? a[i] : bottom - @(matching res[i] yields a[i]) - @(matching a[i] yields res[i]) - } -} - predicate sorted_map_seg(m: Map, i: Int, j: Int) { forall i1: Int, i2: Int :: i <= i1 < i2 < j ==> lt(m[i1], m[i2]) @@ -111,13 +102,13 @@ predicate sorted_map_seg(m: Map, i: Int, j: Int) predicate sorted_array_seg(a: Array, i: Int, j: Int) requires acc(a) { - 0 <= i && j <= a.length && sorted_map_seg(map_of_array(a), i, j) + 0 <= i && j <= a.length && sorted_map_seg(a.map, i, j) } function set_of_array(a: Array, i: Int, j: Int) returns (res: Set) requires acc(a) &*& 0 <= i && j <= a.length { - set_of_map(map_of_array(a), i, j) + set_of_map(a.map, i, j) } predicate sorted_array_seg_with_content(a: Array, i: Int, j: Int, C: Set) @@ -129,7 +120,7 @@ predicate sorted_array_seg_with_content(a: Array, i: Int, j: Int, C: Set) predicate sorted_array_with_content(a: Array, len: Int, m: Map) { 0 <= len &*& - acc(a) &*& sorted_array_seg(a, 0, len) &*& m == map_of_array(a) + acc(a) &*& sorted_array_seg(a, 0, len) &*& m == a.map } @@ -223,9 +214,9 @@ procedure arr_shift(a: Array, src: Int, dst: Int, len: Int) requires acc(a) requires 0 <= src <= src + len <= a.length && 0 <= dst <= dst + len <= a.length ensures acc(a) - ensures map_of_array(a) == map_shift(old(map_of_array(a)), src, dst, len) + ensures a.map == map_shift(old(a.map), src, dst, len) { - ghost var m := map_of_array(a); + ghost var m := a.map; if (src < dst) { var i := len - 1; @@ -236,12 +227,12 @@ procedure arr_shift(a: Array, src: Int, dst: Int, len: Int) invariant 0 <= src <= src + len <= a.length invariant 0 <= dst <= dst + len <= a.length invariant -1 <= i < len - invariant map_shift(m, src + i + 1, dst + i + 1, len - i - 1) == map_of_array(a) + invariant map_shift(m, src + i + 1, dst + i + 1, len - i - 1) == a.map { - ghost var m1 := map_of_array(a); + ghost var m1 := a.map; var tmp := a[src + i]; a[dst + i] := tmp; - pure assert map_of_array(a) == m1[dst + i := tmp]; + pure assert a.map == m1[dst + i := tmp]; i := i - 1; } } else if (src > dst) { @@ -252,12 +243,12 @@ procedure arr_shift(a: Array, src: Int, dst: Int, len: Int) invariant 0 <= src <= src + len <= a.length invariant 0 <= dst <= dst + len <= a.length invariant 0 <= i <= len - invariant map_shift(m, src, dst, i) == map_of_array(a) + invariant map_shift(m, src, dst, i) == a.map { - ghost var m1 := map_of_array(a); + ghost var m1 := a.map; var tmp := a[src + i]; a[dst + i] := a[src + i]; - pure assert map_of_array(a) == m1[dst + i := tmp]; + pure assert a.map == m1[dst + i := tmp]; i := i + 1; } } @@ -269,11 +260,11 @@ procedure arr_copy(a: Array, b: Array, src: Int, dst: Int, len: Int) requires 0 <= src <= src + len <= a.length requires 0 <= dst <= dst + len <= b.length ensures acc(a) &*& acc(b) - ensures map_of_array(a) == old(map_of_array(a)) - ensures map_of_array(b) == map_copy(old(map_of_array(b)), map_of_array(a), src, dst, len) + ensures a.map == old(a.map) + ensures b.map == map_copy(old(b.map), a.map, src, dst, len) { - ghost var mb := map_of_array(b); - ghost var ma := map_of_array(a); + ghost var mb := b.map; + ghost var ma := a.map; var i := 0; while (i < len) @@ -281,13 +272,13 @@ procedure arr_copy(a: Array, b: Array, src: Int, dst: Int, len: Int) invariant 0 <= i <= len invariant 0 <= src <= src + len <= a.length invariant 0 <= dst <= dst + len <= b.length - invariant map_of_array(a) == ma - invariant map_of_array(b) == map_copy(mb, ma, src, dst, i) + invariant a.map == ma + invariant b.map == map_copy(mb, ma, src, dst, i) { - ghost var m1 := map_of_array(b); + ghost var m1 := b.map; var tmp := a[src + i]; b[dst + i] := tmp; - pure assert map_of_array(b) == m1[dst + i := tmp]; + pure assert b.map == m1[dst + i := tmp]; i := i + 1; } } @@ -361,11 +352,11 @@ procedure arr_insert(a: Array, k: K, len: Int, implicit ghost m: Map) arr_shift(a, i, i + 1, len - i); - ghost var ms := map_of_array(a); + ghost var ms := a.map; a[i] := k; - pure assert map_of_array(a) == ms[i := k]; + pure assert a.map == ms[i := k]; return i, len + 1; } @@ -393,7 +384,7 @@ procedure arr_delete(a: Array, k: K, len: Int, implicit ghost m: Map) } // shift array entries a[i+1..len] by 1 entry to the left - //ghost var m := map_of_array(a); + //ghost var m := a.map; arr_shift(a, idx + 1, idx, len - (idx + 1)); return len - 1, idx; From 7d67b88b5e6e048f6c9ff3d536fa4924a78f9a1d Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 15 Jul 2018 10:10:38 -0400 Subject: [PATCH 049/118] fix desugaring of nested comprehensions --- src/frontends/spl/splChecker.ml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 928f67d2..616550ee 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -605,8 +605,15 @@ let flatten_exprs cu = in let locals = extend_locals cu vars1 locals scope in let f1, aux, new_locals = flatten_expr scope aux new_locals locals f in + let cu = + List.fold_left + (fun cu decl -> + { cu with pred_decls = IdMap.add decl.pr_name decl cu.pred_decls } + ) cu (snd aux) + in + let e1 = Binder (b, vars1, f1, pos) in (match b with - | Exists | Forall -> Binder (b, vars1, f1, pos), aux, new_locals + | Exists | Forall -> e1, aux, new_locals | Comp -> (* create auxiliary function for set/map comprehension *) let v_decl = @@ -649,7 +656,7 @@ let flatten_exprs cu = pr_locals = c_locals; pr_contracts = []; pr_is_pure = false; - pr_body = Some e; + pr_body = Some e1; pr_pos = pos; } in From cf42bcac5ddb9a9af236a1bac67475be48ed296e Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 2 Aug 2018 14:27:21 -0400 Subject: [PATCH 050/118] fix bug --- src/prover/axioms.ml | 2 +- tests/spl/list_set/insert.spl | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/prover/axioms.ml b/src/prover/axioms.ml index 73ddfb72..11852632 100644 --- a/src/prover/axioms.ml +++ b/src/prover/axioms.ml @@ -361,7 +361,7 @@ let frame_axioms = mk_axiom fld1 fld2 "reach_frame1" (mk_pattern fld1 [] (mk_sequent - [reachwo_f1 loc1 loc2 (ep loc1)] + [reachwo_f1 loc1 loc3 (ep loc1)] [(mk_iff (reach_f1 loc1 loc2 loc3) (reach_f2 loc1 loc2 loc3))])); diff --git a/tests/spl/list_set/insert.spl b/tests/spl/list_set/insert.spl index e0b23c1c..81a33fdb 100644 --- a/tests/spl/list_set/insert.spl +++ b/tests/spl/list_set/insert.spl @@ -32,7 +32,10 @@ procedure insert(lst: Node, val: Int, implicit ghost C: Set) } var n: Node; - n := append(val, curr); + //n := append(val, curr); + n := new Node; + n.next := curr; + n.data := val; if (prev == null) { return n; From 2d8ca6d08c212d5cbc039c3c6be3bcbacb2439a8 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 6 Aug 2018 16:20:45 -0400 Subject: [PATCH 051/118] improved congruence closure for ADTs --- src/prover/congruenceClosure.ml | 3 ++- src/prover/instGen.ml | 4 ++-- src/prover/prover.ml | 34 +++++++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 273fdef2..1f108ef2 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -178,7 +178,7 @@ class dag = fun expr -> (*print_endline ("CC adding: " ^ (string_of_term expr));*) match expr with | Var (v, _) -> failwith "CC: term not ground" (* create_and_add var (FreeSym v) []*) - | App (c, [], _) as cst -> create_and_add cst c [] + | App (c, [], _) as cst -> create_and_add cst c [] (* TODO: redundant? *) | App (f, args, _) as appl -> let node_args = (List.map convert_exp args) in let new_node = create_and_add appl f node_args in @@ -287,6 +287,7 @@ class dag = fun expr -> end + (* TODO need implied equalities and watch lists *) let congr_classes_fixed_point fs gts = let gterms = TermSet.add GrassUtil.mk_true_term (TermSet.add GrassUtil.mk_false_term gts) in diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml index 28388e32..4a3b65b8 100644 --- a/src/prover/instGen.ml +++ b/src/prover/instGen.ml @@ -376,7 +376,7 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = in List.fold_left instantiate epr_axioms axioms -let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms classes = +let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms classes0 = if !Config.instantiate || force then (* remove theory atoms from congruence classes *) let filter_term t = @@ -390,7 +390,7 @@ let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local a (symbol_of t)) in let classes = - let classes2 = List.map (List.filter filter_term) classes in + let classes2 = List.map (List.filter filter_term) classes0 in List.filter (fun x -> x <> []) classes2 in let _ = diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 815c36eb..a742984a 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -179,6 +179,27 @@ let instantiate_and_prove session fs = | _ -> gts) gts gts in + let generate_adt_terms fs gts = + TermSet.fold + (fun t (fs, gts) -> match t with + | App (Constructor id, ts, Adt (ty_id, adts)) -> + let adt = List.assoc ty_id adts in + let destrs = List.assoc id adt in + List.fold_left2 + (fun acc arg (d_id, d_srt) -> + let d_srt = match d_srt with + | FreeSrt sid -> + List.assoc_opt sid adts |> + Util.Opt.map (fun _ -> Adt (sid, adts)) |> + Util.Opt.get_or_else d_srt + | _ -> d_srt + in + let d = GrassUtil.mk_app d_srt (Destructor d_id) [t] in + GrassUtil.mk_eq arg d :: fs, TermSet.add d gts) + (fs, TermSet.add t gts) ts destrs + | t -> fs, TermSet.add t gts + ) gts (fs, gts) + in let fs1, generators = open_axioms isFunVar fs1 in let btwn_gen = btwn_field_generators fs in let gts = ground_terms ~include_atoms:true (mk_and fs) in @@ -224,7 +245,8 @@ let instantiate_and_prove session fs = | _ -> acc) gts_a TermSet.empty in - let gts2 = generate_terms (btwn_gen @ generators) (TermSet.union gts_inst core_terms) in + let fs, gts2 = generate_adt_terms fs (TermSet.union gts_inst core_terms) in + let gts2 = generate_terms (btwn_gen @ generators) gts2 in let _ = if Debug.is_debug 1 then begin @@ -234,13 +256,13 @@ let instantiate_and_prove session fs = TermSet.iter (fun t -> print_endline (" " ^ (string_of_term t))) (TermSet.diff gts2 gts) end in - if TermSet.subset gts2 gts_inst + if TermSet.subset gts2 gts_inst0 then fs2, gts_inst, classes else - let classes = CongruenceClosure.congr_classes (rev_concat [fs_inst; fs]) gts2 in - let implied = get_implied_equalities classes in - let fs3 = instantiate_with_terms true ((*flatten @@*) linearize fs1) classes in - rev_concat [fs3; implied], gts2, classes + let classes = CongruenceClosure.congr_classes (rev_concat [fs_inst; fs]) gts2 in + let implied = get_implied_equalities classes in + let fs3 = instantiate_with_terms true ((*flatten @@*) linearize fs1) classes in + rev_concat [fs3; implied], gts2, classes in (*let round3 fs_inst gts_inst classes = let generators = From 2b1d5e85666b364bf7e932401a942152c350149c Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 6 Aug 2018 17:26:21 -0400 Subject: [PATCH 052/118] improved congruence closure for ADTs --- src/prover/prover.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index a742984a..a59d9c34 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -186,7 +186,7 @@ let instantiate_and_prove session fs = let adt = List.assoc ty_id adts in let destrs = List.assoc id adt in List.fold_left2 - (fun acc arg (d_id, d_srt) -> + (fun (fs, gts) arg (d_id, d_srt) -> let d_srt = match d_srt with | FreeSrt sid -> List.assoc_opt sid adts |> From a1a4cdab45a8b2fa430f49918360e535d9a64def Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 6 Aug 2018 18:59:03 -0400 Subject: [PATCH 053/118] improved congruence closure for ADTs --- src/prover/prover.ml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index a59d9c34..7831b213 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -183,17 +183,23 @@ let instantiate_and_prove session fs = TermSet.fold (fun t (fs, gts) -> match t with | App (Constructor id, ts, Adt (ty_id, adts)) -> + let rec subst srt = match srt with + | FreeSrt sid -> + List.assoc_opt sid adts |> + Util.Opt.map (fun _ -> Adt (sid, adts)) |> + Util.Opt.get_or_else srt + | Map (asrts, rsrt) -> Map (List.map subst asrts, subst rsrt) + | Set ssrt -> Set (subst ssrt) + | Loc lsrt -> Loc (subst lsrt) + | Array asrt -> Array (subst asrt) + | ArrayCell asrt -> ArrayCell asrt + | _ -> srt + in let adt = List.assoc ty_id adts in let destrs = List.assoc id adt in List.fold_left2 (fun (fs, gts) arg (d_id, d_srt) -> - let d_srt = match d_srt with - | FreeSrt sid -> - List.assoc_opt sid adts |> - Util.Opt.map (fun _ -> Adt (sid, adts)) |> - Util.Opt.get_or_else d_srt - | _ -> d_srt - in + let d_srt = subst d_srt in let d = GrassUtil.mk_app d_srt (Destructor d_id) [t] in GrassUtil.mk_eq arg d :: fs, TermSet.add d gts) (fs, TermSet.add t gts) ts destrs @@ -221,9 +227,11 @@ let instantiate_and_prove session fs = let ground_fs = List.filter is_ground fs_inst in let eqs = instantiate_with_terms true equations classes in let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in + let fs, gts1 = generate_adt_terms fs gts1 in let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in let classes = CongruenceClosure.congr_classes (List.rev_append eqs1 fs) gts1 in let implied = get_implied_equalities classes in + let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and implied)) gts1 in rev_concat [eqs; ground_fs; implied], gts1, classes in let round2 fs_inst gts_inst classes = From 92c916321c5feaa8022fc627383777580e428c8f Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 7 Aug 2018 20:20:50 -0400 Subject: [PATCH 054/118] new prover option for propagating field reads over inferred equalities --- src/main/config.ml | 2 + src/prover/prover.ml | 84 ++++++++++++-------------------- src/prover/reduction.ml | 41 ++++++++-------- tests/spl/include/assoc_list.spl | 2 +- 4 files changed, 55 insertions(+), 74 deletions(-) diff --git a/src/main/config.ml b/src/main/config.ml index 2295de07..059735b0 100644 --- a/src/main/config.ml +++ b/src/main/config.ml @@ -26,6 +26,7 @@ let with_ep = ref true let full_ep = ref false let use_set_theory = ref false let simple_arrays = ref false +let propagate_reads = ref false (* Flag to switch between integer and bitvectors *) let use_bitvector = ref false @@ -107,6 +108,7 @@ let cmd_options_spec = ("-termgen", Arg.Set_int term_gen_max_rounds, " Number of rounds to run the term generation procedure"); ("-nofixedpoint", Arg.Clear ccFixedPoint, " Do not do a fixed point with unit propagation for the congruence closure"); ("-stratify", Arg.Set stratify, " Do not instantiate quantifiers that satisfy stratified sort restrictions\n\nOptions for controlling backend solver:"); + ("-propreads", Arg.Set propagate_reads, " Propagate field reads over inferred field equalities"); ("-splitlemmas", Arg.Set split_lemmas, " Add split lemmas for all terms of sort Loc"); ("-smtsolver", Arg.Set_string smtsolver, " Choose SMT solver (z3, cvc4, cvc4mf), e.g., 'z3+cvc4mf'"); ("-smtpatterns", Arg.Set smtpatterns, " Always add pattern annotations to quantifiers in SMT queries"); diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 7831b213..5531c71b 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -139,22 +139,6 @@ let instantiate_and_prove session fs = fs else fs in - (*let terms_from_neg_assert fs = - let has_label = List.exists (function Label _ -> true | _ -> false) in - let rec process_form terms = function - | Atom (_, anns) as f -> - if has_label anns then - ground_terms ~include_atoms:true f |> TermSet.union terms - else terms - | BoolOp (_, fs) -> process_forms terms fs - | Binder (_, _, f1, anns) as f -> - if has_label anns then - ground_terms ~include_atoms:true f |> TermSet.union terms - else process_form terms f1 - and process_forms terms fs = List.fold_left process_form terms fs - in - process_forms TermSet.empty fs - in*) let rec is_horn seen_pos = function | BoolOp (Or, fs) :: gs -> is_horn seen_pos (fs @ gs) | Binder (Forall, [], f, _) :: gs -> is_horn seen_pos (f :: gs) @@ -236,60 +220,52 @@ let instantiate_and_prove session fs = in let round2 fs_inst gts_inst classes = (* the following seemingly redundant instantiation round is a workaround for not using the -fullep option *) - let fs2 = instantiate_with_terms ~stratify:false true fs1 classes in + let fs_inst0 = (*instantiate_with_terms ~stratify:false true fs1 classes*) fs_inst in let gts_known = generate_knowns gts in let gts_inst0 = TermSet.union gts_inst gts_known in - let gts2_atoms = TermSet.filter (function - | App (_, ts, Bool) -> List.for_all (fun t -> TermSet.mem t gts_inst) ts - | _ -> false) - (ground_terms ~include_atoms:true (mk_and fs2)) - in - let gts_inst = generate_terms generators (TermSet.union gts_inst0 gts2_atoms) in + (*let gts_inst = generate_terms generators (TermSet.union gts_inst0 gts2_atoms) in*) let core_terms = - let gts_a = (*terms_from_neg_assert*) ground_terms (mk_and fs) in + let gts_a = ground_terms (mk_and fs) in TermSet.fold (fun t acc -> match sort_of t with | Loc _ | Int | FreeSrt _ -> TermSet.add (mk_known t) acc | _ -> acc) gts_a TermSet.empty in - let fs, gts2 = generate_adt_terms fs (TermSet.union gts_inst core_terms) in - let gts2 = generate_terms (btwn_gen @ generators) gts2 in + let fs1 = linearize fs1 in + let rec saturate i fs_inst gts_inst0 classes = + (*Printf.printf "Saturate iteration %d\n" i; flush stdout;*) + let gts_inst = TermSet.union gts_inst0 core_terms in + let gts_atoms = (*TermSet.filter (function + | App (_, ts, Bool) -> List.for_all (fun t -> TermSet.mem t gts_inst) ts + | _ -> false)*) + (ground_terms ~include_atoms:true (mk_and fs_inst)) + in + let gts_inst = TermSet.union gts_inst gts_atoms in + let fs, gts_inst = generate_adt_terms fs gts_inst in + let implied_eqs = get_implied_equalities classes in + let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in + let generators = if i > 1 then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in + let gts_inst = generate_terms generators gts_inst in + if i > 1 && not !Config.propagate_reads || TermSet.subset gts_inst gts_inst0 then + rev_concat [fs_inst; implied_eqs], gts_inst, classes + else + let classes = CongruenceClosure.congr_classes (rev_concat [fs_inst; fs]) gts_inst in + let fs_inst = instantiate_with_terms true fs1 classes in + saturate (i + 1) fs_inst gts_inst classes + in + let fs, gts_inst, classes = saturate 1 fs_inst0 gts_inst0 classes in let _ = if Debug.is_debug 1 then begin print_endline "ground terms:"; TermSet.iter (fun t -> print_endline (" " ^ (string_of_term t))) gts; print_endline "generated terms:"; - TermSet.iter (fun t -> print_endline (" " ^ (string_of_term t))) (TermSet.diff gts2 gts) - end + TermSet.iter (fun t -> print_endline (" " ^ (string_of_term t))) (TermSet.diff gts_inst gts) + end in - if TermSet.subset gts2 gts_inst0 - then fs2, gts_inst, classes - else - let classes = CongruenceClosure.congr_classes (rev_concat [fs_inst; fs]) gts2 in - let implied = get_implied_equalities classes in - let fs3 = instantiate_with_terms true ((*flatten @@*) linearize fs1) classes in - rev_concat [fs3; implied], gts2, classes + fs, gts_inst, classes in - (*let round3 fs_inst gts_inst classes = - let generators = - List.map (fun (ms, ts) -> - let ms1 = - List.filter (function - | Match (App (Known, [t], _), _) -> - (match sort_of t with - | Int | Loc _ -> false - | _ -> true) - | _ -> true) - ms - in (ms1, ts)) - generators - in - let gts_inst = generate_terms (btwn_gen @ generators) (TermSet.union (ground_terms ~include_atoms:true (mk_and fs_inst)) gts_inst) in - let classes = CongruenceClosure.congr_classes (rev_concat [fs_inst; fs]) gts_inst in - instantiate_with_terms true fs1 classes, gts_inst, classes - in*) let do_rounds rounds = let dr (k, result, fs_asserted, fs_inst, gts_inst, classes) r = match result with @@ -313,7 +289,7 @@ let instantiate_and_prove session fs = | _ -> k, result, fs_asserted, fs_inst, gts_inst, classes in List.fold_left dr (1, None, FormSet.empty, fs1, gts1, classes) rounds in - let _, result, _, fs_inst, _, _ = do_rounds [round1; round2(*; round3*)] in + let _, result, _, fs_inst, _, _ = do_rounds [round1; round2] in result, session, fs_inst diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 29cdb801..61584bf8 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -248,23 +248,8 @@ let add_ep_axioms fs = in let axioms = SortSet.fold (fun srt axioms -> Axioms.ep_axioms srt @ axioms) struct_sorts [] in axioms @ fs - -let add_read_write_axioms fs = - let gts = ground_terms ~include_atoms:true (mk_and fs) in - let has_loc_field_sort = function - | App (_, _, Map([Loc id1], Loc id2)) -> (*id1 = id2*) true - | _ -> false - in - let basic_pt_flds = TermSet.filter (has_loc_field_sort &&& is_free_const) gts in - let basic_structs = struct_sorts_of_fields basic_pt_flds in - (* instantiate null axioms *) - let axioms = SortSet.fold (fun srt axioms -> Axioms.null_axioms srt @ axioms) basic_structs [] in - let null_ax, _ = open_axioms ~force:true isFld axioms in - let classes = CongruenceClosure.congr_classes fs gts in - (* CAUTION: not forcing the instantiation here would yield an inconsistency with the read/write axioms *) - let null_ax1 = instantiate_with_terms ~force:true false null_ax (CongruenceClosure.restrict_classes classes basic_pt_flds) in - let fs1 = null_ax1 @ fs in - let gts = TermSet.union (ground_terms ~include_atoms:true (mk_and null_ax1)) gts in + +let get_read_propagators gts = let field_sorts = TermSet.fold (fun t srts -> match sort_of t with | Loc (ArrayCell _) as srt -> SortSet.add srt srts @@ -272,7 +257,6 @@ let add_read_write_axioms fs = | _ -> srts) gts SortSet.empty in - (* propagate read terms *) let read_propagators = SortSet.fold (function | Loc (ArrayCell srt) -> fun propagators -> @@ -371,6 +355,25 @@ let add_read_write_axioms fs = | _ -> fun propagators -> propagators) field_sorts [] in + read_propagators + +let add_read_write_axioms fs = + let gts = ground_terms ~include_atoms:true (mk_and fs) in + let has_loc_field_sort = function + | App (_, _, Map([Loc id1], Loc id2)) -> (*id1 = id2*) true + | _ -> false + in + let basic_pt_flds = TermSet.filter (has_loc_field_sort &&& is_free_const) gts in + let basic_structs = struct_sorts_of_fields basic_pt_flds in + (* instantiate null axioms *) + let axioms = SortSet.fold (fun srt axioms -> Axioms.null_axioms srt @ axioms) basic_structs [] in + let null_ax, _ = open_axioms ~force:true isFld axioms in + let classes = CongruenceClosure.congr_classes fs gts in + (* CAUTION: not forcing the instantiation here would yield an inconsistency with the read/write axioms *) + let null_ax1 = instantiate_with_terms ~force:true false null_ax (CongruenceClosure.restrict_classes classes basic_pt_flds) in + let fs1 = null_ax1 @ fs in + let gts = TermSet.union (ground_terms ~include_atoms:true (mk_and null_ax1)) gts in + (* propagate read terms *) (* generate instances of all read over write axioms *) let read_write_ax = let generators_and_axioms = @@ -383,7 +386,7 @@ let add_read_write_axioms fs = generators_and_axioms in let read_propagators = - List.map (fun (ms, t) -> TermGenerator (ms, t)) read_propagators + List.map (fun (ms, t) -> TermGenerator (ms, t)) (get_read_propagators gts) in let fs1 = match fs1 with diff --git a/tests/spl/include/assoc_list.spl b/tests/spl/include/assoc_list.spl index 1cd9364f..f9bde085 100644 --- a/tests/spl/include/assoc_list.spl +++ b/tests/spl/include/assoc_list.spl @@ -25,7 +25,7 @@ function keys(FP: Set) ensures forall v in K :: key_witness(v, FP) in FP ensures forall v: Int :: v !in K ==> key_witness(v, FP) == null ensures forall v in K :: v == key_witness(v, FP).key @(matching v yields key_witness(v, FP).key) - ensures forall v in K, FP1: Set :: key_witness(v, FP) in FP1 ==> key_witness(v, FP) == key_witness(v, FP1) + ensures forall v in K, FP1: Set :: key_witness(v, FP) in FP1 ==> key_witness(v, FP) == key_witness(v, FP1) /* function keys(FP: Set) From 178726031e4b902c573f7bccea69c01586974f90 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 10 Aug 2018 13:42:50 -0400 Subject: [PATCH 055/118] allow forward declarations of types --- src/frontends/spl/splChecker.ml | 21 ++++++++++++--------- src/frontends/spl/splSyntax.ml | 13 +++++++++---- tests/spl/include/node_array.spl | 2 ++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 616550ee..625e92ab 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -46,18 +46,20 @@ let resolve_names cu = in r in - let declare_name pos init_id scope tbl = + let declare_name ?(allow_redecl=false) pos init_id scope tbl = let name = GrassUtil.name init_id in - match SymbolTbl.find_local tbl init_id with - | Some _ -> redeclaration_error init_id pos + let id = match SymbolTbl.find_local tbl init_id with + | Some (id, _) -> + if allow_redecl then id else redeclaration_error init_id pos | None -> - let id = GrassUtil.fresh_ident ~id:(snd init_id) name in - (id, SymbolTbl.add tbl init_id (id, scope)) + GrassUtil.fresh_ident ~id:(snd init_id) name + in + (id, SymbolTbl.add tbl init_id (id, scope)) in let declare_var types decl tbl = let id, tbl = declare_name decl.v_pos decl.v_name decl.v_scope tbl in - let ty = resolve_typ types decl.v_pos tbl decl.v_type in - { decl with v_name = id; v_type = ty }, tbl + let ty = resolve_typ types decl.v_pos tbl decl.v_type in + { decl with v_name = id; v_type = ty }, tbl in let declare_vars vars structs tbl = IdMap.fold @@ -68,11 +70,12 @@ let resolve_names cu = in let tbl = SymbolTbl.empty in (* resolve type names *) + let types0, tbl = IdMap.fold - (fun init_id decl (structs, tbl) -> + (fun init_id decl (types, tbl) -> let id, tbl = declare_name decl.t_pos init_id GrassUtil.global_scope tbl in - IdMap.add id { decl with t_name = id } structs, tbl) + IdMap.add id { decl with t_name = id } types, tbl) cu.type_decls (IdMap.empty, tbl) in (* resolve global variables *) diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 9d502d79..096c9c1b 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -1,7 +1,8 @@ (** SPL abstract syntax. *) open Grass - +open Util + type idents = ident list type pos = source_position @@ -394,15 +395,19 @@ let pred_decl hdr body = { hdr with pr_body = body } let extend_spl_program incls decls bg_th prog = + let redeclaration_error id pos = + ProgError.error pos ("Identifier " ^ GrassUtil.name id ^ " has already been declared in this scope.") + in let check_uniqueness id pos (tdecls, vdecls, pdecls, prdecls, mdecls) = if IdMap.mem id tdecls || IdMap.mem id vdecls || IdMap.mem id pdecls || IdMap.mem id prdecls || IdMap.mem id mdecls - then ProgError.error pos ("redeclaration of identifier " ^ (fst id) ^ "."); + then redeclaration_error id pos in let tdecls, vdecls, pdecls, prdecls, mdecls = List.fold_left (fun (tdecls, vdecls, pdecls, prdecls, mdecls as decls) -> function - | TypeDecl decl -> - check_uniqueness decl.t_name decl.t_pos decls; + | TypeDecl decl -> + IdMap.find_opt decl.t_name tdecls |> + Opt.iter (function { t_def = FreeTypeDef; _ } -> () | _ -> redeclaration_error decl.t_name decl.t_pos); IdMap.add decl.t_name decl tdecls, vdecls, pdecls, prdecls, mdecls | VarDecl decl -> check_uniqueness decl.v_name decl.v_pos decls; diff --git a/tests/spl/include/node_array.spl b/tests/spl/include/node_array.spl index 30217ddc..948f66bc 100644 --- a/tests/spl/include/node_array.spl +++ b/tests/spl/include/node_array.spl @@ -1,3 +1,5 @@ +type Node; + predicate arrayseg(a: Array, i: Int, j: Int) requires acc(a) { From e7ea5d3a9d3c3df52a6c650426fbebd0286db0af Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 10 Aug 2018 13:43:37 -0400 Subject: [PATCH 056/118] minor improvement in term generator synthesis --- src/verifier/verifier.ml | 74 ++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 5b78e95c..b9fd8a27 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -200,8 +200,8 @@ let add_pred_insts prog f = let sts = List.fold_left subterms_term_acc TermSet.empty ts in let no_var_reads = TermSet.for_all (function - | App (Read, _ :: _ :: Var (x, _) :: _, _) -> IdSet.mem x aux_vs - | App (Read, _ :: Var (x, _) :: _, _) -> IdSet.mem x aux_vs + | App (Read, _ :: _ :: (Var (x, _) | App ((Plus | Minus), [Var (x, _); _], _)) :: _, _) -> IdSet.mem x aux_vs + | App (Read, _ :: (Var (x, _) | App ((Plus | Minus), [Var (x, _); _], _)) :: _, _) -> IdSet.mem x aux_vs | _ -> true) sts in if (no_var_reads || is_set_sort srt) && IdSet.subset fvs bvs && not @@ IdSet.is_empty fvs @@ -511,6 +511,40 @@ let vcgen prog proc = | Some body -> List.rev (fst (vcs [] [] body)) | None -> [] +(** Generate error message from labels in the model *) +let get_err_msg_from_labels model labels = + let add_msg pos msg error_msgs = + let filtered_msgs = + List.filter + (fun (pos2, _) -> not (contained_in_src_pos pos pos2)) + error_msgs + in + (pos, msg) :: filtered_msgs + in + let error_msgs = + IdMap.fold + (fun id (pos, msg) error_msgs -> + let p = mk_free_const Bool id in + match Model.eval_bool_opt model p with + | Some true -> + add_msg pos msg error_msgs + | _ -> error_msgs + ) + labels [] + in + let sorted_error_msgs = + List.sort + (fun (pos1, msg1) (pos2, msg2) -> + let mc = + compare (String.get msg1 0) (String.get msg2 0) + in + if mc = 0 then compare_src_pos pos1 pos2 else mc) + error_msgs + in + List.map + (fun (pos, msg) -> ProgError.error_to_string pos msg) + sorted_error_msgs + (** Generate verification conditions for procedure [proc] of program [prog] and check them. *) let check_proc prog proc = let check_vc errors (vc_name, (vc_msg, pp), vc0, labels) = @@ -529,42 +563,8 @@ let check_proc prog proc = match Prover.get_model ~session_name:session_name ~sat_means:sat_means vc with | None -> errors | Some model -> - (* generate error message from model *) - let add_msg pos msg error_msgs = - let filtered_msgs = - List.filter - (fun (pos2, _) -> not (contained_in_src_pos pos pos2)) - error_msgs - in - (pos, msg) :: filtered_msgs - in - let error_msgs = - IdMap.fold - (fun id (pos, msg) error_msgs -> - let p = mk_free_const Bool id in - match Model.eval_bool_opt model p with - | Some true -> - add_msg pos msg error_msgs - | _ -> error_msgs - ) - labels [] - in - let sorted_error_msgs = - List.sort - (fun (pos1, msg1) (pos2, msg2) -> - let mc = - compare (String.get msg1 0) (String.get msg2 0) - in - if mc = 0 then compare_src_pos pos1 pos2 else mc) - error_msgs - in - let error_msg_strings = - List.map - (fun (pos, msg) -> ProgError.error_to_string pos msg) - sorted_error_msgs - in let error_msg = - String.concat "\n\n" (vc_msg :: error_msg_strings) + String.concat "\n\n" (vc_msg :: get_err_msg_from_labels model labels) in (pp, error_msg, model) :: errors end From cf2c952035f04f93273ce07ba5c25ef236756f4b Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 12 Aug 2018 21:36:36 -0400 Subject: [PATCH 057/118] allow specifications of procedures rather than options in regression tests --- bin/regression-tests | 24 ++++++++++++------------ bin/run-tests | 7 ++++++- tests/spl/assoc/contains.spl | 2 ++ tests/spl/assoc/insert.spl | 2 ++ tests/spl/assoc/lookup.spl | 2 ++ tests/spl/assoc/remove.spl | 2 ++ tests/spl/list_set/contains.spl | 2 ++ tests/spl/list_set/delete.spl | 2 ++ tests/spl/list_set/difference.spl | 2 ++ tests/spl/list_set/insert.spl | 2 ++ tests/spl/list_set/intersect.spl | 2 ++ tests/spl/list_set/traverse.spl | 2 ++ tests/spl/list_set/union.spl | 2 ++ 13 files changed, 40 insertions(+), 13 deletions(-) diff --git a/bin/regression-tests b/bin/regression-tests index 553144c2..92625f2c 100755 --- a/bin/regression-tests +++ b/bin/regression-tests @@ -2,7 +2,7 @@ source bin/osx_gnu.sh -# Format: +# Format: TESTS=" soudness0 tests/spl/soundness/soundness0.spl '' fail soudness1 tests/spl/soundness/soundness1.spl '' fail @@ -48,16 +48,16 @@ sls_merge_sort tests/spl/sls/sls_merge_sort.spl '' sls_quicksort tests/spl/sls/sls_quicksort.spl '' pass sls_strand_sort tests/spl/sls/sls_strand_sort.spl '' pass union_find tests/spl/sl/union_find.spl '' pass -list_set_contains tests/spl/list_set/contains.spl '-stratify' pass -list_set_delete tests/spl/list_set/delete.spl '-stratify' pass -list_set_difference tests/spl/list_set/difference.spl '-stratify' pass -list_set_insert tests/spl/list_set/insert.spl '-stratify' pass -list_set_traverse tests/spl/list_set/traverse.spl '-stratify' pass -list_set_union tests/spl/list_set/union.spl '-stratify' pass -assoc_list_contains tests/spl/assoc/contains.spl '-stratify' pass -assoc_list_insert tests/spl/assoc/insert.spl '-stratify' pass -assoc_list_lookup tests/spl/assoc/lookup.spl '-stratify' pass -assoc_list_remove tests/spl/assoc/remove.spl '-stratify' pass +list_set_contains tests/spl/list_set/contains.spl '' pass +list_set_delete tests/spl/list_set/delete.spl '' pass +list_set_difference tests/spl/list_set/difference.spl '' pass +list_set_insert tests/spl/list_set/insert.spl '' pass +list_set_traverse tests/spl/list_set/traverse.spl '' pass +list_set_union tests/spl/list_set/union.spl '' pass +assoc_list_contains tests/spl/assoc/contains.spl '' pass +assoc_list_insert tests/spl/assoc/insert.spl '' pass +assoc_list_lookup tests/spl/assoc/lookup.spl '' pass +assoc_list_remove tests/spl/assoc/remove.spl '' pass array_bsearch tests/spl/array/bsearch.spl '' pass array_bubble tests/spl/array/bubble.spl '' pass array_checkSorted tests/spl/array/checkSorted.spl '' pass @@ -71,7 +71,7 @@ array_reverse tests/spl/array/reverse.spl '' array_selection_sort tests/spl/array/selection_sort.spl '' pass array_test tests/spl/array/test.spl '' pass array_traverse_with_nodes tests/spl/array/traverse_with_nodes.spl '' pass -recursive_def_lists tests/spl/recursive_defs/list.spl '-abspreds' pass +recursive_def_lists tests/spl/recursive_defs/list.spl '' pass adt_lists tests/spl/adt/lists.spl '' pass " diff --git a/bin/run-tests b/bin/run-tests index 4d9026b4..728aaf17 100755 --- a/bin/run-tests +++ b/bin/run-tests @@ -36,7 +36,12 @@ run_test() { echo -ne "Checking benchmark ${1}..." ulimit -St 600 - ./grasshopper.native $2 ${3//\'/} $OPTIONS 2> /dev/null + if [ $3 == "''" ]; then + PROC="" + else + PROC="-procedure ${3//\'/}" + fi + ./grasshopper.native $2 $PROC $OPTIONS 2> /dev/null res=$? ulimit -St unlimited if [ $res -gt 128 ]; then diff --git a/tests/spl/assoc/contains.spl b/tests/spl/assoc/contains.spl index 378c8f54..0131470b 100644 --- a/tests/spl/assoc/contains.spl +++ b/tests/spl/assoc/contains.spl @@ -1,5 +1,7 @@ include "../include/assoc_list.spl"; +options "-stratify" + procedure contains(lst: Node, val: Int, implicit ghost C: Set, implicit ghost D: Map) returns (res: Bool) requires list_map(lst, null, C, D) diff --git a/tests/spl/assoc/insert.spl b/tests/spl/assoc/insert.spl index d5039fe1..c29c1e17 100644 --- a/tests/spl/assoc/insert.spl +++ b/tests/spl/assoc/insert.spl @@ -1,5 +1,7 @@ include "../include/assoc_list.spl"; +options "-stratify" + procedure insert(lst: Node, k: Int, v: Int, implicit ghost C: Set, implicit ghost D1: Map, diff --git a/tests/spl/assoc/lookup.spl b/tests/spl/assoc/lookup.spl index af1612f1..798451df 100644 --- a/tests/spl/assoc/lookup.spl +++ b/tests/spl/assoc/lookup.spl @@ -1,5 +1,7 @@ include "../include/assoc_list.spl"; +options "-stratify" + procedure lookup(lst: Node, val: Int, implicit ghost C: Set, implicit ghost D: Map) returns (res: Int) requires list_map(lst, null, C, D) &*& val in C diff --git a/tests/spl/assoc/remove.spl b/tests/spl/assoc/remove.spl index a6787b43..b783cd0d 100644 --- a/tests/spl/assoc/remove.spl +++ b/tests/spl/assoc/remove.spl @@ -1,5 +1,7 @@ include "../include/assoc_list.spl"; +options "-stratify" + procedure remove(lst: Node, k: Int, implicit ghost C: Set, implicit ghost D1: Map, diff --git a/tests/spl/list_set/contains.spl b/tests/spl/list_set/contains.spl index 45deeefc..12f7869a 100644 --- a/tests/spl/list_set/contains.spl +++ b/tests/spl/list_set/contains.spl @@ -1,5 +1,7 @@ include "../include/slsset.spl"; +options "-stratify" + procedure contains(lst: Node, val: Int, implicit ghost C: Set) returns (res: Bool) requires list_set(lst, null, C) diff --git a/tests/spl/list_set/delete.spl b/tests/spl/list_set/delete.spl index d473029c..80930274 100644 --- a/tests/spl/list_set/delete.spl +++ b/tests/spl/list_set/delete.spl @@ -1,5 +1,7 @@ include "../include/slsset.spl"; +options "-stratify" + procedure delete(lst: Node, val: Int, implicit ghost C: Set) returns (res: Node) requires list_set(lst, null, C) diff --git a/tests/spl/list_set/difference.spl b/tests/spl/list_set/difference.spl index c3c739bb..4f32ab9f 100644 --- a/tests/spl/list_set/difference.spl +++ b/tests/spl/list_set/difference.spl @@ -1,5 +1,7 @@ include "../include/slsset.spl"; +options "-stratify" + procedure difference(lst1: Node, lst2: Node, implicit ghost C1: Set, implicit ghost C2: Set) returns (res: Node) diff --git a/tests/spl/list_set/insert.spl b/tests/spl/list_set/insert.spl index 81a33fdb..f63c4f9b 100644 --- a/tests/spl/list_set/insert.spl +++ b/tests/spl/list_set/insert.spl @@ -1,5 +1,7 @@ include "../include/slsset.spl"; +options "-stratify" + procedure append(val: Int, x: Node, implicit ghost C: Set) returns (res: Node) requires list_set(x, null, C) diff --git a/tests/spl/list_set/intersect.spl b/tests/spl/list_set/intersect.spl index a12af869..b2b3e7cd 100644 --- a/tests/spl/list_set/intersect.spl +++ b/tests/spl/list_set/intersect.spl @@ -1,5 +1,7 @@ include "../include/slsset.spl"; +options "-stratify" + procedure intersect(x: Node, y: Node, implicit ghost x_content: Set, implicit ghost y_content: Set) returns (res: Node) diff --git a/tests/spl/list_set/traverse.spl b/tests/spl/list_set/traverse.spl index 696a2e19..d31026ac 100644 --- a/tests/spl/list_set/traverse.spl +++ b/tests/spl/list_set/traverse.spl @@ -1,5 +1,7 @@ include "../include/slsset.spl"; +options "-stratify" + procedure traverse(lst: Node, implicit ghost C: Set) requires list_set(lst, null, C) ensures list_set(lst, null, C) diff --git a/tests/spl/list_set/union.spl b/tests/spl/list_set/union.spl index 532aca3f..7bde439b 100644 --- a/tests/spl/list_set/union.spl +++ b/tests/spl/list_set/union.spl @@ -1,5 +1,7 @@ include "../include/slsset.spl"; +options "-stratify" + procedure union(lst1: Node, lst2: Node, implicit ghost C1: Set, implicit ghost C2: Set) returns (res: Node) From d3b7e6a4edcc89c0e9d8035dc610ac1cb7cad985 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 13 Aug 2018 16:05:00 -0400 Subject: [PATCH 058/118] flatten boolean expressions in assignments, etc. --- src/frontends/spl/splChecker.ml | 98 +++++++++++++++++------------- src/frontends/spl/splTranslator.ml | 5 +- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 625e92ab..6606e84a 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -560,41 +560,46 @@ let flatten_exprs cu = let locals1 = IdMap.add aux_id decl locals in aux_id, locals1 in - let rec flatten_expr_list scope aux new_locals locals es = + let rec flatten_expr_list ?(flatten_bool=false) scope aux new_locals locals es = List.fold_right (fun e (es1, aux1, new_locals) -> - let e1, aux1, new_locals = flatten_expr scope aux1 new_locals locals e in + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals e in e1 :: es1, aux1, new_locals) es ([], aux, new_locals) - and flatten_expr scope aux new_locals locals = function + and flatten_expr ?(flatten_bool=false) scope aux new_locals locals = + let mk_aux_cmd mk_cmd typ pos aux new_locals e = + let aux_id, new_locals = decl_aux_var "tmp" typ pos scope new_locals in + let aux_var = Ident (aux_id, pos) in + let assign = mk_cmd aux_var e pos in + let aux_cmds, aux_funs = aux in + aux_var, (aux_cmds @ [assign], aux_funs), new_locals + in + let mk_aux_assign = mk_aux_cmd (fun aux_var e pos -> Assign ([aux_var], [e], pos)) in + function | Setenum (ty, args, pos) -> - let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in Setenum (ty, args1, pos), aux1, new_locals | New (ty, args, pos) -> - let aux_id, new_locals = decl_aux_var "tmp" ty pos scope new_locals in - let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in - let aux_var = Ident (aux_id, pos) in - let alloc = Assign ([aux_var], [New (ty, args1, pos)], pos) in - let aux_cmds, aux_funs = aux1 in - aux_var, (alloc :: aux_cmds, aux_funs), new_locals + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:true scope aux new_locals locals args in + mk_aux_assign ty pos aux1 new_locals (New (ty, args1, pos)) | Read (map, idx, pos) -> - let map1, aux1, new_locals = flatten_expr scope aux new_locals locals map in - let idx1, aux2, new_locals = flatten_expr scope aux1 new_locals locals idx in + let map1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals map in + let idx1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals idx in Read (map1, idx1, pos), aux2, new_locals | Write (map, idx, upd, pos) -> - let map1, aux1, new_locals = flatten_expr scope aux new_locals locals map in - let idx1, aux2, new_locals = flatten_expr scope aux1 new_locals locals idx in - let upd1, aux3, new_locals = flatten_expr scope aux2 new_locals locals upd in + let map1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals map in + let idx1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals idx in + let upd1, aux3, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux2 new_locals locals upd in Write (map1, idx1, upd1, pos), aux3, new_locals | Ite (cond, t, e, pos) -> let cond1, aux1, new_locals = flatten_expr scope aux new_locals locals cond in - let t1, aux2, new_locals = flatten_expr scope aux1 new_locals locals t in - let e1, aux3, new_locals = flatten_expr scope aux2 new_locals locals e in + let t1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals t in + let e1, aux3, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux2 new_locals locals e in Ite (cond1, t1, e1, pos), aux3, new_locals | ConstrApp (id, args, pos) -> - let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in ConstrApp (id, args1, pos), aux1, new_locals | DestrApp (id, arg, pos) -> - let arg1, aux1, new_locals = flatten_expr scope aux new_locals locals arg in + let arg1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals arg in DestrApp (id, arg1, pos), aux1, new_locals | Binder (b, vars, f, pos) as e -> let vars1, aux, new_locals = @@ -678,30 +683,41 @@ let flatten_exprs cu = rdecl.v_type | _ -> invalid_nested_proc_call_error pdecl.p_name pos in - let aux_id, new_locals = decl_aux_var "tmp" res_type pos scope new_locals in - let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in - let aux_var = Ident (aux_id, pos) in - let call = Assign ([aux_var], [ProcCall (id, args1, pos)], pos) in - let aux_cmds, aux_funs = aux1 in - aux_var, (aux_cmds @ [call], aux_funs), new_locals + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:true scope aux new_locals locals args in + let call = ProcCall (id, args1, pos) in + mk_aux_assign res_type pos aux1 new_locals call | PredApp (p, args, pos) -> - let args1, aux1, new_locals = flatten_expr_list scope aux new_locals locals args in + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in PredApp(p, args1, pos), aux1, new_locals | UnaryOp (op, e, pos) -> - let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in - UnaryOp (op, e1, pos), aux1, new_locals + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in + let e2 = UnaryOp (op, e1, pos) in + (match op with + | OpNot when flatten_bool -> + let mk_cmd aux_var e pos = + If (e2, Assign ([aux_var], [BoolVal (true, pos)], pos), Assign ([aux_var], [BoolVal (false, pos)], pos), pos) + in + mk_aux_cmd mk_cmd BoolType pos aux1 new_locals e1 + | _ -> e2, aux1, new_locals) | BinaryOp (e1, op, e2, ty, pos) -> - let e21, aux1, new_locals = flatten_expr scope aux new_locals locals e2 in - let e11, aux2, new_locals = flatten_expr scope aux1 new_locals locals e1 in - BinaryOp (e11, op, e21, ty, pos), aux2, new_locals + let e21, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e2 in + let e11, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals e1 in + let e2 = BinaryOp (e11, op, e21, ty, pos) in + (match op with + | (OpAnd | OpOr) when flatten_bool -> + let mk_cmd aux_var e pos = + If (e2, Assign ([aux_var], [BoolVal (true, pos)], pos), Assign ([aux_var], [BoolVal (false, pos)], pos), pos) + in + mk_aux_cmd mk_cmd BoolType pos aux2 new_locals e1 + | _ -> e2, aux2, new_locals) | Annot (e, PatternAnnot p, pos) -> - let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in let p1, aux2, new_locals = flatten_expr scope aux1 new_locals locals p in Annot (e1, PatternAnnot p1, pos), aux2, new_locals | Annot (e, GeneratorAnnot (es, ge), pos) -> let es1, aux1, new_locals = List.fold_right (fun (e, id) (es1, aux1, new_locals) -> - let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in (e1, id) :: es1, aux1, new_locals) es ([], aux, new_locals) in @@ -709,7 +725,7 @@ let flatten_exprs cu = let e1, aux3, new_locals = flatten_expr scope aux2 new_locals locals e in Annot (e1, GeneratorAnnot (es1, ge1), pos), aux3, new_locals | Annot (e, ann, pos) -> - let e1, aux1, new_locals = flatten_expr scope aux new_locals locals e in + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in Annot (e1, ann, pos), aux1, new_locals | e -> e, aux, new_locals in @@ -736,7 +752,7 @@ let flatten_exprs cu = let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in Split (e1, pos), locals, snd aux1 | Assign (lhs, [ProcCall (id, args, cpos)], pos) -> - let args1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals args in + let args1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals args in begin match lhs with | [Read (map, idx, opos)] -> @@ -756,7 +772,7 @@ let flatten_exprs cu = let lhs1, aux2, locals = List.fold_right (fun e (es, aux, locals) -> - let e1, aux1, locals = flatten_expr scope aux locals locals e in + let e1, aux1, locals = flatten_expr ~flatten_bool:true scope aux locals locals e in e1 :: es, aux1, locals ) lhs ([], aux1, locals) @@ -765,7 +781,7 @@ let flatten_exprs cu = mk_block pos (aux_cmds @ [Assign (lhs1, [ProcCall (id, args1, cpos)], pos)]), locals, aux_funs end | Assign (lhs, rhs, pos) -> - let rhs1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals rhs in + let rhs1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals rhs in let lhs1, aux2, locals = List.fold_right (fun e (es, aux, locals) -> @@ -790,10 +806,10 @@ let flatten_exprs cu = let aux_cmds, aux_funs = aux2 in mk_block pos (aux_cmds @ [Assign (lhs1, rhs1, pos)]), locals, aux_funs | Dispose (e, pos) -> - let e1, aux, locals = flatten_expr scope ([], aux_funs) locals locals e in + let e1, aux, locals = flatten_expr ~flatten_bool:true scope ([], aux_funs) locals locals e in mk_block pos (fst aux @ [Dispose (e1, pos)]), locals, snd aux | Havoc (es, pos) -> - let es1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals es in + let es1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals es in let aux_cmds, aux_funs = aux1 in mk_block pos (aux_cmds @ [Havoc (es1, pos)]), locals, aux_funs | If (cond, t, e, pos) -> @@ -823,14 +839,14 @@ let flatten_exprs cu = let postb1, locals, aux_funs = flatten pos locals aux_funs returns postb in Loop (inv1, mk_block pos ([preb1] @ aux_cmds), cond1, postb1, pos), locals, aux_funs | Return ([ProcCall (id, args, cpos)], pos) -> - let args1, aux1, locals = flatten_expr_list scope ([], aux_funs) locals locals args in + let args1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals args in let rts = List.map (fun id -> Ident (id, cpos)) returns in let assign = Assign (rts, [ProcCall (id, args1, cpos)], pos) in let ret = Return (rts, pos) in let aux_cmds, aux_funs = aux1 in mk_block pos (aux_cmds @ [assign; ret]), locals, aux_funs | Return (es, pos) -> - let es1, (aux_cmds, aux_funs), locals = flatten_expr_list scope ([], aux_funs) locals locals es in + let es1, (aux_cmds, aux_funs), locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals es in mk_block pos (aux_cmds @ [Return (es1, pos)]), locals, aux_funs in let flatten_contracts aux_funs locals contracts = diff --git a/src/frontends/spl/splTranslator.ml b/src/frontends/spl/splTranslator.ml index 28dac72c..ef09c33c 100644 --- a/src/frontends/spl/splTranslator.ml +++ b/src/frontends/spl/splTranslator.ml @@ -221,7 +221,10 @@ let convert cu = let t = convert_term locals e in GrassUtil.mk_array_cells t | PredApp (Pred id, es, pos) -> - let decl = IdMap.find id cu.pred_decls in + let decl = + IdMap.find_opt id cu.pred_decls |> + Opt.lazy_get_or_else (fun () -> unknown_ident_error id pos) + in let ts = List.map (convert_term locals) es in (match decl.pr_outputs with | [res] -> From ac60e38b7e8741bb7ddbfdb9b3641ce5096625b0 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 13 Aug 2018 16:24:35 -0400 Subject: [PATCH 059/118] fix bug in SPL pretty printer --- src/frontends/spl/splSyntax.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index 096c9c1b..b2b052d9 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -707,13 +707,13 @@ let rec pr_expr ppf = then fprintf ppf "%s(@[%a@])" op_str pr_expr e1 else fprintf ppf "%s%a" op_str pr_expr e1 | OpOld -> - fprintf ppf "old(@[%a@])" pr_expr e + fprintf ppf "old(@[%a@])" pr_expr e1 | OpKnown -> - fprintf ppf "Known(@[%a@])" pr_expr e + fprintf ppf "Known(@[%a@])" pr_expr e1 | OpToInt -> - fprintf ppf "byte2int(@[%a@])" pr_expr e + fprintf ppf "byte2int(@[%a@])" pr_expr e1 | OpToByte -> - fprintf ppf "int2byte(@[%a@])" pr_expr e) + fprintf ppf "int2byte(@[%a@])" pr_expr e1) | BinaryOp (e1, op, e2, _, _) as e -> let op_str = match op with | OpDiff -> "--" From c77e952752de3530bb56dd177db14f1e3e29608b Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 13 Aug 2018 16:34:22 -0400 Subject: [PATCH 060/118] fix bug in flattening of Boolean expressions --- src/frontends/spl/splChecker.ml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/frontends/spl/splChecker.ml b/src/frontends/spl/splChecker.ml index 6606e84a..19bbf661 100644 --- a/src/frontends/spl/splChecker.ml +++ b/src/frontends/spl/splChecker.ml @@ -690,7 +690,12 @@ let flatten_exprs cu = let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in PredApp(p, args1, pos), aux1, new_locals | UnaryOp (op, e, pos) -> - let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in + let flatten_bool_rec = + match op with + | OpNot -> false + | _ -> flatten_bool + in + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux new_locals locals e in let e2 = UnaryOp (op, e1, pos) in (match op with | OpNot when flatten_bool -> @@ -700,8 +705,13 @@ let flatten_exprs cu = mk_aux_cmd mk_cmd BoolType pos aux1 new_locals e1 | _ -> e2, aux1, new_locals) | BinaryOp (e1, op, e2, ty, pos) -> - let e21, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e2 in - let e11, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals e1 in + let flatten_bool_rec = + match op with + | OpAnd | OpOr -> false + | _ -> flatten_bool + in + let e21, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux new_locals locals e2 in + let e11, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux1 new_locals locals e1 in let e2 = BinaryOp (e11, op, e21, ty, pos) in (match op with | (OpAnd | OpOr) when flatten_bool -> From 59ab7808a090c22381554a294b6d0bf2ebe1e8b3 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 13 Aug 2018 23:15:03 -0400 Subject: [PATCH 061/118] improved equality propagation --- src/formulas/grassUtil.ml | 6 ++++++ src/prover/congruenceClosure.ml | 4 +++- src/prover/instGen.ml | 5 +++-- src/prover/prover.ml | 15 +++++++++++++ src/prover/reduction.ml | 37 ++++++++++++++++++++++++++------- 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index 5adb155e..02612888 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -499,6 +499,12 @@ let mk_old t = mk_app (sort_of t) Old [t] (** Constructor for patterns. *) let mk_known t = mk_app Pat Known [t] + +(** Constructor for ADT destructor terms. *) +let mk_constr srt constr ts = mk_app srt (Constructor constr) ts + +(** Constructor for ADT destructor terms. *) +let mk_destr srt destr t = mk_app srt (Destructor destr) [t] (** Constructor for binder [b], binding variables [bv] in formula [f]. * Annotations [ann] are optional. diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 1f108ef2..30c70fe9 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -299,6 +299,8 @@ let congr_classes_fixed_point fs gts = | Atom (App (Lt, [e1; e2], _), _) | Atom (App (Gt, [e1; e2], _), _) -> if cc_graph#entails_eq e1 e2 then GrassUtil.mk_false else f + | Atom (App (Elem, [e1; e2], _), _) -> + if cc_graph#entails_eq e2 (GrassUtil.mk_empty (sort_of e2)) then GrassUtil.mk_false else f | Atom (pred, _) -> if cc_graph#entails_eq pred GrassUtil.mk_false_term then GrassUtil.mk_false else f | BoolOp (Not, [Atom (pred, _)]) -> @@ -306,7 +308,7 @@ let congr_classes_fixed_point fs gts = | BoolOp (And, fs) -> GrassUtil.smk_and (List.map remove_false1 fs) | BoolOp (Or, fs) -> - GrassUtil.smk_or (List.map remove_false1 fs) + GrassUtil.smk_or (List.map remove_false1 fs) | other -> other in let remove_false f = match f with diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml index 4a3b65b8..41769606 100644 --- a/src/prover/instGen.ml +++ b/src/prover/instGen.ml @@ -181,8 +181,9 @@ let generate_terms generators ground_terms = in let rec generate round new_terms old_terms = function | (guards, gen_terms) :: generators1 -> - (*print_endline "======"; - List.iter (fun t -> print_endline (" generator: " ^ string_of_term t)) gen_terms;*) + (*print_string "======\nGenerator:"; + print_of_format pr_annot [TermGenerator (guards, gen_terms)] stdout; + print_newline ();*) let subst_maps = List.fold_left (fun subst_maps -> function Match (t, filters) -> (*print_endline (" matching " ^ (string_of_term t)); diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 5531c71b..9f2af8bf 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -202,6 +202,19 @@ let instantiate_and_prove session fs = | c :: cls when sort_of c <> Bool && sort_of c <> Pat -> let eq = List.map (fun t -> GrassUtil.mk_eq c t) cls in List.rev_append eq acc + (*| c :: _ as cls when sort_of c = Bool -> + let mk_form = + if List.mem mk_true_term cls then + function + | App (BoolConst _, _, _) -> [] + | t -> [Atom (t, [])] + else if List.mem mk_false_term cls then + function + | App (BoolConst _, _, _) -> [] + | t -> [mk_not (Atom (t, []))] + else fun _ -> [] + in + List.rev_append (flat_map mk_form cls) acc*) | _ -> acc) [] classes @@ -244,6 +257,8 @@ let instantiate_and_prove session fs = let gts_inst = TermSet.union gts_inst gts_atoms in let fs, gts_inst = generate_adt_terms fs gts_inst in let implied_eqs = get_implied_equalities classes in + (*print_endline "Implied equalities:"; + print_endline (string_of_form (mk_and implied_eqs));*) let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in let generators = if i > 1 then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in let gts_inst = generate_terms generators gts_inst in diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 61584bf8..9950aedd 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -252,13 +252,36 @@ let add_ep_axioms fs = let get_read_propagators gts = let field_sorts = TermSet.fold (fun t srts -> match sort_of t with - | Loc (ArrayCell _) as srt -> SortSet.add srt srts - | Map (_ :: _, _) as srt -> SortSet.add srt srts + | (Loc ArrayCell _ | Map (_ :: _, _)) as srt -> SortSet.add srt srts + | Adt (_, _) as srt -> SortSet.add srt srts | _ -> srts) gts SortSet.empty in let read_propagators = SortSet.fold (function + | Adt (_, adts) -> fun propagators -> + let s = fresh_ident "?s" in + let t = fresh_ident "?t" in + List.fold_left + (fun propagators (id, cstrs) -> + let adt_srt = Adt (id, adts) in + let s = mk_var adt_srt s in + let t = mk_var adt_srt t in + let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in + List.fold_left + (fun propagators (destr, srt) -> + ([Match (mk_eq_term s t, []); + (* s == t, s.destr -> t.destr *) + Match (mk_destr srt destr s, [])], + [mk_destr srt destr t]) :: + ([Match (mk_eq_term s t, []); + (* s == t, t.destr -> s.destr *) + Match (mk_destr srt destr t, [])], + [mk_destr srt destr s]) :: propagators + ) + propagators destrs + ) + propagators adts | Loc (ArrayCell srt) -> fun propagators -> let f = fresh_ident "?f", field_sort (ArrayCell srt) srt in let fld = mk_var (snd f) (fst f) in @@ -266,12 +289,12 @@ let get_read_propagators gts = let b = Axioms.loc2 (Array srt) in let i = fresh_ident "?i" in let idx = mk_var Int i in - (* a = b, a.cells[i].f -> b.cells[i].f *) + (* a == b, a.cells[i].f -> b.cells[i].f *) ([Match (mk_eq_term a b, []); Match (mk_read fld [mk_read (mk_array_cells a) [idx]], [])], [mk_read fld [mk_read (mk_array_cells b) [idx]]]) :: ([Match (mk_eq_term a b, []); - (* a = b, b.cells[i].f -> a.cells[i].f *) + (* a == b, b.cells[i].f -> a.cells[i].f *) Match (mk_read fld [mk_read (mk_array_cells b) [idx]], [])], [mk_read fld [mk_read (mk_array_cells a) [idx]]]) :: propagators | Loc (Array srt) -> fun propagators -> @@ -281,11 +304,11 @@ let get_read_propagators gts = let b = Axioms.loc2 (Array srt) in let i = fresh_ident "?i" in let idx = mk_var Int i in - (* a = b, a.f[i] -> b.f[i] *) + (* a == b, a.f[i] -> b.f[i] *) ([Match (mk_eq_term a b, []); Match (mk_read fld [a; idx], [])], [mk_read fld [b; idx]]) :: - (* a = b, b.cells[i].f -> a.cells[i].f *) + (* a == b, b.cells[i].f -> a.cells[i].f *) ([Match (mk_eq_term a b, []); Match (mk_read fld [mk_read (mk_array_cells b) [idx]], [])], [mk_read fld [mk_read (mk_array_cells a) [idx]]]) :: propagators @@ -333,7 +356,7 @@ let get_read_propagators gts = Match (wrap (mk_read fld1 (ivars1)), []) ], [wrap (mk_read fld2 (ivars1))]) :: - (* f = g, x.g -> x.f *) + (* f == g, x.g -> x.f *) ([Match (mk_eq_term fld1 fld2, []); Match (wrap (mk_read fld2 (ivars1)), [])] @ match_ivar1, [wrap (mk_read fld1 (ivars1))]) :: From 8ba22990482bb227e171c645150d9450f3e540b4 Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 15 Aug 2018 11:29:44 -0400 Subject: [PATCH 062/118] fix bug in expansion of Boolean functions --- src/verifier/verifier.ml | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index b9fd8a27..e4d8f174 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -297,31 +297,29 @@ let add_pred_insts prog f = (* Expands definition of predicate [p] for arguments [ts] assuming polarity of occurrence [pol] *) let expand_pred pos p ts = let pred = find_pred prog p in - let locals = locals_of_pred pred in let formals = formals_of_pred pred in let returns = returns_of_pred pred in let name = name_of_pred pred in - let sm = - try - List.fold_left2 - (fun sm id t -> IdMap.add id t sm) - IdMap.empty formals ts - with Invalid_argument _ -> - failwith ("Fatal error while expanding predicate " ^ string_of_ident name) - in - let sm = + let opt_body = match returns with - | [] -> sm - | [id] -> - let var = IdMap.find id locals in - IdMap.add id (mk_free_fun var.var_sort p ts) sm + | [] -> + let sm = + try + List.fold_left2 + (fun sm id t -> IdMap.add id t sm) + IdMap.empty formals ts + with Invalid_argument _ -> + failwith ("Fatal error while expanding predicate " ^ string_of_ident name) + in + pred.pred_body |> + Opt.map form_of_spec |> + Opt.map (subst_consts sm) + | [id] -> None | _ -> failwith "Functions may only have a single return value." in let body = - pred.pred_body |> Opt.map form_of_spec |> - Opt.get_or_else (Atom (mk_free_fun Bool p ts, [])) + Opt.get_or_else (Atom (mk_free_fun Bool p ts, [])) opt_body in - let body = subst_consts sm body in if pos then body else nnf (mk_not body) in let f_inst = From 7bff50e326ae49ac0c422868be799a9ab69efeda Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 15 Aug 2018 15:10:08 -0400 Subject: [PATCH 063/118] infer equalities from membership in singletons --- src/prover/congruenceClosure.ml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 30c70fe9..4e438dd1 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -178,9 +178,8 @@ class dag = fun expr -> (*print_endline ("CC adding: " ^ (string_of_term expr));*) match expr with | Var (v, _) -> failwith "CC: term not ground" (* create_and_add var (FreeSym v) []*) - | App (c, [], _) as cst -> create_and_add cst c [] (* TODO: redundant? *) | App (f, args, _) as appl -> - let node_args = (List.map convert_exp args) in + let node_args = List.map convert_exp args in let new_node = create_and_add appl f node_args in List.iter (fun n -> n#add_ccparent new_node) node_args; new_node @@ -317,6 +316,7 @@ let congr_classes_fixed_point fs gts = GrassUtil.smk_or fs1 | other -> other in + let singletons = TermSet.filter (function App (SetEnum, [_], _) -> true | _ -> false) gts in let rec loop changed toProcess toSimplify = match toProcess with | f :: fs -> begin @@ -327,7 +327,17 @@ let congr_classes_fixed_point fs gts = loop changed (fs1 @ fs) toSimplify | Atom (App (Eq, [e1; e2], _), _) -> cc_graph#add_eq e1 e2; - loop true fs toSimplify + loop true fs toSimplify + | Atom (App (Elem, [e1; e2], _), _) -> + let changed = + TermSet.exists (function + | App (SetEnum, [e1'], _) as e2' when cc_graph#entails_eq e2 e2' -> + cc_graph#add_eq e1 e1'; + true + | _ -> false) + singletons + in + loop changed fs toSimplify | BoolOp (Not, [Atom (App (Eq, [e1; e2], _), _)]) | Atom (App (Lt, [e1; e2], _), _) | Atom (App (Gt, [e1; e2], _), _) -> From ee54911fb44c7ee580328358c0459fbca601fec9 Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 15 Aug 2018 17:13:36 -0400 Subject: [PATCH 064/118] infer equalities from membership in singletons --- src/prover/congruenceClosure.ml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 4e438dd1..3e3c718f 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -298,8 +298,9 @@ let congr_classes_fixed_point fs gts = | Atom (App (Lt, [e1; e2], _), _) | Atom (App (Gt, [e1; e2], _), _) -> if cc_graph#entails_eq e1 e2 then GrassUtil.mk_false else f - | Atom (App (Elem, [e1; e2], _), _) -> - if cc_graph#entails_eq e2 (GrassUtil.mk_empty (sort_of e2)) then GrassUtil.mk_false else f + | Atom (App (Elem, [e1; e2], _) as pred, _) -> + if cc_graph#entails_eq e2 (GrassUtil.mk_empty (sort_of e2)) then GrassUtil.mk_false else + if cc_graph#entails_eq pred GrassUtil.mk_false_term then GrassUtil.mk_false else f | Atom (pred, _) -> if cc_graph#entails_eq pred GrassUtil.mk_false_term then GrassUtil.mk_false else f | BoolOp (Not, [Atom (pred, _)]) -> @@ -328,24 +329,23 @@ let congr_classes_fixed_point fs gts = | Atom (App (Eq, [e1; e2], _), _) -> cc_graph#add_eq e1 e2; loop true fs toSimplify - | Atom (App (Elem, [e1; e2], _), _) -> - let changed = - TermSet.exists (function - | App (SetEnum, [e1'], _) as e2' when cc_graph#entails_eq e2 e2' -> - cc_graph#add_eq e1 e1'; - true - | _ -> false) - singletons - in - loop changed fs toSimplify | BoolOp (Not, [Atom (App (Eq, [e1; e2], _), _)]) | Atom (App (Lt, [e1; e2], _), _) | Atom (App (Gt, [e1; e2], _), _) -> cc_graph#add_neq e1 e2; loop true fs toSimplify | Atom (pred, _) -> - cc_graph#add_eq pred GrassUtil.mk_true_term; - loop true fs toSimplify + let _ = match pred with + | App (Elem, [e1; e2], _) -> + TermSet.iter (function + | App (SetEnum, [e1'], _) as e2' when cc_graph#entails_eq e2 e2' -> + cc_graph#add_eq e1 e1' + | _ -> ()) + singletons + | _ -> () + in + cc_graph#add_eq pred GrassUtil.mk_true_term; + loop true fs toSimplify | BoolOp (Not, [Atom (pred, _)]) -> cc_graph#add_eq pred GrassUtil.mk_false_term; loop true fs toSimplify From 5a3bcdbd67271c7a11eeaee3cd53f25f68a04062 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 16 Aug 2018 12:48:53 -0400 Subject: [PATCH 065/118] add support for nested ITEs --- src/frontends/spl/splTranslator.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontends/spl/splTranslator.ml b/src/frontends/spl/splTranslator.ml index ef09c33c..b71d6d42 100644 --- a/src/frontends/spl/splTranslator.ml +++ b/src/frontends/spl/splTranslator.ml @@ -719,9 +719,9 @@ let convert cu = | Annot (e, a, pos2) -> Annot (desugar_ite r e, a, pos2) | Ite (cond, t, e, pos) -> - BinaryOp (BinaryOp (cond, OpImpl, BinaryOp (r, OpEq, t, BoolType, pos), BoolType, pos), + BinaryOp (BinaryOp (cond, OpImpl, desugar_ite r t, BoolType, pos), OpAnd, - BinaryOp (UnaryOp (OpNot, cond, pos), OpImpl, BinaryOp (r, OpEq, e, BoolType, pos), BoolType, pos), + BinaryOp (UnaryOp (OpNot, cond, pos), OpImpl, desugar_ite r e, BoolType, pos), BoolType, pos) | e -> BinaryOp (r, OpEq, e, BoolType, pos_of_expr e) in From 6654070117c85223a74c8248e6b7c4b5e641f1ca Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 16 Aug 2018 13:08:04 -0400 Subject: [PATCH 066/118] add support for const declarations --- emacs-mode/spl-mode.el | 2 +- src/frontends/spl/splLexer.mll | 1 + src/frontends/spl/splParser.mly | 30 +++++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/emacs-mode/spl-mode.el b/emacs-mode/spl-mode.el index 82311129..cc13f2a8 100644 --- a/emacs-mode/spl-mode.el +++ b/emacs-mode/spl-mode.el @@ -35,7 +35,7 @@ )) -(defconst spl-defuns '("define" "function" "predicate" "lemma" "procedure" "struct" "type" "datatype" +(defconst spl-defuns '("const" "define" "function" "predicate" "lemma" "procedure" "struct" "type" "datatype" "pure function" "pure predicate" "include" "options" "var")) (defconst spl-specifiers '("axiom" "ensures" "free" "invariant" "requires" "pure" "assert" "assume" "split" "returns")) diff --git a/src/frontends/spl/splLexer.mll b/src/frontends/spl/splLexer.mll index df331f79..1a14edb6 100644 --- a/src/frontends/spl/splLexer.mll +++ b/src/frontends/spl/splLexer.mll @@ -20,6 +20,7 @@ let _ = ("Byte", BYTE); ("choose", CHOOSE); ("comment", COMMENT); + ("const", CONST); ("define", DEFINE); ("datatype", DATATYPE); ("else", ELSE); diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 4c03541f..342fe7e7 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -65,7 +65,7 @@ type rhs_string_maybe = %token IF ELSE WHILE %token FUNCTION %token PREDICATE -%token GHOST IMPLICIT VAR STRUCT PURE LEMMA PROCEDURE INCLUDE OPTIONS AXIOM TYPE +%token GHOST IMPLICIT VAR CONST STRUCT PURE LEMMA PROCEDURE INCLUDE OPTIONS AXIOM TYPE %token DEFINE DATATYPE OUTPUTS RETURNS REQUIRES ENSURES INVARIANT %token LOC INT BOOL BYTE SET MAP ARRAY ARRAYCELL %token MATCHING YIELDS WITHOUT COMMENT PATTERN @@ -267,6 +267,7 @@ pred_decl: | function_header pred_impl { pred_decl $1 $2 } +| const_decl { $1 } ; function_header: @@ -304,6 +305,33 @@ pred_impl: } | /* empty */ { None } ; + +const_decl: +| CONST IDENT COLON var_type semicolon_opt { + let res = ("res", 0) in + let res_decl = { + v_name = res; + v_type = $4; + v_ghost = false; + v_implicit = false; + v_aux = false; + v_pos = mk_position 2 2; + v_scope = GrassUtil.global_scope; (* scope is fixed later *) + } + in + let decl = + { pr_name = $2; + pr_formals = []; + pr_outputs = [res]; + pr_locals = IdMap.singleton res res_decl; + pr_contracts = []; + pr_is_pure = true; + pr_body = None; + pr_pos = mk_position 2 2 + } + in + decl +} var_decls_with_modifiers: | var_decl_with_modifiers var_decl_with_modifiers_list { $1 :: $2 } From da8f3e53accde526ace95846d717779fb1370b96 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 16 Aug 2018 13:08:17 -0400 Subject: [PATCH 067/118] simplify ordered type --- tests/spl/include/ordered_type.spl | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/spl/include/ordered_type.spl b/tests/spl/include/ordered_type.spl index 52a9c26f..c6f4c3d6 100644 --- a/tests/spl/include/ordered_type.spl +++ b/tests/spl/include/ordered_type.spl @@ -3,17 +3,23 @@ type K pure predicate lt(x: K, y: K) +const bottom: K +const top: K + +axiom forall a: K :: !lt(a, bottom) +axiom forall a: K :: !lt(top, a) + axiom forall a: K :: !lt(a, a) axiom forall a: K, b: K :: !lt(a, b) || !lt(b, a) axiom forall a: K, b: K :: lt(a, b) || lt(b, a) || a == b axiom forall a: K, b: K, c: K :: lt(a, b) && lt(b, c) ==> lt(a, c) -pure function compare(x: K, y: K) returns (res: Int) - ensures (x == y) == (res == 0) - ensures lt(x, y) == (res < 0) - ensures lt(y, x) == (res > 0) -var bottom: K -var top: K +function le(x: K, y: K) returns (res: Bool) { + lt(x, y) || x == y +} + +function compare(x: K, y: K) returns (res: Int) +{ + x == y ? 0 : (lt(x, y) ? -1 : 1) +} -axiom forall a: K :: !lt(a, bottom) -axiom forall a: K :: !lt(top, a) From 10eefd362474db6d252ac029d518e147f54e287b Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 17 Aug 2018 16:17:15 -0400 Subject: [PATCH 068/118] add profiling info to statistics output --- src/main/grasshopper.ml | 3 +-- src/util/util.ml | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/grasshopper.ml b/src/main/grasshopper.ml index f6988a63..d9f219d4 100644 --- a/src/main/grasshopper.ml +++ b/src/main/grasshopper.ml @@ -171,8 +171,7 @@ let print_stats start_time = print_endline "Statistics: "; Printf.printf " running time for analysis: %.2fs\n" total_time; Printf.printf " # VCs: %d\n" !SmtLibSolver.num_of_sat_queries; - Printf.printf " measured time: %.2fs\n" !Util.measured_time; - Printf.printf " # measured calls: %.2d\n" !Util.measured_calls + print_measures () (** Print C program equivalent *) let print_c_program spl_prog = diff --git a/src/util/util.ml b/src/util/util.ml index 8785e248..297c1c4c 100644 --- a/src/util/util.ml +++ b/src/util/util.ml @@ -301,9 +301,10 @@ let measure_call (id: string) fn arg = raise e let print_measures () = + if Hashtbl.length measures > 0 then print_endline "Profiling:"; Hashtbl.iter (fun id (calls, time) -> - print_endline (id ^ ": " ^ (string_of_int calls) ^ " call(s), " ^ (string_of_float time) ^ " s") + print_endline (" " ^ id ^ ": " ^ (string_of_int calls) ^ " call(s), " ^ (string_of_float time) ^ " s") ) measures From 90ec856271d2782824f8437646446009d0254b97 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 17 Aug 2018 19:21:18 -0400 Subject: [PATCH 069/118] several improvements to congruence closure implementation --- src/prover/congruenceClosure.ml | 397 +++++++++++++++++--------------- src/prover/instGen.ml | 9 +- src/prover/prover.ml | 88 ++++--- src/prover/reduction.ml | 13 +- 4 files changed, 266 insertions(+), 241 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 3e3c718f..eb692b91 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -1,100 +1,69 @@ (** DZ: this is a copy-pasted version from csisat, just adaped to the current types *) open Grass - -(** Ordered sets represented as lists. - * This module is inspired from the Sicstus/SWI prolog library with the same name. - *) +open GrassUtil -module OrdSet = - struct - let remove_duplicates lst = - let rec process last acc lst = match lst with - | x::xs -> - begin - if x <> last then process x (x::acc) xs - else process last acc xs - end - | [] -> List.rev acc - in - match lst with - | x::[] -> [x] - | x::xs -> process x [x] xs - | [] -> [] - - let subtract a b = - let rec process acc a b = match (a,b) with - | (a,[]) -> (List.rev acc)@a - | ([],_) -> (List.rev acc) - | (a::sa, b::bs) -> - begin - if a < b then process (a::acc) sa (b::bs) - else if a > b then process acc (a::sa) bs - else process acc sa (b::bs) - end - in - process [] a b - - let union a b = - let rec process acc a b = match (a,b) with - | (a,[]) -> (List.rev acc)@a - | ([],b) -> (List.rev acc)@b - | (a::sa, b::bs) -> - begin - if a < b then process (a::acc) sa (b::bs) - else if a > b then process (b::acc) (a::sa) bs - else process (a::acc) sa bs - end - in - process [] a b - - let intersection a b = - let rec process acc a b = match (a,b) with - | (_,[]) -> (List.rev acc) - | ([],_) -> (List.rev acc) - | (a::sa, b::bs) -> - begin - if a < b then process acc sa (b::bs) - else if a > b then process acc (a::sa) bs - else process (a::acc) sa bs - end - in - process [] a b - - let rec mem el lst = match lst with - | [] -> false - | x::xs -> - begin - if x < el then mem el xs - else if x > el then false - else true - end - - let list_to_ordSet lst = remove_duplicates (List.sort compare lst) - end +module rec Node : sig + type t = + < get_fname: symbol; + get_args: t list; + get_arity: int; + set_ccparent: NodeSet.t -> unit; + add_ccparent: t -> unit; + get_ccparent: NodeSet.t; + get_parent: t option; + set_parent: t -> unit; + find: t; + union: t -> unit; + ccpar: NodeSet.t; + congruent: t -> bool; + merge: t -> unit + > + val compare: t -> t -> int + val create: symbol -> t list -> t + + end = struct + type t = + < get_fname: symbol; + get_args: t list; + get_arity: int; + set_ccparent: NodeSet.t -> unit; + add_ccparent: t -> unit; + get_ccparent: NodeSet.t; + get_parent: t option; + set_parent: t -> unit; + find: t; + union: t -> unit; + ccpar: NodeSet.t; + congruent: t -> bool; + merge: t -> unit + > -class node = + let compare = compare + + class node = fun (ffname: symbol) - (aargs: node list) -> + (aargs: t list) -> object (self) val fname = ffname method get_fname = fname val args = aargs - method get_args = args + method get_args: node list = args val arity = List.length aargs method get_arity = arity - val mutable ccparent: node list = [] + val mutable ccparent = NodeSet.empty method set_ccparent lst = ccparent <- lst - method add_ccparent n = ccparent <- (OrdSet.union ccparent [n]) + method add_ccparent (n: node) = ccparent <- (NodeSet.add n ccparent) method get_ccparent = ccparent val mutable parent: node option = None - method set_parent n = parent <- Some n + method get_parent: node option = parent + method set_parent (n: node) = parent <- Some n method find: node = match parent with | None -> (self :> node) | Some n -> @@ -108,10 +77,10 @@ class node = let n1 = self#find in let n2 = that#find in n1#set_parent n2; - n2#set_ccparent (OrdSet.union n1#get_ccparent n2#get_ccparent); - n1#set_ccparent [] + n2#set_ccparent (NodeSet.union n1#get_ccparent n2#get_ccparent); + n1#set_ccparent NodeSet.empty - method ccpar: node list = (self#find)#get_ccparent + method ccpar: NodeSet.t = (self#find)#get_ccparent method congruent (that: node) = self#get_fname = that#get_fname @@ -121,18 +90,18 @@ class node = List.for_all (fun (a,b) -> a#find = b#find) (List.rev_map2 (fun x y -> (x,y)) (self#get_args) (that#get_args)) (** return pairs of nodes whose equality may change the result of the 'congruent' method*) - method may_be_congruent (that: node) = + (*method may_be_congruent (that: node) = if self#get_fname <> that#get_fname || self#get_arity <> that#get_arity || self#find = that#find then [] else - List.filter (fun (a,b) -> a#find <> b#find) (List.rev_map2 (fun x y -> (x,y)) (self#get_args) (that#get_args)) + List.filter (fun (a,b) -> a#find <> b#find) (List.rev_map2 (fun x y -> (x,y)) (self#get_args) (that#get_args))*) method merge (that: node) = if self#find <> that#find then begin - let p1 = self#ccpar in - let p2 = that#ccpar in + let p1 = NodeSet.elements self#ccpar in + let p2 = NodeSet.elements that#ccpar in self#union that; let to_test = List.flatten (List.map (fun x -> List.map (fun y -> (x,y)) p2) p1) @@ -141,7 +110,7 @@ class node = end (** return pairs of nodes whose equality comes from congruence*) - method merge_with_applied (that: node) = + (*method merge_with_applied (that: node) = if self#find <> that#find then begin let p1 = self#ccpar in @@ -157,77 +126,89 @@ class node = else acc) [] cong end - else [] + else []*) end -class dag = fun expr -> + let create sym terms: t = new node sym terms + end +and NodeSet: Set.S with type elt = Node.t = Set.Make(Node) + +class dag = fun (terms: TermSet.t) -> let table1 = Hashtbl.create 53 in let table2 = Hashtbl.create 53 in - let create_and_add expr fn args = - try Hashtbl.find table1 expr + let create_and_add t sym args = + try Hashtbl.find table1 t with Not_found -> begin - let n = new node fn args + let n = Node.create sym args in - Hashtbl.replace table1 expr n; - Hashtbl.replace table2 n expr; + Hashtbl.add table1 t n; + Hashtbl.add table2 n t; n end in - let rec convert_exp expr = - (*print_endline ("CC adding: " ^ (string_of_term expr));*) - match expr with + let rec convert_term t = + (*print_endline ("CC adding: " ^ (string_of_term t));*) + match t with | Var (v, _) -> failwith "CC: term not ground" (* create_and_add var (FreeSym v) []*) - | App (f, args, _) as appl -> - let node_args = List.map convert_exp args in - let new_node = create_and_add appl f node_args in + | App (sym, args, _) as appl -> + let node_args = List.map convert_term args in + let new_node = create_and_add appl sym node_args in List.iter (fun n -> n#add_ccparent new_node) node_args; new_node in - let _ = List.iter (fun x -> ignore (convert_exp x)) expr in + let _ = TermSet.iter (fun t -> ignore (convert_term t)) terms in object (self) - val mutable neqs: (node * node) list = [] - val nodes: (term, node) Hashtbl.t = table1 - val node_to_expr: (node, term) Hashtbl.t = table2 - method get_node expr = - try Hashtbl.find nodes expr - with Not_found -> failwith ("CC: cannot find " ^ (string_of_term expr)) - method get_expr n = Hashtbl.find node_to_expr n + val mutable neqs: (Node.t * Node.t) list = [] + val nodes: (term, Node.t) Hashtbl.t = table1 + val node_to_term: (Node.t, term) Hashtbl.t = table2 + + method get_node t = + try Hashtbl.find nodes t + with Not_found -> failwith ("CC: cannot find " ^ (string_of_term t)) + + method get_term n = Hashtbl.find node_to_term n + + method get_terms = Hashtbl.fold (fun t _ acc -> TermSet.add t acc) nodes TermSet.empty + method get_nodes = Hashtbl.copy nodes method print = let buffer = Buffer.create 1000 in - let print_node (n:node) = - Buffer.add_string buffer ("node: "^(string_of_term (self#get_expr n))); + let print_node (n: Node.t) = + Buffer.add_string buffer ("node: "^(string_of_term (self#get_term n))); Buffer.add_char buffer '\n'; - Buffer.add_string buffer (" in class of: "^(string_of_term (self#get_expr n#find))); + Buffer.add_string buffer (" in class of: "^(string_of_term (self#get_term n#find))); Buffer.add_char buffer '\n'; - Buffer.add_string buffer (" ccparent are: "^(String.concat ", " (List.map (fun x -> string_of_term (self#get_expr x)) n#get_ccparent))); + Buffer.add_string buffer (" ccparent are: "^(String.concat ", " (List.map (fun x -> string_of_term (self#get_term x)) (NodeSet.elements n#get_ccparent)))); Buffer.add_char buffer '\n'; - Buffer.add_string buffer (" ccpar are: "^(String.concat ", " (List.map (fun x -> string_of_term (self#get_expr x)) n#ccpar))); + Buffer.add_string buffer (" ccpar are: "^(String.concat ", " (List.map (fun x -> string_of_term (self#get_term x)) (NodeSet.elements n#ccpar)))); Buffer.add_char buffer '\n'; in Hashtbl.iter (fun _ n -> print_node n) nodes; Buffer.contents buffer - - method add_eq e1 e2 = - let n1 = self#get_node e1 in - let n2 = self#get_node e2 in + + method add_term t = + ignore (convert_term t) + + method add_eq t1 t2 = + let n1 = self#get_node t1 in + let n2 = self#get_node t2 in n1#merge n2 - method add_neq e1 e2 = - let n1 = self#get_node e1 in - let n2 = self#get_node e2 in + method add_neq t1 t2 = + let n1 = self#get_node t1 in + let n2 = self#get_node t2 in neqs <- (n1,n2) :: neqs - method entails_eq e1 e2 = - let n1 = self#get_node e1 in - let n2 = self#get_node e2 in + method entails_eq t1 t2 = + let n1 = self#get_node t1 in + let n2 = self#get_node t2 in n1#find = n2#find - method entails_neq e1 e2 = - let n1 = (self#get_node e1)#find in - let n2 = (self#get_node e2)#find in + method entails_neq t1 t2 = + let n1 = (self#get_node t1)#find in + let n2 = (self#get_node t2)#find in List.exists (fun (a,b) -> (a#find = n1 && b#find = n2) || (a#find = n2 && b#find = n1) ) @@ -235,7 +216,7 @@ class dag = fun expr -> (** Returns a method that maps a term to its representative *) - method get_repr = (fun e -> self#get_expr (self#get_node e)#find) + method get_repr = (fun t -> self#get_term (self#get_node t)#find) (** Gets a list of list of equal expressions (connected components). *) method get_cc = @@ -252,12 +233,12 @@ class dag = fun expr -> (* Returns a function that tests if two terms must be different *) method get_conflicts = - let repr = self#get_expr in + let repr = self#get_term in let conflicts = List.fold_left - (fun acc (e1,e2) -> - let n1 = self#get_expr e1#find in - let n2 = self#get_expr e2#find in + (fun acc (t1, t2) -> + let n1 = self#get_term t1#find in + let n2 = self#get_term t2#find in let c1 = try TermMap.find n1 acc with Not_found -> TermSet.empty in let c2 = try TermMap.find n2 acc with Not_found -> TermSet.empty in let c1p = TermSet.add n2 c1 in @@ -266,40 +247,38 @@ class dag = fun expr -> TermMap.empty neqs in - (fun e1 e2 -> - try TermSet.mem (repr e2) (TermMap.find (repr e1) conflicts) + (fun t1 t2 -> + try TermSet.mem (repr t2) (TermMap.find (repr t1) conflicts) with Not_found -> false) method copy = - let expressions = Hashtbl.fold (fun e _ acc -> e::acc ) nodes [] in - let cp = new dag expressions in - let new_of_old = Hashtbl.create (List.length expressions) in - List.iter (fun e -> Hashtbl.add new_of_old (self#get_node e) (cp#get_node e) ) expressions; - List.iter (fun e -> - let new_node = cp#get_node e in - let old_node = self#get_node e in - new_node#set_ccparent (List.map (Hashtbl.find new_of_old) (old_node#get_ccparent)); + let terms = self#get_terms in + let cp = new dag terms in + let new_of_old = Hashtbl.create (TermSet.cardinal terms) in + TermSet.iter (fun t -> Hashtbl.add new_of_old (self#get_node t) (cp#get_node t) ) terms; + TermSet.iter (fun t -> + let new_node = cp#get_node t in + let old_node = self#get_node t in + new_node#set_ccparent (NodeSet.fold (fun n acc -> NodeSet.add (Hashtbl.find new_of_old n) acc) (old_node#get_ccparent) NodeSet.empty); let new_parent = Hashtbl.find new_of_old (old_node#find) in if new_parent <> new_node then new_node#set_parent new_parent - ) expressions; + ) terms; cp end (* TODO need implied equalities and watch lists *) -let congr_classes_fixed_point fs gts = - let gterms = TermSet.add GrassUtil.mk_true_term (TermSet.add GrassUtil.mk_false_term gts) in - let cc_graph = new dag (TermSet.elements gterms) in +let add_conjuncts_fixed_point cc_graph fs : dag = let rec remove_false1 f = match f with - | Atom (App (Eq, [e1; e2], _), _) -> - if cc_graph#entails_neq e1 e2 then GrassUtil.mk_false else f - | BoolOp (Not, [Atom (App (Eq, [e1; e2], _), _)]) - | Atom (App (Lt, [e1; e2], _), _) - | Atom (App (Gt, [e1; e2], _), _) -> - if cc_graph#entails_eq e1 e2 then GrassUtil.mk_false else f - | Atom (App (Elem, [e1; e2], _) as pred, _) -> - if cc_graph#entails_eq e2 (GrassUtil.mk_empty (sort_of e2)) then GrassUtil.mk_false else + | Atom (App (Eq, [t1; t2], _), _) -> + if cc_graph#entails_neq t1 t2 then GrassUtil.mk_false else f + | BoolOp (Not, [Atom (App (Eq, [t1; t2], _), _)]) + | Atom (App (Lt, [t1; t2], _), _) + | Atom (App (Gt, [t1; t2], _), _) -> + if cc_graph#entails_eq t1 t2 then GrassUtil.mk_false else f + | Atom (App (Elem, [t1; t2], _) as pred, _) -> + if cc_graph#entails_eq t2 (GrassUtil.mk_empty (sort_of t2)) then GrassUtil.mk_false else if cc_graph#entails_eq pred GrassUtil.mk_false_term then GrassUtil.mk_false else f | Atom (pred, _) -> if cc_graph#entails_eq pred GrassUtil.mk_false_term then GrassUtil.mk_false else f @@ -317,7 +296,7 @@ let congr_classes_fixed_point fs gts = GrassUtil.smk_or fs1 | other -> other in - let singletons = TermSet.filter (function App (SetEnum, [_], _) -> true | _ -> false) gts in + let singletons = TermSet.filter (function App (SetEnum, [_], _) -> true | _ -> false) cc_graph#get_terms in let rec loop changed toProcess toSimplify = match toProcess with | f :: fs -> begin @@ -326,20 +305,20 @@ let congr_classes_fixed_point fs gts = loop changed (f :: fs) toSimplify | BoolOp (And, fs1) -> loop changed (fs1 @ fs) toSimplify - | Atom (App (Eq, [e1; e2], _), _) -> - cc_graph#add_eq e1 e2; + | Atom (App (Eq, [t1; t2], _), _) -> + cc_graph#add_eq t1 t2; loop true fs toSimplify - | BoolOp (Not, [Atom (App (Eq, [e1; e2], _), _)]) - | Atom (App (Lt, [e1; e2], _), _) - | Atom (App (Gt, [e1; e2], _), _) -> - cc_graph#add_neq e1 e2; + | BoolOp (Not, [Atom (App (Eq, [t1; t2], _), _)]) + | Atom (App (Lt, [t1; t2], _), _) + | Atom (App (Gt, [t1; t2], _), _) -> + cc_graph#add_neq t1 t2; loop true fs toSimplify | Atom (pred, _) -> let _ = match pred with - | App (Elem, [e1; e2], _) -> + | App (Elem, [t1; t2], _) -> TermSet.iter (function - | App (SetEnum, [e1'], _) as e2' when cc_graph#entails_eq e2 e2' -> - cc_graph#add_eq e1 e1' + | App (SetEnum, [t1'], _) as t2' when cc_graph#entails_eq t2 t2' -> + cc_graph#add_eq t1 t1' | _ -> ()) singletons | _ -> () @@ -356,49 +335,89 @@ let congr_classes_fixed_point fs gts = | [] -> if changed then loop false toSimplify [] in - (* Add disequalities between ADT terms with different top-level constructors *) - let cterms = - TermSet.filter - (function App (Constructor _, _, _) -> true | _ -> false) gterms - in - TermSet.iter (function - | App (Constructor id1, _, srt1) as t1 -> - TermSet.iter (function - | App (Constructor id2, _, srt2) as t2 when srt1 = srt2 && id1 <> id2 -> - cc_graph#add_neq t1 t2 - | _ -> ()) - cterms - | _ -> ()) cterms; - (* Add disequality between true and false *) - cc_graph#add_neq GrassUtil.mk_true_term GrassUtil.mk_false_term; (* Add top-level disequality unit clauses in fs *) loop false fs []; - (* Compute congruence classes *) - cc_graph#get_cc + cc_graph -let congr_classes_simple fs gterms = - let cc_graph = new dag (TermSet.elements gterms) in +let add_conjuncts_simple cc_graph fs : dag = let rec add = function | Binder (_, [], f, _) :: fs -> add (f :: fs) | BoolOp (And, fs1) :: fs -> add (fs1 @ fs) - | Atom (App (Eq, [e1; e2], _), _) :: fs -> - cc_graph#add_eq e1 e2; add fs - | BoolOp (Not, [Atom (App (Eq, [e1; e2], _), _)]) :: fs - | Atom (App (Lt, [e1; e2], _), _) :: fs - | Atom (App (Gt, [e1; e2], _), _) :: fs -> - cc_graph#add_neq e1 e2; add fs + | Atom (App (Eq, [t1; t2], _), _) :: fs -> + cc_graph#add_eq t1 t2; add fs + | BoolOp (Not, [Atom (App (Eq, [t1; t2], _), _)]) :: fs + | Atom (App (Lt, [t1; t2], _), _) :: fs + | Atom (App (Gt, [t1; t2], _), _) :: fs -> + cc_graph#add_neq t1 t2; add fs | _ :: fs -> add fs | [] -> () in add fs; - cc_graph#get_cc + cc_graph -let congr_classes fs gterms = +let add_conjuncts fs cc_graph : dag = if !Config.ccFixedPoint then - congr_classes_fixed_point fs gterms + add_conjuncts_fixed_point cc_graph fs else - congr_classes_simple fs gterms + add_conjuncts_simple cc_graph fs + +let add_terms gterms cc_graph = + let new_terms = TermSet.diff gterms (cc_graph#get_terms) in + (* Add gterms to graph *) + TermSet.iter (cc_graph#add_term) new_terms; + (* Add disequalities between ADT terms with different top-level constructors *) + let cterms = + TermSet.filter + (function App (Constructor _, _, _) -> true | _ -> false) new_terms + in + TermSet.iter (function + | App (Constructor id1, _, srt1) as t1 -> + TermSet.iter (function + | App (Constructor id2, _, srt2) as t2 when srt1 = srt2 && id1 <> id2 -> + cc_graph#add_neq t1 t2 + | _ -> ()) + cterms + | _ -> ()) cterms; + cc_graph +let get_implied_equalities cc_graph = + List.fold_left + (fun acc -> function + | c :: cls when sort_of c <> Bool && sort_of c <> Pat -> + let eq = List.map (fun t -> GrassUtil.mk_eq c t) cls in + List.rev_append eq acc + (*| c :: _ as cls when sort_of c = Bool -> + let mk_form = + if List.mem mk_true_term cls then + function + | App (BoolConst _, _, _) -> [] + | t -> [Atom (t, [])] + else if List.mem mk_false_term cls then + function + | App (BoolConst _, _, _) -> [] + | t -> [mk_not (Atom (t, []))] + else fun _ -> [] + in + List.rev_append (flat_map mk_form cls) acc*) + | _ -> acc) + [] + cc_graph#get_cc + +let create () : dag = + let terms = TermSet.of_list [mk_true_term; mk_false_term] in + let cc_graph = new dag terms in + (* Add disequality between true and false *) + cc_graph#add_neq mk_true_term mk_false_term; + cc_graph + +let get_classes cc_graph = cc_graph#get_cc + +let congruence_classes fs = + create () |> + add_terms (ground_terms ~include_atoms:true (mk_and fs)) |> + add_conjuncts fs |> + get_classes + let class_of t classes = List.find (List.mem t) classes let restrict_classes classes ts = diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml index 41769606..cf9d5968 100644 --- a/src/prover/instGen.ml +++ b/src/prover/instGen.ml @@ -223,6 +223,8 @@ let generate_terms generators ground_terms = in generate 1 new_terms ground_terms generators +let generate_terms generators = + measure_call "InstGen.generate_terms" (generate_terms generators) let generate_instances stratify useLocalInst axioms rep_terms egraph = (* *) @@ -376,7 +378,10 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = (fun acc subst_map -> (subst subst_map f) :: acc) acc subst_maps in List.fold_left instantiate epr_axioms axioms - + +let generate_instances stratify useLocalInst axioms rep_terms = + measure_call "InstGen.generate_instances" (generate_instances stratify useLocalInst axioms rep_terms) + let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms classes0 = if !Config.instantiate || force then (* remove theory atoms from congruence classes *) @@ -409,3 +414,5 @@ let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local a else axioms +let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms = + measure_call "InstGen.instantiate_with_terms" (instantiate_with_terms ~force:force ~stratify:stratify local axioms) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 9f2af8bf..0acd63af 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -163,7 +163,7 @@ let instantiate_and_prove session fs = | _ -> gts) gts gts in - let generate_adt_terms fs gts = + let generate_adt_terms gts = TermSet.fold (fun t (fs, gts) -> match t with | App (Constructor id, ts, Adt (ty_id, adts)) -> @@ -188,50 +188,36 @@ let instantiate_and_prove session fs = GrassUtil.mk_eq arg d :: fs, TermSet.add d gts) (fs, TermSet.add t gts) ts destrs | t -> fs, TermSet.add t gts - ) gts (fs, gts) + ) gts ([], gts) in let fs1, generators = open_axioms isFunVar fs1 in let btwn_gen = btwn_field_generators fs in let gts = ground_terms ~include_atoms:true (mk_and fs) in (*let gts = generate_knowns gts in*) let gts1 = generate_terms (btwn_gen @ generators) gts in - let classes = CongruenceClosure.congr_classes fs gts1 in - let get_implied_equalities classes = - List.fold_left - (fun acc -> function - | c :: cls when sort_of c <> Bool && sort_of c <> Pat -> - let eq = List.map (fun t -> GrassUtil.mk_eq c t) cls in - List.rev_append eq acc - (*| c :: _ as cls when sort_of c = Bool -> - let mk_form = - if List.mem mk_true_term cls then - function - | App (BoolConst _, _, _) -> [] - | t -> [Atom (t, [])] - else if List.mem mk_false_term cls then - function - | App (BoolConst _, _, _) -> [] - | t -> [mk_not (Atom (t, []))] - else fun _ -> [] - in - List.rev_append (flat_map mk_form cls) acc*) - | _ -> acc) - [] - classes + let cc_graph = + CongruenceClosure.create () |> + CongruenceClosure.add_terms gts1 |> + CongruenceClosure.add_conjuncts fs in - let round1 fs_inst gts_inst classes = + let round1 fs_inst gts_inst cc_graph = let equations = List.filter (fun f -> is_horn false [f]) fs_inst in let ground_fs = List.filter is_ground fs_inst in - let eqs = instantiate_with_terms true equations classes in + let eqs = instantiate_with_terms true equations (CongruenceClosure.get_classes cc_graph) in let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in - let fs, gts1 = generate_adt_terms fs gts1 in + let adt_fs, gts1 = generate_adt_terms gts1 in let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in - let classes = CongruenceClosure.congr_classes (List.rev_append eqs1 fs) gts1 in - let implied = get_implied_equalities classes in + let cc_graph = + cc_graph |> + CongruenceClosure.add_terms gts1 |> + CongruenceClosure.add_conjuncts (List.rev_append eqs1 adt_fs) + in + let implied = CongruenceClosure.get_implied_equalities cc_graph in let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and implied)) gts1 in - rev_concat [eqs; ground_fs; implied], gts1, classes + rev_concat [eqs; adt_fs; ground_fs; implied], gts1, cc_graph in - let round2 fs_inst gts_inst classes = + let round1 fs_inst gts_inst = measure_call "round1" (round1 fs_inst gts_inst) in + let round2 fs_inst gts_inst cc_graph = (* the following seemingly redundant instantiation round is a workaround for not using the -fullep option *) let fs_inst0 = (*instantiate_with_terms ~stratify:false true fs1 classes*) fs_inst in let gts_known = generate_knowns gts in @@ -246,7 +232,7 @@ let instantiate_and_prove session fs = gts_a TermSet.empty in let fs1 = linearize fs1 in - let rec saturate i fs_inst gts_inst0 classes = + let rec saturate i fs_inst gts_inst0 cc_graph = (*Printf.printf "Saturate iteration %d\n" i; flush stdout;*) let gts_inst = TermSet.union gts_inst0 core_terms in let gts_atoms = (*TermSet.filter (function @@ -255,21 +241,26 @@ let instantiate_and_prove session fs = (ground_terms ~include_atoms:true (mk_and fs_inst)) in let gts_inst = TermSet.union gts_inst gts_atoms in - let fs, gts_inst = generate_adt_terms fs gts_inst in - let implied_eqs = get_implied_equalities classes in + let adt_fs, gts_inst = generate_adt_terms gts_inst in + let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in let generators = if i > 1 then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in let gts_inst = generate_terms generators gts_inst in if i > 1 && not !Config.propagate_reads || TermSet.subset gts_inst gts_inst0 then - rev_concat [fs_inst; implied_eqs], gts_inst, classes + rev_concat [fs_inst; implied_eqs], gts_inst, cc_graph else - let classes = CongruenceClosure.congr_classes (rev_concat [fs_inst; fs]) gts_inst in - let fs_inst = instantiate_with_terms true fs1 classes in - saturate (i + 1) fs_inst gts_inst classes + let cc_graph = + cc_graph |> + measure_call "cc_gen" (fun cc_graph -> cc_graph |> CongruenceClosure.add_terms gts_inst) |> + CongruenceClosure.add_conjuncts fs_inst + in + let fs_inst = instantiate_with_terms true fs1 (CongruenceClosure.get_classes cc_graph) in + saturate (i + 1) fs_inst gts_inst cc_graph in - let fs, gts_inst, classes = saturate 1 fs_inst0 gts_inst0 classes in + let saturate i fs_inst gts_inst0 = measure_call "saturate" (saturate i fs_inst gts_inst0) in + let fs, gts_inst, cc_graph = saturate 1 fs_inst0 gts_inst0 cc_graph in let _ = if Debug.is_debug 1 then begin @@ -279,13 +270,14 @@ let instantiate_and_prove session fs = TermSet.iter (fun t -> print_endline (" " ^ (string_of_term t))) (TermSet.diff gts_inst gts) end in - fs, gts_inst, classes + fs, gts_inst, cc_graph in + let round2 fs_inst gts_inst = measure_call "round2" (round2 fs_inst gts_inst) in let do_rounds rounds = - let dr (k, result, fs_asserted, fs_inst, gts_inst, classes) r = + let dr (k, result, fs_asserted, fs_inst, gts_inst, cc_graph) r = match result with | Some true | None -> - let fs_inst1, gts_inst1, classes1 = r fs_inst gts_inst classes in + let fs_inst1, gts_inst1, classes1 = r fs_inst gts_inst cc_graph in let fs_inst1 = rename_forms fs_inst1 in let fs_asserted1, fs_inst1_assert = List.fold_left (fun (fs_asserted1, fs_inst1_assert) f -> @@ -296,17 +288,18 @@ let instantiate_and_prove session fs = (fs_asserted, []) fs_inst1 in - SmtLibSolver.assert_forms session fs_inst1_assert; + measure_call "SmtLibSolver.assert_forms" (SmtLibSolver.assert_forms session) fs_inst1_assert; Debug.debug (fun () -> Printf.sprintf "Calling SMT solver in instantiation round %d...\n" k); - let result1 = SmtLibSolver.is_sat session in + let result1 = measure_call "SmtLibSolver.is_sat" SmtLibSolver.is_sat session in Debug.debug (fun () -> "Solver done.\n"); k + 1, result1, fs_asserted1, fs_inst1, gts_inst1, classes1 - | _ -> k, result, fs_asserted, fs_inst, gts_inst, classes - in List.fold_left dr (1, None, FormSet.empty, fs1, gts1, classes) rounds + | _ -> k, result, fs_asserted, fs_inst, gts_inst, cc_graph + in List.fold_left dr (1, None, FormSet.empty, fs1, gts1, cc_graph) rounds in let _, result, _, fs_inst, _, _ = do_rounds [round1; round2] in result, session, fs_inst +let instantiate_and_prove session = measure_call "Prover.instantiate_and_prove" (instantiate_and_prove session) let prove name sat_means f = let fs = Reduction.reduce f in @@ -356,6 +349,7 @@ let dump_core session = Config.dump_smt_queries := config end + let check_sat ?(session_name="form") ?(sat_means="sat") f = let result, session, f_inst = prove session_name sat_means f in diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 9950aedd..966aadce 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -352,6 +352,7 @@ let get_read_propagators gts = Match (wrap (mk_read fld1 (ivars1)), []); ], [wrap (mk_read fld2 (ivars1))]) :: + (* f == g, x.f -> x.g *) ([Match (mk_eq_term fld1 fld2, []); Match (wrap (mk_read fld1 (ivars1)), []) ], @@ -391,7 +392,7 @@ let add_read_write_axioms fs = (* instantiate null axioms *) let axioms = SortSet.fold (fun srt axioms -> Axioms.null_axioms srt @ axioms) basic_structs [] in let null_ax, _ = open_axioms ~force:true isFld axioms in - let classes = CongruenceClosure.congr_classes fs gts in + let classes = CongruenceClosure.congruence_classes fs in (* CAUTION: not forcing the instantiation here would yield an inconsistency with the read/write axioms *) let null_ax1 = instantiate_with_terms ~force:true false null_ax (CongruenceClosure.restrict_classes classes basic_pt_flds) in let fs1 = null_ax1 @ fs in @@ -431,8 +432,7 @@ let add_reach_axioms fs = | _ -> struct_sorts) (sorts (mk_and fs)) SortSet.empty in - let gts = ground_terms ~include_atoms:true (mk_and fs) in - let classes = CongruenceClosure.congr_classes fs gts in + let classes = CongruenceClosure.congruence_classes fs in let axioms = SortSet.fold (fun srt axioms -> Axioms.reach_axioms classes srt @ Axioms.reach_write_axioms srt @ axioms) @@ -469,7 +469,12 @@ let add_split_lemmas fs gts = | _ -> structs) gts SortSet.empty in - let classes = CongruenceClosure.congr_classes fs (generated_ground_terms fs) in + let classes = + CongruenceClosure.create () |> + CongruenceClosure.add_terms (generated_ground_terms fs) |> + CongruenceClosure.add_conjuncts fs |> + CongruenceClosure.get_classes + in let add_lemmas srt fs1 = let loc_gts = TermSet.filter (fun t -> sort_of t = Loc srt) gts From 79e7235da97a62d0832e873531d30612dd14e7fe Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 19 Aug 2018 20:28:13 -0400 Subject: [PATCH 070/118] several improvements to congruence closure implementation --- src/prover/congruenceClosure.ml | 29 ++++++++++++++++++----------- src/prover/prover.ml | 24 ++++++++++++++++-------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index eb692b91..66a25ff8 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -1,5 +1,6 @@ (** DZ: this is a copy-pasted version from csisat, just adaped to the current types *) +open Util open Grass open GrassUtil @@ -87,7 +88,7 @@ module rec Node : sig && self#get_arity = that#get_arity && - List.for_all (fun (a,b) -> a#find = b#find) (List.rev_map2 (fun x y -> (x,y)) (self#get_args) (that#get_args)) + List.for_all2 (fun a b -> a#find = b#find) self#get_args that#get_args (** return pairs of nodes whose equality may change the result of the 'congruent' method*) (*method may_be_congruent (that: node) = @@ -100,13 +101,14 @@ module rec Node : sig method merge (that: node) = if self#find <> that#find then begin - let p1 = NodeSet.elements self#ccpar in - let p2 = NodeSet.elements that#ccpar in - self#union that; - let to_test = - List.flatten (List.map (fun x -> List.map (fun y -> (x,y)) p2) p1) - in - List.iter (fun (x,y) -> if x#find <> y#find && x#congruent y then x#merge y) to_test + let p1 = self#ccpar in + let p2 = that#ccpar in + self#union that; + NodeSet.iter (fun x -> + NodeSet.iter + (fun y -> if x#find <> y#find && x#congruent y then x#merge y) + p2) + p1 end (** return pairs of nodes whose equality comes from congruence*) @@ -189,7 +191,10 @@ class dag = fun (terms: TermSet.t) -> Buffer.contents buffer method add_term t = - ignore (convert_term t) + let n = convert_term t in + let arg_opt = List.nth_opt n#get_args 0 in + arg_opt |> + Opt.iter (fun arg -> NodeSet.iter (fun n' -> if n' <> n && n#congruent n' then n#merge n') arg#ccpar) method add_eq t1 t2 = let n1 = self#get_node t1 in @@ -362,7 +367,9 @@ let add_conjuncts fs cc_graph : dag = add_conjuncts_simple cc_graph fs let add_terms gterms cc_graph = - let new_terms = TermSet.diff gterms (cc_graph#get_terms) in + let old_terms = cc_graph#get_terms in + let new_terms = TermSet.diff gterms old_terms in + let all_terms = TermSet.union old_terms new_terms in (* Add gterms to graph *) TermSet.iter (cc_graph#add_term) new_terms; (* Add disequalities between ADT terms with different top-level constructors *) @@ -376,7 +383,7 @@ let add_terms gterms cc_graph = | App (Constructor id2, _, srt2) as t2 when srt1 = srt2 && id1 <> id2 -> cc_graph#add_neq t1 t2 | _ -> ()) - cterms + all_terms | _ -> ()) cterms; cc_graph diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 0acd63af..0b243c54 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -163,7 +163,7 @@ let instantiate_and_prove session fs = | _ -> gts) gts gts in - let generate_adt_terms gts = + let generate_adt_terms fs gts = TermSet.fold (fun t (fs, gts) -> match t with | App (Constructor id, ts, Adt (ty_id, adts)) -> @@ -188,7 +188,7 @@ let instantiate_and_prove session fs = GrassUtil.mk_eq arg d :: fs, TermSet.add d gts) (fs, TermSet.add t gts) ts destrs | t -> fs, TermSet.add t gts - ) gts ([], gts) + ) gts (fs, gts) in let fs1, generators = open_axioms isFunVar fs1 in let btwn_gen = btwn_field_generators fs in @@ -205,16 +205,16 @@ let instantiate_and_prove session fs = let ground_fs = List.filter is_ground fs_inst in let eqs = instantiate_with_terms true equations (CongruenceClosure.get_classes cc_graph) in let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in - let adt_fs, gts1 = generate_adt_terms gts1 in + let fs, gts1 = generate_adt_terms fs gts1 in let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in let cc_graph = cc_graph |> CongruenceClosure.add_terms gts1 |> - CongruenceClosure.add_conjuncts (List.rev_append eqs1 adt_fs) + CongruenceClosure.add_conjuncts (List.rev_append eqs1 fs) in let implied = CongruenceClosure.get_implied_equalities cc_graph in let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and implied)) gts1 in - rev_concat [eqs; adt_fs; ground_fs; implied], gts1, cc_graph + rev_concat [eqs; ground_fs; implied], gts1, cc_graph in let round1 fs_inst gts_inst = measure_call "round1" (round1 fs_inst gts_inst) in let round2 fs_inst gts_inst cc_graph = @@ -241,7 +241,7 @@ let instantiate_and_prove session fs = (ground_terms ~include_atoms:true (mk_and fs_inst)) in let gts_inst = TermSet.union gts_inst gts_atoms in - let adt_fs, gts_inst = generate_adt_terms gts_inst in + let fs, gts_inst = generate_adt_terms fs gts_inst in let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) @@ -254,7 +254,7 @@ let instantiate_and_prove session fs = let cc_graph = cc_graph |> measure_call "cc_gen" (fun cc_graph -> cc_graph |> CongruenceClosure.add_terms gts_inst) |> - CongruenceClosure.add_conjuncts fs_inst + CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) in let fs_inst = instantiate_with_terms true fs1 (CongruenceClosure.get_classes cc_graph) in saturate (i + 1) fs_inst gts_inst cc_graph @@ -296,7 +296,14 @@ let instantiate_and_prove session fs = | _ -> k, result, fs_asserted, fs_inst, gts_inst, cc_graph in List.fold_left dr (1, None, FormSet.empty, fs1, gts1, cc_graph) rounds in - let _, result, _, fs_inst, _, _ = do_rounds [round1; round2] in + let _, result, fs_asserted, fs_inst, _, _ = do_rounds [round1; round2] in + (match result with + | Some true | None -> + Debug.debugl 1 (fun () -> + "\nSMT called with:\n\n" + ^ ((FormSet.elements fs_asserted) + |> smk_and |> string_of_form) ^ "\n\n") + | Some false -> ()); result, session, fs_inst let instantiate_and_prove session = measure_call "Prover.instantiate_and_prove" (instantiate_and_prove session) @@ -339,6 +346,7 @@ let dump_core session = in let core = Opt.get (SmtLibSolver.get_unsat_core session) in let core = minimize core in + Debug.debugl 1 (fun () -> "\n\nCore:\n" ^ (string_of_form (smk_and core) ^ "\n\n")); let config = !Config.dump_smt_queries in Config.dump_smt_queries := true; let s = SmtLibSolver.start core_name session.SmtLibSolver.sat_means in From 644bce877cd8bd20ef607b3579324be4b78a8347 Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 19 Aug 2018 20:36:18 -0400 Subject: [PATCH 071/118] several improvements to congruence closure implementation --- src/prover/congruenceClosure.ml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 66a25ff8..fb6fe182 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -110,25 +110,6 @@ module rec Node : sig p2) p1 end - - (** return pairs of nodes whose equality comes from congruence*) - (*method merge_with_applied (that: node) = - if self#find <> that#find then - begin - let p1 = self#ccpar in - let p2 = that#ccpar in - self#union that; - let to_test = - List.flatten (List.map (fun x -> List.map (fun y -> (x,y)) p2) p1) - in - let cong = List.filter (fun (x,y) -> x#find <> y#find && x#congruent y) to_test in - List.fold_left - (fun acc (x,y) -> if x#find <> y#find then - (x#merge_with_applied y) @ ((x,y)::acc) - else - acc) [] cong - end - else []*) end let create sym terms: t = new node sym terms From c0c935e4d2d388b4fa824c63bbe45f80349cb9d3 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 23 Aug 2018 17:44:08 -0400 Subject: [PATCH 072/118] properly skolemize negated equalities between ADT terms --- src/formulas/grassUtil.ml | 80 ++- src/frontends/spl/#splChecker.ml# | 1114 +++++++++++++++++++++++++++++ src/frontends/spl/.#splChecker.ml | 1 + src/prover/congruenceClosure.ml | 20 +- src/prover/instGen.ml | 21 +- src/prover/prover.ml | 16 +- src/prover/reduction.ml | 51 +- 7 files changed, 1235 insertions(+), 68 deletions(-) create mode 100644 src/frontends/spl/#splChecker.ml# create mode 120000 src/frontends/spl/.#splChecker.ml diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index 02612888..fcc80543 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -892,6 +892,22 @@ let sorts f = in fold_terms s SortSet.empty f +(** Unfold the sort variables standing for ADTs [adts] in sort [srt] *) +let unfold_adts adts srt = + let rec unfold srt = match srt with + | FreeSrt sid -> + List.assoc_opt sid adts |> + Util.Opt.map (fun _ -> Adt (sid, adts)) |> + Util.Opt.get_or_else srt + | Map (asrts, rsrt) -> Map (List.map unfold asrts, unfold rsrt) + | Set ssrt -> Set (unfold ssrt) + | Loc lsrt -> Loc (unfold lsrt) + | Array asrt -> Array (unfold asrt) + | ArrayCell asrt -> ArrayCell asrt + | _ -> srt + in + unfold srt + (** Computes the set of free constants occuring in term [t]. ** Takes accumulator [consts] as additional argument. *) let free_consts_term_acc consts t = @@ -1180,11 +1196,20 @@ let subst_consts subst_map f = ** This operation is not capture avoiding. *) let subst_term subst_map t = let sub_id id t = - try IdMap.find id subst_map with Not_found -> t + IdMap.find_opt id subst_map |> + Opt.get_or_else t in let rec sub = function - | (Var (id, srt) as t) -> sub_id id t - | App (sym, ts, srt) -> App (sym, List.map sub ts, srt) + | Var (id, srt) as t -> sub_id id t + | App (sym, ts, srt) as t -> + let changed, ts1 = + List.fold_right (fun t (changed, ts1) -> + let t1 = sub t in + changed || t != t1, t1 :: ts1) ts (false, []) + in + if changed + then App (sym, ts1, srt) + else t in sub t (** Substitute all function applications in term [t] according to function [fct]. *) @@ -1207,13 +1232,19 @@ let subst_funs fct f = let subst_annot subst_map = function | TermGenerator (guards, gen_terms) -> let guards1 = - List.map + List.map (function Match (t, f) -> Match (subst_term subst_map t, f)) guards in - TermGenerator (guards1, List.map (subst_term subst_map) gen_terms) - | Pattern (t, fs) -> Pattern (subst_term subst_map t, fs) - | Label (pol, t) -> Label (pol, subst_term subst_map t) + let gen_terms1 = + List.map (subst_term subst_map) + gen_terms + in + TermGenerator (guards1, gen_terms1) + | Pattern (t, fs) -> + Pattern (subst_term subst_map t, fs) + | Label (pol, t) -> + Label (pol, subst_term subst_map t) | a -> a (** Substitutes all free variables in formula [f] with terms according to substitution map [subst_map]. @@ -1223,23 +1254,30 @@ let subst subst_map f = let not_bound id _ = not (List.mem_assoc id vs) in let sm1 = IdMap.filter not_bound sm in let occuring = IdMap.fold (fun _ t acc -> fv_term_acc acc t) sm IdSet.empty in - let vs1, sm2 = - List.fold_right - (fun (x, srt) (vs1, sm2) -> - if IdSet.mem x occuring - then - let x1 = fresh_ident (name x) in - (x1, srt) :: vs1, IdMap.add x (Var (x1, srt)) sm2 - else (x, srt) :: vs1, sm2) - vs ([], sm1) - in vs1, sm2 + List.fold_right + (fun (x, srt) (vs1, sm2) -> + if IdSet.mem x occuring + then + let x1 = fresh_ident (name x) in + (x1, srt) :: vs1, IdMap.add x (Var (x1, srt)) sm2 + else (x, srt) :: vs1, sm2) + vs ([], sm1) in let rec sub sm = function - | BoolOp (op, fs) -> BoolOp (op, List.map (sub sm) fs) - | Atom (t, a) -> Atom (subst_term sm t, List.map (subst_annot sm) a) - | Binder (b, vs, f, a) -> + | BoolOp (op, fs) -> + let fs1 = + List.map (sub sm) fs + in + BoolOp (op, fs1) + | Atom (t, aa) -> + let t1 = subst_term sm t in + let aa1 = List.map (subst_annot sm) aa in + Atom (t1, aa1) + | Binder (b, vs, bf, aa) -> let vs1, sm1 = rename_vars vs sm in - Binder (b, vs1, sub sm1 f, List.map (subst_annot sm1) a) + let bf1 = sub sm1 bf in + let aa1 = List.map (subst_annot sm1) aa in + Binder (b, vs1, bf1, aa1) in sub subst_map f diff --git a/src/frontends/spl/#splChecker.ml# b/src/frontends/spl/#splChecker.ml# new file mode 100644 index 00000000..bfe5b4f2 --- /dev/null +++ b/src/frontends/spl/#splChecker.ml# @@ -0,0 +1,1114 @@ +(** {5 Static Checker for SPL programs } *) + +open Util +open Prog +open Sl +open Grass +open SplSyntax +open SplErrors +open SplTypeChecker + +(** Resolve names of identifiers in compilation unit [cu] so that all identifiers have unique names.*) +let resolve_names cu = + let lookup_id init_id tbl pos = + match SymbolTbl.find tbl init_id with + | Some (id, _) -> id + | None -> unknown_ident_error init_id pos + in + let check_type id types pos = + if not (IdMap.mem id types) then + not_a_type_error id pos + in + let check_proc id procs pos = + if not (IdMap.mem id procs) then + not_a_proc_error id pos + in + let check_pred id preds pos = + if not (IdMap.mem id preds) then + not_a_pred_error id pos + in + (* resolve names in type expressions *) + let resolve_typ types pos tbl = + let rec r = function + | IdentType init_id -> + let id = lookup_id init_id tbl pos in + check_type id types pos; + let decl = IdMap.find id types in + (match decl.t_def with + | StructTypeDef _ -> StructType id + | ADTypeDef _ -> ADType id + | _ -> IdentType id) + | ArrayType ty -> ArrayType (r ty) + | ArrayCellType ty -> ArrayCellType (r ty) + | MapType (ty1, ty2) -> MapType (r ty1, r ty2) + | SetType ty -> SetType (r ty) + | ty -> ty + in + r + in + let declare_name ?(allow_redecl=false) pos init_id scope tbl = + let name = GrassUtil.name init_id in + let id = match SymbolTbl.find_local tbl init_id with + | Some (id, _) -> + if allow_redecl then id else redeclaration_error init_id pos + | None -> + GrassUtil.fresh_ident ~id:(snd init_id) name + in + (id, SymbolTbl.add tbl init_id (id, scope)) + in + let declare_var types decl tbl = + let id, tbl = declare_name decl.v_pos decl.v_name decl.v_scope tbl in + let ty = resolve_typ types decl.v_pos tbl decl.v_type in + { decl with v_name = id; v_type = ty }, tbl + in + let declare_vars vars structs tbl = + IdMap.fold + (fun _ decl (vars, tbl) -> + let decl, tbl = declare_var structs decl tbl in + IdMap.add decl.v_name decl vars, tbl) + vars (IdMap.empty, tbl) + in + let tbl = SymbolTbl.empty in + (* resolve type names *) + + let types0, tbl = + IdMap.fold + (fun init_id decl (types, tbl) -> + let id, tbl = declare_name decl.t_pos init_id GrassUtil.global_scope tbl in + IdMap.add id { decl with t_name = id } types, tbl) + cu.type_decls (IdMap.empty, tbl) + in + (* resolve global variables *) + let globals0, tbl = declare_vars cu.var_decls types0 tbl in + (* declare struct fields *) + let types, globals, funs, tbl = + IdMap.fold + (fun id decl (types, globals, funs, tbl) -> + match decl.t_def with + | StructTypeDef fields0 -> + let fields, globals, tbl = + IdMap.fold + (fun init_id fdecl (fields, globals, tbl) -> + let id, tbl = declare_name fdecl.v_pos init_id GrassUtil.global_scope tbl in + let res_type = resolve_typ types0 fdecl.v_pos tbl fdecl.v_type in + let typ = MapType (StructType decl.t_name, res_type) in + let fdecl = { fdecl with v_name = id; v_type = res_type } in + let gfdecl = { fdecl with v_type = typ } in + IdMap.add id fdecl fields, IdMap.add id gfdecl globals, tbl + ) + fields0 (IdMap.empty, globals, tbl) + in + IdMap.add id { decl with t_def = StructTypeDef fields } types, + globals, + funs, + tbl + | ADTypeDef consts0 -> + let consts, funs, tbl = + List.fold_right + (fun cnst (consts, funs, tbl) -> + let cid, tbl = declare_name decl.t_pos cnst.c_name GrassUtil.global_scope tbl in + let args, funs, tbl = + List.fold_right + (fun adecl (args, funs, tbl) -> + let aid, tbl = declare_name decl.t_pos adecl.v_name GrassUtil.global_scope tbl in + let ty = resolve_typ types0 decl.t_pos tbl adecl.v_type in + let fun_decl = { f_name = id; f_args = [ADType id]; f_res = ty; f_is_destr = true} in + { adecl with v_name = aid; v_type = ty } :: args, + IdMap.add aid fun_decl funs, + tbl + ) + cnst.c_args ([], funs, tbl) + in + let arg_tys = + List.map + (fun adecl -> resolve_typ types0 adecl.v_pos tbl adecl.v_type) + cnst.c_args + in + let fun_decl = + { f_name = cid; + f_args = arg_tys; + f_res = ADType id; + f_is_destr = false; + } + in + { cnst with c_name = cid; c_args = args } :: consts, + IdMap.add cid fun_decl funs, + tbl + ) + consts0 ([], funs, tbl) + in + IdMap.add id { decl with t_def = ADTypeDef consts } types, + globals, + funs, + tbl + | _ -> IdMap.add id decl types, globals, funs, tbl + ) + types0 (IdMap.empty, globals0, IdMap.empty, tbl) + in + (* declare procedure names *) + let procs0, tbl = + IdMap.fold (fun init_id decl (procs, tbl) -> + let id, tbl = declare_name decl.p_pos init_id GrassUtil.global_scope tbl in + IdMap.add id { decl with p_name = id } procs, tbl) + cu.proc_decls (IdMap.empty, tbl) + in + (* declare predicate names *) + let preds0, tbl = + IdMap.fold (fun init_id decl (preds, tbl) -> + let id, tbl = declare_name decl.pr_pos init_id GrassUtil.global_scope tbl in + IdMap.add id { decl with pr_name = id } preds, tbl) + cu.pred_decls (IdMap.empty, tbl) + in + let cu = + { cu with + var_decls = globals; + type_decls = types; + proc_decls = procs0; + pred_decls = preds0; + fun_decls = funs; + } + in + (* declare and resolve local variables in given expression *) + let resolve_expr locals tbl e = + let rec re locals tbl = function + | Setenum (ty, args, pos) -> + let ty1 = resolve_typ types pos tbl ty in + let args1 = List.map (re locals tbl) args in + Setenum (ty1, args1, pos) + | New (ty, args, pos) -> + let ty1 = resolve_typ types pos tbl ty in + let args1 = List.map (re locals tbl) args in + New (ty1, args1, pos) + | Read ((Ident (("length", _), _) as map), idx, pos) -> + let idx1 = re locals tbl idx in + (match type_of_expr cu locals idx1 with + | ArrayType _ | AnyType -> UnaryOp (OpLength, idx1, pos) + | ty -> Read (re locals tbl map, idx1, pos)) + | Read ((Ident (("map", _), _) as map), arr, pos) -> + let arr = re locals tbl arr in + (match type_of_expr cu locals arr with + | ArrayType _ | AnyType -> UnaryOp (OpArrayMap, arr, pos) + | ty -> Read (re locals tbl map, arr, pos)) + | Read ((Ident (("cells", _), _) as map), idx, pos) -> + let idx1 = re locals tbl idx in + (match type_of_expr cu locals idx1 with + | ArrayType _ | AnyType -> UnaryOp (OpArrayCells, idx1, pos) + | _ -> Read (re locals tbl map, idx1, pos)) + | Read ((Ident (("array", _), _) as map), idx, pos) -> + let idx1 = re locals tbl idx in + (match type_of_expr cu locals idx1 with + | ArrayCellType _ | AnyType -> UnaryOp (OpArrayOfCell, idx1, pos) + | _ -> Read (re locals tbl map, idx1, pos)) + | Read ((Ident (("index", _), _) as map), idx, pos) -> + let idx1 = re locals tbl idx in + (match type_of_expr cu locals idx1 with + | ArrayCellType _ | AnyType -> UnaryOp (OpIndexOfCell, idx1, pos) + | _ -> Read (re locals tbl map, idx1, pos)) + | Read (Ident (init_id, map_pos), idx, pos) -> + let id = lookup_id init_id tbl map_pos in + let idx1 = re locals tbl idx in + let map1 = Ident (id, map_pos) in + if IdMap.mem id cu.fun_decls + then DestrApp (id, idx1, pos) + else Read (map1, idx1, pos) + | Read (map, idx, pos) -> + Read (re locals tbl map, re locals tbl idx, pos) + | Write (map, idx, upd, pos) -> + Write (re locals tbl map, re locals tbl idx, re locals tbl upd, pos) + | Ite (cond, t, e, pos) -> + Ite (re locals tbl cond, re locals tbl t, re locals tbl e, pos) + | ConstrApp (id, es, pos) -> + ConstrApp (id, List.map (re locals tbl) es, pos) + | DestrApp (id, e, pos) -> + DestrApp (id, re locals tbl e, pos) + | Binder (q, decls, f, pos) -> + let (decls1, (locals1, tbl1)) = + Util.fold_left_map + (fun (locals, tbl) decl -> match decl with + | GuardedVar (init_id, e) -> + let e1 = re locals tbl e in + let id, tbl1 = declare_name pos init_id pos tbl in + (GuardedVar (id, e1)), (locals, tbl1) + | UnguardedVar decl -> + let decl, tbl1 = declare_var types decl tbl in + (UnguardedVar decl), (IdMap.add decl.v_name decl locals, tbl1) + ) + (locals, SymbolTbl.push tbl) + decls + in + let f1 = re locals1 tbl1 f in + Binder (q, decls1, f1, pos) + | ProcCall (("acc", _ as id), args, pos) -> + let args1 = List.map (re locals tbl) args in + (match args1 with + | [arg] -> + (match type_of_expr cu locals arg with + | SetType _ | MapType _ -> + PredApp (AccessPred, [arg], pos) + | ty -> + PredApp (AccessPred, [Setenum (resolve_typ types pos tbl ty, [arg], pos)], pos)) + | [map; idx] -> + (match type_of_expr cu locals map with + | ArrayType typ -> + let map1 = re locals tbl map in + let idx1 = re locals tbl idx in + let cell = Read (UnaryOp (OpArrayCells, map1, pos_of_expr map1), idx1, pos) in + PredApp (AccessPred, [Setenum (ArrayCellType typ, [cell], pos)], pos) + | _ -> pred_arg_mismatch_error pos id 1) + | _ -> pred_arg_mismatch_error pos id 1) + | ProcCall (init_id, args, pos) -> + let args1 = List.map (re locals tbl) args in + (match GrassUtil.name init_id with + | "old" -> + UnaryOp (OpOld, List.hd args1, pos) + | "known" -> + UnaryOp (OpKnown, List.hd args1, pos) + | "Btwn" -> + PredApp (BtwnPred, args1, pos) + | "Reach" -> + PredApp (ReachPred, args1, pos) + | "Frame" -> + PredApp (FramePred, args1, pos) + | "Disjoint" -> + PredApp (DisjointPred, args1, pos) + | _ -> + let id = lookup_id init_id tbl pos in + try + let f_decl_opt = IdMap.find_opt id cu.fun_decls in + f_decl_opt |> + Opt.map (fun f_decl -> + if f_decl.f_is_destr + then begin + match args1 with + | [arg1] -> DestrApp (id, arg1, pos) + | _ -> destr_arg_mismatch_error pos id (List.length args1) + end else ConstrApp (id, args1, pos)) |> + Opt.lazy_get_or_else (fun () -> + check_proc id procs0 pos; + ProcCall (id, args1, pos)) + with ProgError.Prog_error _ -> + check_pred id preds0 pos; + PredApp (Pred id, args1, pos)) + | PredApp (sym, args, pos) -> + PredApp (sym, List.map (re locals tbl) args, pos) + | UnaryOp (op, e, pos) -> + UnaryOp (op, re locals tbl e, pos) + | BinaryOp (e1, op, e2, ty, pos) -> + BinaryOp (re locals tbl e1, op, re locals tbl e2, ty, pos) + | Ident (init_id, pos) -> + let id = lookup_id init_id tbl pos in + if IdMap.mem id preds0 then + PredApp (Pred id, [], pos) + else if IdMap.mem id funs then + ConstrApp (id, [], pos) + else + Ident (id, pos) + | Annot (e, PatternAnnot p, pos) -> + Annot (re locals tbl e, PatternAnnot (re locals tbl p), pos) + | Annot (e, GeneratorAnnot (es, ge), pos) -> + let es1 = + List.map (fun (e, ids) -> + let ids1 = + List.map (fun init_id -> lookup_id init_id tbl pos) ids + in + re locals tbl e, ids1) + es + in + Annot (re locals tbl e, GeneratorAnnot (es1, re locals tbl ge), pos) + | Annot (e, ann, pos) -> + Annot (re locals tbl e, ann, pos) + | e -> e + in + re locals tbl e + in + (* declare and resolve local variables in given statement *) + let rec resolve_stmt first_block in_loop locals tbl = function + | Skip pos -> Skip pos, locals, tbl + | Block (stmts0, pos) -> + let tbl1 = if first_block then tbl else SymbolTbl.push tbl in + let stmts, locals, _ = + List.fold_left + (fun (stmts, locals, tbl) stmt0 -> + let stmt, locals, tbl = resolve_stmt false in_loop locals tbl stmt0 in + stmt :: stmts, locals, tbl + ) + ([], locals, tbl1) stmts0 + in Block (List.rev stmts, pos), locals, tbl + | LocalVars (vars, es_opt, pos) -> + let es_opt1, tys = + match es_opt with + (* var x1, ..., xn := p(e1, ..., em); *) + | Some [ProcCall (_, _, _) as e] -> + let e1 = resolve_expr locals tbl e in + let tys = match e1 with + | ProcCall (id, _, _) + | PredApp (Pred id, _, _) -> + let returns, locals = + try + let decl = IdMap.find id cu.proc_decls in + let returns = List.filter (fun p -> not (IdMap.find p decl.p_locals).v_implicit) decl.p_returns in + returns, decl.p_locals + with Not_found -> + let decl = IdMap.find id cu.pred_decls in + decl.pr_outputs, decl.pr_locals + in + List.map (fun v -> (IdMap.find v locals).v_type) returns + | e -> [type_of_expr cu locals e1] + in + Some [e1], tys + (* var x1, ..., xn := e1, ..., en; *) + | Some es -> + let es1, tys = + Util.map_split (fun e -> + let e1 = resolve_expr locals tbl e in + e1, type_of_expr cu locals e1) es + in + Some es1, tys + (* var x1: T1, ..., xn: Tn; *) + | None -> + None, List.map (fun decl -> decl.v_type) vars + in + let ids, locals, tbl = + try + List.fold_right2 + (fun decl ty (ids, locals, tbl) -> + let decl = + match decl.v_type with + | AnyType -> + if is_abstract_type ty then + abstract_initializer_error pos decl.v_name; + { decl with v_type = ty } + | _ -> decl + in + let decl, tbl = declare_var types decl tbl in + Ident (decl.v_name, decl.v_pos) :: ids, + IdMap.add decl.v_name decl locals, + tbl + ) + vars tys ([], locals, tbl) + with Invalid_argument _ -> assignment_mismatch_error pos + in + (match es_opt1 with + | Some es1 -> + Assign (ids, es1, pos), locals, tbl + | None -> + Havoc (ids, pos), locals, tbl) + | Assume (e, pure, pos) -> + Assume (resolve_expr locals tbl e, pure, pos), locals, tbl + | Assert (e, pure, pos) -> + Assert (resolve_expr locals tbl e, pure, pos), locals, tbl + | Split (e, pos) -> + Split (resolve_expr locals tbl e, pos), locals, tbl + | Assign (lhs, rhs, pos) -> + let lhs1 = List.map (resolve_expr locals tbl) lhs in + let rhs1 = List.map (resolve_expr locals tbl) rhs in + Assign (lhs1, rhs1, pos), locals, tbl + | Dispose (e, pos) -> + Dispose (resolve_expr locals tbl e, pos), locals, tbl + | Havoc (es, pos) -> + let es1 = List.map (resolve_expr locals tbl) es in + Havoc (es1, pos), locals, tbl + | If (cond, t, e, pos) -> + let t1, locals, _ = resolve_stmt false in_loop locals tbl t in + let e1, locals, _ = resolve_stmt false in_loop locals tbl e in + If (resolve_expr locals tbl cond, t1, e1, pos), locals, tbl + | Choice (stmts0, pos) -> + let stmts, locals = + List.fold_left + (fun (stmts, locals) stmt0 -> + let stmt, locals, _ = resolve_stmt false in_loop locals tbl stmt0 in + stmt :: stmts, locals + ) + ([], locals) stmts0 + in + Choice (List.rev stmts, pos), locals, tbl + | Loop (inv, preb, cond, postb, pos) -> + let inv1 = + List.fold_right + (function Invariant (e, pure) -> fun inv1 -> + Invariant (resolve_expr locals tbl e, pure) :: inv1 + ) + inv [] + in + let preb1, locals, _ = resolve_stmt false true locals tbl preb in + let cond1 = resolve_expr locals tbl cond in + let postb1, locals, _ = resolve_stmt false true locals tbl postb in + Loop (inv1, preb1, cond1, postb1, pos), locals, tbl + | Return (es, pos) -> + if in_loop then return_in_loop_error pos; + Return (List.map (resolve_expr locals tbl) es, pos), locals, tbl + in + (* declare and resolve local variables in given contracts *) + let resolve_contracts contracts returns locals tbl = + let pre_locals, pre_tbl = + List.fold_left + (fun (pre_locals, pre_tbl) id -> + IdMap.remove id pre_locals, + SymbolTbl.remove pre_tbl id) + (locals, tbl) + returns + in + List.map + (function + | Requires (e, pure, free) -> Requires (resolve_expr pre_locals pre_tbl e, pure, free) + | Ensures (e, pure, free) -> Ensures (resolve_expr locals tbl e, pure, free) + ) + contracts + in + (* declare and resolve local variables in all procedures *) + let procs = + IdMap.fold + (fun _ decl procs -> + let locals0, tbl0 = declare_vars decl.p_locals types (SymbolTbl.push tbl) in + let process_params params seen = + List.fold_right + (fun id (params, seen) -> + let nid = lookup_id id tbl0 decl.p_pos in + if IdSet.mem id seen + then redeclaration_error id (IdMap.find id decl.p_locals).v_pos + else nid :: params, IdSet.add id seen) + params ([], seen) + in + let formals, seen = process_params decl.p_formals IdSet.empty in + let returns, _ = process_params decl.p_returns seen in + let contracts = resolve_contracts decl.p_contracts returns locals0 tbl0 in + let body, locals, _ = resolve_stmt true false locals0 tbl0 decl.p_body in + let decl1 = + { decl with + p_formals = formals; + p_returns = returns; + p_locals = locals; + p_contracts = contracts; + p_body = body } + in + IdMap.add decl.p_name decl1 procs + ) + procs0 IdMap.empty + in + (* declare and resolve local variables in all predicates *) + let preds = + IdMap.fold + (fun _ decl preds -> + let locals, tbl = declare_vars decl.pr_locals types (SymbolTbl.push tbl) in + let body = Opt.map (resolve_expr locals tbl) decl.pr_body in + let formals = List.map (fun id -> lookup_id id tbl decl.pr_pos) decl.pr_formals in + let outputs = List.map (fun id -> lookup_id id tbl decl.pr_pos) decl.pr_outputs in + let contracts = resolve_contracts decl.pr_contracts outputs locals tbl in + let decl1 = + { decl with + pr_formals = formals; + pr_outputs = outputs; + pr_locals = locals; + pr_contracts = contracts; + pr_body = body + } + in + IdMap.add decl.pr_name decl1 preds + ) + preds0 IdMap.empty + in + (* declare and resolve local variables in all axioms *) + let bg_theory = + List.map + (fun (e, pos) -> resolve_expr globals tbl e, pos) + cu.background_theory + in + { cu with + var_decls = globals; + type_decls = types; + proc_decls = procs; + pred_decls = preds; + background_theory = bg_theory; + } + + +(** Utility function for extending local variable map *) +let extend_locals cu vars locals scope = + let extend locals = function + | GuardedVar (id, e) -> + let decl = + { v_name = id; + v_type = type_of_expr cu locals e; + v_ghost = false; + v_implicit = false; + v_aux = false; + v_pos = pos_of_expr e; + v_scope = scope + } + in + IdMap.add id decl locals + | UnguardedVar decl -> + IdMap.add decl.v_name decl locals + in + List.fold_right (fun vars locals -> extend locals vars) vars locals + + +(** Flatten procedure calls, comprehensions, and new expressions in compilation unit [cu].*) +let flatten_exprs cu = + let decl_aux_var name vtype pos scope locals = + let aux_id = GrassUtil.fresh_ident name in + let decl = + { v_name = aux_id; + v_type = vtype; + v_ghost = false; + v_implicit = false; + v_aux = true; + v_pos = pos; + v_scope = scope; + } + in + let locals1 = IdMap.add aux_id decl locals in + aux_id, locals1 + in + let rec flatten_expr_list ?(flatten_bool=false) scope aux new_locals locals es = + List.fold_right (fun e (es1, aux1, new_locals) -> + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals e in + e1 :: es1, aux1, new_locals) + es ([], aux, new_locals) + and flatten_expr ?(flatten_bool=false) scope aux new_locals locals = + let mk_aux_cmd mk_cmd typ pos aux new_locals e = + let aux_id, new_locals = decl_aux_var "tmp" typ pos scope new_locals in + let aux_var = Ident (aux_id, pos) in + let assign = mk_cmd aux_var e pos in + let aux_cmds, aux_funs = aux in + aux_var, (aux_cmds @ [assign], aux_funs), new_locals + in + let mk_aux_assign = mk_aux_cmd (fun aux_var e pos -> Assign ([aux_var], [e], pos)) in + function + | Setenum (ty, args, pos) -> + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in + Setenum (ty, args1, pos), aux1, new_locals + | New (ty, args, pos) -> + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:true scope aux new_locals locals args in + mk_aux_assign ty pos aux1 new_locals (New (ty, args1, pos)) + | Read (map, idx, pos) -> + let map1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals map in + let idx1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals idx in + Read (map1, idx1, pos), aux2, new_locals + | Write (map, idx, upd, pos) -> + let map1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals map in + let idx1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals idx in + let upd1, aux3, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux2 new_locals locals upd in + Write (map1, idx1, upd1, pos), aux3, new_locals + | Ite (cond, t, e, pos) -> + let cond1, aux1, new_locals = flatten_expr scope aux new_locals locals cond in + let t1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals t in + let e1, aux3, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux2 new_locals locals e in + Ite (cond1, t1, e1, pos), aux3, new_locals + | ConstrApp (id, args, pos) -> + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in + ConstrApp (id, args1, pos), aux1, new_locals + | DestrApp (id, arg, pos) -> + let arg1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals arg in + DestrApp (id, arg1, pos), aux1, new_locals + | Binder (b, vars, f, pos) as e -> + let vars1, aux, new_locals = + List.fold_right (fun v (vars1, aux, new_locals) -> + match v with + | GuardedVar (x, s) -> + let s1, aux1, new_locals = flatten_expr scope aux new_locals locals s in + GuardedVar (x, s) :: vars1, aux1, new_locals + | _ -> v :: vars1, aux, new_locals) + vars ([], aux, new_locals) + in + let locals = extend_locals cu vars1 locals scope in + let f1, aux, new_locals = flatten_expr scope aux new_locals locals f in + let cu = + List.fold_left + (fun cu decl -> + { cu with pred_decls = IdMap.add decl.pr_name decl cu.pred_decls } + ) cu (snd aux) + in + let e1 = Binder (b, vars1, f1, pos) in + (match b with + | Exists | Forall -> e1, aux, new_locals + | Comp -> + (* create auxiliary function for set/map comprehension *) + let v_decl = + match vars1 with + | [UnguardedVar decl] -> decl + | _ -> failwith "unexpected set comprehension" + in + let c_id = GrassUtil.fresh_ident "compr" in + let fv = IdSet.elements (free_vars e) in + let res_id = GrassUtil.fresh_ident "res" in + let res_ty = + match type_of_expr cu locals f1 with + | BoolType -> SetType v_decl.v_type + | rty -> MapType (v_decl.v_type, rty) + in + let res_decl = + { v_name = res_id; + v_type = res_ty; + v_ghost = false; + v_implicit = false; + v_aux = true; + v_pos = pos; + v_scope = pos; + } + in + let c_locals = + IdMap.add v_decl.v_name v_decl + (IdMap.singleton res_id res_decl) + in + let formals = List.filter (fun id -> IdMap.mem id locals) fv in + let c_locals = + List.fold_left + (fun c_locals id -> IdMap.add id (IdMap.find id locals) c_locals) + c_locals formals + in + + let c_decl = + { pr_name = c_id; + pr_formals = formals; + pr_outputs = [res_id]; + pr_locals = c_locals; + pr_contracts = []; + pr_is_pure = false; + pr_body = Some e1; + pr_pos = pos; + } + in + let actuals = List.map (fun id -> Ident (id, pos)) formals in + let c_app = PredApp (Pred c_id, actuals, pos) in + let aux_cmds, aux_funs = aux in + c_app, (aux_cmds, c_decl :: aux_funs), new_locals + ) + | ProcCall (id, args, pos) -> + let pdecl = IdMap.find id cu.proc_decls in + let returns = List.filter (fun p -> not (IdMap.find p pdecl.p_locals).v_implicit) pdecl.p_returns in + let res_type = + match returns with + | [res] -> + let rdecl = IdMap.find res pdecl.p_locals in + rdecl.v_type + | _ -> invalid_nested_proc_call_error pdecl.p_name pos + in + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:true scope aux new_locals locals args in + let call = ProcCall (id, args1, pos) in + mk_aux_assign res_type pos aux1 new_locals call + | PredApp (p, args, pos) -> + let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in + PredApp(p, args1, pos), aux1, new_locals + | UnaryOp (op, e, pos) -> + let flatten_bool_rec = + match op with + | OpNot -> false + | _ -> flatten_bool + in + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux new_locals locals e in + let e2 = UnaryOp (op, e1, pos) in + (match op with + | OpNot when flatten_bool -> + let mk_cmd aux_var e pos = + If (e2, Assign ([aux_var], [BoolVal (true, pos)], pos), Assign ([aux_var], [BoolVal (false, pos)], pos), pos) + in + mk_aux_cmd mk_cmd BoolType pos aux1 new_locals e1 + | _ -> e2, aux1, new_locals) + | BinaryOp (e1, op, e2, ty, pos) -> + let flatten_bool_rec = + match op with + | OpAnd | OpOr -> false + | _ -> flatten_bool + in + let e21, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux new_locals locals e2 in + let e11, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux1 new_locals locals e1 in + let e2 = BinaryOp (e11, op, e21, ty, pos) in + (match op with + | (OpAnd | OpOr) when flatten_bool -> + let mk_cmd aux_var e pos = + If (e2, Assign ([aux_var], [BoolVal (true, pos)], pos), Assign ([aux_var], [BoolVal (false, pos)], pos), pos) + in + mk_aux_cmd mk_cmd BoolType pos aux2 new_locals e1 + | _ -> e2, aux2, new_locals) + | Annot (e, PatternAnnot p, pos) -> + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in + let p1, aux2, new_locals = flatten_expr scope aux1 new_locals locals p in + Annot (e1, PatternAnnot p1, pos), aux2, new_locals + | Annot (e, GeneratorAnnot (es, ge), pos) -> + let es1, aux1, new_locals = + List.fold_right (fun (e, id) (es1, aux1, new_locals) -> + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in + (e1, id) :: es1, aux1, new_locals) + es ([], aux, new_locals) + in + let ge1, aux2, new_locals = flatten_expr scope aux1 new_locals locals ge in + let e1, aux3, new_locals = flatten_expr scope aux2 new_locals locals e in + Annot (e1, GeneratorAnnot (es1, ge1), pos), aux3, new_locals + | Annot (e, ann, pos) -> + let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in + Annot (e1, ann, pos), aux1, new_locals + | e -> e, aux, new_locals + in + let rec flatten scope locals aux_funs returns = function + | Skip pos -> Skip pos, locals, aux_funs + | Block (stmts0, pos) -> + let stmts, locals, aux_funs = + List.fold_left + (fun (stmts, locals, aux_funs) stmt0 -> + let stmt, locals, aux_funs = flatten pos locals aux_funs returns stmt0 in + stmt :: stmts, locals, aux_funs + ) + ([], locals, aux_funs) stmts0 + in Block (List.rev stmts, pos), locals, aux_funs + | LocalVars (_, _, pos) -> + failwith "flatten_exprs: LocalVars should have been eliminated" + | Assume (e, pure, pos) -> + let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in + Assume (e1, pure, pos), locals, snd aux1 + | Assert (e, pure, pos) -> + let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in + Assert (e1, pure, pos), locals, snd aux1 + | Split (e, pos) -> + let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in + Split (e1, pos), locals, snd aux1 + | Assign (lhs, [ProcCall (id, args, cpos)], pos) -> + let args1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals args in + begin + match lhs with + | [Read (map, idx, opos)] -> + let res_ty = + match type_of_expr cu locals map with + | MapType (_, ty) -> ty + | ArrayType ty -> ty + | ty -> ty + in + let aux_id, locals = decl_aux_var "tmp" res_ty opos scope locals in + let aux_var = Ident (aux_id, pos) in + let aux_cmds, aux_funs = aux1 in + let assign_aux, locals, aux_funs = + flatten scope locals aux_funs returns (Assign ([Read (map, idx, opos)], [aux_var], pos)) in + mk_block pos (aux_cmds @ [Assign ([aux_var], [ProcCall (id, args1, cpos)], pos)] @ [assign_aux]), locals, aux_funs + | _ -> + let lhs1, aux2, locals = + List.fold_right + (fun e (es, aux, locals) -> + let e1, aux1, locals = flatten_expr ~flatten_bool:true scope aux locals locals e in + e1 :: es, aux1, locals + ) + lhs ([], aux1, locals) + in + let aux_cmds, aux_funs = aux2 in + mk_block pos (aux_cmds @ [Assign (lhs1, [ProcCall (id, args1, cpos)], pos)]), locals, aux_funs + end + | Assign (lhs, rhs, pos) -> + let rhs1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals rhs in + let lhs1, aux2, locals = + List.fold_right + (fun e (es, aux, locals) -> + let e1, aux1, locals = flatten_expr scope aux locals locals e in + match e1 with + | Read(Ident _, _, _) -> + e1 :: es, aux1, locals + | Read (map, ind, opos) -> + let mpos = pos_of_expr map in + let aux_id, locals = + decl_aux_var "tmp" (type_of_expr cu locals map) mpos scope locals + in + let aux_var = Ident (aux_id, mpos) in + let assign_aux = Assign ([aux_var], [map], mpos) in + let aux_cmds, aux_funs = aux1 in + Read (aux_var, ind, opos) :: es, (aux_cmds @ [assign_aux], aux_funs), locals + | _ -> + e1 :: es, aux1, locals + ) + lhs ([], aux1, locals) + in + let aux_cmds, aux_funs = aux2 in + mk_block pos (aux_cmds @ [Assign (lhs1, rhs1, pos)]), locals, aux_funs + | Dispose (e, pos) -> + let e1, aux, locals = flatten_expr ~flatten_bool:true scope ([], aux_funs) locals locals e in + mk_block pos (fst aux @ [Dispose (e1, pos)]), locals, snd aux + | Havoc (es, pos) -> + let es1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals es in + let aux_cmds, aux_funs = aux1 in + mk_block pos (aux_cmds @ [Havoc (es1, pos)]), locals, aux_funs + | If (cond, t, e, pos) -> + let cond1, (aux_cmds, aux_funs), locals = flatten_expr scope ([], aux_funs) locals locals cond in + let t1, locals, aux_funs = flatten scope locals aux_funs returns t in + let e1, locals, aux_funs = flatten scope locals aux_funs returns e in + mk_block pos (aux_cmds @ [If (cond1, t1, e1, pos)]), locals, aux_funs + | Choice (stmts0, pos) -> + let stmts, locals, aux_funs = + List.fold_left + (fun (stmts, locals, aux_funs) stmt0 -> + let stmt, locals, aux_funs = flatten pos locals aux_funs returns stmt0 in + stmt :: stmts, locals, aux_funs + ) + ([], locals, aux_funs) stmts0 + in Choice (List.rev stmts, pos), locals, aux_funs + | Loop (inv, preb, cond, postb, pos) -> + let inv1, aux_funs, locals = + List.fold_right + (function Invariant (e, pos) -> fun (inv1, aux_funs, locals) -> + let e1, (_, aux_funs), locals = flatten_expr scope ([], aux_funs) locals locals e in + Invariant (e1, pos) :: inv1, aux_funs, locals) + inv ([], aux_funs, locals) + in + let preb1, locals, aux_funs = flatten pos locals aux_funs returns preb in + let cond1, (aux_cmds, aux_funs), locals = flatten_expr pos ([], aux_funs) locals locals cond in + let postb1, locals, aux_funs = flatten pos locals aux_funs returns postb in + Loop (inv1, mk_block pos ([preb1] @ aux_cmds), cond1, postb1, pos), locals, aux_funs + | Return ([ProcCall (id, args, cpos)], pos) -> + let args1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals args in + let rts = List.map (fun id -> Ident (id, cpos)) returns in + let assign = Assign (rts, [ProcCall (id, args1, cpos)], pos) in + let ret = Return (rts, pos) in + let aux_cmds, aux_funs = aux1 in + mk_block pos (aux_cmds @ [assign; ret]), locals, aux_funs + | Return (es, pos) -> + let es1, (aux_cmds, aux_funs), locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals es in + mk_block pos (aux_cmds @ [Return (es1, pos)]), locals, aux_funs + in + let flatten_contracts aux_funs locals contracts = + List.fold_right + (fun c (contracts, aux_funs, locals) -> + let flatten_spec e = + let e1, (aux_cmds, aux_funs), locals = + flatten_expr (pos_of_expr e) ([], aux_funs) locals locals e + in + match aux_cmds with + | [] -> e1, aux_funs, locals + | cmd :: _ -> illegal_side_effect_error (pos_of_stmt cmd) "specifications" + in + match c with + | Requires (e, pure, free) -> + let e1, aux_funs, locals = flatten_spec e in + Requires (e1, pure, free) :: contracts, aux_funs, locals + | Ensures (e, pure, free) -> + let e1, aux_funs, locals = flatten_spec e in + Ensures (e1, pure, free) :: contracts, aux_funs, locals) + contracts ([], aux_funs, locals) + in + let procs, aux_funs = + IdMap.fold + (fun _ decl (procs, aux_funs) -> + let contracts, aux_funs, locals = + flatten_contracts aux_funs decl.p_locals decl.p_contracts + in + let body, locals, aux_funs = + flatten decl.p_pos locals aux_funs decl.p_returns decl.p_body + in + let decl1 = + { decl with + p_locals = locals; + p_contracts = contracts; + p_body = body } + in + IdMap.add decl.p_name decl1 procs, aux_funs) + cu.proc_decls (IdMap.empty, []) + in + let preds, aux_funs = + IdMap.fold + (fun _ decl (preds, aux_funs) -> + let contracts, aux_funs, locals = + flatten_contracts aux_funs decl.pr_locals decl.pr_contracts + in + let body, (aux_cmds, aux_funs), locals = + match decl.pr_body with + | Some body -> + let body, aux, locals = flatten_expr decl.pr_pos ([], aux_funs) decl.pr_locals decl.pr_locals body in + Some body, aux, locals + | None -> None, ([], aux_funs), locals + in + (* if auxiliary function comes from top-level expression in body then undo the flattening *) + let body, aux_funs = + match body, aux_funs with + | Some (PredApp (Pred id, _, _) | Annot(PredApp (Pred id, _, _), _, _)), aux_decl :: aux_decls + when id = aux_decl.pr_name -> aux_decl.pr_body, aux_decls + | _ -> body, aux_funs + in + match aux_cmds with + | cmd :: _ -> + illegal_side_effect_error (pos_of_stmt cmd) "function and procedure bodies" + | _ -> (); + let decl1 = + { decl with + pr_locals = locals; + pr_contracts = contracts; + pr_body = body + } + in + IdMap.add decl.pr_name decl1 preds, aux_funs) + cu.pred_decls (IdMap.empty, aux_funs) + in + let preds_all = List.fold_left (fun preds_all decl -> IdMap.add decl.pr_name decl preds_all) preds aux_funs in + { cu with proc_decls = procs; pred_decls = preds_all } + + +(** Type check compilation unit [cu]. Missing type annotations are inferred along the way.*) +let infer_types cu = + let check_spec locals pure e = + let ty = if pure then BoolType else PermType in + infer_types cu locals ty e + in + (* infer types within given statement *) + let rec check_stmt proc = + let locals = proc.p_locals in + function + | Skip pos -> Skip pos + | Block (stmts, pos) -> + Block (List.map (check_stmt proc) stmts, pos) + | LocalVars (_, _, pos) -> + failwith "infer_types: LocalVars should have been eliminated" + | Assume (e, pure, pos) -> + Assume (check_spec locals pure e, pure, pos) + | Assert (e, pure, pos) -> + Assert (check_spec locals pure e, pure, pos) + | Split (e, pos) -> + Split (check_spec locals false e, pos) + | Assign (lhs, [ProcCall (id, args, cpos) as e], pos) -> + let decl = IdMap.find id cu.proc_decls in + let returns = List.filter (fun p -> not (IdMap.find p decl.p_locals).v_implicit) decl.p_returns in + let rtys = + List.map (fun fid -> + let vdecl = IdMap.find fid decl.p_locals in + vdecl.v_type) + returns + in + let lhs1 = + try List.map2 (infer_types cu locals) rtys lhs + with Invalid_argument _ -> + ProgError.error cpos + (Printf.sprintf "Procedure %s has %d explicit return value(s)" + (fst id) (List.length rtys)) + in + let e1 = infer_types cu locals AnyType e in + Assign (lhs1, [e1], pos) + | Assign (lhs, rhs, pos) -> + let _ = List.fold_left (fun seen -> function + | Ident (x, pos) -> + if List.mem x seen + then assignment_multiple_error pos + else x :: seen + | _ -> seen) + [] lhs + in + let rhs1, tys = + Util.map_split (fun e -> + let e1 = infer_types cu locals AnyType e in + let ty = + match type_of_expr cu locals e with + | PermType -> + ProgError.error (pos_of_expr e) "Permissions cannot be assigned" + | ty -> ty + in + e1, ty) rhs + in + let lhs1 = + try + List.map2 (infer_types cu locals) tys lhs + with Invalid_argument _ -> + assignment_mismatch_error pos + in + let rhs2 = + List.map2 + (fun e1 e2 -> infer_types cu locals (type_of_expr cu locals e1) e2) + lhs1 rhs1 + in + Assign (lhs1, rhs2, pos) + | Dispose (e, pos) -> + let e1 = infer_types cu locals AnyRefType e in + Dispose (e1, pos) + | Havoc (es, pos) -> + let es1 = + List.map (fun e -> + let e1 = infer_types cu locals AnyType e in + match type_of_expr cu locals e1 with + | PermType -> + ProgError.error (pos_of_expr e) "Permissions cannot be assigned" + | _ -> e1) + es + in + Havoc (es1, pos) + | If (cond, t, e, pos) -> + let cond1 = infer_types cu locals BoolType cond in + let t1 = check_stmt proc t in + let e1 = check_stmt proc e in + If (cond1, t1, e1, pos) + | Choice (stmts, pos) -> + Choice (List.map (check_stmt proc) stmts, pos) + | Loop (inv, preb, cond, postb, pos) -> + let inv1 = + List.map + (function Invariant (e, pure) -> + Invariant (check_spec locals pure e, pure)) + inv + in + let preb1 = check_stmt proc preb in + let cond1 = infer_types cu locals BoolType cond in + let postb1 = check_stmt proc postb in + Loop (inv1, preb1, cond1, postb1, pos) + | Return (es, pos) -> + let returns = List.filter (fun x -> not (IdMap.find x proc.p_locals).v_implicit) proc.p_returns in + let rtys = + List.map (fun id -> (IdMap.find id locals).v_type) returns + in + let es1 = + try List.map2 (infer_types cu locals) rtys es + with Invalid_argument _ -> + ProgError.error pos + (Printf.sprintf "Procedure %s returns %d explicit values(s), found %d" + (fst proc.p_name) (List.length rtys) (List.length es)) + in + Return (es1, pos) + in + (* infer types of predicates *) + let cu = + IdMap.fold + (fun id pred cu -> + let rtype = + match pred.pr_outputs with + | [res_id] -> + (IdMap.find res_id pred.pr_locals).v_type + | _ -> PermType + in + let contracts = + List.map (function + | Requires (e, pure, free) -> + Requires (check_spec pred.pr_locals pure e, pure, free) + | Ensures (e, pure, free) -> + Ensures (check_spec pred.pr_locals pure e, pure, free)) pred.pr_contracts + in + let body = + Opt.map (infer_types cu pred.pr_locals rtype) pred.pr_body + in + let pred1 = + { pred with + pr_contracts = contracts; + pr_body = body + } + in + let pred_decls1 = IdMap.add id pred1 cu.pred_decls in + { cu with pred_decls = pred_decls1 }) + cu.pred_decls cu + in + (* infer types of procedures/lemmas *) + let procs = + IdMap.fold + (fun _ proc procs -> + let contracts = + List.map (function + | Requires (e, pure, free) -> + Requires (check_spec proc.p_locals pure e, pure, free) + | Ensures (e, pure, free) -> + Ensures (check_spec proc.p_locals pure e, pure, free)) proc.p_contracts + in + let body = check_stmt proc proc.p_body in + let proc1 = { proc with p_contracts = contracts; p_body = body } in + IdMap.add proc.p_name proc1 procs) + cu.proc_decls IdMap.empty + in + let bg_theory = + List.map + (fun (e, pos) -> (check_spec IdMap.empty true e, pos) ) + cu.background_theory + in + { cu with proc_decls = procs; background_theory = bg_theory; } + +(** Check compilation unit [cu]. *) +let check cu = + let cu1 = resolve_names cu in + let cu2 = infer_types cu1 in + let cu3 = flatten_exprs cu2 in + cu3 diff --git a/src/frontends/spl/.#splChecker.ml b/src/frontends/spl/.#splChecker.ml new file mode 120000 index 00000000..7872bea9 --- /dev/null +++ b/src/frontends/spl/.#splChecker.ml @@ -0,0 +1 @@ +wies@cantor.17059:1535018249 \ No newline at end of file diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index fb6fe182..10adf3c7 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -1,4 +1,4 @@ -(** DZ: this is a copy-pasted version from csisat, just adaped to the current types *) +(** {5 Congruence closure computation} *) open Util open Grass @@ -6,7 +6,7 @@ open GrassUtil module rec Node : sig type t = - < get_fname: symbol; + < get_sym: symbol; get_args: t list; get_arity: int; set_ccparent: NodeSet.t -> unit; @@ -26,7 +26,7 @@ module rec Node : sig end = struct type t = - < get_fname: symbol; + < get_sym: symbol; get_args: t list; get_arity: int; set_ccparent: NodeSet.t -> unit; @@ -45,16 +45,14 @@ module rec Node : sig class node = fun - (ffname: symbol) - (aargs: t list) -> + (sym: symbol) + (args: t list) -> object (self) - val fname = ffname - method get_fname = fname + method get_sym = sym - val args = aargs method get_args: node list = args - val arity = List.length aargs + val arity = List.length args method get_arity = arity val mutable ccparent = NodeSet.empty @@ -84,7 +82,7 @@ module rec Node : sig method ccpar: NodeSet.t = (self#find)#get_ccparent method congruent (that: node) = - self#get_fname = that#get_fname + self#get_sym = that#get_sym && self#get_arity = that#get_arity && @@ -92,7 +90,7 @@ module rec Node : sig (** return pairs of nodes whose equality may change the result of the 'congruent' method*) (*method may_be_congruent (that: node) = - if self#get_fname <> that#get_fname + if self#get_sym <> that#get_sym || self#get_arity <> that#get_arity || self#find = that#find then [] else diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml index cf9d5968..fd29c8dc 100644 --- a/src/prover/instGen.ml +++ b/src/prover/instGen.ml @@ -111,11 +111,12 @@ let ematch filters t rep_terms egraph subst_maps = | Var (x, srt1) when srt1 = sort_of t2 -> List.fold_left (fun out_subst_maps sm -> - if IdMap.mem x sm then - if IdMap.find x sm = t2 - then sm :: out_subst_maps - else out_subst_maps - else IdMap.add x t2 sm :: out_subst_maps) + match IdMap.find_opt x sm with + | Some t1 -> + if t1 = t2 + then sm :: out_subst_maps + else out_subst_maps + | None -> IdMap.add x t2 sm :: out_subst_maps) [] subst_maps | _ -> (*print_endline (string_of_sort (sort_of t1) ^ " " ^ string_of_sort (sort_of t2) ^ " fail 2");*) @@ -352,7 +353,7 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = else ematch [] (Var (v, srt)) rep_terms egraph subst_maps) fvars proto_subst_maps in - let subst_maps = (*measure*) subst_maps () in + let subst_maps = measure_call "InstGen.subst_maps" subst_maps () in let _ = if Debug.is_debug 1 then begin print_endline "--------------------"; @@ -374,8 +375,8 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = end in (* generate instances of axiom *) - List.fold_left - (fun acc subst_map -> (subst subst_map f) :: acc) acc subst_maps + measure_call "InstGen.substitute" (List.fold_left + (fun acc subst_map -> (subst subst_map f) :: acc) acc) subst_maps in List.fold_left instantiate epr_axioms axioms @@ -383,7 +384,7 @@ let generate_instances stratify useLocalInst axioms rep_terms = measure_call "InstGen.generate_instances" (generate_instances stratify useLocalInst axioms rep_terms) let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms classes0 = - if !Config.instantiate || force then + if not !Config.instantiate && not force then axioms else (* remove theory atoms from congruence classes *) let filter_term t = sort_of t <> Bool || @@ -411,8 +412,6 @@ let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local a (* choose representatives for instantiation *) let reps_f, egraph = choose_rep_terms classes in generate_instances stratify local axioms reps_f egraph - else - axioms let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms = measure_call "InstGen.instantiate_with_terms" (instantiate_with_terms ~force:force ~stratify:stratify local axioms) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 0b243c54..e7c141a5 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -167,23 +167,11 @@ let instantiate_and_prove session fs = TermSet.fold (fun t (fs, gts) -> match t with | App (Constructor id, ts, Adt (ty_id, adts)) -> - let rec subst srt = match srt with - | FreeSrt sid -> - List.assoc_opt sid adts |> - Util.Opt.map (fun _ -> Adt (sid, adts)) |> - Util.Opt.get_or_else srt - | Map (asrts, rsrt) -> Map (List.map subst asrts, subst rsrt) - | Set ssrt -> Set (subst ssrt) - | Loc lsrt -> Loc (subst lsrt) - | Array asrt -> Array (subst asrt) - | ArrayCell asrt -> ArrayCell asrt - | _ -> srt - in let adt = List.assoc ty_id adts in let destrs = List.assoc id adt in List.fold_left2 (fun (fs, gts) arg (d_id, d_srt) -> - let d_srt = subst d_srt in + let d_srt = unfold_adts adts d_srt in let d = GrassUtil.mk_app d_srt (Destructor d_id) [t] in GrassUtil.mk_eq arg d :: fs, TermSet.add d gts) (fs, TermSet.add t gts) ts destrs @@ -246,7 +234,7 @@ let instantiate_and_prove session fs = (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in - let generators = if i > 1 then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in + let generators = if i > 1 && false then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in let gts_inst = generate_terms generators gts_inst in if i > 1 && not !Config.propagate_reads || TermSet.subset gts_inst gts_inst0 then rev_concat [fs_inst; implied_eqs], gts_inst, cc_graph diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 966aadce..f4e636d4 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -17,34 +17,63 @@ let generated_ground_terms fs = ** Assumes that [f] is typed and in negation normal form. *) let elim_exists = let e = fresh_ident "?e" in - let rec elim_neq bvs = function - | BoolOp (Not, [Atom (App (Eq, [s1; s2], _), a)]) as f when bvs = [] -> - (match sort_of s1 with + let rec elim_neq seen_adts bvs = function + | BoolOp (Not, [Atom (App (Eq, [t1; t2], _), a)]) as f when bvs = [] -> + (match sort_of t1 with | Set srt -> let ve = mk_var srt e in - mk_exists [(e, srt)] (smk_or [smk_and [smk_elem ~ann:a ve s1; mk_not (smk_elem ~ann:a ve s2)]; - smk_and [smk_elem ~ann:a ve s2; mk_not (smk_elem ~ann:a ve s1)]]) + mk_exists [(e, srt)] (smk_or [smk_and [smk_elem ~ann:a ve t1; mk_not (smk_elem ~ann:a ve t2)]; + smk_and [smk_elem ~ann:a ve t2; mk_not (smk_elem ~ann:a ve t1)]]) | Map (dsrts, rsrt) -> let vs = List.map (fun srt -> fresh_ident "?i", srt) dsrts in let vts = List.map (fun (v, srt) -> mk_var srt v) vs in - mk_exists vs (annotate (mk_neq (mk_read s1 vts) (mk_read s2 vts)) a) + mk_exists vs (annotate (mk_neq (mk_read t1 vts) (mk_read t2 vts)) a) + | Adt (id, adts) when not @@ IdSet.mem id seen_adts -> + let cstrs = List.assoc id adts in + let expand new_vs = function + | App (Constructor cid, ts, _) -> new_vs, [(cid, mk_true, ts)] + | t -> + List.fold_left + (fun (new_vs, cases) (cid, dstrs) -> + let vs = List.map (fun (id, srt) -> fresh_ident "?x", unfold_adts adts srt) dstrs in + let vts = List.map (fun (v, srt) -> mk_var srt v) vs in + vs @ new_vs, (cid, mk_eq t (mk_constr (Adt (id, adts)) cid vts), vts) :: cases + ) (new_vs, []) cstrs + in + let new_vs1, t1_cases = expand [] t1 in + let new_vs2, t2_cases = expand new_vs1 t2 in + let cases = List.fold_left + (fun cases (cid1, def_t1, args1) -> + List.fold_left + (fun cases (cid2, def_t2, args2) -> + if cid1 = cid2 then + let seen_adts1 = IdSet.add id seen_adts in + let sub_cases = + List.map2 (fun arg1 arg2 -> elim_neq seen_adts1 bvs (mk_neq arg1 arg2)) args1 args2 + in + mk_and [def_t1; def_t2; mk_or sub_cases] :: cases + else mk_and [def_t1; def_t2] :: cases + ) cases t2_cases + ) [] t1_cases + in + mk_exists ~ann:a new_vs2 (mk_or cases) | _ -> f) | BoolOp (Not, [Atom (App (Disjoint, [s1; s2], _), a)]) when bvs = [] -> let srt = element_sort_of_set s1 in - elim_neq bvs (mk_not (Atom (App (Eq, [mk_inter [s1; s2]; mk_empty (Set srt)], Bool), a))) + elim_neq seen_adts bvs (mk_not (Atom (App (Eq, [mk_inter [s1; s2]; mk_empty (Set srt)], Bool), a))) | BoolOp (Not, [Atom (App (SubsetEq, [s1; s2], _), a)]) when bvs = [] -> let srt = element_sort_of_set s1 in let ve = mk_var srt e in mk_exists [(e, srt)] (annotate (smk_and [smk_elem ve s1; mk_not (smk_elem ve s2)]) a) - | BoolOp (op, fs) -> smk_op op (List.map (elim_neq bvs) fs) + | BoolOp (op, fs) -> smk_op op (List.map (elim_neq IdSet.empty bvs) fs) | Binder (Exists, vs, f, a) -> - mk_exists ~ann:a vs (elim_neq bvs f) + mk_exists ~ann:a vs (elim_neq seen_adts bvs f) | Binder (Forall, vs, f, a) -> - mk_forall ~ann:a vs (elim_neq (bvs @ vs) f) + mk_forall ~ann:a vs (elim_neq seen_adts (bvs @ vs) f) | f -> f in List.map (fun f -> - let f1 = elim_neq [] f in + let f1 = elim_neq IdSet.empty [] f in let f2 = propagate_exists_up f1 in let f3 = skolemize f2 in f3) From f76b9fe94a56f7456560d7930e32ce815286c359 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 23 Aug 2018 17:45:12 -0400 Subject: [PATCH 073/118] remove tmp files --- src/frontends/spl/#splChecker.ml# | 1114 ----------------------------- src/frontends/spl/.#splChecker.ml | 1 - 2 files changed, 1115 deletions(-) delete mode 100644 src/frontends/spl/#splChecker.ml# delete mode 120000 src/frontends/spl/.#splChecker.ml diff --git a/src/frontends/spl/#splChecker.ml# b/src/frontends/spl/#splChecker.ml# deleted file mode 100644 index bfe5b4f2..00000000 --- a/src/frontends/spl/#splChecker.ml# +++ /dev/null @@ -1,1114 +0,0 @@ -(** {5 Static Checker for SPL programs } *) - -open Util -open Prog -open Sl -open Grass -open SplSyntax -open SplErrors -open SplTypeChecker - -(** Resolve names of identifiers in compilation unit [cu] so that all identifiers have unique names.*) -let resolve_names cu = - let lookup_id init_id tbl pos = - match SymbolTbl.find tbl init_id with - | Some (id, _) -> id - | None -> unknown_ident_error init_id pos - in - let check_type id types pos = - if not (IdMap.mem id types) then - not_a_type_error id pos - in - let check_proc id procs pos = - if not (IdMap.mem id procs) then - not_a_proc_error id pos - in - let check_pred id preds pos = - if not (IdMap.mem id preds) then - not_a_pred_error id pos - in - (* resolve names in type expressions *) - let resolve_typ types pos tbl = - let rec r = function - | IdentType init_id -> - let id = lookup_id init_id tbl pos in - check_type id types pos; - let decl = IdMap.find id types in - (match decl.t_def with - | StructTypeDef _ -> StructType id - | ADTypeDef _ -> ADType id - | _ -> IdentType id) - | ArrayType ty -> ArrayType (r ty) - | ArrayCellType ty -> ArrayCellType (r ty) - | MapType (ty1, ty2) -> MapType (r ty1, r ty2) - | SetType ty -> SetType (r ty) - | ty -> ty - in - r - in - let declare_name ?(allow_redecl=false) pos init_id scope tbl = - let name = GrassUtil.name init_id in - let id = match SymbolTbl.find_local tbl init_id with - | Some (id, _) -> - if allow_redecl then id else redeclaration_error init_id pos - | None -> - GrassUtil.fresh_ident ~id:(snd init_id) name - in - (id, SymbolTbl.add tbl init_id (id, scope)) - in - let declare_var types decl tbl = - let id, tbl = declare_name decl.v_pos decl.v_name decl.v_scope tbl in - let ty = resolve_typ types decl.v_pos tbl decl.v_type in - { decl with v_name = id; v_type = ty }, tbl - in - let declare_vars vars structs tbl = - IdMap.fold - (fun _ decl (vars, tbl) -> - let decl, tbl = declare_var structs decl tbl in - IdMap.add decl.v_name decl vars, tbl) - vars (IdMap.empty, tbl) - in - let tbl = SymbolTbl.empty in - (* resolve type names *) - - let types0, tbl = - IdMap.fold - (fun init_id decl (types, tbl) -> - let id, tbl = declare_name decl.t_pos init_id GrassUtil.global_scope tbl in - IdMap.add id { decl with t_name = id } types, tbl) - cu.type_decls (IdMap.empty, tbl) - in - (* resolve global variables *) - let globals0, tbl = declare_vars cu.var_decls types0 tbl in - (* declare struct fields *) - let types, globals, funs, tbl = - IdMap.fold - (fun id decl (types, globals, funs, tbl) -> - match decl.t_def with - | StructTypeDef fields0 -> - let fields, globals, tbl = - IdMap.fold - (fun init_id fdecl (fields, globals, tbl) -> - let id, tbl = declare_name fdecl.v_pos init_id GrassUtil.global_scope tbl in - let res_type = resolve_typ types0 fdecl.v_pos tbl fdecl.v_type in - let typ = MapType (StructType decl.t_name, res_type) in - let fdecl = { fdecl with v_name = id; v_type = res_type } in - let gfdecl = { fdecl with v_type = typ } in - IdMap.add id fdecl fields, IdMap.add id gfdecl globals, tbl - ) - fields0 (IdMap.empty, globals, tbl) - in - IdMap.add id { decl with t_def = StructTypeDef fields } types, - globals, - funs, - tbl - | ADTypeDef consts0 -> - let consts, funs, tbl = - List.fold_right - (fun cnst (consts, funs, tbl) -> - let cid, tbl = declare_name decl.t_pos cnst.c_name GrassUtil.global_scope tbl in - let args, funs, tbl = - List.fold_right - (fun adecl (args, funs, tbl) -> - let aid, tbl = declare_name decl.t_pos adecl.v_name GrassUtil.global_scope tbl in - let ty = resolve_typ types0 decl.t_pos tbl adecl.v_type in - let fun_decl = { f_name = id; f_args = [ADType id]; f_res = ty; f_is_destr = true} in - { adecl with v_name = aid; v_type = ty } :: args, - IdMap.add aid fun_decl funs, - tbl - ) - cnst.c_args ([], funs, tbl) - in - let arg_tys = - List.map - (fun adecl -> resolve_typ types0 adecl.v_pos tbl adecl.v_type) - cnst.c_args - in - let fun_decl = - { f_name = cid; - f_args = arg_tys; - f_res = ADType id; - f_is_destr = false; - } - in - { cnst with c_name = cid; c_args = args } :: consts, - IdMap.add cid fun_decl funs, - tbl - ) - consts0 ([], funs, tbl) - in - IdMap.add id { decl with t_def = ADTypeDef consts } types, - globals, - funs, - tbl - | _ -> IdMap.add id decl types, globals, funs, tbl - ) - types0 (IdMap.empty, globals0, IdMap.empty, tbl) - in - (* declare procedure names *) - let procs0, tbl = - IdMap.fold (fun init_id decl (procs, tbl) -> - let id, tbl = declare_name decl.p_pos init_id GrassUtil.global_scope tbl in - IdMap.add id { decl with p_name = id } procs, tbl) - cu.proc_decls (IdMap.empty, tbl) - in - (* declare predicate names *) - let preds0, tbl = - IdMap.fold (fun init_id decl (preds, tbl) -> - let id, tbl = declare_name decl.pr_pos init_id GrassUtil.global_scope tbl in - IdMap.add id { decl with pr_name = id } preds, tbl) - cu.pred_decls (IdMap.empty, tbl) - in - let cu = - { cu with - var_decls = globals; - type_decls = types; - proc_decls = procs0; - pred_decls = preds0; - fun_decls = funs; - } - in - (* declare and resolve local variables in given expression *) - let resolve_expr locals tbl e = - let rec re locals tbl = function - | Setenum (ty, args, pos) -> - let ty1 = resolve_typ types pos tbl ty in - let args1 = List.map (re locals tbl) args in - Setenum (ty1, args1, pos) - | New (ty, args, pos) -> - let ty1 = resolve_typ types pos tbl ty in - let args1 = List.map (re locals tbl) args in - New (ty1, args1, pos) - | Read ((Ident (("length", _), _) as map), idx, pos) -> - let idx1 = re locals tbl idx in - (match type_of_expr cu locals idx1 with - | ArrayType _ | AnyType -> UnaryOp (OpLength, idx1, pos) - | ty -> Read (re locals tbl map, idx1, pos)) - | Read ((Ident (("map", _), _) as map), arr, pos) -> - let arr = re locals tbl arr in - (match type_of_expr cu locals arr with - | ArrayType _ | AnyType -> UnaryOp (OpArrayMap, arr, pos) - | ty -> Read (re locals tbl map, arr, pos)) - | Read ((Ident (("cells", _), _) as map), idx, pos) -> - let idx1 = re locals tbl idx in - (match type_of_expr cu locals idx1 with - | ArrayType _ | AnyType -> UnaryOp (OpArrayCells, idx1, pos) - | _ -> Read (re locals tbl map, idx1, pos)) - | Read ((Ident (("array", _), _) as map), idx, pos) -> - let idx1 = re locals tbl idx in - (match type_of_expr cu locals idx1 with - | ArrayCellType _ | AnyType -> UnaryOp (OpArrayOfCell, idx1, pos) - | _ -> Read (re locals tbl map, idx1, pos)) - | Read ((Ident (("index", _), _) as map), idx, pos) -> - let idx1 = re locals tbl idx in - (match type_of_expr cu locals idx1 with - | ArrayCellType _ | AnyType -> UnaryOp (OpIndexOfCell, idx1, pos) - | _ -> Read (re locals tbl map, idx1, pos)) - | Read (Ident (init_id, map_pos), idx, pos) -> - let id = lookup_id init_id tbl map_pos in - let idx1 = re locals tbl idx in - let map1 = Ident (id, map_pos) in - if IdMap.mem id cu.fun_decls - then DestrApp (id, idx1, pos) - else Read (map1, idx1, pos) - | Read (map, idx, pos) -> - Read (re locals tbl map, re locals tbl idx, pos) - | Write (map, idx, upd, pos) -> - Write (re locals tbl map, re locals tbl idx, re locals tbl upd, pos) - | Ite (cond, t, e, pos) -> - Ite (re locals tbl cond, re locals tbl t, re locals tbl e, pos) - | ConstrApp (id, es, pos) -> - ConstrApp (id, List.map (re locals tbl) es, pos) - | DestrApp (id, e, pos) -> - DestrApp (id, re locals tbl e, pos) - | Binder (q, decls, f, pos) -> - let (decls1, (locals1, tbl1)) = - Util.fold_left_map - (fun (locals, tbl) decl -> match decl with - | GuardedVar (init_id, e) -> - let e1 = re locals tbl e in - let id, tbl1 = declare_name pos init_id pos tbl in - (GuardedVar (id, e1)), (locals, tbl1) - | UnguardedVar decl -> - let decl, tbl1 = declare_var types decl tbl in - (UnguardedVar decl), (IdMap.add decl.v_name decl locals, tbl1) - ) - (locals, SymbolTbl.push tbl) - decls - in - let f1 = re locals1 tbl1 f in - Binder (q, decls1, f1, pos) - | ProcCall (("acc", _ as id), args, pos) -> - let args1 = List.map (re locals tbl) args in - (match args1 with - | [arg] -> - (match type_of_expr cu locals arg with - | SetType _ | MapType _ -> - PredApp (AccessPred, [arg], pos) - | ty -> - PredApp (AccessPred, [Setenum (resolve_typ types pos tbl ty, [arg], pos)], pos)) - | [map; idx] -> - (match type_of_expr cu locals map with - | ArrayType typ -> - let map1 = re locals tbl map in - let idx1 = re locals tbl idx in - let cell = Read (UnaryOp (OpArrayCells, map1, pos_of_expr map1), idx1, pos) in - PredApp (AccessPred, [Setenum (ArrayCellType typ, [cell], pos)], pos) - | _ -> pred_arg_mismatch_error pos id 1) - | _ -> pred_arg_mismatch_error pos id 1) - | ProcCall (init_id, args, pos) -> - let args1 = List.map (re locals tbl) args in - (match GrassUtil.name init_id with - | "old" -> - UnaryOp (OpOld, List.hd args1, pos) - | "known" -> - UnaryOp (OpKnown, List.hd args1, pos) - | "Btwn" -> - PredApp (BtwnPred, args1, pos) - | "Reach" -> - PredApp (ReachPred, args1, pos) - | "Frame" -> - PredApp (FramePred, args1, pos) - | "Disjoint" -> - PredApp (DisjointPred, args1, pos) - | _ -> - let id = lookup_id init_id tbl pos in - try - let f_decl_opt = IdMap.find_opt id cu.fun_decls in - f_decl_opt |> - Opt.map (fun f_decl -> - if f_decl.f_is_destr - then begin - match args1 with - | [arg1] -> DestrApp (id, arg1, pos) - | _ -> destr_arg_mismatch_error pos id (List.length args1) - end else ConstrApp (id, args1, pos)) |> - Opt.lazy_get_or_else (fun () -> - check_proc id procs0 pos; - ProcCall (id, args1, pos)) - with ProgError.Prog_error _ -> - check_pred id preds0 pos; - PredApp (Pred id, args1, pos)) - | PredApp (sym, args, pos) -> - PredApp (sym, List.map (re locals tbl) args, pos) - | UnaryOp (op, e, pos) -> - UnaryOp (op, re locals tbl e, pos) - | BinaryOp (e1, op, e2, ty, pos) -> - BinaryOp (re locals tbl e1, op, re locals tbl e2, ty, pos) - | Ident (init_id, pos) -> - let id = lookup_id init_id tbl pos in - if IdMap.mem id preds0 then - PredApp (Pred id, [], pos) - else if IdMap.mem id funs then - ConstrApp (id, [], pos) - else - Ident (id, pos) - | Annot (e, PatternAnnot p, pos) -> - Annot (re locals tbl e, PatternAnnot (re locals tbl p), pos) - | Annot (e, GeneratorAnnot (es, ge), pos) -> - let es1 = - List.map (fun (e, ids) -> - let ids1 = - List.map (fun init_id -> lookup_id init_id tbl pos) ids - in - re locals tbl e, ids1) - es - in - Annot (re locals tbl e, GeneratorAnnot (es1, re locals tbl ge), pos) - | Annot (e, ann, pos) -> - Annot (re locals tbl e, ann, pos) - | e -> e - in - re locals tbl e - in - (* declare and resolve local variables in given statement *) - let rec resolve_stmt first_block in_loop locals tbl = function - | Skip pos -> Skip pos, locals, tbl - | Block (stmts0, pos) -> - let tbl1 = if first_block then tbl else SymbolTbl.push tbl in - let stmts, locals, _ = - List.fold_left - (fun (stmts, locals, tbl) stmt0 -> - let stmt, locals, tbl = resolve_stmt false in_loop locals tbl stmt0 in - stmt :: stmts, locals, tbl - ) - ([], locals, tbl1) stmts0 - in Block (List.rev stmts, pos), locals, tbl - | LocalVars (vars, es_opt, pos) -> - let es_opt1, tys = - match es_opt with - (* var x1, ..., xn := p(e1, ..., em); *) - | Some [ProcCall (_, _, _) as e] -> - let e1 = resolve_expr locals tbl e in - let tys = match e1 with - | ProcCall (id, _, _) - | PredApp (Pred id, _, _) -> - let returns, locals = - try - let decl = IdMap.find id cu.proc_decls in - let returns = List.filter (fun p -> not (IdMap.find p decl.p_locals).v_implicit) decl.p_returns in - returns, decl.p_locals - with Not_found -> - let decl = IdMap.find id cu.pred_decls in - decl.pr_outputs, decl.pr_locals - in - List.map (fun v -> (IdMap.find v locals).v_type) returns - | e -> [type_of_expr cu locals e1] - in - Some [e1], tys - (* var x1, ..., xn := e1, ..., en; *) - | Some es -> - let es1, tys = - Util.map_split (fun e -> - let e1 = resolve_expr locals tbl e in - e1, type_of_expr cu locals e1) es - in - Some es1, tys - (* var x1: T1, ..., xn: Tn; *) - | None -> - None, List.map (fun decl -> decl.v_type) vars - in - let ids, locals, tbl = - try - List.fold_right2 - (fun decl ty (ids, locals, tbl) -> - let decl = - match decl.v_type with - | AnyType -> - if is_abstract_type ty then - abstract_initializer_error pos decl.v_name; - { decl with v_type = ty } - | _ -> decl - in - let decl, tbl = declare_var types decl tbl in - Ident (decl.v_name, decl.v_pos) :: ids, - IdMap.add decl.v_name decl locals, - tbl - ) - vars tys ([], locals, tbl) - with Invalid_argument _ -> assignment_mismatch_error pos - in - (match es_opt1 with - | Some es1 -> - Assign (ids, es1, pos), locals, tbl - | None -> - Havoc (ids, pos), locals, tbl) - | Assume (e, pure, pos) -> - Assume (resolve_expr locals tbl e, pure, pos), locals, tbl - | Assert (e, pure, pos) -> - Assert (resolve_expr locals tbl e, pure, pos), locals, tbl - | Split (e, pos) -> - Split (resolve_expr locals tbl e, pos), locals, tbl - | Assign (lhs, rhs, pos) -> - let lhs1 = List.map (resolve_expr locals tbl) lhs in - let rhs1 = List.map (resolve_expr locals tbl) rhs in - Assign (lhs1, rhs1, pos), locals, tbl - | Dispose (e, pos) -> - Dispose (resolve_expr locals tbl e, pos), locals, tbl - | Havoc (es, pos) -> - let es1 = List.map (resolve_expr locals tbl) es in - Havoc (es1, pos), locals, tbl - | If (cond, t, e, pos) -> - let t1, locals, _ = resolve_stmt false in_loop locals tbl t in - let e1, locals, _ = resolve_stmt false in_loop locals tbl e in - If (resolve_expr locals tbl cond, t1, e1, pos), locals, tbl - | Choice (stmts0, pos) -> - let stmts, locals = - List.fold_left - (fun (stmts, locals) stmt0 -> - let stmt, locals, _ = resolve_stmt false in_loop locals tbl stmt0 in - stmt :: stmts, locals - ) - ([], locals) stmts0 - in - Choice (List.rev stmts, pos), locals, tbl - | Loop (inv, preb, cond, postb, pos) -> - let inv1 = - List.fold_right - (function Invariant (e, pure) -> fun inv1 -> - Invariant (resolve_expr locals tbl e, pure) :: inv1 - ) - inv [] - in - let preb1, locals, _ = resolve_stmt false true locals tbl preb in - let cond1 = resolve_expr locals tbl cond in - let postb1, locals, _ = resolve_stmt false true locals tbl postb in - Loop (inv1, preb1, cond1, postb1, pos), locals, tbl - | Return (es, pos) -> - if in_loop then return_in_loop_error pos; - Return (List.map (resolve_expr locals tbl) es, pos), locals, tbl - in - (* declare and resolve local variables in given contracts *) - let resolve_contracts contracts returns locals tbl = - let pre_locals, pre_tbl = - List.fold_left - (fun (pre_locals, pre_tbl) id -> - IdMap.remove id pre_locals, - SymbolTbl.remove pre_tbl id) - (locals, tbl) - returns - in - List.map - (function - | Requires (e, pure, free) -> Requires (resolve_expr pre_locals pre_tbl e, pure, free) - | Ensures (e, pure, free) -> Ensures (resolve_expr locals tbl e, pure, free) - ) - contracts - in - (* declare and resolve local variables in all procedures *) - let procs = - IdMap.fold - (fun _ decl procs -> - let locals0, tbl0 = declare_vars decl.p_locals types (SymbolTbl.push tbl) in - let process_params params seen = - List.fold_right - (fun id (params, seen) -> - let nid = lookup_id id tbl0 decl.p_pos in - if IdSet.mem id seen - then redeclaration_error id (IdMap.find id decl.p_locals).v_pos - else nid :: params, IdSet.add id seen) - params ([], seen) - in - let formals, seen = process_params decl.p_formals IdSet.empty in - let returns, _ = process_params decl.p_returns seen in - let contracts = resolve_contracts decl.p_contracts returns locals0 tbl0 in - let body, locals, _ = resolve_stmt true false locals0 tbl0 decl.p_body in - let decl1 = - { decl with - p_formals = formals; - p_returns = returns; - p_locals = locals; - p_contracts = contracts; - p_body = body } - in - IdMap.add decl.p_name decl1 procs - ) - procs0 IdMap.empty - in - (* declare and resolve local variables in all predicates *) - let preds = - IdMap.fold - (fun _ decl preds -> - let locals, tbl = declare_vars decl.pr_locals types (SymbolTbl.push tbl) in - let body = Opt.map (resolve_expr locals tbl) decl.pr_body in - let formals = List.map (fun id -> lookup_id id tbl decl.pr_pos) decl.pr_formals in - let outputs = List.map (fun id -> lookup_id id tbl decl.pr_pos) decl.pr_outputs in - let contracts = resolve_contracts decl.pr_contracts outputs locals tbl in - let decl1 = - { decl with - pr_formals = formals; - pr_outputs = outputs; - pr_locals = locals; - pr_contracts = contracts; - pr_body = body - } - in - IdMap.add decl.pr_name decl1 preds - ) - preds0 IdMap.empty - in - (* declare and resolve local variables in all axioms *) - let bg_theory = - List.map - (fun (e, pos) -> resolve_expr globals tbl e, pos) - cu.background_theory - in - { cu with - var_decls = globals; - type_decls = types; - proc_decls = procs; - pred_decls = preds; - background_theory = bg_theory; - } - - -(** Utility function for extending local variable map *) -let extend_locals cu vars locals scope = - let extend locals = function - | GuardedVar (id, e) -> - let decl = - { v_name = id; - v_type = type_of_expr cu locals e; - v_ghost = false; - v_implicit = false; - v_aux = false; - v_pos = pos_of_expr e; - v_scope = scope - } - in - IdMap.add id decl locals - | UnguardedVar decl -> - IdMap.add decl.v_name decl locals - in - List.fold_right (fun vars locals -> extend locals vars) vars locals - - -(** Flatten procedure calls, comprehensions, and new expressions in compilation unit [cu].*) -let flatten_exprs cu = - let decl_aux_var name vtype pos scope locals = - let aux_id = GrassUtil.fresh_ident name in - let decl = - { v_name = aux_id; - v_type = vtype; - v_ghost = false; - v_implicit = false; - v_aux = true; - v_pos = pos; - v_scope = scope; - } - in - let locals1 = IdMap.add aux_id decl locals in - aux_id, locals1 - in - let rec flatten_expr_list ?(flatten_bool=false) scope aux new_locals locals es = - List.fold_right (fun e (es1, aux1, new_locals) -> - let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals e in - e1 :: es1, aux1, new_locals) - es ([], aux, new_locals) - and flatten_expr ?(flatten_bool=false) scope aux new_locals locals = - let mk_aux_cmd mk_cmd typ pos aux new_locals e = - let aux_id, new_locals = decl_aux_var "tmp" typ pos scope new_locals in - let aux_var = Ident (aux_id, pos) in - let assign = mk_cmd aux_var e pos in - let aux_cmds, aux_funs = aux in - aux_var, (aux_cmds @ [assign], aux_funs), new_locals - in - let mk_aux_assign = mk_aux_cmd (fun aux_var e pos -> Assign ([aux_var], [e], pos)) in - function - | Setenum (ty, args, pos) -> - let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in - Setenum (ty, args1, pos), aux1, new_locals - | New (ty, args, pos) -> - let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:true scope aux new_locals locals args in - mk_aux_assign ty pos aux1 new_locals (New (ty, args1, pos)) - | Read (map, idx, pos) -> - let map1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals map in - let idx1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals idx in - Read (map1, idx1, pos), aux2, new_locals - | Write (map, idx, upd, pos) -> - let map1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals map in - let idx1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals idx in - let upd1, aux3, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux2 new_locals locals upd in - Write (map1, idx1, upd1, pos), aux3, new_locals - | Ite (cond, t, e, pos) -> - let cond1, aux1, new_locals = flatten_expr scope aux new_locals locals cond in - let t1, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux1 new_locals locals t in - let e1, aux3, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux2 new_locals locals e in - Ite (cond1, t1, e1, pos), aux3, new_locals - | ConstrApp (id, args, pos) -> - let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in - ConstrApp (id, args1, pos), aux1, new_locals - | DestrApp (id, arg, pos) -> - let arg1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals arg in - DestrApp (id, arg1, pos), aux1, new_locals - | Binder (b, vars, f, pos) as e -> - let vars1, aux, new_locals = - List.fold_right (fun v (vars1, aux, new_locals) -> - match v with - | GuardedVar (x, s) -> - let s1, aux1, new_locals = flatten_expr scope aux new_locals locals s in - GuardedVar (x, s) :: vars1, aux1, new_locals - | _ -> v :: vars1, aux, new_locals) - vars ([], aux, new_locals) - in - let locals = extend_locals cu vars1 locals scope in - let f1, aux, new_locals = flatten_expr scope aux new_locals locals f in - let cu = - List.fold_left - (fun cu decl -> - { cu with pred_decls = IdMap.add decl.pr_name decl cu.pred_decls } - ) cu (snd aux) - in - let e1 = Binder (b, vars1, f1, pos) in - (match b with - | Exists | Forall -> e1, aux, new_locals - | Comp -> - (* create auxiliary function for set/map comprehension *) - let v_decl = - match vars1 with - | [UnguardedVar decl] -> decl - | _ -> failwith "unexpected set comprehension" - in - let c_id = GrassUtil.fresh_ident "compr" in - let fv = IdSet.elements (free_vars e) in - let res_id = GrassUtil.fresh_ident "res" in - let res_ty = - match type_of_expr cu locals f1 with - | BoolType -> SetType v_decl.v_type - | rty -> MapType (v_decl.v_type, rty) - in - let res_decl = - { v_name = res_id; - v_type = res_ty; - v_ghost = false; - v_implicit = false; - v_aux = true; - v_pos = pos; - v_scope = pos; - } - in - let c_locals = - IdMap.add v_decl.v_name v_decl - (IdMap.singleton res_id res_decl) - in - let formals = List.filter (fun id -> IdMap.mem id locals) fv in - let c_locals = - List.fold_left - (fun c_locals id -> IdMap.add id (IdMap.find id locals) c_locals) - c_locals formals - in - - let c_decl = - { pr_name = c_id; - pr_formals = formals; - pr_outputs = [res_id]; - pr_locals = c_locals; - pr_contracts = []; - pr_is_pure = false; - pr_body = Some e1; - pr_pos = pos; - } - in - let actuals = List.map (fun id -> Ident (id, pos)) formals in - let c_app = PredApp (Pred c_id, actuals, pos) in - let aux_cmds, aux_funs = aux in - c_app, (aux_cmds, c_decl :: aux_funs), new_locals - ) - | ProcCall (id, args, pos) -> - let pdecl = IdMap.find id cu.proc_decls in - let returns = List.filter (fun p -> not (IdMap.find p pdecl.p_locals).v_implicit) pdecl.p_returns in - let res_type = - match returns with - | [res] -> - let rdecl = IdMap.find res pdecl.p_locals in - rdecl.v_type - | _ -> invalid_nested_proc_call_error pdecl.p_name pos - in - let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:true scope aux new_locals locals args in - let call = ProcCall (id, args1, pos) in - mk_aux_assign res_type pos aux1 new_locals call - | PredApp (p, args, pos) -> - let args1, aux1, new_locals = flatten_expr_list ~flatten_bool:flatten_bool scope aux new_locals locals args in - PredApp(p, args1, pos), aux1, new_locals - | UnaryOp (op, e, pos) -> - let flatten_bool_rec = - match op with - | OpNot -> false - | _ -> flatten_bool - in - let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux new_locals locals e in - let e2 = UnaryOp (op, e1, pos) in - (match op with - | OpNot when flatten_bool -> - let mk_cmd aux_var e pos = - If (e2, Assign ([aux_var], [BoolVal (true, pos)], pos), Assign ([aux_var], [BoolVal (false, pos)], pos), pos) - in - mk_aux_cmd mk_cmd BoolType pos aux1 new_locals e1 - | _ -> e2, aux1, new_locals) - | BinaryOp (e1, op, e2, ty, pos) -> - let flatten_bool_rec = - match op with - | OpAnd | OpOr -> false - | _ -> flatten_bool - in - let e21, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux new_locals locals e2 in - let e11, aux2, new_locals = flatten_expr ~flatten_bool:flatten_bool_rec scope aux1 new_locals locals e1 in - let e2 = BinaryOp (e11, op, e21, ty, pos) in - (match op with - | (OpAnd | OpOr) when flatten_bool -> - let mk_cmd aux_var e pos = - If (e2, Assign ([aux_var], [BoolVal (true, pos)], pos), Assign ([aux_var], [BoolVal (false, pos)], pos), pos) - in - mk_aux_cmd mk_cmd BoolType pos aux2 new_locals e1 - | _ -> e2, aux2, new_locals) - | Annot (e, PatternAnnot p, pos) -> - let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in - let p1, aux2, new_locals = flatten_expr scope aux1 new_locals locals p in - Annot (e1, PatternAnnot p1, pos), aux2, new_locals - | Annot (e, GeneratorAnnot (es, ge), pos) -> - let es1, aux1, new_locals = - List.fold_right (fun (e, id) (es1, aux1, new_locals) -> - let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in - (e1, id) :: es1, aux1, new_locals) - es ([], aux, new_locals) - in - let ge1, aux2, new_locals = flatten_expr scope aux1 new_locals locals ge in - let e1, aux3, new_locals = flatten_expr scope aux2 new_locals locals e in - Annot (e1, GeneratorAnnot (es1, ge1), pos), aux3, new_locals - | Annot (e, ann, pos) -> - let e1, aux1, new_locals = flatten_expr ~flatten_bool:flatten_bool scope aux new_locals locals e in - Annot (e1, ann, pos), aux1, new_locals - | e -> e, aux, new_locals - in - let rec flatten scope locals aux_funs returns = function - | Skip pos -> Skip pos, locals, aux_funs - | Block (stmts0, pos) -> - let stmts, locals, aux_funs = - List.fold_left - (fun (stmts, locals, aux_funs) stmt0 -> - let stmt, locals, aux_funs = flatten pos locals aux_funs returns stmt0 in - stmt :: stmts, locals, aux_funs - ) - ([], locals, aux_funs) stmts0 - in Block (List.rev stmts, pos), locals, aux_funs - | LocalVars (_, _, pos) -> - failwith "flatten_exprs: LocalVars should have been eliminated" - | Assume (e, pure, pos) -> - let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in - Assume (e1, pure, pos), locals, snd aux1 - | Assert (e, pure, pos) -> - let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in - Assert (e1, pure, pos), locals, snd aux1 - | Split (e, pos) -> - let e1, aux1, locals = flatten_expr scope ([], aux_funs) locals locals e in - Split (e1, pos), locals, snd aux1 - | Assign (lhs, [ProcCall (id, args, cpos)], pos) -> - let args1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals args in - begin - match lhs with - | [Read (map, idx, opos)] -> - let res_ty = - match type_of_expr cu locals map with - | MapType (_, ty) -> ty - | ArrayType ty -> ty - | ty -> ty - in - let aux_id, locals = decl_aux_var "tmp" res_ty opos scope locals in - let aux_var = Ident (aux_id, pos) in - let aux_cmds, aux_funs = aux1 in - let assign_aux, locals, aux_funs = - flatten scope locals aux_funs returns (Assign ([Read (map, idx, opos)], [aux_var], pos)) in - mk_block pos (aux_cmds @ [Assign ([aux_var], [ProcCall (id, args1, cpos)], pos)] @ [assign_aux]), locals, aux_funs - | _ -> - let lhs1, aux2, locals = - List.fold_right - (fun e (es, aux, locals) -> - let e1, aux1, locals = flatten_expr ~flatten_bool:true scope aux locals locals e in - e1 :: es, aux1, locals - ) - lhs ([], aux1, locals) - in - let aux_cmds, aux_funs = aux2 in - mk_block pos (aux_cmds @ [Assign (lhs1, [ProcCall (id, args1, cpos)], pos)]), locals, aux_funs - end - | Assign (lhs, rhs, pos) -> - let rhs1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals rhs in - let lhs1, aux2, locals = - List.fold_right - (fun e (es, aux, locals) -> - let e1, aux1, locals = flatten_expr scope aux locals locals e in - match e1 with - | Read(Ident _, _, _) -> - e1 :: es, aux1, locals - | Read (map, ind, opos) -> - let mpos = pos_of_expr map in - let aux_id, locals = - decl_aux_var "tmp" (type_of_expr cu locals map) mpos scope locals - in - let aux_var = Ident (aux_id, mpos) in - let assign_aux = Assign ([aux_var], [map], mpos) in - let aux_cmds, aux_funs = aux1 in - Read (aux_var, ind, opos) :: es, (aux_cmds @ [assign_aux], aux_funs), locals - | _ -> - e1 :: es, aux1, locals - ) - lhs ([], aux1, locals) - in - let aux_cmds, aux_funs = aux2 in - mk_block pos (aux_cmds @ [Assign (lhs1, rhs1, pos)]), locals, aux_funs - | Dispose (e, pos) -> - let e1, aux, locals = flatten_expr ~flatten_bool:true scope ([], aux_funs) locals locals e in - mk_block pos (fst aux @ [Dispose (e1, pos)]), locals, snd aux - | Havoc (es, pos) -> - let es1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals es in - let aux_cmds, aux_funs = aux1 in - mk_block pos (aux_cmds @ [Havoc (es1, pos)]), locals, aux_funs - | If (cond, t, e, pos) -> - let cond1, (aux_cmds, aux_funs), locals = flatten_expr scope ([], aux_funs) locals locals cond in - let t1, locals, aux_funs = flatten scope locals aux_funs returns t in - let e1, locals, aux_funs = flatten scope locals aux_funs returns e in - mk_block pos (aux_cmds @ [If (cond1, t1, e1, pos)]), locals, aux_funs - | Choice (stmts0, pos) -> - let stmts, locals, aux_funs = - List.fold_left - (fun (stmts, locals, aux_funs) stmt0 -> - let stmt, locals, aux_funs = flatten pos locals aux_funs returns stmt0 in - stmt :: stmts, locals, aux_funs - ) - ([], locals, aux_funs) stmts0 - in Choice (List.rev stmts, pos), locals, aux_funs - | Loop (inv, preb, cond, postb, pos) -> - let inv1, aux_funs, locals = - List.fold_right - (function Invariant (e, pos) -> fun (inv1, aux_funs, locals) -> - let e1, (_, aux_funs), locals = flatten_expr scope ([], aux_funs) locals locals e in - Invariant (e1, pos) :: inv1, aux_funs, locals) - inv ([], aux_funs, locals) - in - let preb1, locals, aux_funs = flatten pos locals aux_funs returns preb in - let cond1, (aux_cmds, aux_funs), locals = flatten_expr pos ([], aux_funs) locals locals cond in - let postb1, locals, aux_funs = flatten pos locals aux_funs returns postb in - Loop (inv1, mk_block pos ([preb1] @ aux_cmds), cond1, postb1, pos), locals, aux_funs - | Return ([ProcCall (id, args, cpos)], pos) -> - let args1, aux1, locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals args in - let rts = List.map (fun id -> Ident (id, cpos)) returns in - let assign = Assign (rts, [ProcCall (id, args1, cpos)], pos) in - let ret = Return (rts, pos) in - let aux_cmds, aux_funs = aux1 in - mk_block pos (aux_cmds @ [assign; ret]), locals, aux_funs - | Return (es, pos) -> - let es1, (aux_cmds, aux_funs), locals = flatten_expr_list ~flatten_bool:true scope ([], aux_funs) locals locals es in - mk_block pos (aux_cmds @ [Return (es1, pos)]), locals, aux_funs - in - let flatten_contracts aux_funs locals contracts = - List.fold_right - (fun c (contracts, aux_funs, locals) -> - let flatten_spec e = - let e1, (aux_cmds, aux_funs), locals = - flatten_expr (pos_of_expr e) ([], aux_funs) locals locals e - in - match aux_cmds with - | [] -> e1, aux_funs, locals - | cmd :: _ -> illegal_side_effect_error (pos_of_stmt cmd) "specifications" - in - match c with - | Requires (e, pure, free) -> - let e1, aux_funs, locals = flatten_spec e in - Requires (e1, pure, free) :: contracts, aux_funs, locals - | Ensures (e, pure, free) -> - let e1, aux_funs, locals = flatten_spec e in - Ensures (e1, pure, free) :: contracts, aux_funs, locals) - contracts ([], aux_funs, locals) - in - let procs, aux_funs = - IdMap.fold - (fun _ decl (procs, aux_funs) -> - let contracts, aux_funs, locals = - flatten_contracts aux_funs decl.p_locals decl.p_contracts - in - let body, locals, aux_funs = - flatten decl.p_pos locals aux_funs decl.p_returns decl.p_body - in - let decl1 = - { decl with - p_locals = locals; - p_contracts = contracts; - p_body = body } - in - IdMap.add decl.p_name decl1 procs, aux_funs) - cu.proc_decls (IdMap.empty, []) - in - let preds, aux_funs = - IdMap.fold - (fun _ decl (preds, aux_funs) -> - let contracts, aux_funs, locals = - flatten_contracts aux_funs decl.pr_locals decl.pr_contracts - in - let body, (aux_cmds, aux_funs), locals = - match decl.pr_body with - | Some body -> - let body, aux, locals = flatten_expr decl.pr_pos ([], aux_funs) decl.pr_locals decl.pr_locals body in - Some body, aux, locals - | None -> None, ([], aux_funs), locals - in - (* if auxiliary function comes from top-level expression in body then undo the flattening *) - let body, aux_funs = - match body, aux_funs with - | Some (PredApp (Pred id, _, _) | Annot(PredApp (Pred id, _, _), _, _)), aux_decl :: aux_decls - when id = aux_decl.pr_name -> aux_decl.pr_body, aux_decls - | _ -> body, aux_funs - in - match aux_cmds with - | cmd :: _ -> - illegal_side_effect_error (pos_of_stmt cmd) "function and procedure bodies" - | _ -> (); - let decl1 = - { decl with - pr_locals = locals; - pr_contracts = contracts; - pr_body = body - } - in - IdMap.add decl.pr_name decl1 preds, aux_funs) - cu.pred_decls (IdMap.empty, aux_funs) - in - let preds_all = List.fold_left (fun preds_all decl -> IdMap.add decl.pr_name decl preds_all) preds aux_funs in - { cu with proc_decls = procs; pred_decls = preds_all } - - -(** Type check compilation unit [cu]. Missing type annotations are inferred along the way.*) -let infer_types cu = - let check_spec locals pure e = - let ty = if pure then BoolType else PermType in - infer_types cu locals ty e - in - (* infer types within given statement *) - let rec check_stmt proc = - let locals = proc.p_locals in - function - | Skip pos -> Skip pos - | Block (stmts, pos) -> - Block (List.map (check_stmt proc) stmts, pos) - | LocalVars (_, _, pos) -> - failwith "infer_types: LocalVars should have been eliminated" - | Assume (e, pure, pos) -> - Assume (check_spec locals pure e, pure, pos) - | Assert (e, pure, pos) -> - Assert (check_spec locals pure e, pure, pos) - | Split (e, pos) -> - Split (check_spec locals false e, pos) - | Assign (lhs, [ProcCall (id, args, cpos) as e], pos) -> - let decl = IdMap.find id cu.proc_decls in - let returns = List.filter (fun p -> not (IdMap.find p decl.p_locals).v_implicit) decl.p_returns in - let rtys = - List.map (fun fid -> - let vdecl = IdMap.find fid decl.p_locals in - vdecl.v_type) - returns - in - let lhs1 = - try List.map2 (infer_types cu locals) rtys lhs - with Invalid_argument _ -> - ProgError.error cpos - (Printf.sprintf "Procedure %s has %d explicit return value(s)" - (fst id) (List.length rtys)) - in - let e1 = infer_types cu locals AnyType e in - Assign (lhs1, [e1], pos) - | Assign (lhs, rhs, pos) -> - let _ = List.fold_left (fun seen -> function - | Ident (x, pos) -> - if List.mem x seen - then assignment_multiple_error pos - else x :: seen - | _ -> seen) - [] lhs - in - let rhs1, tys = - Util.map_split (fun e -> - let e1 = infer_types cu locals AnyType e in - let ty = - match type_of_expr cu locals e with - | PermType -> - ProgError.error (pos_of_expr e) "Permissions cannot be assigned" - | ty -> ty - in - e1, ty) rhs - in - let lhs1 = - try - List.map2 (infer_types cu locals) tys lhs - with Invalid_argument _ -> - assignment_mismatch_error pos - in - let rhs2 = - List.map2 - (fun e1 e2 -> infer_types cu locals (type_of_expr cu locals e1) e2) - lhs1 rhs1 - in - Assign (lhs1, rhs2, pos) - | Dispose (e, pos) -> - let e1 = infer_types cu locals AnyRefType e in - Dispose (e1, pos) - | Havoc (es, pos) -> - let es1 = - List.map (fun e -> - let e1 = infer_types cu locals AnyType e in - match type_of_expr cu locals e1 with - | PermType -> - ProgError.error (pos_of_expr e) "Permissions cannot be assigned" - | _ -> e1) - es - in - Havoc (es1, pos) - | If (cond, t, e, pos) -> - let cond1 = infer_types cu locals BoolType cond in - let t1 = check_stmt proc t in - let e1 = check_stmt proc e in - If (cond1, t1, e1, pos) - | Choice (stmts, pos) -> - Choice (List.map (check_stmt proc) stmts, pos) - | Loop (inv, preb, cond, postb, pos) -> - let inv1 = - List.map - (function Invariant (e, pure) -> - Invariant (check_spec locals pure e, pure)) - inv - in - let preb1 = check_stmt proc preb in - let cond1 = infer_types cu locals BoolType cond in - let postb1 = check_stmt proc postb in - Loop (inv1, preb1, cond1, postb1, pos) - | Return (es, pos) -> - let returns = List.filter (fun x -> not (IdMap.find x proc.p_locals).v_implicit) proc.p_returns in - let rtys = - List.map (fun id -> (IdMap.find id locals).v_type) returns - in - let es1 = - try List.map2 (infer_types cu locals) rtys es - with Invalid_argument _ -> - ProgError.error pos - (Printf.sprintf "Procedure %s returns %d explicit values(s), found %d" - (fst proc.p_name) (List.length rtys) (List.length es)) - in - Return (es1, pos) - in - (* infer types of predicates *) - let cu = - IdMap.fold - (fun id pred cu -> - let rtype = - match pred.pr_outputs with - | [res_id] -> - (IdMap.find res_id pred.pr_locals).v_type - | _ -> PermType - in - let contracts = - List.map (function - | Requires (e, pure, free) -> - Requires (check_spec pred.pr_locals pure e, pure, free) - | Ensures (e, pure, free) -> - Ensures (check_spec pred.pr_locals pure e, pure, free)) pred.pr_contracts - in - let body = - Opt.map (infer_types cu pred.pr_locals rtype) pred.pr_body - in - let pred1 = - { pred with - pr_contracts = contracts; - pr_body = body - } - in - let pred_decls1 = IdMap.add id pred1 cu.pred_decls in - { cu with pred_decls = pred_decls1 }) - cu.pred_decls cu - in - (* infer types of procedures/lemmas *) - let procs = - IdMap.fold - (fun _ proc procs -> - let contracts = - List.map (function - | Requires (e, pure, free) -> - Requires (check_spec proc.p_locals pure e, pure, free) - | Ensures (e, pure, free) -> - Ensures (check_spec proc.p_locals pure e, pure, free)) proc.p_contracts - in - let body = check_stmt proc proc.p_body in - let proc1 = { proc with p_contracts = contracts; p_body = body } in - IdMap.add proc.p_name proc1 procs) - cu.proc_decls IdMap.empty - in - let bg_theory = - List.map - (fun (e, pos) -> (check_spec IdMap.empty true e, pos) ) - cu.background_theory - in - { cu with proc_decls = procs; background_theory = bg_theory; } - -(** Check compilation unit [cu]. *) -let check cu = - let cu1 = resolve_names cu in - let cu2 = infer_types cu1 in - let cu3 = flatten_exprs cu2 in - cu3 diff --git a/src/frontends/spl/.#splChecker.ml b/src/frontends/spl/.#splChecker.ml deleted file mode 120000 index 7872bea9..00000000 --- a/src/frontends/spl/.#splChecker.ml +++ /dev/null @@ -1 +0,0 @@ -wies@cantor.17059:1535018249 \ No newline at end of file From 578bff132f8e458821a52ab3aab888ef9085aaf9 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 27 Aug 2018 20:10:35 -0400 Subject: [PATCH 074/118] towards better e-matching --- src/formulas/grass.ml | 16 ++- src/formulas/grassUtil.ml | 11 +- src/prover/axioms.ml | 6 +- src/prover/congruenceClosure.ml | 9 +- src/prover/eMatching.ml | 211 ++++++++++++++++++++++++++++++++ src/prover/instGen.ml | 35 ++---- src/prover/prover.ml | 4 +- src/prover/reduction.ml | 13 +- src/util/prioQueue.ml | 19 ++- src/util/prioQueue.mli | 3 +- 10 files changed, 286 insertions(+), 41 deletions(-) create mode 100644 src/prover/eMatching.ml diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index 6bdc4465..08fff728 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -67,7 +67,7 @@ module SortMap = Map.Make(struct type sorted_ident = ident * sort type arity = sort list * sort - + (** symbols *) type symbol = (* interpreted constant symbols *) @@ -95,6 +95,8 @@ type symbol = (* for patterns *) | Known +type sorted_symbol = symbol * arity + let symbols = [BoolConst true; BoolConst false; Null; Read; Write; EntPnt; @@ -118,12 +120,12 @@ module SymbolMap = Map.Make(struct end) module SortedSymbolSet = Set.Make(struct - type t = symbol * arity + type t = sorted_symbol let compare = compare end) module SortedSymbolMap = Map.Make(struct - type t = symbol * arity + type t = sorted_symbol let compare = compare end) @@ -141,9 +143,13 @@ let symbol_of = function | App (sym, _, _) -> Some sym let sort_of = function - | Var (_, s) - | App (_, _, s) -> s + | Var (_, srt) + | App (_, _, srt) -> srt +let sorted_symbol_of = function + | Var _ -> None + | App (sym, ts, srt) -> Some (sym, (List.map sort_of ts, srt)) + let rec sort_ofs = function | [] -> failwith "tried to extract sort from empty list of terms" | t :: ts -> sort_of t diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index fcc80543..eb24fa27 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -981,10 +981,17 @@ let ground_terms_term_acc ?(include_atoms=false) terms t = in fst (gt terms t) -(** Computes the set of all ground terms in term [t]. - ** Takes accumulator [terms] as additional arguments *) +(** Computes the set of all ground terms in term [t]. *) let ground_terms_term ?(include_atoms=false) t = ground_terms_term_acc ~include_atoms:include_atoms TermSet.empty t + +(** Computes the set of ground terms appearing in [f]. + ** Free variables are treated as implicitly universally quantified. + ** Takes accumulator [terms] as additional argument. *) +let ground_terms_acc ?(include_atoms=false) terms f = + fold_terms + (ground_terms_term_acc ~include_atoms:include_atoms) + terms f (** Computes the set of ground terms appearing in [f]. ** Free variables are treated as implicitly universally quantified *) diff --git a/src/prover/axioms.ml b/src/prover/axioms.ml index 11852632..180c3798 100644 --- a/src/prover/axioms.ml +++ b/src/prover/axioms.ml @@ -315,7 +315,11 @@ let reach_axioms classes struct_srt = let null_axioms struct_srt1 = let n = mk_null struct_srt1 in let nll = mk_eq (f n) n in - [mk_axiom "read_null" nll] + let generator = + [Match (fld1 struct_srt1, [])], + [f n] + in + [mk_axiom ~gen:[generator] "read_null" nll] (** Frame axioms *) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 10adf3c7..2482c287 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -396,11 +396,15 @@ let create () : dag = cc_graph#add_neq mk_true_term mk_false_term; cc_graph +let rep_of_term cc_graph t = (cc_graph#get_node t)#find + +let term_of_rep cc_graph n = cc_graph#get_term n + let get_classes cc_graph = cc_graph#get_cc -let congruence_classes fs = +let congruence_classes gts fs = create () |> - add_terms (ground_terms ~include_atoms:true (mk_and fs)) |> + add_terms (ground_terms_acc ~include_atoms:true gts (mk_and fs)) |> add_conjuncts fs |> get_classes @@ -408,3 +412,4 @@ let class_of t classes = List.find (List.mem t) classes let restrict_classes classes ts = List.filter (fun cc -> List.exists (fun t -> TermSet.mem t ts) cc) classes + diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml new file mode 100644 index 00000000..686a6876 --- /dev/null +++ b/src/prover/eMatching.ml @@ -0,0 +1,211 @@ +(** E-Matching. + + Implements a variant of the e-matching technique presented in this paper: + Efficient E-Matching for SMT Solvers + Leonardo de Moura and Nikolaj Bjorner, CADE 2007 + +*) + +open Util +open Grass +open GrassUtil + +module CC = CongruenceClosure + +(** E-matching code trees *) +type ematch_code = + | Init of sorted_symbol * int * ematch_code + | Bind of int * sorted_symbol * int * ematch_code + | Check of int * term * ematch_code + | Compare of int * int * ematch_code + | Choose of ematch_code list + | Yield of int IdMap.t * form + | ChooseApp of int * ematch_code * CC.Node.t list list + +let continuation = function + | Init (_, _, c) -> c + | Bind (_, _, _, c) -> c + | Check (_, _, c) -> c + | Compare (_, _, c) -> c + | _ -> failwith "illegal argument to continuation" + +let max_reg c = + let rec mr m = function + | Init (_, i, c) + | Check (i, _, c) + | ChooseApp (i, c, _) -> mr (max m i) c + | Bind (i, _, o, c) + | Compare (i, o, c) -> mr (max (max m i) o) c + | Choose cs -> + List.fold_left mr m cs + | Yield _ -> m + in + mr 0 c + +let mk_choose c1 c2 = + match c1, c2 with + | Choose cs1, Choose cs2 -> Choose (cs1 @ cs2) + | Choose cs1, _ -> Choose (cs1 @ [c2]) + | _, Choose cs2 -> Choose (c1 :: cs2) + | _ -> Choose [c1; c2] + +(** Work queue for pattern compilation *) +module WQ = PrioQueue.Make + (struct + type t = int + let compare = compare + end) + (struct + type t = term + let compare t1 t2 = + if is_ground_term t1 && not @@ is_ground_term t2 then -1 + else match t1, t2 with + | Var _, App _ -> -1 + | App _, Var _ -> 1 + | _ -> compare t1 t2 + end) + +(** Compile the list of patterns [patterns] into an ematching code tree *) +let compile patterns = + let add_args_to_queue w o args = + List.fold_left + (fun (w', o') arg -> WQ.insert o' arg w', o' + 1) + (w, o) args + in + let rec compile f pattern w v o = + let rec c w v o = + if WQ.is_empty w then init f pattern v o else + let i, t, w' = WQ.extract_min w in + match t with + | Var (x, _) when not @@ IdMap.mem x v -> + c w' (IdMap.add x i v) o + | Var (x, _) -> + Compare (i, IdMap.find x v, c w' v o) + | t when is_ground_term t -> + Check (i, t, c w' v o) + | App (sym, args, _) -> + let w'', o' = add_args_to_queue w' o args in + Bind (i, sorted_symbol_of t |> Opt.get, o, c w'' v o') + in + c w v o + and init f pattern v o = + match pattern with + | App (_, args, _) as t :: pattern -> + let w, o' = add_args_to_queue WQ.empty o args in + Init (sorted_symbol_of t |> Opt.get, o, compile f pattern w v o') + | Var _ :: _ -> failwith "TODO" + | [] -> Yield (v, f) + in + let seq cs fchild = + let rec s = function + | Init (sym, o, c) :: cs -> Init (sym, o, s cs) + | Check (i, t, c) :: cs -> Check (i, t, s cs) + | Compare (i, j, c) :: cs -> Compare (i, j, s cs) + | Bind (i, sym, o, c) :: cs -> Bind (i, sym, o, s cs) + | [] -> fchild + | _ -> assert false + in + s (List.rev cs) + in + let branch f pattern comps fchild w o = + seq comps (mk_choose (compile f pattern w IdMap.empty o) fchild) + in + let compatible pattern w = function + | Init (sym, o, _) -> + if WQ.is_empty w then + match pattern with + | App (_, args, _) as t :: pattern' + when sorted_symbol_of t = Some sym -> + let w', _ = add_args_to_queue WQ.empty o args in + Some w' + | _ -> None + else None + | Check (i, t, _) -> + (match WQ.find_opt i w with + | Some t' when t = t' -> Some (WQ.delete i w) + | _ -> None) + | Compare (i, j, _) -> + let ti_opt = WQ.find_opt i w in + let tj_opt = WQ.find_opt j w in + (match ti_opt, tj_opt with + | Some (Var _ as ti), Some tj when ti = tj -> + Some (WQ.delete j w) + | _ -> None) + | Bind (i, sym, o, _) -> + (match WQ.find_opt i w with + | Some (App (_, args, _) as t) when sorted_symbol_of t = Some sym -> + let w', _ = add_args_to_queue w o args in + Some (WQ.delete i w') + | _ -> None) + | _ -> None + in + let rec insert_code f pattern w o comps incomps code = + match code with + | Choose cs -> + if incomps = [] then + let code', score = insert_choose f pattern cs w o in + seq comps code', score + List.length comps + else branch f pattern comps (seq incomps code) w o, List.length comps + | Yield _ -> + branch f pattern comps (seq incomps code) w o, List.length comps + | code -> + let code' = continuation code in + compatible pattern w code |> + Opt.map (fun w' -> insert_code f pattern w' o (code :: comps) incomps code') |> + Opt.lazy_get_or_else (fun () -> insert_code f pattern w o comps (code :: incomps) code') + and insert_choose f pattern cs w o = + let cs', _, best = + List.fold_right + (fun code (cs', cs, best) -> + match insert_code f pattern w o [] [] code with + | code', score when score > best -> + code' :: cs, code :: cs, score + | _ -> code :: cs', code :: cs, best + ) + cs ([], [], 0) + in + if best = 0 + then Choose (compile f pattern w IdMap.empty o :: cs), 0 + else Choose cs', best + in + List.fold_left + (fun code (f, pattern) -> + let code', _ = insert_code f pattern WQ.empty (max_reg code) [] [] code in + code' + ) + (Choose []) patterns + +(** Run the given e-matching code tree. Yields a list of instantiated formulas. *) +let run code ccgraph = + let rec run insts (regs: CC.Node.t array) = function + | Init (sym, o, next) :: stack -> + let apps = CC.get_apps ccgraph sym in + run insts regs (ChooseApp (o, next, apps) :: stack) + | Bind (i, sym, o, next) :: stack -> + let apps = CC.get_apps_of_node ccgraph (regs.(i)) sym in + run insts regs (ChooseApp (o, next, apps) :: stack) + | Check (i, t, next) :: stack -> + if CC.node_of_term ccgraph t = regs.(i) + then run insts regs (next :: stack) + else run insts regs stack + | Compare (i, j, next) :: stack -> + if regs.(i) = regs.(j) + then run insts regs (next :: stack) + else run insts regs stack + | Choose cs :: stack -> + run insts regs (cs @ stack) + | Yield (vs, f) :: stack -> + let sm = IdMap.map (fun i -> CC.term_of_node ccgraph regs.(i)) vs in + run (subst sm f :: insts) regs stack + | ChooseApp (o, next, args :: apps) :: stack -> + let _ = + List.fold_left + (fun i arg -> regs.(i) <- arg; i + 1) + o args + in + run insts regs (next :: ChooseApp (o, next, apps) :: stack) + | ChooseApp (_, _, []) :: stack -> + run insts regs stack + | [] -> insts + in + run [] (Array.make (max_reg code) (CC.node_of_term ccgraph mk_true_term) diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml index fd29c8dc..1fa0ee04 100644 --- a/src/prover/instGen.ml +++ b/src/prover/instGen.ml @@ -227,11 +227,11 @@ let generate_terms generators ground_terms = let generate_terms generators = measure_call "InstGen.generate_terms" (generate_terms generators) -let generate_instances stratify useLocalInst axioms rep_terms egraph = +let generate_instances stratify axioms rep_terms egraph = (* *) let epr_axioms, axioms = List.partition - (fun f -> useLocalInst && IdSrtSet.is_empty (vars_in_fun_terms f)) + (fun f -> IdSrtSet.is_empty (vars_in_fun_terms f)) axioms in (*print_endline "EPR:"; @@ -240,11 +240,7 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = let _ = print_forms stdout axioms in*) let instantiate acc f = let fvars0 = sorted_free_vars f in - let fvars = - if useLocalInst - then IdSrtSet.inter fvars0 (vars_in_fun_terms f) - else fvars0 - in + let fvars = IdSrtSet.inter fvars0 (vars_in_fun_terms f) in (* filter out stratified variables *) let fvars, strat_vars = let merge_map k a b = match (a,b) with @@ -266,7 +262,7 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = (fun_terms_with_vars f) IdMap.empty in - if stratify && useLocalInst then + if stratify then IdSrtSet.partition (fun (id, srt) -> try @@ -284,7 +280,6 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = let strat_var_ids = IdSrtSet.fold (fun (id, _) acc -> IdSet.add id acc) strat_vars IdSet.empty in (* collect all terms in which free variables appear below function symbols *) let fun_terms, fun_vars, strat_terms = - if not useLocalInst then TermSet.empty, IdSet.empty, TermSet.empty else let rec tt (fun_terms, fun_vars, strat_terms) t = match t with | App (sym, _ :: _, srt) when srt <> Bool -> @@ -335,7 +330,7 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = (* generate substitution maps *) let subst_maps () = (* generate substitution maps for variables that appear below function symbols *) - let proto_subst_maps = + let subst_maps = List.fold_left (fun subst_maps (t, fs) -> (*print_endline ("Matching term " ^ string_of_term t);*) @@ -345,13 +340,7 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = ) [IdMap.empty] fun_terms_with_filters in - (* complete substitution maps for remaining variables *) - IdSrtSet.fold - (fun (v, srt) subst_maps -> - if IdSet.mem v fun_vars - then subst_maps - else ematch [] (Var (v, srt)) rep_terms egraph subst_maps) - fvars proto_subst_maps + subst_maps in let subst_maps = measure_call "InstGen.subst_maps" subst_maps () in let _ = if Debug.is_debug 1 then @@ -380,10 +369,10 @@ let generate_instances stratify useLocalInst axioms rep_terms egraph = in List.fold_left instantiate epr_axioms axioms -let generate_instances stratify useLocalInst axioms rep_terms = - measure_call "InstGen.generate_instances" (generate_instances stratify useLocalInst axioms rep_terms) +let generate_instances stratify axioms rep_terms = + measure_call "InstGen.generate_instances" (generate_instances stratify axioms rep_terms) -let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms classes0 = +let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) axioms classes0 = if not !Config.instantiate && not force then axioms else (* remove theory atoms from congruence classes *) let filter_term t = @@ -411,7 +400,7 @@ let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local a in (* choose representatives for instantiation *) let reps_f, egraph = choose_rep_terms classes in - generate_instances stratify local axioms reps_f egraph + generate_instances stratify axioms reps_f egraph -let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) local axioms = - measure_call "InstGen.instantiate_with_terms" (instantiate_with_terms ~force:force ~stratify:stratify local axioms) +let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) axioms = + measure_call "InstGen.instantiate_with_terms" (instantiate_with_terms ~force:force ~stratify:stratify axioms) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index e7c141a5..75fec7e3 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -191,7 +191,7 @@ let instantiate_and_prove session fs = let round1 fs_inst gts_inst cc_graph = let equations = List.filter (fun f -> is_horn false [f]) fs_inst in let ground_fs = List.filter is_ground fs_inst in - let eqs = instantiate_with_terms true equations (CongruenceClosure.get_classes cc_graph) in + let eqs = instantiate_with_terms equations (CongruenceClosure.get_classes cc_graph) in let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in let fs, gts1 = generate_adt_terms fs gts1 in let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in @@ -244,7 +244,7 @@ let instantiate_and_prove session fs = measure_call "cc_gen" (fun cc_graph -> cc_graph |> CongruenceClosure.add_terms gts_inst) |> CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) in - let fs_inst = instantiate_with_terms true fs1 (CongruenceClosure.get_classes cc_graph) in + let fs_inst = instantiate_with_terms fs1 (CongruenceClosure.get_classes cc_graph) in saturate (i + 1) fs_inst gts_inst cc_graph in let saturate i fs_inst gts_inst0 = measure_call "saturate" (saturate i fs_inst gts_inst0) in diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index f4e636d4..f389cb29 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -420,10 +420,15 @@ let add_read_write_axioms fs = let basic_structs = struct_sorts_of_fields basic_pt_flds in (* instantiate null axioms *) let axioms = SortSet.fold (fun srt axioms -> Axioms.null_axioms srt @ axioms) basic_structs [] in - let null_ax, _ = open_axioms ~force:true isFld axioms in - let classes = CongruenceClosure.congruence_classes fs in + let null_ax, generators = open_axioms ~force:true isFld axioms in + let generators = match generators with + | [[Match (v, _)], t] -> [[Match (v, [FilterGeneric (fun sm t -> TermSet.mem (subst_term sm v) basic_pt_flds)])], t] + | gs -> gs + in + let gts = generate_terms generators gts in + let classes = CongruenceClosure.congruence_classes gts fs in (* CAUTION: not forcing the instantiation here would yield an inconsistency with the read/write axioms *) - let null_ax1 = instantiate_with_terms ~force:true false null_ax (CongruenceClosure.restrict_classes classes basic_pt_flds) in + let null_ax1 = instantiate_with_terms ~force:true null_ax classes in let fs1 = null_ax1 @ fs in let gts = TermSet.union (ground_terms ~include_atoms:true (mk_and null_ax1)) gts in (* propagate read terms *) @@ -461,7 +466,7 @@ let add_reach_axioms fs = | _ -> struct_sorts) (sorts (mk_and fs)) SortSet.empty in - let classes = CongruenceClosure.congruence_classes fs in + let classes = CongruenceClosure.congruence_classes TermSet.empty fs in let axioms = SortSet.fold (fun srt axioms -> Axioms.reach_axioms classes srt @ Axioms.reach_write_axioms srt @ axioms) diff --git a/src/util/prioQueue.ml b/src/util/prioQueue.ml index 81425fee..6a956b1e 100644 --- a/src/util/prioQueue.ml +++ b/src/util/prioQueue.ml @@ -158,6 +158,23 @@ module Make(K: OrderedType)(P: OrderedType) = struct let extract_min t = let k, p, l, m = get_winner t in k, p, second_best l m - + + let rec fold fn init = function + | Leaf -> init + | Node (_, k, p, l, _, r) -> + let rinit = fold fn (fn init k p) l in + fold fn rinit r + + let rec find_opt k = function + | Leaf -> None + | t -> + if size t = 1 then + let k2, p = get_singleton t in + if K.compare k k2 = 0 then Some p else None + else + let l, r = get_play t in + if K.compare k (max_key l) <= 0 + then find_opt k l + else find_opt k r end diff --git a/src/util/prioQueue.mli b/src/util/prioQueue.mli index c3f6715c..f7346291 100644 --- a/src/util/prioQueue.mli +++ b/src/util/prioQueue.mli @@ -36,5 +36,6 @@ module Make(K: OrderedType)(P: OrderedType): sig val adjust: (P.t -> P.t) -> K.t -> t -> t (* runs in O(log n) *) - + + val find_opt: K.t -> t -> P.t option end From bd0c29a4fe29f67159331765b11bcf447640058d Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 28 Aug 2018 11:10:58 -0400 Subject: [PATCH 075/118] do not print trivial instantiations --- src/prover/instGen.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml index 1fa0ee04..7e85baf1 100644 --- a/src/prover/instGen.ml +++ b/src/prover/instGen.ml @@ -343,7 +343,7 @@ let generate_instances stratify axioms rep_terms egraph = subst_maps in let subst_maps = measure_call "InstGen.subst_maps" subst_maps () in - let _ = if Debug.is_debug 1 then + let _ = if Debug.is_debug 1 && subst_maps <> [IdMap.empty] then begin print_endline "--------------------"; print_endline (string_of_form f); From 57c69af182342e0f1ef2b0768a964cf490d7c889 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 3 Sep 2018 23:04:10 -0400 Subject: [PATCH 076/118] add bloom filter data structure for e-matching --- src/util/bloomFilter.ml | 53 ++++++++++++++++++++++++++++++++++++++++ src/util/bloomFilter.mli | 29 ++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/util/bloomFilter.ml create mode 100644 src/util/bloomFilter.mli diff --git a/src/util/bloomFilter.ml b/src/util/bloomFilter.ml new file mode 100644 index 00000000..06cd8451 --- /dev/null +++ b/src/util/bloomFilter.ml @@ -0,0 +1,53 @@ +(** Bloom Filters for Approximate Sets. + + The implementation is geared towards efficiency at the risk of + potentially larger false positive rates for membership queries. It + uses fixed sized bit arrays that fit into OCaml's int values. So the + implementation is platform-dependant. + +*) + +(** The type of approximate sets. *) +type 'a t = int + +(** Size of bit array. *) +let max = Sys.int_size + +(** Compute hash of [x]. *) +let hash (x: 'a) = + Hashtbl.hash x + +(** Compute hash of [x] with added 'salt' [salt] *) +let hash_with_salt (x: 'a) (salt: int): int = + hash (salt + hash x) + +(** The empty set. *) +let empty: 'a t = 0 + +(** Compute index of [x] into bit array. *) +let get_index x = + let rec get_index h salt = + if h = max then get_index (hash_with_salt x salt) (salt + 1) else h + in + get_index (hash x) 1 + +(** Create the singleton approximate from element [x]. *) +let singleton x = + let index = get_index x in + 1 lsl index + +(** Insert [x] into approximate set [xs]. *) +let add (x: 'a) (xs: 'a t) = + let index = get_index x in + xs lor (1 lsl index) + +(** Compute intersection of approximate sets [xs] and [ys]. *) +let inter (xs: 'a t) (ys: 'a t) = xs land ys + +(** Compute union of approximate sets [xs] and [ys]. *) +let union (xs: 'a t) (ys: 'a t) = xs lor ys + +(** Check whether [x] is contained in approximate set [xs]. *) +let mem (x: 'a) (xs: 'a t): bool = + let index = get_index x in + xs land (1 lsl index) <> 0 diff --git a/src/util/bloomFilter.mli b/src/util/bloomFilter.mli new file mode 100644 index 00000000..2d45fcfa --- /dev/null +++ b/src/util/bloomFilter.mli @@ -0,0 +1,29 @@ +(** Bloom Filters for Approximate Sets. + + The implementation is geared towards efficiency at the risk of + potentially larger false positive rates for membership queries. It + uses fixed sized bit array that fit into OCaml's int values. So the + implementation is platform-dependant. + +*) + +(** The type of approximate sets. *) +type 'a t + +(** The empty set. *) +val empty: 'a t + +(** Create the singleton approximate set from given element. *) +val singleton: 'a -> 'a t + +(** Insert element into an approximate set. *) +val add: 'a -> 'a t -> 'a t + +(** Compute intersection of two approximate sets. *) +val inter: 'a t -> 'a t -> 'a t + +(** Compute union of two approximate sets. *) +val union: 'a t -> 'a t -> 'a t + +(** Check whether elemet is contained in an approximate set. *) +val mem: 'a -> 'a t -> bool From 7a5c5f3dd906d7299e3d65f8053900a3df0f4f3d Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 3 Sep 2018 23:04:37 -0400 Subject: [PATCH 077/118] switch to new e-matching procedure --- src/formulas/grass.ml | 43 +-- src/formulas/grassUtil.ml | 4 + src/prover/congruenceClosure.ml | 87 +++++- src/prover/eMatching.ml | 480 +++++++++++++++++++++++++++----- src/prover/instGen.ml | 2 +- src/prover/prover.ml | 14 +- src/prover/reduction.ml | 9 +- src/util/prioQueue.ml | 23 +- src/util/prioQueue.mli | 6 + src/util/util.ml | 29 +- 10 files changed, 568 insertions(+), 129 deletions(-) diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index 08fff728..a3dcebd2 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -469,31 +469,32 @@ let rec pr_form ppf = function | Atom (t, a) -> fprintf ppf "@[(%a%a)@]" pr_term t pr_annot a and pr_form_paran ppf f = fprintf ppf "(%a)" pr_form f - + +and pr_filter ppf fs = + let ids = + List.fold_right + (fun f ids -> match f with + | FilterSymbolNotOccurs sym -> + string_of_symbol sym :: ids + | FilterReadNotOccurs (name, _) -> + name :: ids + | FilterNotNull -> + string_of_symbol Null :: ids + | _ -> + ids) + fs [] + in + match ids with + | [] -> () + | _ -> fprintf ppf "@ without@ %s" (String.concat ", " ids) + + and pr_annot ppf a = let gen = extract_gens a in let name = extract_name a in let pos = extract_src_pos a in let lbl = extract_label a in let pat = extract_patterns a in - let pr_filter ppf fs = - let ids = - List.fold_right - (fun f ids -> match f with - | FilterSymbolNotOccurs sym -> - string_of_symbol sym :: ids - | FilterReadNotOccurs (name, _) -> - name :: ids - | FilterNotNull -> - string_of_symbol Null :: ids - | _ -> - ids) - fs [] - in - match ids with - | [] -> () - | _ -> fprintf ppf "@ without@ %s" (String.concat ", " ids) - in let rec pr_match_list ppf = function | [] -> () | [(m, f)] -> @@ -573,10 +574,10 @@ let string_of_arity arity = (** Print term [t] to out channel [out_chan]. *) -let print_term out_ch t = fprintf (formatter_of_out_channel out_ch) "%a@?" pr_term t +let print_term out_ch t = print_of_format pr_term t out_ch (** Print formula [f] to out channel [out_chan]. *) -let print_form out_ch f = fprintf (formatter_of_out_channel out_ch) "%a@?" pr_form f +let print_form out_ch f = print_of_format pr_form f out_ch let print_forms ch fs = List.iter (fun f -> print_form ch f; output_string ch "\n") fs diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index eb24fa27..7dcf06c4 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -551,6 +551,10 @@ let filter_annotations fn f = Binder (b, vs, f1, List.filter fn ann) in fa f +(** Remove all annotations from formula [f]. *) +let strip_annotations f = + filter_annotations (fun _ -> false) f + (** Remove all comments from formula [f]. *) let strip_comments f = filter_annotations diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 2482c287..cee41433 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -6,7 +6,7 @@ open GrassUtil module rec Node : sig type t = - < get_sym: symbol; + < get_sym: sorted_symbol; get_args: t list; get_arity: int; set_ccparent: NodeSet.t -> unit; @@ -14,6 +14,8 @@ module rec Node : sig get_ccparent: NodeSet.t; get_parent: t option; set_parent: t -> unit; + get_funs: sorted_symbol BloomFilter.t; + set_funs: sorted_symbol BloomFilter.t -> unit; find: t; union: t -> unit; ccpar: NodeSet.t; @@ -22,11 +24,11 @@ module rec Node : sig > val compare: t -> t -> int - val create: symbol -> t list -> t + val create: sorted_symbol -> t list -> t end = struct type t = - < get_sym: symbol; + < get_sym: sorted_symbol; get_args: t list; get_arity: int; set_ccparent: NodeSet.t -> unit; @@ -34,6 +36,8 @@ module rec Node : sig get_ccparent: NodeSet.t; get_parent: t option; set_parent: t -> unit; + get_funs: sorted_symbol BloomFilter.t; + set_funs: sorted_symbol BloomFilter.t -> unit; find: t; union: t -> unit; ccpar: NodeSet.t; @@ -45,7 +49,7 @@ module rec Node : sig class node = fun - (sym: symbol) + (sym: sorted_symbol) (args: t list) -> object (self) method get_sym = sym @@ -61,8 +65,11 @@ module rec Node : sig method get_ccparent = ccparent val mutable parent: node option = None + method get_parent: node option = parent + method set_parent (n: node) = parent <- Some n + method find: node = match parent with | None -> (self :> node) | Some n -> @@ -72,19 +79,31 @@ module rec Node : sig p end + val mutable funs: sorted_symbol BloomFilter.t = + match args with + | [] -> BloomFilter.empty + | _ -> BloomFilter.singleton sym + + method get_funs: sorted_symbol BloomFilter.t = funs + method set_funs symbols = funs <- symbols + method union (that: node) = let n1 = self#find in let n2 = that#find in - n1#set_parent n2; - n2#set_ccparent (NodeSet.union n1#get_ccparent n2#get_ccparent); - n1#set_ccparent NodeSet.empty + let n1, n2 = + if n1#get_arity <> 0 && n2#get_arity = 0 + then n1, n2 + else n2, n1 + in + n1#set_parent n2; + n2#set_ccparent (NodeSet.union n1#get_ccparent n2#get_ccparent); + n1#set_ccparent NodeSet.empty; + n2#set_funs (BloomFilter.union n1#get_funs n2#get_funs) method ccpar: NodeSet.t = (self#find)#get_ccparent method congruent (that: node) = self#get_sym = that#get_sym - && - self#get_arity = that#get_arity && List.for_all2 (fun a b -> a#find = b#find) self#get_args that#get_args @@ -113,7 +132,19 @@ module rec Node : sig let create sym terms: t = new node sym terms end and NodeSet: Set.S with type elt = Node.t = Set.Make(Node) - + + +module NodeListSet = Set.Make(struct + type t = Node.t list + let compare = compare + end) + +module EGraph = Map.Make(struct + type t = Node.t * sorted_symbol + let compare = compare + end) + + class dag = fun (terms: TermSet.t) -> let table1 = Hashtbl.create 53 in let table2 = Hashtbl.create 53 in @@ -132,9 +163,9 @@ class dag = fun (terms: TermSet.t) -> (*print_endline ("CC adding: " ^ (string_of_term t));*) match t with | Var (v, _) -> failwith "CC: term not ground" (* create_and_add var (FreeSym v) []*) - | App (sym, args, _) as appl -> + | App (_, args, _) as appl -> let node_args = List.map convert_term args in - let new_node = create_and_add appl sym node_args in + let new_node = create_and_add appl (sorted_symbol_of appl |> Opt.get) node_args in List.iter (fun n -> n#add_ccparent new_node) node_args; new_node in @@ -215,6 +246,23 @@ class dag = fun (terms: TermSet.t) -> ) nodes; Hashtbl.fold (fun _ cc acc -> cc::acc) node_to_cc [] + method get_egraph = + let egraph = + Hashtbl.fold (fun _ n egraph -> + let n_rep = n#find in + let arg_reps = + List.map (fun n -> n#find) n#get_args + in + let other_args_reps = + EGraph.find_opt (n_rep, n#get_sym) egraph |> + Opt.get_or_else NodeListSet.empty + in + EGraph.add (n_rep, n#get_sym) (NodeListSet.add arg_reps other_args_reps) egraph) + nodes EGraph.empty + in + egraph + + (* Returns a function that tests if two terms must be different *) method get_conflicts = let repr = self#get_term in @@ -399,17 +447,28 @@ let create () : dag = let rep_of_term cc_graph t = (cc_graph#get_node t)#find let term_of_rep cc_graph n = cc_graph#get_term n + +let funs_of_rep ccgraph n = n#get_funs + +let get_egraph cc_graph = cc_graph#get_egraph let get_classes cc_graph = cc_graph#get_cc let congruence_classes gts fs = create () |> add_terms (ground_terms_acc ~include_atoms:true gts (mk_and fs)) |> - add_conjuncts fs |> - get_classes + add_conjuncts fs let class_of t classes = List.find (List.mem t) classes +let print_classes cc_graph = + ignore + (List.fold_left (fun num cl -> + print_endline ("Class " ^ string_of_int num ^ ": " ^ (string_of_sort (sort_of (List.hd cl)))); + List.iter (fun t -> print_endline (" " ^ (string_of_term t))) cl; + print_newline (); + num + 1) 1 (List.sort compare (cc_graph#get_cc))) + let restrict_classes classes ts = List.filter (fun cc -> List.exists (fun t -> TermSet.mem t ts) cc) classes diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index 686a6876..69fb72e9 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -13,35 +13,90 @@ open GrassUtil module CC = CongruenceClosure (** E-matching code trees *) -type ematch_code = - | Init of sorted_symbol * int * ematch_code - | Bind of int * sorted_symbol * int * ematch_code - | Check of int * term * ematch_code - | Compare of int * int * ematch_code - | Choose of ematch_code list - | Yield of int IdMap.t * form - | ChooseApp of int * ematch_code * CC.Node.t list list +type 'a ematch_code = + | Init of TermSet.t * sorted_symbol * int * 'a ematch_code + | Bind of int * sorted_symbol * int * 'a ematch_code + | Check of int * term * 'a ematch_code + | Compare of int * int * 'a ematch_code + | Choose of 'a ematch_code list + | Yield of int IdMap.t * 'a list + | Prune of int * sorted_symbol * 'a ematch_code + | Filter of (int IdMap.t * filter list) TermMap.t * 'a ematch_code + | ChooseApp of int * 'a ematch_code * CC.Node.t list list +(** Pretty printing of E-matching code trees *) +open Format + +let pr_var_binding ppf (x, i) = + fprintf ppf "%a -> %d" pr_ident x i + +let pr_ematch_code pr_inst ppf = + let rec pr_ematch_code ppf = function + | Choose cs -> + fprintf ppf "@<-3>%s@ %a" "choose" pr_choose cs + | Init (ts, sym, i, next) -> + fprintf ppf "init(@[%a,@ %d@])@\n%a" pr_term_list (TermSet.elements ts) i pr_ematch_code next + | Bind (i, sym, o, next) -> + fprintf ppf "bind(@[%d,@ %a,@ %d@])@\n%a" i pr_sym (fst sym) o pr_ematch_code next + | Check (i, t, next) -> + fprintf ppf "check(@[%d,@ %a@])@\n%a" i pr_term t pr_ematch_code next + | Compare (i, j, next) -> + fprintf ppf "compare(@[%d,@ %d@])@\n%a" i j pr_ematch_code next + | Yield (vs, fs) -> + fprintf ppf "@[<2>yield(@[[%a]@]) ->@\n%a@]" (pr_list_comma pr_var_binding) (IdMap.bindings vs) + (pr_list 0 + (fun ppf _ -> fprintf ppf ",@\n") + (fun i ppf f -> fprintf ppf "@[<2>%2d:@ %a@]" i pr_inst f)) fs + | Prune (i, sym, next) -> + fprintf ppf "prune(@[%d,@ %a@])@\n%a" i pr_sym (fst sym) pr_ematch_code next + | Filter (filters, next) -> + fprintf ppf "filter(@[%a@])@\n%a" + pr_term_list (filters |> TermMap.bindings |> List.map fst) + (*(pr_list_comma pr_var_binding) (IdMap.bindings vs) + pr_term t + pr_filter filters*) + pr_ematch_code next + | ChooseApp (i, next, _) -> + fprintf ppf "chooseApp(@[%d@])@\n%a" i pr_ematch_code next + + and pr_choose ppf = function + | [] -> () + | [c] -> fprintf ppf "{@[<1>@\n%a@]@\n}" pr_ematch_code c + | c :: cs -> fprintf ppf "{@[<1>@\n%a@]@\n}@ @ @<-3>%s@ %a" pr_ematch_code c "or" pr_choose cs + in pr_ematch_code ppf + +let print_ematch_code string_of_inst out_ch code = + print_of_format (pr_ematch_code string_of_inst) code out_ch + +(** A few utility functions *) + +(** Get the continuation of the first command *) let continuation = function - | Init (_, _, c) -> c - | Bind (_, _, _, c) -> c - | Check (_, _, c) -> c - | Compare (_, _, c) -> c + | Init (_, _, _, next) + | Bind (_, _, _, next) + | Check (_, _, next) + | Compare (_, _, next) + | Filter (_, next) -> next | _ -> failwith "illegal argument to continuation" +(** Get the maximum register in code tree c, yields -1 if c uses no registers *) let max_reg c = let rec mr m = function - | Init (_, i, c) + | Init (_, _, i, c) | Check (i, _, c) | ChooseApp (i, c, _) -> mr (max m i) c - | Bind (i, _, o, c) - | Compare (i, o, c) -> mr (max (max m i) o) c + | Filter (_, c) + | Prune (_, _, c) -> mr m c + | Bind (i, _, j, c) + | Compare (i, j, c) -> mr (max (max m i) j) c | Choose cs -> List.fold_left mr m cs - | Yield _ -> m + | Yield (vs, _) -> IdMap.fold (fun _ -> max) vs m in - mr 0 c + mr (-1) c +(** Make a choice tree out of trees c1 and + * Avoids the creation of nested choice nodes. *) let mk_choose c1 c2 = match c1, c2 with | Choose cs1, Choose cs2 -> Choose (cs1 @ cs2) @@ -58,6 +113,9 @@ module WQ = PrioQueue.Make (struct type t = term let compare t1 t2 = + (* Rational for the following definition: + * process entries i -> x and i -> t where t is ground before entries i -> f(t_1, ..., t_n) + * in order to delay the creation of branching nodes. *) if is_ground_term t1 && not @@ is_ground_term t2 then -1 else match t1, t2 with | Var _, App _ -> -1 @@ -72,92 +130,172 @@ let compile patterns = (fun (w', o') arg -> WQ.insert o' arg w', o' + 1) (w, o) args in + let prune args o next = + let code, _ = + List.fold_left + (fun (next, o) -> function + | App (sym, _, _) as t when not @@ is_ground_term t -> + Prune (o, sorted_symbol_of t |> Opt.get, next), o + 1 + | _ -> next, o + 1) + (next, o) args + in + code + in + (* Compile a (multi-)pattern into a code tree. + * f: info about current term of the multi-pattern that is being processed. + * pattern: remaining terms of the multi-pattern that still need to be processed. + * w: work queue of subterms of [f] that still need to be processed. + * v: accumulated map of variable to register bindings. + * o: number of next unused register. + *) let rec compile f pattern w v o = let rec c w v o = - if WQ.is_empty w then init f pattern v o else + if WQ.is_empty w then match f with + | (f, filters_opt) -> + let next = init f pattern v o in + Opt.fold (fun n -> function + | t, (_ :: _ as fs) -> + let filters = TermMap.singleton t (v, fs) in + Filter (filters, n) + | _ -> n) next + filters_opt + else let i, t, w' = WQ.extract_min w in match t with | Var (x, _) when not @@ IdMap.mem x v -> c w' (IdMap.add x i v) o | Var (x, _) -> - Compare (i, IdMap.find x v, c w' v o) + let next = c w' v o in + let j = IdMap.find x v in + if i = j then next + else Compare (i, j, next) | t when is_ground_term t -> Check (i, t, c w' v o) | App (sym, args, _) -> let w'', o' = add_args_to_queue w' o args in - Bind (i, sorted_symbol_of t |> Opt.get, o, c w'' v o') + let next = prune args o (c w'' v o') in + Bind (i, sorted_symbol_of t |> Opt.get, o, next) in c w v o and init f pattern v o = match pattern with - | App (_, args, _) as t :: pattern -> + | (App (_, args, _) as t, filters) :: pattern -> + let ts = TermSet.singleton t in let w, o' = add_args_to_queue WQ.empty o args in - Init (sorted_symbol_of t |> Opt.get, o, compile f pattern w v o') - | Var _ :: _ -> failwith "TODO" - | [] -> Yield (v, f) + let next = prune args o (compile (f, Some (t, filters)) pattern w v o') in + Init (ts, sorted_symbol_of t |> Opt.get, o, next) + | (Var _, _) :: _ -> failwith "TODO" + | [] -> + Yield (v, [f]) in let seq cs fchild = let rec s = function - | Init (sym, o, c) :: cs -> Init (sym, o, s cs) + | Init (ts, sym, o, c) :: cs -> Init (ts, sym, o, s cs) | Check (i, t, c) :: cs -> Check (i, t, s cs) | Compare (i, j, c) :: cs -> Compare (i, j, s cs) | Bind (i, sym, o, c) :: cs -> Bind (i, sym, o, s cs) + | Prune (i, fs, c) :: cs -> Prune (i, fs, s cs) + | Filter (filters, c) :: cs -> Filter (filters, s cs) | [] -> fchild | _ -> assert false in s (List.rev cs) in - let branch f pattern comps fchild w o = - seq comps (mk_choose (compile f pattern w IdMap.empty o) fchild) + let branch f pattern comps fchild w v o = + seq comps (mk_choose (compile f pattern w v o) fchild) in - let compatible pattern w = function - | Init (sym, o, _) -> - if WQ.is_empty w then + let check_if_queue_processed w v = + WQ.fold + (fun i t (v', fully_processed) -> + match t with + | App _ -> v', false + | Var (x, _) -> + IdMap.add x i v', + fully_processed && (IdMap.find_opt x v' |> Opt.get_or_else i |> (=) i) + ) + w (v, true) + in + (* Check whether [(f, pattern, w, v)] is compatible with the first node of [code]. + * If yes, return updated values [(code', w', v', f', pattern')]. *) + let compatible f pattern w v code = + match code with + | Init (ts, sym, o, next) -> + (* Has w been fully processed? *) + let v', fully_processed = check_if_queue_processed w v in + if fully_processed then match pattern with - | App (_, args, _) as t :: pattern' + | (App (_, args, _) as t, filters) :: pattern' when sorted_symbol_of t = Some sym -> let w', _ = add_args_to_queue WQ.empty o args in - Some w' + let f' = (fst f, Some (t, filters)) in + let code' = Init (TermSet.add t ts, sym, o, next) in + Some (code', w', v', f', pattern') | _ -> None else None + | Filter (filters, next) -> + (match f with + | inst, Some (t, fs) -> + let v' = + WQ.fold + (fun i t v -> match t with + | Var (x, _) -> IdMap.add x i v + | _ -> v) + w IdMap.empty + in + let filters' = TermMap.add t (v', fs) filters in + Some (Filter (filters', next), w, v, (inst, None), pattern) + | _, None -> None) | Check (i, t, _) -> (match WQ.find_opt i w with - | Some t' when t = t' -> Some (WQ.delete i w) + | Some t' when t = t' -> Some (code, WQ.delete i w, v, f, pattern) | _ -> None) | Compare (i, j, _) -> let ti_opt = WQ.find_opt i w in - let tj_opt = WQ.find_opt j w in - (match ti_opt, tj_opt with - | Some (Var _ as ti), Some tj when ti = tj -> - Some (WQ.delete j w) + (match ti_opt with + | Some (Var (x, _) as ti) -> + let v'_opt = match WQ.find_opt j w with + | Some tj when ti = tj -> Some (IdMap.add x j v) + | None when IdMap.find_opt x v = Some j -> Some v + | _ -> None + in + Opt.map (fun v' -> code, WQ.delete i w, v', f, pattern) v'_opt | _ -> None) | Bind (i, sym, o, _) -> (match WQ.find_opt i w with | Some (App (_, args, _) as t) when sorted_symbol_of t = Some sym -> let w', _ = add_args_to_queue w o args in - Some (WQ.delete i w') + Some (code, WQ.delete i w', v, f, pattern) | _ -> None) | _ -> None in - let rec insert_code f pattern w o comps incomps code = + let rec insert_code f pattern w v o comps incomps code = match code with | Choose cs -> if incomps = [] then - let code', score = insert_choose f pattern cs w o in + let code', score = insert_choose f pattern cs w v o in seq comps code', score + List.length comps - else branch f pattern comps (seq incomps code) w o, List.length comps - | Yield _ -> - branch f pattern comps (seq incomps code) w o, List.length comps + else branch f pattern comps (seq incomps code) w v o, List.length comps + | Yield (v1, fs) -> + let v2, fully_processed = check_if_queue_processed w v in + let v1_union_v2 = IdMap.union (fun x i j -> Some i) v1 v2 in + let v2_union_v1 = IdMap.union (fun x i j -> Some j) v1 v2 in + let filters = snd f |> Opt.map snd |> Opt.get_or_else [] in + if incomps = [] && pattern = [] && fully_processed && filters = [] && IdMap.equal (=) v1_union_v2 v2_union_v1 + then seq comps (Yield (v1_union_v2, fst f :: fs)), List.length comps + 1 + else branch f pattern comps (seq incomps code) w v o, List.length comps + | Prune _ -> + branch f pattern comps (seq incomps code) w v o, List.length comps | code -> - let code' = continuation code in - compatible pattern w code |> - Opt.map (fun w' -> insert_code f pattern w' o (code :: comps) incomps code') |> - Opt.lazy_get_or_else (fun () -> insert_code f pattern w o comps (code :: incomps) code') - and insert_choose f pattern cs w o = + let next = continuation code in + compatible f pattern w v code |> + Opt.map (fun (code', w', v', f', pattern') -> + insert_code f' pattern' w' v' o (code' :: comps) incomps next) |> + Opt.lazy_get_or_else (fun () -> insert_code f pattern w v o comps (code :: incomps) next) + and insert_choose f pattern cs w v o = let cs', _, best = List.fold_right (fun code (cs', cs, best) -> - match insert_code f pattern w o [] [] code with + match insert_code f pattern w v o [] [] code with | code', score when score > best -> code' :: cs, code :: cs, score | _ -> code :: cs', code :: cs, best @@ -165,47 +303,251 @@ let compile patterns = cs ([], [], 0) in if best = 0 - then Choose (compile f pattern w IdMap.empty o :: cs), 0 + then Choose (compile f pattern w v o :: cs), 0 else Choose cs', best in List.fold_left (fun code (f, pattern) -> - let code', _ = insert_code f pattern WQ.empty (max_reg code) [] [] code in + let code', _ = insert_code (f, None) pattern WQ.empty IdMap.empty (max_reg code + 1) [] [] code in code' ) (Choose []) patterns +(** Generate an E-matching code tree from the given set of axioms. *) +let generate_ematch_code_from_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms = + (* *) + let epr_axioms, axioms = + List.partition + (fun f -> IdSrtSet.is_empty (vars_in_fun_terms f) || not !Config.instantiate && not force) + axioms + in + (*print_endline "EPR:"; + List.iter print_endline (List.map string_of_form epr_axioms);*) + (*let _ = print_endline "Candidate axioms for instantiation:" in + let _ = print_forms stdout axioms in*) + let generate_pattern f = + let fvars0 = sorted_free_vars f in + let fvars = IdSrtSet.inter fvars0 (vars_in_fun_terms f) in + (* filter out stratified variables *) + let fvars, strat_vars = + let merge_map k a b = match (a,b) with + | (Some a, Some b) -> Some (a @ b) + | (Some c, None) | (None, Some c) -> Some c + | (None, None) -> None + in + let rec gen_tpe acc t = match t with + | App (_, ts, srt) -> + List.fold_left + (IdMap.merge merge_map) + IdMap.empty + (List.map (gen_tpe (srt :: acc)) ts) + | Var (id, _) -> IdMap.add id acc IdMap.empty + in + let gen_map = + TermSet.fold + (fun t acc -> IdMap.merge merge_map (gen_tpe [] t) acc) + (fun_terms_with_vars f) + IdMap.empty + in + if stratify then + IdSrtSet.partition + (fun (id, srt) -> + try + let generating = IdMap.find id gen_map in + not (List.for_all (TypeStrat.is_stratified srt) generating) + with Not_found -> + begin + Debug.warn (fun () -> "BUG in stratification: " ^ (string_of_ident id) ^ "\n"); + true + end + ) + fvars + else fvars, IdSrtSet.empty + in + let strat_var_ids = IdSrtSet.fold (fun (id, _) acc -> IdSet.add id acc) strat_vars IdSet.empty in + (* collect all terms in which free variables appear below function symbols *) + let fun_terms, fun_vars, strat_terms = + let rec tt (fun_terms, fun_vars, strat_terms) t = + match t with + | App (sym, _ :: _, srt) when srt <> Bool -> + let tvs = fv_term t in + if IdSet.is_empty tvs then + fun_terms, fun_vars, strat_terms + else if IdSet.is_empty (IdSet.inter strat_var_ids tvs) + then TermSet.add t fun_terms, IdSet.union tvs fun_vars, strat_terms + else fun_terms, fun_vars, TermSet.add t strat_terms + | App (_, ts, _) -> + List.fold_left tt (fun_terms, fun_vars, strat_terms) ts + | _ -> fun_terms, fun_vars, strat_terms + in fold_terms tt (TermSet.empty, IdSet.empty, TermSet.empty) f + in + let unmatched_vars = + IdSrtSet.fold (fun (id, _) acc -> + if IdSet.mem id fun_vars then acc + else IdSet.add id acc) fvars IdSet.empty + in + let fun_terms, fun_vars = + TermSet.fold (fun t (fun_terms, fun_vars) -> + let vs = fv_term t in + if IdSet.is_empty (IdSet.inter unmatched_vars vs) + then fun_terms, fun_vars + else TermSet.add t fun_terms, IdSet.union vs fun_vars) + strat_terms (fun_terms, fun_vars) + in + let strat_vars1 = + IdSrtSet.filter (fun (id, _) -> not (IdSet.mem id fun_vars)) strat_vars + in + (* extract patterns separately to obtain auxilary filters *) + let patterns = extract_patterns f in + let fun_terms_with_filters = + TermSet.fold (fun t acc -> + try (t, List.assoc t patterns) :: acc + with Not_found -> (t, []) :: acc) + fun_terms [] + in + let fun_terms_with_filters = + List.sort (fun (t1, _) (t2, _) -> + - (compare + (IdSet.cardinal (fv_term t1)) + (IdSet.cardinal (fv_term t2)))) + fun_terms_with_filters + in + let f = mk_forall (IdSrtSet.elements strat_vars1) f in + (f, fvars0, strat_vars1, unmatched_vars, fun_vars, fun_terms_with_filters), fun_terms_with_filters + in + let patterns = [] in + let patterns = List.fold_right (fun f patterns -> ((f, IdSrtSet.empty, IdSrtSet.empty, IdSet.empty, IdSet.empty, []), []) :: patterns) epr_axioms patterns in + let patterns = List.fold_right (fun f patterns -> generate_pattern f :: patterns) axioms patterns in + compile patterns, patterns + (** Run the given e-matching code tree. Yields a list of instantiated formulas. *) let run code ccgraph = - let rec run insts (regs: CC.Node.t array) = function - | Init (sym, o, next) :: stack -> - let apps = CC.get_apps ccgraph sym in - run insts regs (ChooseApp (o, next, apps) :: stack) + let egraph = CC.get_egraph ccgraph in + let get_apps sym = + CC.EGraph.fold + (fun (n, sym') n_apps apps -> + if sym = sym' + then CC.NodeListSet.union n_apps apps + else apps) + egraph CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + let get_apps_of_node n sym = + CC.EGraph.find_opt (n, sym) egraph |> + Opt.get_or_else CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + let regs = Array.make (max_reg code + 1) (CC.rep_of_term ccgraph mk_true_term) in + let insts = Hashtbl.create 1000 in + let rec run ts = function + | Init (ts, sym, o, next) :: stack -> + let apps = get_apps sym in + run ts (ChooseApp (o, next, apps) :: stack) | Bind (i, sym, o, next) :: stack -> - let apps = CC.get_apps_of_node ccgraph (regs.(i)) sym in - run insts regs (ChooseApp (o, next, apps) :: stack) + let apps = get_apps_of_node (regs.(i)) sym in + run ts (ChooseApp (o, next, apps) :: stack) | Check (i, t, next) :: stack -> - if CC.node_of_term ccgraph t = regs.(i) - then run insts regs (next :: stack) - else run insts regs stack + if CC.rep_of_term ccgraph t = regs.(i) + then run ts (next :: stack) + else run ts stack | Compare (i, j, next) :: stack -> if regs.(i) = regs.(j) - then run insts regs (next :: stack) - else run insts regs stack + then run ts (next :: stack) + else run ts stack | Choose cs :: stack -> - run insts regs (cs @ stack) - | Yield (vs, f) :: stack -> - let sm = IdMap.map (fun i -> CC.term_of_node ccgraph regs.(i)) vs in - run (subst sm f :: insts) regs stack + run ts (cs @ stack) + | Yield (vs, fs) :: stack -> + let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in + List.iter (fun f -> Hashtbl.add insts f sm) fs; + run ts stack + | Prune (i, sym, next) :: stack -> + let n = regs.(i) in + if BloomFilter.mem sym (CC.funs_of_rep ccgraph n) + then run ts (next :: stack) + else run ts stack + | Filter (filters, next) :: stack -> + let ts' = + TermSet.filter + (fun t -> + let t_filters = TermMap.find_opt t filters in + match t_filters with + | Some (vs, fs) -> + let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in + InstGen.filter_term fs (subst_term sm t) sm + | None -> true) + ts + in + if TermSet.is_empty ts' + then run ts stack + else run ts' (next :: stack) | ChooseApp (o, next, args :: apps) :: stack -> let _ = List.fold_left (fun i arg -> regs.(i) <- arg; i + 1) o args in - run insts regs (next :: ChooseApp (o, next, apps) :: stack) + run ts (next :: ChooseApp (o, next, apps) :: stack) | ChooseApp (_, _, []) :: stack -> - run insts regs stack + run ts stack | [] -> insts in - run [] (Array.make (max_reg code) (CC.node_of_term ccgraph mk_true_term) + run TermSet.empty [code] + +let instantiate_axioms_from_code patterns code ccgraph = + let _ = + if Debug.is_debug 1 then CC.print_classes ccgraph + in + let instances = run code ccgraph in + let get_form (f, _, _, _, _, _) = f in + let print_debug ((f, fvars, strat_vars, unmatched_vars, fun_vars, fun_terms_with_filters), subst_maps) = + print_endline "--------------------"; + print_endline (string_of_form f); + print_string "all vars: "; + flush stdout; + print_list stdout pr_ident (List.map fst (IdSrtSet.elements fvars)); + print_newline (); + print_string "strat vars: "; + print_list stdout pr_ident (List.map fst (IdSrtSet.elements strat_vars)); + print_newline (); + print_string "unmatched vars: "; + print_list stdout pr_ident (IdSet.elements unmatched_vars); + print_newline (); + print_string "inst vars: "; + print_list stdout pr_ident (IdSet.elements fun_vars); + print_newline (); + print_string "fun terms: "; + print_list stdout pr_term (List.map fst fun_terms_with_filters); + print_newline (); + Printf.printf "# of insts: %d\n" (List.length subst_maps); + print_endline "subst_maps:"; + List.iter print_subst_map subst_maps; + print_endline "--------------------"; + in + let grouped_instances = + List.rev_map + (fun (f, _) -> + let sms = Hashtbl.find_all instances f in + (f, sms)) + patterns + in + let _ = + if Debug.is_debug 1 then begin + grouped_instances |> + List.rev_map (fun (f, sms) -> (f, List.filter ((<>) IdMap.empty) sms)) |> + List.iter print_debug + end + in + grouped_instances |> + List.fold_left + (fun instances (f_aux, sms) -> + List.fold_left (fun instances sm -> + subst sm (get_form f_aux) :: instances) + instances sms) [] + +let instantiate_axioms_from_code patterns code ccgraph = + measure_call "EMatching.instantiate_axioms" (instantiate_axioms_from_code patterns code) ccgraph + +let instantiate_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms ccgraph = + let code, patterns = generate_ematch_code_from_axioms ~force:force ~stratify:stratify axioms in + instantiate_axioms_from_code patterns code ccgraph + diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml index 7e85baf1..ea1c0695 100644 --- a/src/prover/instGen.ml +++ b/src/prover/instGen.ml @@ -376,7 +376,7 @@ let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) axioms if not !Config.instantiate && not force then axioms else (* remove theory atoms from congruence classes *) let filter_term t = - sort_of t <> Bool || + true || sort_of t <> Bool || Opt.get_or_else false (Opt.map (((=) Frame) ||| diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 75fec7e3..3f3a2b0d 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -191,7 +191,9 @@ let instantiate_and_prove session fs = let round1 fs_inst gts_inst cc_graph = let equations = List.filter (fun f -> is_horn false [f]) fs_inst in let ground_fs = List.filter is_ground fs_inst in - let eqs = instantiate_with_terms equations (CongruenceClosure.get_classes cc_graph) in + let code, patterns = EMatching.generate_ematch_code_from_axioms equations in + let eqs = EMatching.instantiate_axioms_from_code patterns code cc_graph in + (*let eqs = instantiate_with_terms equations (CongruenceClosure.get_classes cc_graph) in*) let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in let fs, gts1 = generate_adt_terms fs gts1 in let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in @@ -220,6 +222,13 @@ let instantiate_and_prove session fs = gts_a TermSet.empty in let fs1 = linearize fs1 in + let code, patterns = EMatching.generate_ematch_code_from_axioms fs1 in + (*let _ = + print_endline "E-matching code:"; + EMatching.print_ematch_code + (fun ppf (f, _, _, _, _, _) -> pr_form ppf f) stdout code; + print_newline () + in*) let rec saturate i fs_inst gts_inst0 cc_graph = (*Printf.printf "Saturate iteration %d\n" i; flush stdout;*) let gts_inst = TermSet.union gts_inst0 core_terms in @@ -244,7 +253,8 @@ let instantiate_and_prove session fs = measure_call "cc_gen" (fun cc_graph -> cc_graph |> CongruenceClosure.add_terms gts_inst) |> CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) in - let fs_inst = instantiate_with_terms fs1 (CongruenceClosure.get_classes cc_graph) in + (*let fs_inst = instantiate_with_terms fs1 (CongruenceClosure.get_classes cc_graph) in*) + let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in saturate (i + 1) fs_inst gts_inst cc_graph in let saturate i fs_inst gts_inst0 = measure_call "saturate" (saturate i fs_inst gts_inst0) in diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index f389cb29..4a246577 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -426,9 +426,9 @@ let add_read_write_axioms fs = | gs -> gs in let gts = generate_terms generators gts in - let classes = CongruenceClosure.congruence_classes gts fs in + let ccgraph = CongruenceClosure.congruence_classes gts fs in (* CAUTION: not forcing the instantiation here would yield an inconsistency with the read/write axioms *) - let null_ax1 = instantiate_with_terms ~force:true null_ax classes in + let null_ax1 = EMatching.instantiate_axioms ~force:true null_ax ccgraph in let fs1 = null_ax1 @ fs in let gts = TermSet.union (ground_terms ~include_atoms:true (mk_and null_ax1)) gts in (* propagate read terms *) @@ -466,7 +466,10 @@ let add_reach_axioms fs = | _ -> struct_sorts) (sorts (mk_and fs)) SortSet.empty in - let classes = CongruenceClosure.congruence_classes TermSet.empty fs in + let classes = + CongruenceClosure.congruence_classes TermSet.empty fs |> + CongruenceClosure.get_classes + in let axioms = SortSet.fold (fun srt axioms -> Axioms.reach_axioms classes srt @ Axioms.reach_write_axioms srt @ axioms) diff --git a/src/util/prioQueue.ml b/src/util/prioQueue.ml index 6a956b1e..720486f8 100644 --- a/src/util/prioQueue.ml +++ b/src/util/prioQueue.ml @@ -159,12 +159,6 @@ module Make(K: OrderedType)(P: OrderedType) = struct let k, p, l, m = get_winner t in k, p, second_best l m - let rec fold fn init = function - | Leaf -> init - | Node (_, k, p, l, _, r) -> - let rinit = fold fn (fn init k p) l in - fold fn rinit r - let rec find_opt k = function | Leaf -> None | t -> @@ -176,5 +170,22 @@ module Make(K: OrderedType)(P: OrderedType) = struct if K.compare k (max_key l) <= 0 then find_opt k l else find_opt k r + + let rec fold fn t init = match t with + | Leaf -> init + | Node (_, k, p, l, _, r) -> + let rinit = fold fn l (fn k p init) in + fold fn r rinit + + let rec exists fn = function + | Leaf -> false + | Node (_, k, p, l, _, r) -> + fn k p || exists fn l || exists fn r + + let rec forall fn = function + | Leaf -> true + | Node (_, k, p, l, _, r) -> + fn k p && forall fn l && forall fn r + end diff --git a/src/util/prioQueue.mli b/src/util/prioQueue.mli index f7346291..e6bdc52b 100644 --- a/src/util/prioQueue.mli +++ b/src/util/prioQueue.mli @@ -38,4 +38,10 @@ module Make(K: OrderedType)(P: OrderedType): sig (* runs in O(log n) *) val find_opt: K.t -> t -> P.t option + + val fold: (K.t -> P.t -> 'a -> 'a) -> t -> 'a -> 'a + + val exists: (K.t -> P.t -> bool) -> t -> bool + + val forall: (K.t -> P.t -> bool) -> t -> bool end diff --git a/src/util/util.ml b/src/util/util.ml index 297c1c4c..e9d196ee 100644 --- a/src/util/util.ml +++ b/src/util/util.ml @@ -81,6 +81,8 @@ let generate_list (f : int -> 'a) (n : int) : 'a list = else mk_tl (n - 1) (f (n - 1) :: acc) in mk_tl n [] +(** Tail-recursive concatenation of lists *) +let rev_concat lists = List.fold_left (List.fold_left (fun acc f -> f :: acc)) [] lists (** Composition of [List.map] and [List.filter] *) let filter_map p f xs = @@ -89,7 +91,6 @@ let filter_map p f xs = (** Composition of [List.filter] and [List.rev_map] *) let filter_rev_map p f xs = List.fold_left (fun ys x -> if p x then f x :: ys else ys) [] xs - (** Composition of [List.map] and [List.partition] *) let partition_map p f xs = @@ -117,8 +118,11 @@ let rec find_map fn = function match fn x with | None -> find_map fn xs | v -> v - -let flat_map f ls = List.flatten (List.map f ls) + +let flat_map f ls = + ls |> + List.rev_map (fun l -> f l |> List.rev) |> + rev_concat let find_index elt ls = let rec traverse i lst = match lst with @@ -185,9 +189,6 @@ let unzip xys = in uz ([], []) xys -(** Tail-recursive concatenation of lists *) -let rev_concat lists = List.fold_left (List.fold_left (fun acc f -> f :: acc)) [] lists - let iteri fct lst = let rec iter idx lst = match lst with @@ -332,15 +333,16 @@ let rec remove_duplicates eq lst = match lst with open Format -let rec pr_list_comma pr_x ppf = function +let rec pr_list i pr_sep pr_x ppf = function | [] -> () - | [x] -> fprintf ppf "%a" pr_x x - | x :: xs -> fprintf ppf "%a,@ %a" pr_x x (pr_list_comma pr_x) xs + | [x] -> fprintf ppf "%a" (pr_x i) x + | x :: xs -> fprintf ppf "%a%a%a" (pr_x i) x pr_sep () (pr_list (i + 1) pr_sep pr_x) xs + +let rec pr_list_comma pr_x ppf = + pr_list 0 (fun ppf _ -> fprintf ppf ",@ ") (fun _ -> pr_x) ppf -let rec pr_list_nl pr_x ppf = function - | [] -> () - | [x] -> fprintf ppf "%a" pr_x x - | x :: xs -> fprintf ppf "%a@\n%a" pr_x x (pr_list_nl pr_x) xs +let rec pr_list_nl pr_x ppf = + pr_list 0 (fun ppf _ -> fprintf ppf "@\n") (fun _ -> pr_x) ppf let print_of_format pr x out_ch = fprintf (formatter_of_out_channel out_ch) "%a@?" pr x @@ -349,3 +351,4 @@ let print_of_format pr x out_ch = fprintf (formatter_of_out_channel out_ch) "%a@ let string_of_format pr t = pr str_formatter t; flush_str_formatter () let print_list out_ch pr xs = print_of_format (pr_list_comma pr) xs out_ch + From 5ac579e3d8b5fd1126ce43ef6ffacd9a2abe21a5 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 4 Sep 2018 02:25:00 -0400 Subject: [PATCH 078/118] fix in bloom filter --- src/util/bloomFilter.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/bloomFilter.ml b/src/util/bloomFilter.ml index 06cd8451..f1240576 100644 --- a/src/util/bloomFilter.ml +++ b/src/util/bloomFilter.ml @@ -27,9 +27,9 @@ let empty: 'a t = 0 (** Compute index of [x] into bit array. *) let get_index x = let rec get_index h salt = - if h = max then get_index (hash_with_salt x salt) (salt + 1) else h + if h = max then get_index (hash_with_salt x salt land max) (salt + 1) else h in - get_index (hash x) 1 + get_index (hash x land max) 1 (** Create the singleton approximate from element [x]. *) let singleton x = From f2464704170e41322eb938f55445fdbf843430da Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 5 Sep 2018 21:59:21 -0400 Subject: [PATCH 079/118] improvements to E-matching engine --- src/formulas/grass.ml | 4 + src/prover/eMatching.ml | 501 ++++++++++++++++++++++++++++------------ src/prover/prover.ml | 6 +- src/util/util.ml | 31 +++ 4 files changed, 385 insertions(+), 157 deletions(-) diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index a3dcebd2..472782b9 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -154,6 +154,10 @@ let rec sort_ofs = function | [] -> failwith "tried to extract sort from empty list of terms" | t :: ts -> sort_of t +let args_of = function + | Var _ -> [] + | App (_, args, _) -> args + type subst_map = term IdMap.t module TermSet = Set.Make(struct diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index 69fb72e9..27b01b78 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -1,6 +1,6 @@ (** E-Matching. - Implements a variant of the e-matching technique presented in this paper: + Implements a variant of the E-matching technique presented in this paper: Efficient E-Matching for SMT Solvers Leonardo de Moura and Nikolaj Bjorner, CADE 2007 @@ -12,17 +12,37 @@ open GrassUtil module CC = CongruenceClosure -(** E-matching code trees *) + +(** Top-level symbol of current pattern to be matched. + * If we are matching a trivial pattern consisting only of + * a variable, we only record its sort, hence the Either type. + *) +type esymbol = (sort, sorted_symbol) Either.t + +(** Auxiliary state of the E-matching abstract machine. + * Stores the currently active patterns. + * Used for per-multi-pattern pruning and filtering. + *) +type state = TermSet.t + +(** Transition between two consecutive trigger terms in a multi-pattern. + * Used for updating/initializing the auxiliary state. + *) +type step = (TermSet.t, (term * term) list) Either.t + + +(** E-matching code trees for instantiating templates of some type 'a *) type 'a ematch_code = - | Init of TermSet.t * sorted_symbol * int * 'a ematch_code + | Init of step * esymbol * int * 'a ematch_code | Bind of int * sorted_symbol * int * 'a ematch_code | Check of int * term * 'a ematch_code | Compare of int * int * 'a ematch_code | Choose of 'a ematch_code list - | Yield of int IdMap.t * 'a list + | Yield of int IdMap.t * ('a list) TermMap.t * 'a list | Prune of int * sorted_symbol * 'a ematch_code | Filter of (int IdMap.t * filter list) TermMap.t * 'a ematch_code - | ChooseApp of int * 'a ematch_code * CC.Node.t list list + | Backtrack of state * 'a ematch_code + | ChooseTerm of state * int * 'a ematch_code * CC.Node.t list list (** Pretty printing of E-matching code trees *) open Format @@ -31,22 +51,34 @@ let pr_var_binding ppf (x, i) = fprintf ppf "%a -> %d" pr_ident x i let pr_ematch_code pr_inst ppf = + let pr_step ppf = function + | Either.First ts -> pr_term_list ppf (TermSet.elements ts) + | Either.Second tts -> pr_list_comma (fun ppf (t1, t2) -> fprintf ppf "%a -> %a" pr_term t1 pr_term t2) ppf tts + in let rec pr_ematch_code ppf = function | Choose cs -> fprintf ppf "@<-3>%s@ %a" "choose" pr_choose cs - | Init (ts, sym, i, next) -> - fprintf ppf "init(@[%a,@ %d@])@\n%a" pr_term_list (TermSet.elements ts) i pr_ematch_code next + | Init (step, sym, i, next) -> + fprintf ppf "init(@[%a,@ %d@])@\n%a" pr_step step i pr_ematch_code next | Bind (i, sym, o, next) -> - fprintf ppf "bind(@[%d,@ %a,@ %d@])@\n%a" i pr_sym (fst sym) o pr_ematch_code next + fprintf ppf "bind(@[%d,@ (%a, %d),@ %d@])@\n%a" i pr_sym (fst sym) (List.length (fst @@ snd sym)) o pr_ematch_code next | Check (i, t, next) -> fprintf ppf "check(@[%d,@ %a@])@\n%a" i pr_term t pr_ematch_code next | Compare (i, j, next) -> fprintf ppf "compare(@[%d,@ %d@])@\n%a" i j pr_ematch_code next - | Yield (vs, fs) -> - fprintf ppf "@[<2>yield(@[[%a]@]) ->@\n%a@]" (pr_list_comma pr_var_binding) (IdMap.bindings vs) + | Yield (vs, fs, gs) -> + let pr_numbered_forms ppf fs = (pr_list 0 (fun ppf _ -> fprintf ppf ",@\n") - (fun i ppf f -> fprintf ppf "@[<2>%2d:@ %a@]" i pr_inst f)) fs + (fun i ppf f -> fprintf ppf "@[<2>%2d:@ @[%a@]@]" i pr_inst f)) ppf fs + in + fprintf ppf "@[<2>yield(@[[%a]@]) ->@\n%a@\n%a@]" + (pr_list_comma pr_var_binding) (IdMap.bindings vs) + (pr_list 0 + (fun ppf _ -> fprintf ppf ",@\n") + (fun _ ppf (t, fs) -> fprintf ppf "@[<2>%a:@\n%a@]" + pr_term t pr_numbered_forms fs)) (TermMap.bindings fs) + pr_numbered_forms gs | Prune (i, sym, next) -> fprintf ppf "prune(@[%d,@ %a@])@\n%a" i pr_sym (fst sym) pr_ematch_code next | Filter (filters, next) -> @@ -56,8 +88,10 @@ let pr_ematch_code pr_inst ppf = pr_term t pr_filter filters*) pr_ematch_code next - | ChooseApp (i, next, _) -> - fprintf ppf "chooseApp(@[%d@])@\n%a" i pr_ematch_code next + | Backtrack (ts, next) -> + fprintf ppf "backtrack(@[[%a]@])@\n@%a" pr_term_list (TermSet.elements ts) pr_ematch_code next + | ChooseTerm (ts, i, next, _) -> + fprintf ppf "choose_term(@[%d@])@\n%a" i pr_ematch_code next and pr_choose ppf = function | [] -> () @@ -76,7 +110,8 @@ let continuation = function | Bind (_, _, _, next) | Check (_, _, next) | Compare (_, _, next) - | Filter (_, next) -> next + | Filter (_, next) + | Backtrack (_, next) -> next | _ -> failwith "illegal argument to continuation" (** Get the maximum register in code tree c, yields -1 if c uses no registers *) @@ -84,14 +119,15 @@ let max_reg c = let rec mr m = function | Init (_, _, i, c) | Check (i, _, c) - | ChooseApp (i, c, _) -> mr (max m i) c + | ChooseTerm (_, i, c, _) -> mr (max m i) c | Filter (_, c) - | Prune (_, _, c) -> mr m c + | Prune (_, _, c) + | Backtrack (_, c) -> mr m c | Bind (i, _, j, c) | Compare (i, j, c) -> mr (max (max m i) j) c | Choose cs -> List.fold_left mr m cs - | Yield (vs, _) -> IdMap.fold (fun _ -> max) vs m + | Yield (vs, _, _) -> IdMap.fold (fun _ -> max) vs m in mr (-1) c @@ -114,8 +150,8 @@ module WQ = PrioQueue.Make type t = term let compare t1 t2 = (* Rational for the following definition: - * process entries i -> x and i -> t where t is ground before entries i -> f(t_1, ..., t_n) - * in order to delay the creation of branching nodes. *) + * Entries i -> x and i -> t where t is ground should be processed before entries i -> f(t_1, ..., t_n). + * This is to delay the creation of branching nodes that introduce backtracking points. *) if is_ground_term t1 && not @@ is_ground_term t2 then -1 else match t1, t2 with | Var _, App _ -> -1 @@ -123,8 +159,57 @@ module WQ = PrioQueue.Make | _ -> compare t1 t2 end) -(** Compile the list of patterns [patterns] into an ematching code tree *) -let compile patterns = +(** Triggers of multi-patterns. *) +type trigger = term * filter list + +(** A multi-pattern for instantiating templates of generic type 'a. *) +type 'a pattern = 'a * trigger list + +(** Compile the list of multi-patterns [patterns] into an ematching code tree. *) +let compile (patterns: ('a pattern) list): 'a ematch_code = + (* do some heuristic analysis on the patterns so as to optimize the order + * of their triggers to faciliate sharing across patterns in the code tree *) + (* First count how often each trigger term occurs across all patterns *) + let occurs = + List.fold_left + (fun acc (_, pattern) -> + List.fold_left + (fun acc -> function + | App _ as t, _ -> + let curr = TermMap.find_opt t acc |> Opt.get_or_else 0 in + TermMap.add t (curr + 1) acc + | _ -> acc) + acc pattern) + TermMap.empty + patterns + in + let patterns = + let compare_triggers (t1, _) (t2, _) = + let get_count t = + TermMap.find_opt t occurs |> + Opt.get_or_else 0 + in + (* first prioritize terms that bind more variables as this helps to avoid back-tracking *) + let c1 = + - (compare + (IdSet.cardinal (fv_term t1)) + (IdSet.cardinal (fv_term t2))) + in + if c1 = 0 then + (* next, prioritize terms that occur more often across many patterns *) + let c2 = compare (get_count t2) (get_count t1) in + if c2 = 0 then compare t1 t2 + else c2 + else c1 + in + List.map (fun (tpl, pattern) -> + (tpl, List.sort compare_triggers pattern)) + patterns + in + let esymbol_of = function + | Var (_, srt) -> Either.first srt + | t -> Either.second (sorted_symbol_of t |> Opt.get) + in let add_args_to_queue w o args = List.fold_left (fun (w', o') arg -> WQ.insert o' arg w', o' + 1) @@ -141,25 +226,23 @@ let compile patterns = in code in - (* Compile a (multi-)pattern into a code tree. + let update_yield_templates t_filters_opt fs gs template = + match t_filters_opt with + | Some (t, _) -> + let t_templates = TermMap.find_opt t fs |> Opt.get_or_else [] in + TermMap.add t (template :: t_templates) fs, gs + | None -> fs, template :: gs + in + (* Compile a multi-pattern into a code tree. * f: info about current term of the multi-pattern that is being processed. - * pattern: remaining terms of the multi-pattern that still need to be processed. + * pattern: remaining trigger terms of the multi-pattern that still need to be processed. * w: work queue of subterms of [f] that still need to be processed. * v: accumulated map of variable to register bindings. * o: number of next unused register. *) let rec compile f pattern w v o = let rec c w v o = - if WQ.is_empty w then match f with - | (f, filters_opt) -> - let next = init f pattern v o in - Opt.fold (fun n -> function - | t, (_ :: _ as fs) -> - let filters = TermMap.singleton t (v, fs) in - Filter (filters, n) - | _ -> n) next - filters_opt - else + if WQ.is_empty w then init f pattern v o else let i, t, w' = WQ.extract_min w in match t with | Var (x, _) when not @@ IdMap.mem x v -> @@ -177,16 +260,28 @@ let compile patterns = Bind (i, sorted_symbol_of t |> Opt.get, o, next) in c w v o - and init f pattern v o = - match pattern with - | (App (_, args, _) as t, filters) :: pattern -> - let ts = TermSet.singleton t in - let w, o' = add_args_to_queue WQ.empty o args in - let next = prune args o (compile (f, Some (t, filters)) pattern w v o') in - Init (ts, sorted_symbol_of t |> Opt.get, o, next) - | (Var _, _) :: _ -> failwith "TODO" - | [] -> - Yield (v, [f]) + and init f pattern v o : 'a ematch_code = + let template, t_filters_opt = f in + let code = + match pattern with + | [] -> + let fs, gs = update_yield_templates t_filters_opt TermMap.empty [] template in + Yield (v, fs, gs) + | (t', filters') :: pattern' -> + let args = args_of t' in + let w, o' = add_args_to_queue WQ.empty o args in + let next = prune args o (compile (template, Some (t', filters')) pattern' w v o') in + let ts = match t_filters_opt with + | Some (t, _) -> Either.second [t, t'] + | None -> Either.first @@ TermSet.singleton t' + in + Init (ts, esymbol_of t', o, next) + in + match t_filters_opt with + | Some (t, (_ :: _ as fs)) -> + let filters = TermMap.singleton t (v, fs) in + Filter (filters, code) + | _ -> code in let seq cs fchild = let rec s = function @@ -215,6 +310,10 @@ let compile patterns = ) w (v, true) in + let _print_wq w = + WQ.fold (fun i t _ -> Printf.printf "%d -> %s, " i (string_of_term t)) w (); + print_newline (); + in (* Check whether [(f, pattern, w, v)] is compatible with the first node of [code]. * If yes, return updated values [(code', w', v', f', pattern')]. *) let compatible f pattern w v code = @@ -222,13 +321,22 @@ let compile patterns = | Init (ts, sym, o, next) -> (* Has w been fully processed? *) let v', fully_processed = check_if_queue_processed w v in - if fully_processed then + if fully_processed then match pattern with - | (App (_, args, _) as t, filters) :: pattern' - when sorted_symbol_of t = Some sym -> - let w', _ = add_args_to_queue WQ.empty o args in - let f' = (fst f, Some (t, filters)) in - let code' = Init (TermSet.add t ts, sym, o, next) in + | (t', filters') :: pattern' when esymbol_of t' = sym -> + let w', _ = add_args_to_queue WQ.empty o (args_of t') in + let f' = (fst f, Some (t', filters')) in + let ts' = + ts |> + Either.value_map + (fun ts -> TermSet.add t' ts |> Either.first) + (fun tts -> + snd f |> + Opt.map (fun (t, _) -> + (t, t') :: tts |> Util.remove_duplicates (=) |> Either.second) |> + Opt.get_or_else (TermSet.singleton t' |> Either.first)) + in + let code' = Init (ts', sym, o, next) in Some (code', w', v', f', pattern') | _ -> None else None @@ -242,8 +350,16 @@ let compile patterns = | _ -> v) w IdMap.empty in - let filters' = TermMap.add t (v', fs) filters in - Some (Filter (filters', next), w, v, (inst, None), pattern) + let filters'_opt = + (* Make sure that there is no entry for t in filters, respectively, that the entry is compatible *) + match TermMap.find_opt t filters with + | None -> + Some (TermMap.add t (v', fs) filters) + | Some (v'', fs'') when Hashtbl.hash (v'', fs'') = Hashtbl.hash (v', fs) -> + Some (TermMap.add t (v', fs) filters) + | _ -> None + in + filters'_opt |> Opt.map (fun filters' -> Filter (filters', next), w, v, (inst, None), pattern) | _, None -> None) | Check (i, t, _) -> (match WQ.find_opt i w with @@ -268,21 +384,36 @@ let compile patterns = | _ -> None) | _ -> None in - let rec insert_code f pattern w v o comps incomps code = + let rec insert_code f pattern w v o comps incomps code: 'a ematch_code * int = match code with | Choose cs -> if incomps = [] then let code', score = insert_choose f pattern cs w v o in seq comps code', score + List.length comps else branch f pattern comps (seq incomps code) w v o, List.length comps - | Yield (v1, fs) -> + | Yield (v1, fs, gs) -> let v2, fully_processed = check_if_queue_processed w v in let v1_union_v2 = IdMap.union (fun x i j -> Some i) v1 v2 in let v2_union_v1 = IdMap.union (fun x i j -> Some j) v1 v2 in let filters = snd f |> Opt.map snd |> Opt.get_or_else [] in if incomps = [] && pattern = [] && fully_processed && filters = [] && IdMap.equal (=) v1_union_v2 v2_union_v1 - then seq comps (Yield (v1_union_v2, fst f :: fs)), List.length comps + 1 - else branch f pattern comps (seq incomps code) w v o, List.length comps + then + let yield = + let fs', gs' = update_yield_templates (snd f) fs gs (fst f) in + Yield (v1_union_v2, fs', gs') + in + seq comps yield, List.length comps + 1 + else begin + (*Printf.printf "failed\n"; + print_ematch_code (fun ppf (f, _) -> pr_form ppf f) stdout code; + print_newline(); + if incomps <> [] then Printf.printf "incomps"; + if pattern <> [] then Printf.printf "pattern"; + if not fully_processed then Printf.printf "processed"; + if filters <> [] then Printf.printf "filters"; + print_newline();*) + branch f pattern comps (seq incomps code) w v o, List.length comps + end | Prune _ -> branch f pattern comps (seq incomps code) w v o, List.length comps | code -> @@ -290,7 +421,10 @@ let compile patterns = compatible f pattern w v code |> Opt.map (fun (code', w', v', f', pattern') -> insert_code f' pattern' w' v' o (code' :: comps) incomps next) |> - Opt.lazy_get_or_else (fun () -> insert_code f pattern w v o comps (code :: incomps) next) + Opt.lazy_get_or_else (fun () -> + match code with + | Init _ -> branch f pattern comps (seq incomps code) w v o, List.length comps + | _ -> insert_code f pattern w v o comps (code :: incomps) next) and insert_choose f pattern cs w v o = let cs', _, best = List.fold_right @@ -312,9 +446,141 @@ let compile patterns = code' ) (Choose []) patterns + +(** The E-matching abstract machine. + * Runs the given E-matching code tree [code] on the E-graph [ccgraph]. + * Yields a hash table mapping templates of type 'a to the computed variable substitutions. + *) +let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = + let egraph = CC.get_egraph ccgraph in + (* some utility functions for working with the e-graph *) + let get_apps sym = + CC.EGraph.fold + (fun (n, sym') n_apps apps -> + if sym = sym' + then CC.NodeListSet.union n_apps apps + else apps) + egraph CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + let get_reps srt = + CC.EGraph.fold + (fun (n, sym) _ reps -> + if snd @@ snd sym = srt + then CC.NodeListSet.add [n] reps + else reps) + egraph CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + let get_apps_of_node n sym = + CC.EGraph.find_opt (n, sym) egraph |> + Opt.get_or_else CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + (* initialize registers *) + let regs = Array.make (max_reg code + 1) (CC.rep_of_term ccgraph mk_true_term) in + (* create result table *) + let insts = Hashtbl.create 1000 in + (* the actual machine *) + let rec run state = function + | Init (step, esym, o, next) :: stack -> + let ts = state in + (*print_endline ("init " ^ (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym)))); + Printf.printf "active terms: "; + print_list stdout pr_term (TermSet.elements ts); + print_newline ();*) + let terms = + esym |> Either.map get_reps get_apps |> Either.value + in + let ts' = + step |> + Either.value_map + (fun ts' -> ts') + (fun tts -> + List.fold_left (fun ts' (t, t') -> + if TermSet.mem t ts then TermSet.add t' ts' + else ts') + TermSet.empty tts) + in + run ts' (ChooseTerm (ts', o, next, terms) :: stack) + | Bind (i, sym, o, next) :: stack -> + (*Printf.printf "bind(%d, %s)\n" i (string_of_symbol @@ fst sym);*) + let apps = get_apps_of_node (regs.(i)) sym in + run state (ChooseTerm (state, o, next, apps) :: stack) + | Check (i, t, next) :: stack -> + (*Printf.printf "check(%d, %s)\n" i (string_of_term t);*) + if CC.rep_of_term ccgraph t = regs.(i) + then run state (next :: stack) + else begin + (* let t' = CC.term_of_rep ccgraph regs.(i) in + Printf.printf " failed with %d = %s\n" i (string_of_term t');*) + run state stack + end + | Compare (i, j, next) :: stack -> + (* Printf.printf "compare(%d, %d)\n" i j;*) + if regs.(i) = regs.(j) + then run state (next :: stack) + else begin + (* let ti = CC.term_of_rep ccgraph regs.(i) in + let tj = CC.term_of_rep ccgraph regs.(j) in + Printf.printf " failed with %d = %s and %d = %s \n" i (string_of_term ti) j (string_of_term tj);*) + run state stack + end + | Choose cs :: stack -> + run state (List.map (fun c -> Backtrack (state, c)) cs @ stack) + | Yield (vs, fs, gs) :: stack -> + let ts = state in + let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in + TermSet.iter (fun t -> + TermMap.find_opt t fs |> + Opt.iter (List.iter (fun f -> Hashtbl.add insts f sm))) ts; + List.iter (fun f -> Hashtbl.add insts f sm) gs; + run state stack + | Prune (i, sym, next) :: stack -> + (* Printf.printf "prune(%d, %s)\n" i (string_of_symbol @@ fst sym);*) + let n = regs.(i) in + let funs_n = CC.funs_of_rep ccgraph n in + if BloomFilter.mem sym funs_n + then run state (next :: stack) + else run state stack + | Filter (filters, next) :: stack -> + let ts = state in + let ts' = + TermSet.filter + (fun t -> + let t_filters = TermMap.find_opt t filters in + match t_filters with + | Some (vs, fs) -> + let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in + InstGen.filter_term fs (subst_term sm t) sm + | None -> true) + ts + in + if TermSet.is_empty ts' + then run state stack + else run ts' (next :: stack) + | ChooseTerm (state', o, next, args :: apps) :: stack -> + (* Printf.printf "choosing app: ";*) + let _ = + List.fold_left + (fun i arg -> + (* Printf.printf "%d -> %s, " i (string_of_term @@ CC.term_of_rep ccgraph arg);*) + regs.(i) <- arg; i + 1) + o args + in + (*print_newline ();*) + run state' (next :: ChooseTerm (state', o, next, apps) :: stack) + | Backtrack (state', next) :: stack -> run state' (next :: stack) + | ChooseTerm (_, _, _, []) :: stack -> + run state stack + | [] -> insts + in + run TermSet.empty [code] + (** Generate an E-matching code tree from the given set of axioms. *) -let generate_ematch_code_from_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms = +let compile_axioms_to_ematch_code ?(force=false) ?(stratify=(!Config.stratify)) axioms : + (form * _) ematch_code * (form * _) pattern list = (* *) let epr_axioms, axioms = List.partition @@ -405,117 +671,39 @@ let generate_ematch_code_from_axioms ?(force=false) ?(stratify=(!Config.stratify with Not_found -> (t, []) :: acc) fun_terms [] in - let fun_terms_with_filters = - List.sort (fun (t1, _) (t2, _) -> - - (compare - (IdSet.cardinal (fv_term t1)) - (IdSet.cardinal (fv_term t2)))) - fun_terms_with_filters - in let f = mk_forall (IdSrtSet.elements strat_vars1) f in - (f, fvars0, strat_vars1, unmatched_vars, fun_vars, fun_terms_with_filters), fun_terms_with_filters + (f, (fvars0, strat_vars1, unmatched_vars, fun_vars, fun_terms_with_filters)), fun_terms_with_filters in let patterns = [] in - let patterns = List.fold_right (fun f patterns -> ((f, IdSrtSet.empty, IdSrtSet.empty, IdSet.empty, IdSet.empty, []), []) :: patterns) epr_axioms patterns in - let patterns = List.fold_right (fun f patterns -> generate_pattern f :: patterns) axioms patterns in - compile patterns, patterns - -(** Run the given e-matching code tree. Yields a list of instantiated formulas. *) -let run code ccgraph = - let egraph = CC.get_egraph ccgraph in - let get_apps sym = - CC.EGraph.fold - (fun (n, sym') n_apps apps -> - if sym = sym' - then CC.NodeListSet.union n_apps apps - else apps) - egraph CC.NodeListSet.empty |> - CC.NodeListSet.elements + let patterns = + List.fold_right + (fun f patterns -> ((f, (IdSrtSet.empty, IdSrtSet.empty, IdSet.empty, IdSet.empty, [])), []) :: patterns) + epr_axioms patterns in - let get_apps_of_node n sym = - CC.EGraph.find_opt (n, sym) egraph |> - Opt.get_or_else CC.NodeListSet.empty |> - CC.NodeListSet.elements + let patterns = + List.fold_right + (fun f patterns -> generate_pattern f :: patterns) axioms patterns in - let regs = Array.make (max_reg code + 1) (CC.rep_of_term ccgraph mk_true_term) in - let insts = Hashtbl.create 1000 in - let rec run ts = function - | Init (ts, sym, o, next) :: stack -> - let apps = get_apps sym in - run ts (ChooseApp (o, next, apps) :: stack) - | Bind (i, sym, o, next) :: stack -> - let apps = get_apps_of_node (regs.(i)) sym in - run ts (ChooseApp (o, next, apps) :: stack) - | Check (i, t, next) :: stack -> - if CC.rep_of_term ccgraph t = regs.(i) - then run ts (next :: stack) - else run ts stack - | Compare (i, j, next) :: stack -> - if regs.(i) = regs.(j) - then run ts (next :: stack) - else run ts stack - | Choose cs :: stack -> - run ts (cs @ stack) - | Yield (vs, fs) :: stack -> - let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in - List.iter (fun f -> Hashtbl.add insts f sm) fs; - run ts stack - | Prune (i, sym, next) :: stack -> - let n = regs.(i) in - if BloomFilter.mem sym (CC.funs_of_rep ccgraph n) - then run ts (next :: stack) - else run ts stack - | Filter (filters, next) :: stack -> - let ts' = - TermSet.filter - (fun t -> - let t_filters = TermMap.find_opt t filters in - match t_filters with - | Some (vs, fs) -> - let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in - InstGen.filter_term fs (subst_term sm t) sm - | None -> true) - ts - in - if TermSet.is_empty ts' - then run ts stack - else run ts' (next :: stack) - | ChooseApp (o, next, args :: apps) :: stack -> - let _ = - List.fold_left - (fun i arg -> regs.(i) <- arg; i + 1) - o args - in - run ts (next :: ChooseApp (o, next, apps) :: stack) - | ChooseApp (_, _, []) :: stack -> - run ts stack - | [] -> insts - in - run TermSet.empty [code] - -let instantiate_axioms_from_code patterns code ccgraph = - let _ = - if Debug.is_debug 1 then CC.print_classes ccgraph - in - let instances = run code ccgraph in - let get_form (f, _, _, _, _, _) = f in - let print_debug ((f, fvars, strat_vars, unmatched_vars, fun_vars, fun_terms_with_filters), subst_maps) = + compile patterns, patterns + +(** Instantiate the axioms encoded in patterns [patterns] and code tree [code] for E-graph [ccgraph]. *) +let instantiate_axioms_from_code patterns code ccgraph : form list = + let print_debug ((f, (fvars, strat_vars, unmatched_vars, fun_vars, fun_terms_with_filters)), subst_maps) = print_endline "--------------------"; print_endline (string_of_form f); - print_string "all vars: "; - flush stdout; + Printf.printf "all vars: "; print_list stdout pr_ident (List.map fst (IdSrtSet.elements fvars)); print_newline (); - print_string "strat vars: "; + Printf.printf "strat vars: "; print_list stdout pr_ident (List.map fst (IdSrtSet.elements strat_vars)); print_newline (); - print_string "unmatched vars: "; + Printf.printf "unmatched vars: "; print_list stdout pr_ident (IdSet.elements unmatched_vars); print_newline (); - print_string "inst vars: "; + Printf.printf "inst vars: "; print_list stdout pr_ident (IdSet.elements fun_vars); print_newline (); - print_string "fun terms: "; + Printf.printf "fun terms: "; print_list stdout pr_term (List.map fst fun_terms_with_filters); print_newline (); Printf.printf "# of insts: %d\n" (List.length subst_maps); @@ -523,6 +711,11 @@ let instantiate_axioms_from_code patterns code ccgraph = List.iter print_subst_map subst_maps; print_endline "--------------------"; in + let _ = + if Debug.is_debug 1 then CC.print_classes ccgraph + in + let instances = run code ccgraph in + let get_form (f, _) = f in let grouped_instances = List.rev_map (fun (f, _) -> @@ -548,6 +741,6 @@ let instantiate_axioms_from_code patterns code ccgraph = measure_call "EMatching.instantiate_axioms" (instantiate_axioms_from_code patterns code) ccgraph let instantiate_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms ccgraph = - let code, patterns = generate_ematch_code_from_axioms ~force:force ~stratify:stratify axioms in + let code, patterns = compile_axioms_to_ematch_code ~force:force ~stratify:stratify axioms in instantiate_axioms_from_code patterns code ccgraph diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 3f3a2b0d..167c5882 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -191,7 +191,7 @@ let instantiate_and_prove session fs = let round1 fs_inst gts_inst cc_graph = let equations = List.filter (fun f -> is_horn false [f]) fs_inst in let ground_fs = List.filter is_ground fs_inst in - let code, patterns = EMatching.generate_ematch_code_from_axioms equations in + let code, patterns = EMatching.compile_axioms_to_ematch_code equations in let eqs = EMatching.instantiate_axioms_from_code patterns code cc_graph in (*let eqs = instantiate_with_terms equations (CongruenceClosure.get_classes cc_graph) in*) let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in @@ -222,11 +222,11 @@ let instantiate_and_prove session fs = gts_a TermSet.empty in let fs1 = linearize fs1 in - let code, patterns = EMatching.generate_ematch_code_from_axioms fs1 in + let code, patterns = EMatching.compile_axioms_to_ematch_code fs1 in (*let _ = print_endline "E-matching code:"; EMatching.print_ematch_code - (fun ppf (f, _, _, _, _, _) -> pr_form ppf f) stdout code; + (fun ppf (f, _) -> pr_form ppf f) stdout code; print_newline () in*) let rec saturate i fs_inst gts_inst0 cc_graph = diff --git a/src/util/util.ml b/src/util/util.ml index e9d196ee..474665b5 100644 --- a/src/util/util.ml +++ b/src/util/util.ml @@ -72,6 +72,37 @@ module Opt = struct | o -> o end +(** The Either type to represent values of two possible types. *) +module Either = struct + type ('a, 'b) t = + | First of 'a + | Second of 'b + + let first a = First a + + let second b = Second b + + let is_first = function + | First _ -> true + | Second _ -> false + + let is_second = function + | First _ -> false + | Second _ -> true + + let map f s = function + | First a -> First (f a) + | Second b -> Second (s b) + + let value_map f s = function + | First a -> f a + | Second b -> s b + + let value = function + | First a + | Second a -> a +end + (** Utility functions on lists *) (** generate a list of length [n] using generator [f] *) From c3129a465da6238202fd89fc731e0ba8a70e5e63 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 6 Sep 2018 21:49:52 -0400 Subject: [PATCH 080/118] improvements to E-matching engine --- src/prover/eMatching.ml | 121 +++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index 27b01b78..83796c02 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -30,19 +30,19 @@ type state = TermSet.t *) type step = (TermSet.t, (term * term) list) Either.t +type prune_map = (int * sorted_symbol) list TermMap.t (** E-matching code trees for instantiating templates of some type 'a *) type 'a ematch_code = - | Init of step * esymbol * int * 'a ematch_code - | Bind of int * sorted_symbol * int * 'a ematch_code + | Init of step * esymbol * int * prune_map * 'a ematch_code + | Bind of int * sorted_symbol * int * prune_map * 'a ematch_code | Check of int * term * 'a ematch_code | Compare of int * int * 'a ematch_code | Choose of 'a ematch_code list | Yield of int IdMap.t * ('a list) TermMap.t * 'a list - | Prune of int * sorted_symbol * 'a ematch_code | Filter of (int IdMap.t * filter list) TermMap.t * 'a ematch_code | Backtrack of state * 'a ematch_code - | ChooseTerm of state * int * 'a ematch_code * CC.Node.t list list + | ChooseTerm of state * int * 'a ematch_code * prune_map * CC.Node.t list list (** Pretty printing of E-matching code trees *) open Format @@ -55,13 +55,22 @@ let pr_ematch_code pr_inst ppf = | Either.First ts -> pr_term_list ppf (TermSet.elements ts) | Either.Second tts -> pr_list_comma (fun ppf (t1, t2) -> fprintf ppf "%a -> %a" pr_term t1 pr_term t2) ppf tts in + let pr_prune ppf ppf (t, pl) = + fprintf ppf "prune(@[%a,@ [%a]@])" pr_term t + (pr_list_comma (fun ppf (i, sym) -> fprintf ppf "(%d,@ %a)" i pr_sym (fst sym))) pl + in let rec pr_ematch_code ppf = function | Choose cs -> fprintf ppf "@<-3>%s@ %a" "choose" pr_choose cs - | Init (step, sym, i, next) -> - fprintf ppf "init(@[%a,@ %d@])@\n%a" pr_step step i pr_ematch_code next - | Bind (i, sym, o, next) -> - fprintf ppf "bind(@[%d,@ (%a, %d),@ %d@])@\n%a" i pr_sym (fst sym) (List.length (fst @@ snd sym)) o pr_ematch_code next + | Init (step, sym, i, pm, next) -> + fprintf ppf "init(@[%a,@ %d@])@\n%a@\n%a" + pr_step step i + (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) + pr_ematch_code next + | Bind (i, sym, o, pm, next) -> + fprintf ppf "bind(@[%d,@ (%a, %d),@ %d@])@\n%a@\n%a" i pr_sym (fst sym) (List.length (fst @@ snd sym)) o + (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) + pr_ematch_code next | Check (i, t, next) -> fprintf ppf "check(@[%d,@ %a@])@\n%a" i pr_term t pr_ematch_code next | Compare (i, j, next) -> @@ -79,8 +88,8 @@ let pr_ematch_code pr_inst ppf = (fun _ ppf (t, fs) -> fprintf ppf "@[<2>%a:@\n%a@]" pr_term t pr_numbered_forms fs)) (TermMap.bindings fs) pr_numbered_forms gs - | Prune (i, sym, next) -> - fprintf ppf "prune(@[%d,@ %a@])@\n%a" i pr_sym (fst sym) pr_ematch_code next + (*| Prune (i, sym, next) -> + fprintf ppf "prune(@[%d,@ %a@])@\n%a" i pr_sym (fst sym) pr_ematch_code next*) | Filter (filters, next) -> fprintf ppf "filter(@[%a@])@\n%a" pr_term_list (filters |> TermMap.bindings |> List.map fst) @@ -90,7 +99,7 @@ let pr_ematch_code pr_inst ppf = pr_ematch_code next | Backtrack (ts, next) -> fprintf ppf "backtrack(@[[%a]@])@\n@%a" pr_term_list (TermSet.elements ts) pr_ematch_code next - | ChooseTerm (ts, i, next, _) -> + | ChooseTerm (ts, i, next, _, _) -> fprintf ppf "choose_term(@[%d@])@\n%a" i pr_ematch_code next and pr_choose ppf = function @@ -106,8 +115,8 @@ let print_ematch_code string_of_inst out_ch code = (** Get the continuation of the first command *) let continuation = function - | Init (_, _, _, next) - | Bind (_, _, _, next) + | Init (_, _, _, _, next) + | Bind (_, _, _, _, next) | Check (_, _, next) | Compare (_, _, next) | Filter (_, next) @@ -117,13 +126,12 @@ let continuation = function (** Get the maximum register in code tree c, yields -1 if c uses no registers *) let max_reg c = let rec mr m = function - | Init (_, _, i, c) + | Init (_, _, i, _, c) | Check (i, _, c) - | ChooseTerm (_, i, c, _) -> mr (max m i) c + | ChooseTerm (_, i, c, _, _) -> mr (max m i) c | Filter (_, c) - | Prune (_, _, c) | Backtrack (_, c) -> mr m c - | Bind (i, _, j, c) + | Bind (i, _, j, _, c) | Compare (i, j, c) -> mr (max (max m i) j) c | Choose cs -> List.fold_left mr m cs @@ -215,16 +223,16 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = (fun (w', o') arg -> WQ.insert o' arg w', o' + 1) (w, o) args in - let prune args o next = - let code, _ = + let prune args o = + let pl, _ = List.fold_left - (fun (next, o) -> function + (fun (pl, o) -> function | App (sym, _, _) as t when not @@ is_ground_term t -> - Prune (o, sorted_symbol_of t |> Opt.get, next), o + 1 - | _ -> next, o + 1) - (next, o) args + (o, sorted_symbol_of t |> Opt.get) :: pl, o + 1 + | _ -> pl, o + 1) + ([], o) args in - code + pl in let update_yield_templates t_filters_opt fs gs template = match t_filters_opt with @@ -256,8 +264,9 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = Check (i, t, c w' v o) | App (sym, args, _) -> let w'', o' = add_args_to_queue w' o args in - let next = prune args o (c w'' v o') in - Bind (i, sorted_symbol_of t |> Opt.get, o, next) + let next = c w'' v o' in + let pl = prune args o in + Bind (i, sorted_symbol_of t |> Opt.get, o, TermMap.singleton t pl, next) in c w v o and init f pattern v o : 'a ematch_code = @@ -270,12 +279,13 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = | (t', filters') :: pattern' -> let args = args_of t' in let w, o' = add_args_to_queue WQ.empty o args in - let next = prune args o (compile (template, Some (t', filters')) pattern' w v o') in + let next = compile (template, Some (t', filters')) pattern' w v o' in + let pl = prune args o in let ts = match t_filters_opt with | Some (t, _) -> Either.second [t, t'] | None -> Either.first @@ TermSet.singleton t' in - Init (ts, esymbol_of t', o, next) + Init (ts, esymbol_of t', o, TermMap.singleton t' pl, next) in match t_filters_opt with | Some (t, (_ :: _ as fs)) -> @@ -285,11 +295,10 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = in let seq cs fchild = let rec s = function - | Init (ts, sym, o, c) :: cs -> Init (ts, sym, o, s cs) + | Init (ts, sym, o, pm, c) :: cs -> Init (ts, sym, o, pm, s cs) | Check (i, t, c) :: cs -> Check (i, t, s cs) | Compare (i, j, c) :: cs -> Compare (i, j, s cs) - | Bind (i, sym, o, c) :: cs -> Bind (i, sym, o, s cs) - | Prune (i, fs, c) :: cs -> Prune (i, fs, s cs) + | Bind (i, sym, o, pm, c) :: cs -> Bind (i, sym, o, pm, s cs) | Filter (filters, c) :: cs -> Filter (filters, s cs) | [] -> fchild | _ -> assert false @@ -318,13 +327,14 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = * If yes, return updated values [(code', w', v', f', pattern')]. *) let compatible f pattern w v code = match code with - | Init (ts, sym, o, next) -> + | Init (ts, sym, o, pm, next) -> (* Has w been fully processed? *) let v', fully_processed = check_if_queue_processed w v in if fully_processed then match pattern with | (t', filters') :: pattern' when esymbol_of t' = sym -> - let w', _ = add_args_to_queue WQ.empty o (args_of t') in + let args = args_of t' in + let w', _ = add_args_to_queue WQ.empty o args in let f' = (fst f, Some (t', filters')) in let ts' = ts |> @@ -336,7 +346,8 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = (t, t') :: tts |> Util.remove_duplicates (=) |> Either.second) |> Opt.get_or_else (TermSet.singleton t' |> Either.first)) in - let code' = Init (ts', sym, o, next) in + let pm' = TermMap.add t' (prune args o) pm in + let code' = Init (ts', sym, o, pm', next) in Some (code', w', v', f', pattern') | _ -> None else None @@ -376,11 +387,13 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = in Opt.map (fun v' -> code, WQ.delete i w, v', f, pattern) v'_opt | _ -> None) - | Bind (i, sym, o, _) -> + | Bind (i, sym, o, pm, next) -> (match WQ.find_opt i w with | Some (App (_, args, _) as t) when sorted_symbol_of t = Some sym -> let w', _ = add_args_to_queue w o args in - Some (code, WQ.delete i w', v, f, pattern) + let pl = prune args o in + let code' = Bind (i, sym, o, TermMap.add t pl pm, next) in + Some (code', WQ.delete i w', v, f, pattern) | _ -> None) | _ -> None in @@ -414,8 +427,6 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = print_newline();*) branch f pattern comps (seq incomps code) w v o, List.length comps end - | Prune _ -> - branch f pattern comps (seq incomps code) w v o, List.length comps | code -> let next = continuation code in compatible f pattern w v code |> @@ -482,8 +493,18 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = (* create result table *) let insts = Hashtbl.create 1000 in (* the actual machine *) + let prune pm ts = + TermSet.filter (fun t -> + TermMap.find_opt t pm |> + Opt.map (fun pl -> + List.for_all (fun (i, sym) -> + let n = regs.(i) in + let funs_n = CC.funs_of_rep ccgraph n in + BloomFilter.mem sym funs_n) pl) |> + Opt.get_or_else true) ts + in let rec run state = function - | Init (step, esym, o, next) :: stack -> + | Init (step, esym, o, pm, next) :: stack -> let ts = state in (*print_endline ("init " ^ (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym)))); Printf.printf "active terms: "; @@ -502,11 +523,11 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = else ts') TermSet.empty tts) in - run ts' (ChooseTerm (ts', o, next, terms) :: stack) - | Bind (i, sym, o, next) :: stack -> + run ts' (ChooseTerm (ts', o, next, pm, terms) :: stack) + | Bind (i, sym, o, pm, next) :: stack -> (*Printf.printf "bind(%d, %s)\n" i (string_of_symbol @@ fst sym);*) let apps = get_apps_of_node (regs.(i)) sym in - run state (ChooseTerm (state, o, next, apps) :: stack) + run state (ChooseTerm (state, o, next, pm, apps) :: stack) | Check (i, t, next) :: stack -> (*Printf.printf "check(%d, %s)\n" i (string_of_term t);*) if CC.rep_of_term ccgraph t = regs.(i) @@ -536,13 +557,6 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = Opt.iter (List.iter (fun f -> Hashtbl.add insts f sm))) ts; List.iter (fun f -> Hashtbl.add insts f sm) gs; run state stack - | Prune (i, sym, next) :: stack -> - (* Printf.printf "prune(%d, %s)\n" i (string_of_symbol @@ fst sym);*) - let n = regs.(i) in - let funs_n = CC.funs_of_rep ccgraph n in - if BloomFilter.mem sym funs_n - then run state (next :: stack) - else run state stack | Filter (filters, next) :: stack -> let ts = state in let ts' = @@ -559,7 +573,7 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = if TermSet.is_empty ts' then run state stack else run ts' (next :: stack) - | ChooseTerm (state', o, next, args :: apps) :: stack -> + | ChooseTerm (state', o, next, pm, args :: apps) :: stack -> (* Printf.printf "choosing app: ";*) let _ = List.fold_left @@ -569,9 +583,12 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = o args in (*print_newline ();*) - run state' (next :: ChooseTerm (state', o, next, apps) :: stack) + let ts' = prune pm state' in + if TermSet.is_empty ts' + then run state (ChooseTerm (state', o, next, pm, apps) :: stack) + else run ts' (next :: ChooseTerm (state', o, next, pm, apps) :: stack) | Backtrack (state', next) :: stack -> run state' (next :: stack) - | ChooseTerm (_, _, _, []) :: stack -> + | ChooseTerm (_, _, _, _, []) :: stack -> run state stack | [] -> insts in From fb70fef3a9b324f8e2e80cf20bdb3926be82fead Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 8 Sep 2018 16:57:13 -0400 Subject: [PATCH 081/118] improvements to E-matching engine --- src/prover/congruenceClosure.ml | 39 +++++++-- src/prover/eMatching.ml | 135 ++++++++++++++++++++++++-------- 2 files changed, 135 insertions(+), 39 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index cee41433..de6cf10c 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -134,16 +134,25 @@ module rec Node : sig and NodeSet: Set.S with type elt = Node.t = Set.Make(Node) -module NodeListSet = Set.Make(struct +module NodeListSet = + Set.Make(struct type t = Node.t list let compare = compare end) -module EGraph = Map.Make(struct +module EGraphA = + Map.Make(struct type t = Node.t * sorted_symbol let compare = compare end) +module EGraphP = + Map.Make(struct + type t = Node.t * sorted_symbol * int + let compare = compare + end) + +type egraph = NodeListSet.t EGraphA.t * (NodeListSet.t * NodeSet.t) EGraphP.t class dag = fun (terms: TermSet.t) -> let table1 = Hashtbl.create 53 in @@ -246,19 +255,35 @@ class dag = fun (terms: TermSet.t) -> ) nodes; Hashtbl.fold (fun _ cc acc -> cc::acc) node_to_cc [] - method get_egraph = + method get_egraph: egraph = let egraph = - Hashtbl.fold (fun _ n egraph -> + Hashtbl.fold (fun _ n (egrapha, egraphp) -> let n_rep = n#find in let arg_reps = List.map (fun n -> n#find) n#get_args in let other_args_reps = - EGraph.find_opt (n_rep, n#get_sym) egraph |> + EGraphA.find_opt (n_rep, n#get_sym) egrapha |> Opt.get_or_else NodeListSet.empty in - EGraph.add (n_rep, n#get_sym) (NodeListSet.add arg_reps other_args_reps) egraph) - nodes EGraph.empty + let egrapha' = + EGraphA.add (n_rep, n#get_sym) (NodeListSet.add arg_reps other_args_reps) egrapha + in + let egraphp', _ = + List.fold_left + (fun (egraphp', k) arg_rep -> + let other_args, other_parents = + EGraphP.find_opt (arg_rep, n#get_sym, k) egraphp' |> + Opt.get_or_else (NodeListSet.empty, NodeSet.empty) + in + let args' = NodeListSet.add arg_reps other_args in + let parents' = NodeSet.add n_rep other_parents in + EGraphP.add (arg_rep, n#get_sym, k) (args', parents') egraphp', + k + 1) + (egraphp, 0) arg_reps + in + egrapha', egraphp') + nodes (EGraphA.empty, EGraphP.empty) in egraph diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index 83796c02..beea71d7 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -30,11 +30,15 @@ type state = TermSet.t *) type step = (TermSet.t, (term * term) list) Either.t +(** Maps to keep track of register/symbol pairs that should be pruned after init/bind *) type prune_map = (int * sorted_symbol) list TermMap.t + +(** Inverted paths of symbol/argument position pairs from subterms to parent terms *) +type inv_path = (sorted_symbol * int) list (** E-matching code trees for instantiating templates of some type 'a *) type 'a ematch_code = - | Init of step * esymbol * int * prune_map * 'a ematch_code + | Init of step * esymbol * (int * inv_path) option * int * prune_map * 'a ematch_code | Bind of int * sorted_symbol * int * prune_map * 'a ematch_code | Check of int * term * 'a ematch_code | Compare of int * int * 'a ematch_code @@ -62,11 +66,19 @@ let pr_ematch_code pr_inst ppf = let rec pr_ematch_code ppf = function | Choose cs -> fprintf ppf "@<-3>%s@ %a" "choose" pr_choose cs - | Init (step, sym, i, pm, next) -> + | Init (step, sym, None, i, pm, next) -> fprintf ppf "init(@[%a,@ %d@])@\n%a@\n%a" pr_step step i (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) pr_ematch_code next + | Init (step, sym, Some (i, path), o, pm, next) -> + fprintf ppf "join(@[%a,@ %d,@ [%a],@ %d@])@\n%a@\n%a" + pr_step step + i + (pr_list_comma (fun ppf (sym, i) -> fprintf ppf "(%a,@ %d)" pr_sym (fst sym) i)) path + o + (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) + pr_ematch_code next | Bind (i, sym, o, pm, next) -> fprintf ppf "bind(@[%d,@ (%a, %d),@ %d@])@\n%a@\n%a" i pr_sym (fst sym) (List.length (fst @@ snd sym)) o (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) @@ -115,7 +127,7 @@ let print_ematch_code string_of_inst out_ch code = (** Get the continuation of the first command *) let continuation = function - | Init (_, _, _, _, next) + | Init (_, _, _, _, _, next) | Bind (_, _, _, _, next) | Check (_, _, next) | Compare (_, _, next) @@ -126,7 +138,7 @@ let continuation = function (** Get the maximum register in code tree c, yields -1 if c uses no registers *) let max_reg c = let rec mr m = function - | Init (_, _, i, _, c) + | Init (_, _, _, i, _, c) | Check (i, _, c) | ChooseTerm (_, i, c, _, _) -> mr (max m i) c | Filter (_, c) @@ -159,7 +171,7 @@ module WQ = PrioQueue.Make let compare t1 t2 = (* Rational for the following definition: * Entries i -> x and i -> t where t is ground should be processed before entries i -> f(t_1, ..., t_n). - * This is to delay the creation of branching nodes that introduce backtracking points. *) + * This is to delay the creation of e-matching code that introduces backtracking points. *) if is_ground_term t1 && not @@ is_ground_term t2 then -1 else match t1, t2 with | Var _, App _ -> -1 @@ -206,7 +218,7 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = if c1 = 0 then (* next, prioritize terms that occur more often across many patterns *) let c2 = compare (get_count t2) (get_count t1) in - if c2 = 0 then compare t1 t2 + if c2 = 0 then compare t2 t1 else c2 else c1 in @@ -241,12 +253,38 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = TermMap.add t (template :: t_templates) fs, gs | None -> fs, template :: gs in + (* Get shortest inverted path from some variable x bound + * in v that is a subterm of t, if such x exists. *) + let get_path v t = + let rec gp shortest path = function + | App (_, [], _) -> shortest + | Var (x, _) -> + let smaller = + shortest |> + Opt.fold (fun _ (_, p) -> List.length path < List.length p) true + in + if smaller then + IdMap.find_opt x v |> + Opt.map (fun i -> (i, path)) |> + Opt.lazy_or_else (fun () -> shortest) + else shortest + | App (_, args, _) as t -> + let sym = sorted_symbol_of t |> Opt.get in + let new_shortest, _ = + List.fold_left + (fun (ns, k) arg -> gp ns ((sym, k) :: path) arg, k + 1) + (shortest, 0) args + in + new_shortest + in + gp None [] t + in (* Compile a multi-pattern into a code tree. * f: info about current term of the multi-pattern that is being processed. * pattern: remaining trigger terms of the multi-pattern that still need to be processed. * w: work queue of subterms of [f] that still need to be processed. * v: accumulated map of variable to register bindings. - * o: number of next unused register. + * o: index of next unused register. *) let rec compile f pattern w v o = let rec c w v o = @@ -285,7 +323,10 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = | Some (t, _) -> Either.second [t, t'] | None -> Either.first @@ TermSet.singleton t' in - Init (ts, esymbol_of t', o, TermMap.singleton t' pl, next) + let pm = TermMap.singleton t' pl in + let esym = esymbol_of t' in + let path = get_path v t' in + Init (ts, esym, path, o, pm, next) in match t_filters_opt with | Some (t, (_ :: _ as fs)) -> @@ -295,7 +336,7 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = in let seq cs fchild = let rec s = function - | Init (ts, sym, o, pm, c) :: cs -> Init (ts, sym, o, pm, s cs) + | Init (ts, sym, path, o, pm, c) :: cs -> Init (ts, sym, path, o, pm, s cs) | Check (i, t, c) :: cs -> Check (i, t, s cs) | Compare (i, j, c) :: cs -> Compare (i, j, s cs) | Bind (i, sym, o, pm, c) :: cs -> Bind (i, sym, o, pm, s cs) @@ -327,7 +368,7 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = * If yes, return updated values [(code', w', v', f', pattern')]. *) let compatible f pattern w v code = match code with - | Init (ts, sym, o, pm, next) -> + | Init (ts, sym, path, o, pm, next) -> (* Has w been fully processed? *) let v', fully_processed = check_if_queue_processed w v in if fully_processed then @@ -347,7 +388,7 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = Opt.get_or_else (TermSet.singleton t' |> Either.first)) in let pm' = TermMap.add t' (prune args o) pm in - let code' = Init (ts', sym, o, pm', next) in + let code' = Init (ts', sym, path, o, pm', next) in Some (code', w', v', f', pattern') | _ -> None else None @@ -463,36 +504,39 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = * Yields a hash table mapping templates of type 'a to the computed variable substitutions. *) let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = - let egraph = CC.get_egraph ccgraph in + let egrapha, egraphp = CC.get_egraph ccgraph in (* some utility functions for working with the e-graph *) let get_apps sym = - CC.EGraph.fold + CC.EGraphA.fold (fun (n, sym') n_apps apps -> if sym = sym' then CC.NodeListSet.union n_apps apps else apps) - egraph CC.NodeListSet.empty |> + egrapha CC.NodeListSet.empty |> CC.NodeListSet.elements in let get_reps srt = - CC.EGraph.fold + CC.EGraphA.fold (fun (n, sym) _ reps -> if snd @@ snd sym = srt then CC.NodeListSet.add [n] reps else reps) - egraph CC.NodeListSet.empty |> + egrapha CC.NodeListSet.empty |> CC.NodeListSet.elements in let get_apps_of_node n sym = - CC.EGraph.find_opt (n, sym) egraph |> + CC.EGraphA.find_opt (n, sym) egrapha |> Opt.get_or_else CC.NodeListSet.empty |> CC.NodeListSet.elements in + let get_parents n sym k = + CC.EGraphP.find_opt (n, sym, k) egraphp |> + Opt.get_or_else (CC.NodeListSet.empty, CC.NodeSet.empty) + in (* initialize registers *) let regs = Array.make (max_reg code + 1) (CC.rep_of_term ccgraph mk_true_term) in (* create result table *) let insts = Hashtbl.create 1000 in - (* the actual machine *) let prune pm ts = TermSet.filter (fun t -> TermMap.find_opt t pm |> @@ -503,26 +547,53 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = BloomFilter.mem sym funs_n) pl) |> Opt.get_or_else true) ts in + let take_step step ts = + step |> + Either.value_map + (fun ts' -> ts') + (fun tts -> + List.fold_left (fun ts' (t, t') -> + if TermSet.mem t ts then TermSet.add t' ts' + else ts') + TermSet.empty tts) + in + (* collect nodes containing one of the nodes in [terms] following the given inverted path *) + let rec collect (esym: esymbol) args terms = function + | [] -> + esym |> + Either.map + (fun _ -> CC.NodeSet.fold (fun n acc -> [n] :: acc) terms []) + (fun _ -> CC.NodeListSet.elements args) |> + Either.value + | (sym, k) :: path' -> + let args', terms' = + CC.NodeSet.fold + (fun n (args', terms') -> + let n_args, n_terms = get_parents n sym k in + CC.NodeListSet.union n_args args', + CC.NodeSet.union n_terms terms' + ) + terms (CC.NodeListSet.empty, CC.NodeSet.empty) + in + collect esym args' terms' path' + in + (* the actual machine *) let rec run state = function - | Init (step, esym, o, pm, next) :: stack -> + | Init (step, esym, path_opt, o, pm, next) :: stack -> let ts = state in - (*print_endline ("init " ^ (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym)))); + (*Printf.printf "init %s\n" (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym))); Printf.printf "active terms: "; print_list stdout pr_term (TermSet.elements ts); - print_newline ();*) + Printf.printf "\n";*) let terms = - esym |> Either.map get_reps get_apps |> Either.value - in - let ts' = - step |> - Either.value_map - (fun ts' -> ts') - (fun tts -> - List.fold_left (fun ts' (t, t') -> - if TermSet.mem t ts then TermSet.add t' ts' - else ts') - TermSet.empty tts) + path_opt |> + Opt.map + (fun (i, path) -> + collect esym CC.NodeListSet.empty (CC.NodeSet.singleton regs.(i)) path) |> + Opt.lazy_get_or_else + (fun () -> esym |> Either.map get_reps get_apps |> Either.value) in + let ts' = take_step step ts in run ts' (ChooseTerm (ts', o, next, pm, terms) :: stack) | Bind (i, sym, o, pm, next) :: stack -> (*Printf.printf "bind(%d, %s)\n" i (string_of_symbol @@ fst sym);*) From 074b39a8aa9c1124455c74712953e4aef8002af0 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 10 Sep 2018 14:35:51 -0400 Subject: [PATCH 082/118] use new e-matching for term generation --- src/formulas/grass.ml | 2 +- src/prover/congruenceClosure.ml | 54 +++++++++++++++------- src/prover/eMatching.ml | 82 +++++++++++++++++++++++++++++---- src/prover/prover.ml | 42 +++++++++-------- src/prover/reduction.ml | 61 +++++++----------------- 5 files changed, 150 insertions(+), 91 deletions(-) diff --git a/src/formulas/grass.ml b/src/formulas/grass.ml index 472782b9..1a953380 100644 --- a/src/formulas/grass.ml +++ b/src/formulas/grass.ml @@ -380,7 +380,7 @@ let rec pr_term ppf = function | App (sym, [], _) -> fprintf ppf "%a" pr_sym sym | App (Read, [map; t], _) -> (match map, sort_of t with - | App (FreeSym _, [], _), Loc _ -> fprintf ppf "%a.%a" pr_term t pr_term map + | (App (FreeSym _, [], _) | Var _), Loc _ -> fprintf ppf "%a.%a" pr_term t pr_term map | _ -> fprintf ppf "%a[%a]" pr_term map pr_term t) | App (Read, map :: t :: ts, _) -> fprintf ppf "%a[%a].%a" pr_term t pr_term_list ts pr_term map diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index de6cf10c..bbb3820d 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -20,7 +20,7 @@ module rec Node : sig union: t -> unit; ccpar: NodeSet.t; congruent: t -> bool; - merge: t -> unit + merge: t -> bool; > val compare: t -> t -> int @@ -42,7 +42,7 @@ module rec Node : sig union: t -> unit; ccpar: NodeSet.t; congruent: t -> bool; - merge: t -> unit + merge: t -> bool; > let compare = compare @@ -116,17 +116,18 @@ module rec Node : sig List.filter (fun (a,b) -> a#find <> b#find) (List.rev_map2 (fun x y -> (x,y)) (self#get_args) (that#get_args))*) method merge (that: node) = - if self#find <> that#find then - begin - let p1 = self#ccpar in - let p2 = that#ccpar in - self#union that; - NodeSet.iter (fun x -> - NodeSet.iter - (fun y -> if x#find <> y#find && x#congruent y then x#merge y) - p2) - p1 - end + self#find <> that#find && + begin + let p1 = self#ccpar in + let p2 = that#ccpar in + self#union that; + NodeSet.iter (fun x -> + NodeSet.iter + (fun y -> if x#find <> y#find && x#congruent y then ignore (x#merge y)) + p2) + p1; + true + end end let create sym terms: t = new node sym terms @@ -171,7 +172,7 @@ class dag = fun (terms: TermSet.t) -> let rec convert_term t = (*print_endline ("CC adding: " ^ (string_of_term t));*) match t with - | Var (v, _) -> failwith "CC: term not ground" (* create_and_add var (FreeSym v) []*) + | Var (v, _) -> failwith ("CC: term not ground " ^ string_of_term t) (* create_and_add var (FreeSym v) []*) | App (_, args, _) as appl -> let node_args = List.map convert_term args in let new_node = create_and_add appl (sorted_symbol_of appl |> Opt.get) node_args in @@ -180,10 +181,15 @@ class dag = fun (terms: TermSet.t) -> in let _ = TermSet.iter (fun t -> ignore (convert_term t)) terms in object (self) + val mutable _has_mods: bool = true val mutable neqs: (Node.t * Node.t) list = [] val nodes: (term, Node.t) Hashtbl.t = table1 val node_to_term: (Node.t, term) Hashtbl.t = table2 + method has_mods = _has_mods + + method reset = _has_mods <- false + method get_node t = try Hashtbl.find nodes t with Not_found -> failwith ("CC: cannot find " ^ (string_of_term t)) @@ -213,12 +219,13 @@ class dag = fun (terms: TermSet.t) -> let n = convert_term t in let arg_opt = List.nth_opt n#get_args 0 in arg_opt |> - Opt.iter (fun arg -> NodeSet.iter (fun n' -> if n' <> n && n#congruent n' then n#merge n') arg#ccpar) + Opt.iter (fun arg -> NodeSet.iter (fun n' -> if n' <> n && n#congruent n' then ignore @@ n#merge n') arg#ccpar); + _has_mods <- n#find = n; method add_eq t1 t2 = let n1 = self#get_node t1 in let n2 = self#get_node t2 in - n1#merge n2 + _has_mods <- n1#merge n2 method add_neq t1 t2 = let n1 = self#get_node t1 in @@ -242,6 +249,11 @@ class dag = fun (terms: TermSet.t) -> (** Returns a method that maps a term to its representative *) method get_repr = (fun t -> self#get_term (self#get_node t)#find) + method get_reps = + Hashtbl.fold + (fun _ n reps -> NodeSet.add (n#find) reps) + nodes NodeSet.empty + (** Gets a list of list of equal expressions (connected components). *) method get_cc = let node_to_cc = Hashtbl.create (Hashtbl.length nodes) in @@ -471,6 +483,8 @@ let create () : dag = let rep_of_term cc_graph t = (cc_graph#get_node t)#find +let get_terms cc_graph = cc_graph#get_terms + let term_of_rep cc_graph n = cc_graph#get_term n let funs_of_rep ccgraph n = n#get_funs @@ -478,6 +492,8 @@ let funs_of_rep ccgraph n = n#get_funs let get_egraph cc_graph = cc_graph#get_egraph let get_classes cc_graph = cc_graph#get_cc + +let get_reps cc_graph = cc_graph#get_reps let congruence_classes gts fs = create () |> @@ -486,6 +502,12 @@ let congruence_classes gts fs = let class_of t classes = List.find (List.mem t) classes +let find_rep ccgraph n = n#find + +let has_mods ccgraph = ccgraph#has_mods + +let reset ccgraph = ccgraph#reset; ccgraph + let print_classes cc_graph = ignore (List.fold_left (fun num cl -> diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index beea71d7..2f841e0f 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -279,6 +279,10 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = in gp None [] t in + let args_of = function + | Var _ as t -> [t] + | App (_, args, _) -> args + in (* Compile a multi-pattern into a code tree. * f: info about current term of the multi-pattern that is being processed. * pattern: remaining trigger terms of the multi-pattern that still need to be processed. @@ -373,7 +377,7 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = let v', fully_processed = check_if_queue_processed w v in if fully_processed then match pattern with - | (t', filters') :: pattern' when esymbol_of t' = sym -> + | (t', filters') :: pattern' when esymbol_of t' = sym && get_path v' t' = path -> let args = args_of t' in let w', _ = add_args_to_queue WQ.empty o args in let f' = (fst f, Some (t', filters')) in @@ -530,8 +534,14 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = CC.NodeListSet.elements in let get_parents n sym k = + (* Printf.printf "get_parents (%s, %s, %d)...\n" + (CC.term_of_rep ccgraph n |> string_of_term) + (fst sym |> string_of_symbol) + k + ;*) CC.EGraphP.find_opt (n, sym, k) egraphp |> - Opt.get_or_else (CC.NodeListSet.empty, CC.NodeSet.empty) + Opt.get_or_else (CC.NodeListSet.empty, CC.NodeSet.empty) (*|> + (fun (ps, ns) -> print_list stdout pr_term (List.map (CC.term_of_rep ccgraph) (CC.NodeSet.elements ns)); (ps, ns))*) in (* initialize registers *) let regs = Array.make (max_reg code + 1) (CC.rep_of_term ccgraph mk_true_term) in @@ -581,10 +591,6 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = let rec run state = function | Init (step, esym, path_opt, o, pm, next) :: stack -> let ts = state in - (*Printf.printf "init %s\n" (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym))); - Printf.printf "active terms: "; - print_list stdout pr_term (TermSet.elements ts); - Printf.printf "\n";*) let terms = path_opt |> Opt.map @@ -594,6 +600,19 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = (fun () -> esym |> Either.map get_reps get_apps |> Either.value) in let ts' = take_step step ts in + (*print_of_format (fun ppf esym -> fprintf ppf "init %s: [%a] -> %a\n" + (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym))) + pr_sorts + (esym |> Either.value_map (fun _ -> []) (fun sym -> fst @@ snd sym)) + pr_sort + (esym |> Either.value_map (fun srt -> srt) (fun sym -> snd @@ snd sym))) esym stdout + ; + Printf.printf "active terms: "; + print_list stdout pr_term (TermSet.elements ts'); + Printf.printf "\n"; + Printf.printf "choosing from: "; + print_list stdout (fun ppf ns -> fprintf ppf "[%a]" pr_term_list (List.map (CC.term_of_rep ccgraph) ns)) terms; + Printf.printf "\n";*) run ts' (ChooseTerm (ts', o, next, pm, terms) :: stack) | Bind (i, sym, o, pm, next) :: stack -> (*Printf.printf "bind(%d, %s)\n" i (string_of_symbol @@ fst sym);*) @@ -623,6 +642,9 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = | Yield (vs, fs, gs) :: stack -> let ts = state in let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in + (*Printf.printf "yield\n"; + print_list stdout (fun ppf (x, t) -> fprintf ppf "%s -> %a" (string_of_ident x) pr_term t) (IdMap.bindings sm); + print_newline ();*) TermSet.iter (fun t -> TermMap.find_opt t fs |> Opt.iter (List.iter (fun f -> Hashtbl.add insts f sm))) ts; @@ -645,15 +667,16 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = then run state stack else run ts' (next :: stack) | ChooseTerm (state', o, next, pm, args :: apps) :: stack -> - (* Printf.printf "choosing app: ";*) + (*Printf.printf "choosing app: "; + print_of_format (fun ppf args -> fprintf ppf "[%a]" pr_term_list (List.map (CC.term_of_rep ccgraph) args)) args stdout; + print_newline ();*) let _ = List.fold_left (fun i arg -> - (* Printf.printf "%d -> %s, " i (string_of_term @@ CC.term_of_rep ccgraph arg);*) + (*Printf.printf "%d -> %s, " i (string_of_term @@ CC.term_of_rep ccgraph arg);*) regs.(i) <- arg; i + 1) o args in - (*print_newline ();*) let ts' = prune pm state' in if TermSet.is_empty ts' then run state (ChooseTerm (state', o, next, pm, apps) :: stack) @@ -832,3 +855,44 @@ let instantiate_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms ccgr let code, patterns = compile_axioms_to_ematch_code ~force:force ~stratify:stratify axioms in instantiate_axioms_from_code patterns code ccgraph + +let compile_term_generators_to_ematch_code generators = + let generators = + let remove_generic_filters (ms, ts) = + List.map (function Match (m, fs) -> (m, List.filter (function FilterGeneric _ -> false | _ -> true) fs)) ms, ts + in + remove_duplicates (fun g1 g2 -> remove_generic_filters g1 = remove_generic_filters g2) generators + in + let generate_pattern patterns (guards, ts) = + match ts with + | [] -> patterns + | _ -> + let triggers = List.map (function Match (t, filters) -> (t, filters)) guards in + (ts, triggers) :: patterns + in + let patterns = List.fold_left generate_pattern [] generators in + compile patterns + +let generate_terms_from_code code ccgraph = + let rec round i reps = + (*Printf.printf "Generating terms, round %d...\n" i; + CC.print_classes ccgraph;*) + let insts = run code ccgraph in + let new_terms = + Hashtbl.fold (fun ts sm new_terms -> + List.fold_left (fun new_terms gen_term -> + let t = subst_term sm gen_term in + (*let _ = print_endline (" Adding generated term " ^ string_of_term t); flush stdout in *) + TermSet.add t new_terms) new_terms ts) + insts TermSet.empty + in + let _ = CC.add_terms new_terms ccgraph in + let new_reps = TermSet.fold (fun t -> CC.NodeSet.add (CC.rep_of_term ccgraph t)) new_terms CC.NodeSet.empty in + let reps = CC.NodeSet.fold (fun n -> CC.NodeSet.add (CC.find_rep ccgraph n)) reps CC.NodeSet.empty in + if CC.NodeSet.subset new_reps reps then reps else round (i + 1) (CC.NodeSet.union new_reps reps) + in + let reps = round 0 (CC.get_reps ccgraph) in + reps + +let generate_terms_from_code code ccgraph = + measure_call "EMatching.generate_terms_from_code" (generate_terms_from_code code) ccgraph diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 167c5882..fa9d4d76 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -182,12 +182,18 @@ let instantiate_and_prove session fs = let btwn_gen = btwn_field_generators fs in let gts = ground_terms ~include_atoms:true (mk_and fs) in (*let gts = generate_knowns gts in*) - let gts1 = generate_terms (btwn_gen @ generators) gts in + let tgcode = EMatching.compile_term_generators_to_ematch_code (btwn_gen @ generators) in + (*let _ = + print_endline "Term Generator code:"; + EMatching.print_ematch_code pr_term_list stdout tgcode; + print_newline () + in*) let cc_graph = CongruenceClosure.create () |> - CongruenceClosure.add_terms gts1 |> + CongruenceClosure.add_terms gts |> CongruenceClosure.add_conjuncts fs in + let _ = EMatching.generate_terms_from_code tgcode cc_graph in let round1 fs_inst gts_inst cc_graph = let equations = List.filter (fun f -> is_horn false [f]) fs_inst in let ground_fs = List.filter is_ground fs_inst in @@ -208,10 +214,8 @@ let instantiate_and_prove session fs = in let round1 fs_inst gts_inst = measure_call "round1" (round1 fs_inst gts_inst) in let round2 fs_inst gts_inst cc_graph = - (* the following seemingly redundant instantiation round is a workaround for not using the -fullep option *) - let fs_inst0 = (*instantiate_with_terms ~stratify:false true fs1 classes*) fs_inst in + let fs_inst0 = fs_inst in let gts_known = generate_knowns gts in - let gts_inst0 = TermSet.union gts_inst gts_known in (*let gts_inst = generate_terms generators (TermSet.union gts_inst0 gts2_atoms) in*) let core_terms = let gts_a = ground_terms (mk_and fs) in @@ -219,8 +223,10 @@ let instantiate_and_prove session fs = match sort_of t with | Loc _ | Int | FreeSrt _ -> TermSet.add (mk_known t) acc | _ -> acc) - gts_a TermSet.empty + gts_a gts_known in + let gts_inst0 = TermSet.union gts_inst core_terms in + let cc_graph = CongruenceClosure.add_terms gts_inst0 cc_graph in let fs1 = linearize fs1 in let code, patterns = EMatching.compile_axioms_to_ematch_code fs1 in (*let _ = @@ -231,31 +237,27 @@ let instantiate_and_prove session fs = in*) let rec saturate i fs_inst gts_inst0 cc_graph = (*Printf.printf "Saturate iteration %d\n" i; flush stdout;*) - let gts_inst = TermSet.union gts_inst0 core_terms in - let gts_atoms = (*TermSet.filter (function - | App (_, ts, Bool) -> List.for_all (fun t -> TermSet.mem t gts_inst) ts - | _ -> false)*) - (ground_terms ~include_atoms:true (mk_and fs_inst)) - in - let gts_inst = TermSet.union gts_inst gts_atoms in + let gts_inst = ground_terms_acc ~include_atoms:true gts_inst0 (mk_and fs_inst) in let fs, gts_inst = generate_adt_terms fs gts_inst in - let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) + let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in - let generators = if i > 1 && false then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in - let gts_inst = generate_terms generators gts_inst in - if i > 1 && not !Config.propagate_reads || TermSet.subset gts_inst gts_inst0 then + let cc_graph = CongruenceClosure.add_terms gts_inst cc_graph in + (*let generators = if i > 1 && false then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in*) + let _ = EMatching.generate_terms_from_code tgcode cc_graph in + let gts_inst = CongruenceClosure.get_terms cc_graph in + if i > 1 && (not !Config.propagate_reads || CongruenceClosure.has_mods cc_graph) then rev_concat [fs_inst; implied_eqs], gts_inst, cc_graph else let cc_graph = cc_graph |> - measure_call "cc_gen" (fun cc_graph -> cc_graph |> CongruenceClosure.add_terms gts_inst) |> + (*measure_call "cc_gen" (fun cc_graph -> cc_graph |> CongruenceClosure.add_terms gts_inst) |>*) CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) in (*let fs_inst = instantiate_with_terms fs1 (CongruenceClosure.get_classes cc_graph) in*) let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in - saturate (i + 1) fs_inst gts_inst cc_graph + saturate (i + 1) fs_inst gts_inst (CongruenceClosure.reset cc_graph) in let saturate i fs_inst gts_inst0 = measure_call "saturate" (saturate i fs_inst gts_inst0) in let fs, gts_inst, cc_graph = saturate 1 fs_inst0 gts_inst0 cc_graph in @@ -292,7 +294,7 @@ let instantiate_and_prove session fs = Debug.debug (fun () -> "Solver done.\n"); k + 1, result1, fs_asserted1, fs_inst1, gts_inst1, classes1 | _ -> k, result, fs_asserted, fs_inst, gts_inst, cc_graph - in List.fold_left dr (1, None, FormSet.empty, fs1, gts1, cc_graph) rounds + in List.fold_left dr (1, None, FormSet.empty, fs1, cc_graph#get_terms, cc_graph) rounds in let _, result, fs_asserted, fs_inst, _, _ = do_rounds [round1; round2] in (match result with diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 4a246577..0711849b 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -266,15 +266,17 @@ let array_sorts fs = (** Adds theory axioms for the entry point function to formulas [fs]. ** Assumes that all frame predicates have been reduced in formulas [fs]. *) let add_ep_axioms fs = - let gts = generated_ground_terms fs in - let struct_sorts = - TermSet.fold - (fun t struct_sorts -> match t with - | App (_, _, Map([Loc srt1], Loc srt2)) - | Var (_, Map([Loc srt1], srt2)) when srt1 = srt2 -> SortSet.add srt1 struct_sorts - | _ -> struct_sorts) - gts SortSet.empty + (*let gts = generated_ground_terms fs in*) + let rec get_struct_sorts acc = function + | App (_, ts, srt) -> + let acc = List.fold_left get_struct_sorts acc ts in + (match srt with + | Map([Loc srt1], Loc srt2) when srt1 = srt2 -> SortSet.add srt1 acc + | _ -> acc) + | Var (_, Map([Loc srt1], srt2)) when srt1 = srt2 -> SortSet.add srt1 acc + | Var _ -> acc in + let struct_sorts = fold_terms get_struct_sorts SortSet.empty (mk_and fs) in let axioms = SortSet.fold (fun srt axioms -> Axioms.ep_axioms srt @ axioms) struct_sorts [] in axioms @ fs @@ -425,12 +427,15 @@ let add_read_write_axioms fs = | [[Match (v, _)], t] -> [[Match (v, [FilterGeneric (fun sm t -> TermSet.mem (subst_term sm v) basic_pt_flds)])], t] | gs -> gs in - let gts = generate_terms generators gts in let ccgraph = CongruenceClosure.congruence_classes gts fs in + let _ = + let tgcode = EMatching.compile_term_generators_to_ematch_code generators in + EMatching.generate_terms_from_code tgcode ccgraph + in (* CAUTION: not forcing the instantiation here would yield an inconsistency with the read/write axioms *) let null_ax1 = EMatching.instantiate_axioms ~force:true null_ax ccgraph in let fs1 = null_ax1 @ fs in - let gts = TermSet.union (ground_terms ~include_atoms:true (mk_and null_ax1)) gts in + let gts = TermSet.union (ground_terms ~include_atoms:true (mk_and null_ax1)) (CongruenceClosure.get_terms ccgraph) in (* propagate read terms *) (* generate instances of all read over write axioms *) let read_write_ax = @@ -496,41 +501,7 @@ let add_terms fs gts = mk_pred ("inst-closure", 0) [t] :: fs1) extra_gts fs in fs1 - -let add_split_lemmas fs gts = - if not !Config.split_lemmas then fs else - let structs = - TermSet.fold (fun t structs -> - match sort_of t with - | Loc srt -> SortSet.add srt structs - | _ -> structs) - gts SortSet.empty - in - let classes = - CongruenceClosure.create () |> - CongruenceClosure.add_terms (generated_ground_terms fs) |> - CongruenceClosure.add_conjuncts fs |> - CongruenceClosure.get_classes - in - let add_lemmas srt fs1 = - let loc_gts = - TermSet.filter (fun t -> sort_of t = Loc srt) gts - in - let classes = CongruenceClosure.restrict_classes classes loc_gts in - let rec lem fs1 = function - | [] -> fs1 - | c :: cs -> - let t = List.hd c in - List.fold_left - (fun fs1 c -> - let t1 = List.hd c in - mk_or [mk_eq t t1; mk_neq t t1] :: fs1) - fs1 cs - in - lem fs1 classes - in - SortSet.fold add_lemmas structs fs - + (** Reduces the given formula to the target theory fragment, as specified b the configuration. *) let reduce f = (* split f into conjuncts and eliminate all existential quantifiers *) From 4d16e46441d38c41bb86985926da6242d4395b4d Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 11 Sep 2018 00:50:45 -0400 Subject: [PATCH 083/118] complete work on new E-matching engine --- src/formulas/grassUtil.ml | 54 +++++++++++++++++++++++++ src/prover/axioms.ml | 14 +------ src/prover/congruenceClosure.ml | 52 ++++++++++++++++-------- src/prover/eMatching.ml | 72 ++++++++++++++++++++++++++------- src/prover/prover.ml | 36 ++++++++++------- src/prover/reduction.ml | 7 ---- src/prover/simplifyGrass.ml | 47 +++++++++++++++++++-- src/verifier/verifier.ml | 2 +- 8 files changed, 215 insertions(+), 69 deletions(-) diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index 7dcf06c4..6a96e59b 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -283,6 +283,18 @@ let symbol_of_ident = (** {6 (Smart) constructors} *) +let mk_loc_var name = + let id = fresh_ident name in + fun struct_srt -> id, Loc struct_srt + +let mk_loc_field_var name = + let id = fresh_ident name in + fun struct_srt -> id, loc_field_sort struct_srt + +let mk_loc_set_var name = + let id = fresh_ident name in + fun struct_srt -> id, Set (Loc struct_srt) + let mk_true = BoolOp (And, []) let mk_false = BoolOp (Or, []) let mk_bool b = if b then mk_true else mk_false @@ -806,6 +818,37 @@ let map_terms fn f = | Binder (b, vs, f, a) -> Binder (b, vs, mt f, ma a) in mt f +(** Fold and map all terms appearing in the formula [f] + using [fn] and initial value [init] *) +let fold_map_terms fn init f = + let fa acc = function + | Pattern (t, fs) -> + let t, acc = fn acc t in + Pattern(t, fs), acc + | Label (b, t) -> let t, acc = fn acc t in Label (b, t), acc + | TermGenerator (gs, ts) -> + let gs, acc = (* TODO should we also apply it to terms in filters? *) + fold_left_map (fun acc -> function Match (t, fs) -> + let t, acc = fn acc t in Match (t, fs), acc) acc gs + in + let ts, acc = fold_left_map fn acc ts in + TermGenerator (gs, ts), acc + | Comment _ | SrcPos _ | Name _ | ErrorMsg _ as a -> a, acc + in + let rec ft acc = function + | Atom (t, a) -> + let a, acc = fold_left_map fa acc a in + let t, acc = fn acc t in + Atom (t, a), acc + | BoolOp (op, fs) -> + let fs, acc = fold_left_map ft acc fs in + BoolOp (op, fs), acc + | Binder (b, vs, f, a) -> + let a, acc = fold_left_map fa acc a in + let f, acc = ft acc f in + Binder (b, vs, f, a), acc + in ft init f + (** Apply the function fn to all atoms appearing in [f] *) let map_atoms fn f = let rec mt = function @@ -875,6 +918,13 @@ let smk_forall ?(ann=[]) bv f = smk_binder ~ann:ann Forall bv f (** Smart constructor for existential quantifiers.*) let smk_exists ?(ann=[]) bv f = smk_binder ~ann:ann Exists bv f +(** Computes the size of the term [t] (in number of function applications) *) +let size_of_term t = + let rec s acc = function + | Var _ -> acc + | App (_, ts, _) -> List.fold_left s (acc + 1) ts + in s 0 t + (** Computes the set of free variables of formula [f] together with their sorts. *) let sorted_free_vars f = let rec fvt bv vars = function @@ -1261,6 +1311,10 @@ let subst_annot subst_map = function (** Substitutes all free variables in formula [f] with terms according to substitution map [subst_map]. ** This operation is capture avoiding. *) let subst subst_map f = + (** Given a list of bound variables [vs] and a substitution map [sm], discards mappings + of variables that are bound and renames bound variables that conflict with terms in + the RHS of substitutions. + *) let rename_vars vs sm = let not_bound id _ = not (List.mem_assoc id vs) in let sm1 = IdMap.filter not_bound sm in diff --git a/src/prover/axioms.ml b/src/prover/axioms.ml index 180c3798..5af05978 100644 --- a/src/prover/axioms.ml +++ b/src/prover/axioms.ml @@ -7,19 +7,7 @@ open Util (** {6 Variable and short-hand declarations} *) - -let mk_loc_var name = - let id = fresh_ident name in - fun struct_srt -> id, Loc struct_srt - -let mk_loc_field_var name = - let id = fresh_ident name in - fun struct_srt -> id, loc_field_sort struct_srt - -let mk_loc_set_var name = - let id = fresh_ident name in - fun struct_srt -> id, Set (Loc struct_srt) - + let l1 = mk_loc_var "?x" let l2 = mk_loc_var "?y" let l3 = mk_loc_var "?z" diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index bbb3820d..2e6c4bce 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -176,7 +176,7 @@ class dag = fun (terms: TermSet.t) -> | App (_, args, _) as appl -> let node_args = List.map convert_term args in let new_node = create_and_add appl (sorted_symbol_of appl |> Opt.get) node_args in - List.iter (fun n -> n#add_ccparent new_node) node_args; + List.iter (fun n -> n#find#add_ccparent new_node) node_args; new_node in let _ = TermSet.iter (fun t -> ignore (convert_term t)) terms in @@ -215,17 +215,32 @@ class dag = fun (terms: TermSet.t) -> Hashtbl.iter (fun _ n -> print_node n) nodes; Buffer.contents buffer - method add_term t = + method add_term t = + (*Printf.printf "Adding term to cc: %s\n" (string_of_term t);*) let n = convert_term t in let arg_opt = List.nth_opt n#get_args 0 in - arg_opt |> - Opt.iter (fun arg -> NodeSet.iter (fun n' -> if n' <> n && n#congruent n' then ignore @@ n#merge n') arg#ccpar); - _has_mods <- n#find = n; - + let has_mod = + arg_opt |> + Opt.fold + (fun _ arg -> + (*Printf.printf "Getting parents of %s\n" (string_of_term @@ self#get_term (arg#find));*) + NodeSet.exists (fun n' -> + (*Printf.printf "Checking congruence with: %s %b\n" + (string_of_term @@ self#get_term n') (n#congruent n');*) + n' <> n && n#congruent n' && n'#merge n) arg#ccpar) + true + in + _has_mods <- _has_mods || has_mod + method add_eq t1 t2 = let n1 = self#get_node t1 in let n2 = self#get_node t2 in - _has_mods <- n1#merge n2 + let has_mod = + if size_of_term t1 > size_of_term t2 + then n2#merge n1 + else n1#merge n2 + in + _has_mods <- _has_mods || has_mod method add_neq t1 t2 = let n1 = self#get_node t1 in @@ -336,6 +351,14 @@ class dag = fun (terms: TermSet.t) -> end +let print_classes cc_graph = + ignore + (List.fold_left (fun num cl -> + print_endline ("Class " ^ string_of_int num ^ ": " ^ (string_of_sort (sort_of (List.hd cl)))); + List.iter (fun t -> print_endline (" " ^ (string_of_term t))) cl; + print_newline (); + num + 1) 1 (List.sort compare (cc_graph#get_cc))) + (* TODO need implied equalities and watch lists *) let add_conjuncts_fixed_point cc_graph fs : dag = @@ -435,7 +458,12 @@ let add_terms gterms cc_graph = let new_terms = TermSet.diff gterms old_terms in let all_terms = TermSet.union old_terms new_terms in (* Add gterms to graph *) - TermSet.iter (cc_graph#add_term) new_terms; + TermSet.iter (fun t -> + let st = SimplifyGrass.simplify_term t in + if st <> t && not @@ TermSet.mem st old_terms then cc_graph#add_term st; + cc_graph#add_term t; + cc_graph#add_eq st t) + new_terms; (* Add disequalities between ADT terms with different top-level constructors *) let cterms = TermSet.filter @@ -508,14 +536,6 @@ let has_mods ccgraph = ccgraph#has_mods let reset ccgraph = ccgraph#reset; ccgraph -let print_classes cc_graph = - ignore - (List.fold_left (fun num cl -> - print_endline ("Class " ^ string_of_int num ^ ": " ^ (string_of_sort (sort_of (List.hd cl)))); - List.iter (fun t -> print_endline (" " ^ (string_of_term t))) cl; - print_newline (); - num + 1) 1 (List.sort compare (cc_graph#get_cc))) - let restrict_classes classes ts = List.filter (fun cc -> List.exists (fun t -> TermSet.mem t ts) cc) classes diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index 2f841e0f..7c955fa0 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -404,7 +404,7 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = (fun i t v -> match t with | Var (x, _) -> IdMap.add x i v | _ -> v) - w IdMap.empty + w v in let filters'_opt = (* Make sure that there is no entry for t in filters, respectively, that the entry is compatible *) @@ -502,12 +502,45 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = code' ) (Choose []) patterns + +let filter_term filters t sm = + List.for_all + (fun f -> match f with + | FilterNotNull -> + (match t with + | App (Null, [], _) -> false + | _ -> true) + | FilterSymbolNotOccurs sym -> + let rec not_occurs = function + | App (EntPnt, _, _) -> sym <> EntPnt + | App (sym1, _, _) when sym1 = sym -> false + | App (_, ts, _) -> List.for_all not_occurs ts + | _ -> true + in not_occurs t + | FilterReadNotOccurs (name, (arg_srts, res_srt)) -> + let rec not_occurs = function + | App (EntPnt, _, _) -> true + | App ((Read | ArrayCells), (App (FreeSym (name1, _), arg_ts, res_srt1) :: _ as ts), _) -> + let ok = + try + name1 <> name || + res_srt1 <> res_srt || + List.fold_left2 (fun acc t1 srt -> acc || sort_of t1 <> srt) false arg_ts arg_srts + with Invalid_argument _ -> true + in ok && List.for_all not_occurs ts + | App (_, ts, _) -> List.for_all not_occurs ts + | _ -> true + in not_occurs t + | FilterGeneric fn -> fn sm t + ) + filters + (** The E-matching abstract machine. * Runs the given E-matching code tree [code] on the E-graph [ccgraph]. * Yields a hash table mapping templates of type 'a to the computed variable substitutions. *) -let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = +let run ?(pr_tpl = (fun ppf _ -> ())) (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = let egrapha, egraphp = CC.get_egraph ccgraph in (* some utility functions for working with the e-graph *) let get_apps sym = @@ -647,7 +680,9 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = print_newline ();*) TermSet.iter (fun t -> TermMap.find_opt t fs |> - Opt.iter (List.iter (fun f -> Hashtbl.add insts f sm))) ts; + Opt.iter (List.iter (fun f -> + (*print_of_format (fun ppf f -> fprintf ppf " for %a\n" pr_tpl f) f stdout;*) + Hashtbl.add insts f sm))) ts; List.iter (fun f -> Hashtbl.add insts f sm) gs; run state stack | Filter (filters, next) :: stack -> @@ -659,10 +694,14 @@ let run (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = match t_filters with | Some (vs, fs) -> let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in - InstGen.filter_term fs (subst_term sm t) sm + (*Printf.printf "Filtering %d, %s...\n" (IdMap.cardinal vs) (string_of_term (subst_term sm t));*) + filter_term fs (subst_term sm t) sm | None -> true) ts in + (*Printf.printf "filter;\nactive terms: "; + print_list stdout pr_term (TermSet.elements ts'); + Printf.printf "\n";*) if TermSet.is_empty ts' then run state stack else run ts' (next :: stack) @@ -874,25 +913,28 @@ let compile_term_generators_to_ematch_code generators = compile patterns let generate_terms_from_code code ccgraph = - let rec round i reps = - (*Printf.printf "Generating terms, round %d...\n" i; - CC.print_classes ccgraph;*) - let insts = run code ccgraph in + let rec round i has_mods ccgraph = + (*Printf.printf "Generating terms, round %d...\n" i; flush stdout; + CC.print_classes ccgraph;*) + (*let terms = CC.get_terms ccgraph in*) + let insts = run ~pr_tpl:pr_term_list code ccgraph in let new_terms = Hashtbl.fold (fun ts sm new_terms -> List.fold_left (fun new_terms gen_term -> let t = subst_term sm gen_term in - (*let _ = print_endline (" Adding generated term " ^ string_of_term t); flush stdout in *) + (*let _ = + if not @@ TermSet.mem t terms then + print_endline (" Adding generated term " ^ string_of_term gen_term ^ " -> " ^ string_of_term t); flush stdout + in*) TermSet.add t new_terms) new_terms ts) insts TermSet.empty in - let _ = CC.add_terms new_terms ccgraph in - let new_reps = TermSet.fold (fun t -> CC.NodeSet.add (CC.rep_of_term ccgraph t)) new_terms CC.NodeSet.empty in - let reps = CC.NodeSet.fold (fun n -> CC.NodeSet.add (CC.find_rep ccgraph n)) reps CC.NodeSet.empty in - if CC.NodeSet.subset new_reps reps then reps else round (i + 1) (CC.NodeSet.union new_reps reps) + Hashtbl.clear insts; + let ccgraph = CC.add_terms new_terms ccgraph in + if not @@ CC.has_mods ccgraph then has_mods, ccgraph + else round (i + 1) true (CC.reset ccgraph) in - let reps = round 0 (CC.get_reps ccgraph) in - reps + round 0 false ccgraph let generate_terms_from_code code ccgraph = measure_call "EMatching.generate_terms_from_code" (generate_terms_from_code code) ccgraph diff --git a/src/prover/prover.ml b/src/prover/prover.ml index fa9d4d76..eef11486 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -2,7 +2,6 @@ open Grass open GrassUtil open Util open Axioms -open InstGen let encode_labels fs = let mk_label annots f = @@ -193,7 +192,7 @@ let instantiate_and_prove session fs = CongruenceClosure.add_terms gts |> CongruenceClosure.add_conjuncts fs in - let _ = EMatching.generate_terms_from_code tgcode cc_graph in + let _, cc_graph = EMatching.generate_terms_from_code tgcode cc_graph in let round1 fs_inst gts_inst cc_graph = let equations = List.filter (fun f -> is_horn false [f]) fs_inst in let ground_fs = List.filter is_ground fs_inst in @@ -237,26 +236,35 @@ let instantiate_and_prove session fs = in*) let rec saturate i fs_inst gts_inst0 cc_graph = (*Printf.printf "Saturate iteration %d\n" i; flush stdout;*) + let has_mods2, cc_graph = + cc_graph |> + EMatching.generate_terms_from_code tgcode + in + let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in + let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in let gts_inst = ground_terms_acc ~include_atoms:true gts_inst0 (mk_and fs_inst) in let fs, gts_inst = generate_adt_terms fs gts_inst in + let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in + let cc_graph = + cc_graph |> + CongruenceClosure.add_terms gts_inst |> + CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) + in + let has_mods1 = CongruenceClosure.has_mods cc_graph in (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) - let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in - let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in - let cc_graph = CongruenceClosure.add_terms gts_inst cc_graph in - (*let generators = if i > 1 && false then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in*) - let _ = EMatching.generate_terms_from_code tgcode cc_graph in let gts_inst = CongruenceClosure.get_terms cc_graph in - if i > 1 && (not !Config.propagate_reads || CongruenceClosure.has_mods cc_graph) then + (*let generators = if i > 1 && false then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in*) + if not !Config.propagate_reads || not (has_mods1 || has_mods2) + then + (*let _ = + if not @@ TermSet.subset gts_inst gts_inst0 + then print_list stdout pr_term (TermSet.diff gts_inst gts_inst0 |> TermSet.elements) + in + let _ = assert (TermSet.subset gts_inst gts_inst0) in*) rev_concat [fs_inst; implied_eqs], gts_inst, cc_graph else - let cc_graph = - cc_graph |> - (*measure_call "cc_gen" (fun cc_graph -> cc_graph |> CongruenceClosure.add_terms gts_inst) |>*) - CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) - in (*let fs_inst = instantiate_with_terms fs1 (CongruenceClosure.get_classes cc_graph) in*) - let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in saturate (i + 1) fs_inst gts_inst (CongruenceClosure.reset cc_graph) in let saturate i fs_inst gts_inst0 = measure_call "saturate" (saturate i fs_inst gts_inst0) in diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 0711849b..d4664dcf 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -3,15 +3,8 @@ open Util open Grass open GrassUtil -open InstGen open Axioms open SimplifyGrass - -(** Compute the set of generated ground terms for formulas [fs] *) -let generated_ground_terms fs = - let _, generators = open_axioms isFunVar fs in - let gts = generate_terms generators (ground_terms ~include_atoms:true (mk_and fs)) in - gts (** Eliminate all implicit and explicit existential quantifiers using skolemization. ** Assumes that [f] is typed and in negation normal form. *) diff --git a/src/prover/simplifyGrass.ml b/src/prover/simplifyGrass.ml index 2039a380..bc9e55fa 100644 --- a/src/prover/simplifyGrass.ml +++ b/src/prover/simplifyGrass.ml @@ -80,8 +80,8 @@ let massage_field_reads fs = | Map ([Loc s], _) -> s | _ -> failwith "massage_field_reads: field has not Map type" in - let l1 = Axioms.l1 sid in - let loc1 = Axioms.loc1 sid in + let l1 = mk_loc_var "?x" sid in + let loc1 = mk_var (snd l1) (fst l1) in let f1 = annotate (mk_and [mk_btwn fld arg t t; @@ -155,10 +155,51 @@ let rec simplify_sets fs = if IdMap.is_empty submap then fs2 else simplify_sets (List.map (subst_consts submap) fs2) +let simplify_int_term t = + let rec simp t has_simp = match t with + | App ((Plus | Mult as sym1), [App ((Plus | Mult as sym2), [t1; App (IntConst i1, [], _)], _); + App (IntConst i2, [], _)], srt) when sym1 = sym2 + -> + let op = match sym1 with + | Plus -> Int64.add + | Mult -> Int64.mul + | _ -> assert false + in + simp (App (sym1, [t1; App (IntConst (op i1 i2), [], srt)], srt)) has_simp + | App ((Plus | Minus | Mult | Div as sym), [App (IntConst i1, [], _); App (IntConst i2, [], _)], srt) -> + let op = match sym with + | Plus -> Int64.add + | Minus -> fun i1 i2 -> Int64.add i1 (Int64.neg i2) + | Mult -> Int64.mul + | Div -> Int64.div + | _ -> assert false + in + App (IntConst (op i1 i2), [], srt), true + | App (sym, ts, srt) -> + let ts1, has_simp1 = List.fold_right (fun t (ts1, has_simp1) -> + let t1, has_simp1 = simp t has_simp1 in + t1 :: ts1, has_simp1) + ts ([], false) + in + if has_simp1 then simp (App (sym, ts1, srt)) has_simp + else App (sym, ts1, srt), has_simp + | _ -> t, has_simp + in + simp t false |> fst + +let simplify_term t = simplify_int_term t + +let simplify_ints fs = + let fs1 = List.map (fun f -> map_terms simplify_int_term f) fs in + fs1 + let simplify_one_sets f = split_ands [f] |> simplify_sets |> mk_and -let simplify fs = simplify_sets fs +let simplify fs = + fs |> + simplify_sets |> + simplify_ints diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index e4d8f174..8a107569 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -132,7 +132,7 @@ let add_match_filters = let flt, aux_matches = TermSet.fold (fun t (flt, aux_matches) -> match t with - | App ((FreeSym _ | Constructor _ as sym), (_ :: _ as ts), srt) + | App ((ArrayOfCell | IndexOfCell | FreeSym _ | Constructor _ as sym), (_ :: _ as ts), srt) when sym_of_e <> Some sym && occurs_below_var_terms_of_e ts -> add (FilterSymbolNotOccurs sym) flt, aux_matches | App (Read, ([App (FreeSym sym, [], srt); l] as ts), _) From 669d9409afeed47c447fe7d666f0f474f1bbdce2 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 11 Sep 2018 01:10:43 -0400 Subject: [PATCH 084/118] throw out old E-matching engine - good riddance! --- src/prover/instGen.ml | 406 ------------------------------------------ 1 file changed, 406 deletions(-) delete mode 100644 src/prover/instGen.ml diff --git a/src/prover/instGen.ml b/src/prover/instGen.ml deleted file mode 100644 index ea1c0695..00000000 --- a/src/prover/instGen.ml +++ /dev/null @@ -1,406 +0,0 @@ -open Util -open Grass -open GrassUtil -open Axioms - -module TermListSet = Set.Make(struct - type t = term list - let compare = compare - end) - -module EGraph = Map.Make(struct - type t = term * symbol - let compare = compare - end) - -let choose_rep_terms classes = - let find_rep cl = - try List.find (function App (_, [], _) -> true | _ -> false) cl - with Not_found -> List.hd cl - in - let reps, rep_map = - List.fold_left (fun (reps, rep_map) cl -> - let cl_rep : term = find_rep cl in - (TermSet.add cl_rep reps, - List.fold_left (fun rep_map t -> TermMap.add t cl_rep rep_map) rep_map cl)) - (TermSet.empty, TermMap.empty) classes - in - let egraph = - List.fold_left (fun egraph -> - function - | App (sym, ts, _) as t -> - begin - try - let t_rep = TermMap.find t rep_map in - let ts_reps = - List.map (fun t -> TermMap.find t rep_map) ts - in - let other_ts_reps = - try EGraph.find (t_rep, sym) egraph - with Not_found -> TermListSet.empty - in - EGraph.add (t_rep, sym) (TermListSet.add ts_reps other_ts_reps) egraph - with Not_found -> - egraph - end - | _ -> egraph) - EGraph.empty (List.concat classes) - in reps, egraph - -let filter_term filters t sm = - List.for_all - (fun f -> match f with - | FilterNotNull -> - (match t with - | App (Null, [], _) -> false - | _ -> true) - | FilterSymbolNotOccurs sym -> - let rec not_occurs = function - | App (EntPnt, _, _) -> sym <> EntPnt - | App (sym1, _, _) when sym1 = sym -> false - | App (_, ts, _) -> List.for_all not_occurs ts - | _ -> true - in not_occurs t - | FilterReadNotOccurs (name, (arg_srts, res_srt)) -> - let rec not_occurs = function - | App (EntPnt, _, _) -> true - | App ((Read | ArrayCells), (App (FreeSym (name1, _), arg_ts, res_srt1) :: _ as ts), _) -> - let ok = - try - name1 <> name || - res_srt1 <> res_srt || - List.fold_left2 (fun acc t1 srt -> acc || sort_of t1 <> srt) false arg_ts arg_srts - with Invalid_argument _ -> true - in ok && List.for_all not_occurs ts - | App (_, ts, _) -> List.for_all not_occurs ts - | _ -> true - in not_occurs t - | FilterGeneric fn -> fn sm t - ) - filters - - -let ematch filters t rep_terms egraph subst_maps = - let rec ematches ts1 ts2s subst_maps = - TermListSet.fold - (fun ts2 out_subst_maps -> - try - let out_subst_maps1 = - List.fold_left2 - (fun subst_maps1 t1 t2 -> - match subst_maps with - | [] -> [] - | _ -> ematch t1 t2 subst_maps1) - subst_maps ts1 ts2 - in - List.rev_append out_subst_maps1 out_subst_maps - with Invalid_argument _ -> out_subst_maps) - ts2s [] - and ematch t1 t2 subst_maps = - (*print_endline ("matching " ^ string_of_term t1 ^ " with " ^ string_of_term t2);*) - match t1 with - | App (sym1, ts1, srt1) when srt1 = sort_of t2 -> - begin - try - let ts2s = EGraph.find (t2, sym1) egraph in - ematches ts1 ts2s subst_maps - with Not_found -> - (*print_endline "fail 1";*) - [] - end - | Var (x, srt1) when srt1 = sort_of t2 -> - List.fold_left - (fun out_subst_maps sm -> - match IdMap.find_opt x sm with - | Some t1 -> - if t1 = t2 - then sm :: out_subst_maps - else out_subst_maps - | None -> IdMap.add x t2 sm :: out_subst_maps) - [] subst_maps - | _ -> - (*print_endline (string_of_sort (sort_of t1) ^ " " ^ string_of_sort (sort_of t2) ^ " fail 2");*) - [] - in - let terms = - TermSet.fold - (fun t terms -> TermListSet.add [t] terms) - rep_terms TermListSet.empty - in - let subst_maps1 = ematches [t] terms subst_maps in - List.filter (fun sm -> filter_term filters (subst_term sm t) sm) subst_maps1 - -let generate_terms generators ground_terms = - let generators = - let remove_generic_filters (ms, ts) = - List.map (function Match (m, fs) -> (m, List.filter (function FilterGeneric _ -> false | _ -> true) fs)) ms, ts - in - remove_duplicates (fun g1 g2 -> remove_generic_filters g1 = remove_generic_filters g2) generators in - let rec add_terms new_terms t = - if TermSet.mem t new_terms then new_terms else - match t with - | App (sym, ts, _) -> - List.fold_left add_terms (TermSet.add t new_terms) ts - | Var _ -> failwith ("InstGen.generate_terms: ground term expected, found " ^ (string_of_term t)) - in - let new_terms = - TermSet.fold (fun t acc -> add_terms acc t) - ground_terms TermSet.empty - in - let find_matches candidates t1 sm = - if is_ground_term t1 then - if TermSet.mem t1 candidates then [t1, sm] else [] - else - let rec mt sm t1 t2 = - match t1, t2 with - | App (sym1, ts1, srt1), App (sym2, ts2, srt2) - when sym1 = sym2 && srt1 = srt2 && List.length ts1 = List.length ts2 -> - List.fold_left2 (fun sm_opt t1 t2 -> - match sm_opt with - | None -> None - | Some sm -> mt sm t1 t2) - (Some sm) ts1 ts2 - | Var (x, srt1), t2 when srt1 = sort_of t2 -> - if IdMap.mem x sm then - if IdMap.find x sm = t2 then Some sm - else None - else Some (IdMap.add x t2 sm) - | _, _ -> - (*Printf.printf "%s != %s\n" (string_of_term t1) (string_of_term t2); - Printf.printf "%s != %s\n" (string_of_sort (sort_of t1)) (string_of_sort (sort_of t2));*) - None - in - TermSet.fold (fun t2 subst_maps -> - match mt sm t1 t2 with - | None -> - (*Printf.printf "Failed to match %s and %s\n" (string_of_term t1) (string_of_term t2);*) - subst_maps - | Some sm -> - (*Printf.printf "Succeeded to match %s and %s\n" (string_of_term t1) (string_of_term t2);*) - (t2, sm) :: subst_maps) - candidates [] - in - let rec generate round new_terms old_terms = function - | (guards, gen_terms) :: generators1 -> - (*print_string "======\nGenerator:"; - print_of_format pr_annot [TermGenerator (guards, gen_terms)] stdout; - print_newline ();*) - let subst_maps = - List.fold_left (fun subst_maps -> function Match (t, filters) -> - (*print_endline (" matching " ^ (string_of_term t)); - List.iter (fun f -> print_endline (" subject to " ^ (string_of_filter f))) filters;*) - let new_subst_maps = - List.fold_left - (fun new_subst_maps sm -> - let matches = find_matches new_terms (subst_term sm t) sm in - Util.filter_rev_map - (fun (t_matched, sm) -> filter_term filters t_matched sm) - (fun (t_matched, sm) -> sm) - matches |> - fun new_matches -> List.rev_append new_matches new_subst_maps - ) [] subst_maps - in - new_subst_maps) - [IdMap.empty] guards - in - let new_terms1 = - List.fold_left - (fun acc sm -> - List.fold_left - (fun acc gen_term -> - let t = subst_term sm gen_term in - (*let _ = print_endline (" Adding generated term " ^ string_of_term t) in *) - add_terms acc t) - acc gen_terms - ) - new_terms subst_maps - in - generate round new_terms1 old_terms generators1 - | [] -> - (*Printf.printf "Round: %d\n" round;*) - if round < !Config.term_gen_max_rounds && new_terms <> old_terms - then generate (round + 1) new_terms new_terms generators - else new_terms - in - generate 1 new_terms ground_terms generators - -let generate_terms generators = - measure_call "InstGen.generate_terms" (generate_terms generators) - -let generate_instances stratify axioms rep_terms egraph = - (* *) - let epr_axioms, axioms = - List.partition - (fun f -> IdSrtSet.is_empty (vars_in_fun_terms f)) - axioms - in - (*print_endline "EPR:"; - List.iter print_endline (List.map string_of_form epr_axioms);*) - (*let _ = print_endline "Candidate axioms for instantiation:" in - let _ = print_forms stdout axioms in*) - let instantiate acc f = - let fvars0 = sorted_free_vars f in - let fvars = IdSrtSet.inter fvars0 (vars_in_fun_terms f) in - (* filter out stratified variables *) - let fvars, strat_vars = - let merge_map k a b = match (a,b) with - | (Some a, Some b) -> Some (a @ b) - | (Some c, None) | (None, Some c) -> Some c - | (None, None) -> None - in - let rec gen_tpe acc t = match t with - | App (_, ts, srt) -> - List.fold_left - (IdMap.merge merge_map) - IdMap.empty - (List.map (gen_tpe (srt :: acc)) ts) - | Var (id, _) -> IdMap.add id acc IdMap.empty - in - let gen_map = - TermSet.fold - (fun t acc -> IdMap.merge merge_map (gen_tpe [] t) acc) - (fun_terms_with_vars f) - IdMap.empty - in - if stratify then - IdSrtSet.partition - (fun (id, srt) -> - try - let generating = IdMap.find id gen_map in - not (List.for_all (TypeStrat.is_stratified srt) generating) - with Not_found -> - begin - Debug.warn (fun () -> "BUG in stratification: " ^ (string_of_ident id) ^ "\n"); - true - end - ) - fvars - else fvars, IdSrtSet.empty - in - let strat_var_ids = IdSrtSet.fold (fun (id, _) acc -> IdSet.add id acc) strat_vars IdSet.empty in - (* collect all terms in which free variables appear below function symbols *) - let fun_terms, fun_vars, strat_terms = - let rec tt (fun_terms, fun_vars, strat_terms) t = - match t with - | App (sym, _ :: _, srt) when srt <> Bool -> - let tvs = fv_term t in - if IdSet.is_empty tvs then - fun_terms, fun_vars, strat_terms - else if IdSet.is_empty (IdSet.inter strat_var_ids tvs) - then TermSet.add t fun_terms, IdSet.union tvs fun_vars, strat_terms - else fun_terms, fun_vars, TermSet.add t strat_terms - | App (_, ts, _) -> - List.fold_left tt (fun_terms, fun_vars, strat_terms) ts - | _ -> fun_terms, fun_vars, strat_terms - in fold_terms tt (TermSet.empty, IdSet.empty, TermSet.empty) f - in - let unmatched_vars = - IdSrtSet.fold (fun (id, _) acc -> - if IdSet.mem id fun_vars then acc - else IdSet.add id acc) fvars IdSet.empty - in - let fun_terms, fun_vars = - TermSet.fold (fun t (fun_terms, fun_vars) -> - let vs = fv_term t in - if IdSet.is_empty (IdSet.inter unmatched_vars vs) - then fun_terms, fun_vars - else TermSet.add t fun_terms, IdSet.union vs fun_vars) - strat_terms (fun_terms, fun_vars) - in - let strat_vars1 = - IdSrtSet.filter (fun (id, _) -> not (IdSet.mem id fun_vars)) strat_vars - in - (* extract patterns separately to obtain auxilary filters *) - let patterns = extract_patterns f in - let fun_terms_with_filters = - TermSet.fold (fun t acc -> - try (t, List.assoc t patterns) :: acc - with Not_found -> (t, []) :: acc) - fun_terms [] - in - let fun_terms_with_filters = - List.sort (fun (t1, _) (t2, _) -> - - (compare - (IdSet.cardinal (fv_term t1)) - (IdSet.cardinal (fv_term t2)))) - fun_terms_with_filters - in - (* close the strat_vars so they are not instantiated *) - let f = mk_forall (IdSrtSet.elements strat_vars1) f in - (* generate substitution maps *) - let subst_maps () = - (* generate substitution maps for variables that appear below function symbols *) - let subst_maps = - List.fold_left - (fun subst_maps (t, fs) -> - (*print_endline ("Matching term " ^ string_of_term t);*) - let subst_maps1 = ematch fs t rep_terms egraph subst_maps in - (*List.iter print_subst_map subst_maps1;*) - subst_maps1 - ) - [IdMap.empty] fun_terms_with_filters - in - subst_maps - in - let subst_maps = measure_call "InstGen.subst_maps" subst_maps () in - let _ = if Debug.is_debug 1 && subst_maps <> [IdMap.empty] then - begin - print_endline "--------------------"; - print_endline (string_of_form f); - print_string "all vars: "; - print_endline (String.concat ", " (List.map string_of_ident (List.map fst (IdSrtSet.elements fvars0)))); - print_string "strat vars: "; - print_endline (String.concat ", " (List.map string_of_ident (List.map fst (IdSrtSet.elements strat_vars1)))); - print_string "unmatched vars: "; - print_endline (String.concat ", " (List.map string_of_ident (IdSet.elements unmatched_vars))); - print_string "inst vars: "; - print_endline (String.concat ", " (List.map string_of_ident (IdSet.elements fun_vars))); - print_string "fun terms: "; - print_endline (String.concat ", " (List.map string_of_term (List.map fst fun_terms_with_filters))); - Printf.printf "# of insts: %d\n" (List.length subst_maps); - print_endline "subst_maps:"; - List.iter print_subst_map subst_maps; - print_endline "--------------------" - end - in - (* generate instances of axiom *) - measure_call "InstGen.substitute" (List.fold_left - (fun acc subst_map -> (subst subst_map f) :: acc) acc) subst_maps - in - List.fold_left instantiate epr_axioms axioms - -let generate_instances stratify axioms rep_terms = - measure_call "InstGen.generate_instances" (generate_instances stratify axioms rep_terms) - -let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) axioms classes0 = - if not !Config.instantiate && not force then axioms else - (* remove theory atoms from congruence classes *) - let filter_term t = - true || sort_of t <> Bool || - Opt.get_or_else false - (Opt.map - (((=) Frame) ||| - is_free_symbol ||| - ((=) (BoolConst true)) ||| - ((=) (BoolConst false))) - (symbol_of t)) - in - let classes = - let classes2 = List.map (List.filter filter_term) classes0 in - List.filter (fun x -> x <> []) classes2 - in - let _ = - if Debug.is_debug 1 then - ignore - (List.fold_left (fun num cl -> - print_endline ("Class " ^ string_of_int num ^ ": " ^ (string_of_sort (sort_of (List.hd cl)))); - List.iter (fun t -> print_endline (" " ^ (string_of_term t))) cl; - print_newline (); - num + 1) 1 (List.sort compare classes)) - in - (* choose representatives for instantiation *) - let reps_f, egraph = choose_rep_terms classes in - generate_instances stratify axioms reps_f egraph - -let instantiate_with_terms ?(force=false) ?(stratify=(!Config.stratify)) axioms = - measure_call "InstGen.instantiate_with_terms" (instantiate_with_terms ~force:force ~stratify:stratify axioms) From 3ba2e551d7816f24144703d9762e9915380542bf Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 17 Sep 2018 14:02:08 -0400 Subject: [PATCH 085/118] some clean-up --- src/formulas/grassUtil.ml | 2 +- src/prover/congruenceClosure.ml | 8 ++++++++ src/prover/eMatching.ml | 7 +++++-- src/prover/prover.ml | 32 ++++++++++++-------------------- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index 6a96e59b..babb7e11 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -22,7 +22,7 @@ let global_scope = } let merge_src_pos pos1 pos2 = - assert (pos1.sp_file = "" || pos2.sp_file = "" || pos1.sp_file = pos2.sp_file); + (*assert (pos1.sp_file = "" || pos2.sp_file = "" || pos1.sp_file = pos2.sp_file);*) let file = max pos1.sp_file pos2.sp_file in let start_line, start_col = if pos1.sp_start_line < pos2.sp_start_line diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 2e6c4bce..e306d619 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -476,6 +476,14 @@ let add_terms gterms cc_graph = cc_graph#add_neq t1 t2 | _ -> ()) all_terms + | App (Destructor did, [App (Constructor cid, args, Adt (adt, adts))], _) as t -> + let cnstrs = List.assoc adt adts in + let destrs = List.assoc cid cnstrs in + let _, arg = + List.combine destrs args |> + List.find (fun ((did', _), _) -> did = did') + in + cc_graph#add_eq t arg | _ -> ()) cterms; cc_graph diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index 7c955fa0..a2f8f45f 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -503,6 +503,7 @@ let compile (patterns: ('a pattern) list): 'a ematch_code = ) (Choose []) patterns +(** Check whether term [t] and substitution [sm] satisfy the filters [filters]. *) let filter_term filters t sm = List.for_all (fun f -> match f with @@ -894,7 +895,7 @@ let instantiate_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms ccgr let code, patterns = compile_axioms_to_ematch_code ~force:force ~stratify:stratify axioms in instantiate_axioms_from_code patterns code ccgraph - +(** Compile term generators [generators] into an E-matching code tree. *) let compile_term_generators_to_ematch_code generators = let generators = let remove_generic_filters (ms, ts) = @@ -912,6 +913,9 @@ let compile_term_generators_to_ematch_code generators = let patterns = List.fold_left generate_pattern [] generators in compile patterns +(** Generate new terms according to term generators encoded in [code] and E-graph [ccgraph]. + * The new terms are added to the E-graph. Returns a Boolean indicating whether new terms + * were derived (modulo equality) and the new E-graph. *) let generate_terms_from_code code ccgraph = let rec round i has_mods ccgraph = (*Printf.printf "Generating terms, round %d...\n" i; flush stdout; @@ -929,7 +933,6 @@ let generate_terms_from_code code ccgraph = TermSet.add t new_terms) new_terms ts) insts TermSet.empty in - Hashtbl.clear insts; let ccgraph = CC.add_terms new_terms ccgraph in if not @@ CC.has_mods ccgraph then has_mods, ccgraph else round (i + 1) true (CC.reset ccgraph) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index eef11486..17781009 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -162,20 +162,20 @@ let instantiate_and_prove session fs = | _ -> gts) gts gts in - let generate_adt_terms fs gts = + let generate_adt_terms gts = TermSet.fold - (fun t (fs, gts) -> match t with + (fun t gts -> match t with | App (Constructor id, ts, Adt (ty_id, adts)) -> let adt = List.assoc ty_id adts in let destrs = List.assoc id adt in List.fold_left2 - (fun (fs, gts) arg (d_id, d_srt) -> + (fun gts arg (d_id, d_srt) -> let d_srt = unfold_adts adts d_srt in let d = GrassUtil.mk_app d_srt (Destructor d_id) [t] in - GrassUtil.mk_eq arg d :: fs, TermSet.add d gts) - (fs, TermSet.add t gts) ts destrs - | t -> fs, TermSet.add t gts - ) gts (fs, gts) + TermSet.add d gts) + (TermSet.add t gts) ts destrs + | t -> TermSet.add t gts + ) gts gts in let fs1, generators = open_axioms isFunVar fs1 in let btwn_gen = btwn_field_generators fs in @@ -200,7 +200,7 @@ let instantiate_and_prove session fs = let eqs = EMatching.instantiate_axioms_from_code patterns code cc_graph in (*let eqs = instantiate_with_terms equations (CongruenceClosure.get_classes cc_graph) in*) let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in - let fs, gts1 = generate_adt_terms fs gts1 in + let gts1 = generate_adt_terms gts1 in let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in let cc_graph = cc_graph |> @@ -215,7 +215,6 @@ let instantiate_and_prove session fs = let round2 fs_inst gts_inst cc_graph = let fs_inst0 = fs_inst in let gts_known = generate_knowns gts in - (*let gts_inst = generate_terms generators (TermSet.union gts_inst0 gts2_atoms) in*) let core_terms = let gts_a = ground_terms (mk_and fs) in TermSet.fold (fun t acc -> @@ -240,10 +239,12 @@ let instantiate_and_prove session fs = cc_graph |> EMatching.generate_terms_from_code tgcode in - let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in let gts_inst = ground_terms_acc ~include_atoms:true gts_inst0 (mk_and fs_inst) in - let fs, gts_inst = generate_adt_terms fs gts_inst in + let gts_inst = generate_adt_terms gts_inst in + let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in + (*print_endline "Implied equalities:"; + print_endline (string_of_form (mk_and implied_eqs));*) let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in let cc_graph = cc_graph |> @@ -251,20 +252,11 @@ let instantiate_and_prove session fs = CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) in let has_mods1 = CongruenceClosure.has_mods cc_graph in - (*print_endline "Implied equalities:"; - print_endline (string_of_form (mk_and implied_eqs));*) let gts_inst = CongruenceClosure.get_terms cc_graph in - (*let generators = if i > 1 && false then Reduction.get_read_propagators gts_inst else btwn_gen @ generators in*) if not !Config.propagate_reads || not (has_mods1 || has_mods2) then - (*let _ = - if not @@ TermSet.subset gts_inst gts_inst0 - then print_list stdout pr_term (TermSet.diff gts_inst gts_inst0 |> TermSet.elements) - in - let _ = assert (TermSet.subset gts_inst gts_inst0) in*) rev_concat [fs_inst; implied_eqs], gts_inst, cc_graph else - (*let fs_inst = instantiate_with_terms fs1 (CongruenceClosure.get_classes cc_graph) in*) saturate (i + 1) fs_inst gts_inst (CongruenceClosure.reset cc_graph) in let saturate i fs_inst gts_inst0 = measure_call "saturate" (saturate i fs_inst gts_inst0) in From 89c8abcb67be5639f2c7601cf22c5a15012d7a86 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Mon, 17 Sep 2018 14:23:30 -0400 Subject: [PATCH 086/118] command line args: allow multiple procedure names --- src/main/grasshopper.ml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/grasshopper.ml b/src/main/grasshopper.ml index d9f219d4..e13e00d9 100644 --- a/src/main/grasshopper.ml +++ b/src/main/grasshopper.ml @@ -141,20 +141,24 @@ let check_spl_program spl_prog proc = match proc with | None -> Prog.fold_procs (check simple_prog) true simple_prog | Some p -> + (* Split string to get names of multiple procedures *) let procs = - Prog.find_proc_with_deps simple_prog (p, 0) + p |> String.split_on_char ' ' + |> List.map (fun p -> + match Prog.find_proc_with_deps simple_prog (p, 0) with + | [] -> + let available = + Prog.fold_procs + (fun acc proc -> + let name = Prog.name_of_proc proc in + "\t" ^ Grass.string_of_ident name ^ "\n" ^ acc) + "" prog + in + failwith ("Could not find a procedure named " ^ p ^ + ". Available procedures are:\n" ^ available) + | ps -> ps) + |> List.concat |> List.sort_uniq compare in - if procs = [] then begin - let available = - Prog.fold_procs - (fun acc proc -> - let name = Prog.name_of_proc proc in - "\t" ^ Grass.string_of_ident name ^ "\n" ^ acc) - "" prog - in - failwith ("Could not find a procedure named " ^ p ^ - ". Available procedures are:\n" ^ available) - end; List.fold_left (check simple_prog) true procs From 7cd995ff022cedd7d5e7e3339084777b3220b169 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 17 Sep 2018 18:21:23 -0400 Subject: [PATCH 087/118] minor fix --- src/prover/congruenceClosure.ml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index e306d619..063a7891 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -475,16 +475,14 @@ let add_terms gterms cc_graph = | App (Constructor id2, _, srt2) as t2 when srt1 = srt2 && id1 <> id2 -> cc_graph#add_neq t1 t2 | _ -> ()) - all_terms + cterms | App (Destructor did, [App (Constructor cid, args, Adt (adt, adts))], _) as t -> let cnstrs = List.assoc adt adts in let destrs = List.assoc cid cnstrs in - let _, arg = - List.combine destrs args |> - List.find (fun ((did', _), _) -> did = did') - in - cc_graph#add_eq t arg - | _ -> ()) cterms; + List.combine destrs args |> + List.find_opt (fun ((did', _), _) -> did = did') |> + Opt.iter (fun (_, arg) -> cc_graph#add_eq t arg) + | _ -> ()) all_terms; cc_graph let get_implied_equalities cc_graph = From 2b0f19281705421745ad4821599a74d9d47b25ce Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 24 Sep 2018 10:44:19 -0400 Subject: [PATCH 088/118] allow non-linear constraints --- src/backends/smtlib/smtLibSolver.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backends/smtlib/smtLibSolver.ml b/src/backends/smtlib/smtLibSolver.ml index 4a3ca4f9..9c2b7c5c 100644 --- a/src/backends/smtlib/smtLibSolver.ml +++ b/src/backends/smtlib/smtLibSolver.ml @@ -593,7 +593,8 @@ let init_session session sign = (if !Config.encode_fields_as_arrays then "A" else "") ^ "UF" ^ (if has_adt then solver.info.dt_logic_string else "") ^ - (if !Config.use_bitvector then "BV" else if has_int then "LIA" else "") ^ + (if !Config.use_bitvector then "BV" else + if has_int then if has_adt then "LIA" else "NIA" else "") ^ (if solver.info.has_set_theory && !Config.use_set_theory then "FS" else "") in set_logic state.out_chan logic_str; From 67c27a50d46be4c5754d4fa8451638315cc516a9 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 24 Sep 2018 10:44:43 -0400 Subject: [PATCH 089/118] use term generators for adt term genreation --- src/prover/prover.ml | 8 ++++---- src/prover/reduction.ml | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 17781009..7a263446 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -162,7 +162,7 @@ let instantiate_and_prove session fs = | _ -> gts) gts gts in - let generate_adt_terms gts = + (*let generate_adt_terms gts = TermSet.fold (fun t gts -> match t with | App (Constructor id, ts, Adt (ty_id, adts)) -> @@ -176,7 +176,7 @@ let instantiate_and_prove session fs = (TermSet.add t gts) ts destrs | t -> TermSet.add t gts ) gts gts - in + in*) let fs1, generators = open_axioms isFunVar fs1 in let btwn_gen = btwn_field_generators fs in let gts = ground_terms ~include_atoms:true (mk_and fs) in @@ -200,7 +200,7 @@ let instantiate_and_prove session fs = let eqs = EMatching.instantiate_axioms_from_code patterns code cc_graph in (*let eqs = instantiate_with_terms equations (CongruenceClosure.get_classes cc_graph) in*) let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in - let gts1 = generate_adt_terms gts1 in + (* let gts1 = generate_adt_terms gts1 in*) let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in let cc_graph = cc_graph |> @@ -241,7 +241,7 @@ let instantiate_and_prove session fs = in let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in let gts_inst = ground_terms_acc ~include_atoms:true gts_inst0 (mk_and fs_inst) in - let gts_inst = generate_adt_terms gts_inst in + (*let gts_inst = generate_adt_terms gts_inst in*) let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index d4664dcf..e070820e 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -292,18 +292,34 @@ let get_read_propagators gts = let s = mk_var adt_srt s in let t = mk_var adt_srt t in let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in - List.fold_left - (fun propagators (destr, srt) -> - ([Match (mk_eq_term s t, []); - (* s == t, s.destr -> t.destr *) - Match (mk_destr srt destr s, [])], - [mk_destr srt destr t]) :: - ([Match (mk_eq_term s t, []); - (* s == t, t.destr -> s.destr *) - Match (mk_destr srt destr t, [])], - [mk_destr srt destr s]) :: propagators - ) - propagators destrs + let propagators = + List.fold_left + (fun propagators (destr, srt) -> + let srt = unfold_adts adts srt in + ([Match (mk_eq_term s t, []); + (* s == t, s.destr -> t.destr *) + Match (mk_destr srt destr s, [])], + [mk_destr srt destr t]) :: + ([Match (mk_eq_term s t, []); + (* s == t, t.destr -> s.destr *) + Match (mk_destr srt destr t, [])], + [mk_destr srt destr s]) :: propagators + ) + propagators destrs + in + List.fold_left (fun propagators (cid, destrs) -> + let args = + List.map (fun (destr, srt) -> + let srt = unfold_adts adts srt in + mk_var srt (fresh_ident "?v")) + destrs + in + let t = mk_constr adt_srt cid args in + let gen_terms = + List.map (fun (destr, srt) -> mk_destr srt destr t) destrs + in + ([Match (t, [])], gen_terms) :: propagators) + propagators cstrs ) propagators adts | Loc (ArrayCell srt) -> fun propagators -> From dd8ac09fd5d0a6e33413d77f01713e380d395740 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 24 Sep 2018 14:12:37 -0400 Subject: [PATCH 090/118] use term generators for adt term genreation --- src/prover/#eMatching.ml# | 943 ++++++++++++++++++++++++++++++++++++++ src/prover/eMatching.ml | 2 +- src/prover/reduction.ml | 76 +-- 3 files changed, 982 insertions(+), 39 deletions(-) create mode 100644 src/prover/#eMatching.ml# diff --git a/src/prover/#eMatching.ml# b/src/prover/#eMatching.ml# new file mode 100644 index 00000000..dac2d6cb --- /dev/null +++ b/src/prover/#eMatching.ml# @@ -0,0 +1,943 @@ +(** E-Matching. + + Implements a variant of the E-matching technique presented in this paper: + Efficient E-Matching for SMT Solvers + Leonardo de Moura and Nikolaj Bjorner, CADE 2007 + +*) + +open Util +open Grass +open GrassUtil + +module CC = CongruenceClosure + + +(** Top-level symbol of current pattern to be matched. + * If we are matching a trivial pattern consisting only of + * a variable, we only record its sort, hence the Either type. + *) +type esymbol = (sort, sorted_symbol) Either.t + +(** Auxiliary state of the E-matching abstract machine. + * Stores the currently active patterns. + * Used for per-multi-pattern pruning and filtering. + *) +type state = TermSet.t + +(** Transition between two consecutive trigger terms in a multi-pattern. + * Used for updating/initializing the auxiliary state. + *) +type step = (TermSet.t, (term * term) list) Either.t + +(** Maps to keep track of register/symbol pairs that should be pruned after init/bind *) +type prune_map = (int * sorted_symbol) list TermMap.t + +(** Inverted paths of symbol/argument position pairs from subterms to parent terms *) +type inv_path = (sorted_symbol * int) list + +(** E-matching code trees for instantiating templates of some type 'a *) +type 'a ematch_code = + | Init of step * esymbol * (int * inv_path) option * int * prune_map * 'a ematch_code + | Bind of int * sorted_symbol * int * prune_map * 'a ematch_code + | Check of int * term * 'a ematch_code + | Compare of int * int * 'a ematch_code + | Choose of 'a ematch_code list + | Yield of int IdMap.t * ('a list) TermMap.t * 'a list + | Filter of (int IdMap.t * filter list) TermMap.t * 'a ematch_code + | Backtrack of state * 'a ematch_code + | ChooseTerm of state * int * 'a ematch_code * prune_map * CC.Node.t list list + +(** Pretty printing of E-matching code trees *) +open Format + +let pr_var_binding ppf (x, i) = + fprintf ppf "%a -> %d" pr_ident x i + +let pr_ematch_code pr_inst ppf = + let pr_step ppf = function + | Either.First ts -> pr_term_list ppf (TermSet.elements ts) + | Either.Second tts -> pr_list_comma (fun ppf (t1, t2) -> fprintf ppf "%a -> %a" pr_term t1 pr_term t2) ppf tts + in + let pr_prune ppf ppf (t, pl) = + fprintf ppf "prune(@[%a,@ [%a]@])" pr_term t + (pr_list_comma (fun ppf (i, sym) -> fprintf ppf "(%d,@ %a)" i pr_sym (fst sym))) pl + in + let rec pr_ematch_code ppf = function + | Choose cs -> + fprintf ppf "%s@ %a" "choose" pr_choose cs + | Init (step, sym, None, i, pm, next) -> + fprintf ppf "init(@[%a,@ %d@])@\n%a@\n%a" + pr_step step i + (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) + pr_ematch_code next + | Init (step, sym, Some (i, path), o, pm, next) -> + fprintf ppf "join(@[%a,@ %d,@ [%a],@ %d@])@\n%a@\n%a" + pr_step step + i + (pr_list_comma (fun ppf (sym, i) -> fprintf ppf "(%a,@ %d)" pr_sym (fst sym) i)) path + o + (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) + pr_ematch_code next + | Bind (i, sym, o, pm, next) -> + fprintf ppf "bind(@[%d,@ (%a, %d),@ %d@])@\n%a@\n%a" i pr_sym (fst sym) (List.length (fst @@ snd sym)) o + (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) + pr_ematch_code next + | Check (i, t, next) -> + fprintf ppf "check(@[%d,@ %a@])@\n%a" i pr_term t pr_ematch_code next + | Compare (i, j, next) -> + fprintf ppf "compare(@[%d,@ %d@])@\n%a" i j pr_ematch_code next + | Yield (vs, fs, gs) -> + let pr_numbered_forms ppf fs = + (pr_list 0 + (fun ppf _ -> fprintf ppf ",@\n") + (fun i ppf f -> fprintf ppf "@[<2>%2d:@ @[%a@]@]" i pr_inst f)) ppf fs + in + fprintf ppf "@[<2>yield(@[[%a]@]) ->@\n%a@\n%a@]" + (pr_list_comma pr_var_binding) (IdMap.bindings vs) + (pr_list 0 + (fun ppf _ -> fprintf ppf ",@\n") + (fun _ ppf (t, fs) -> fprintf ppf "@[<2>%a:@\n%a@]" + pr_term t pr_numbered_forms fs)) (TermMap.bindings fs) + pr_numbered_forms gs + (*| Prune (i, sym, next) -> + fprintf ppf "prune(@[%d,@ %a@])@\n%a" i pr_sym (fst sym) pr_ematch_code next*) + | Filter (filters, next) -> + fprintf ppf "filter(@[%a@])@\n%a" + pr_term_list (filters |> TermMap.bindings |> List.map fst) + (*(pr_list_comma pr_var_binding) (IdMap.bindings vs) + pr_term t + pr_filter filters*) + pr_ematch_code next + | Backtrack (ts, next) -> + fprintf ppf "backtrack(@[[%a]@])@\n@%a" pr_term_list (TermSet.elements ts) pr_ematch_code next + | ChooseTerm (ts, i, next, _, _) -> + fprintf ppf "choose_term(@[%d@])@\n%a" i pr_ematch_code next + + and pr_choose ppf = function + | [] -> () + | [c] -> fprintf ppf "{@[<1>@\n%a@]@\n}" pr_ematch_code c + | c :: cs -> fprintf ppf "{@[<1>@\n%a@]@\n}@ @ @<-3>%s@ %a" pr_ematch_code c "or" pr_choose cs + in pr_ematch_code ppf + +let print_ematch_code string_of_inst out_ch code = + print_of_format (pr_ematch_code string_of_inst) code out_ch + +(** A few utility functions *) + +(** Get the continuation of the first command *) +let continuation = function + | Init (_, _, _, _, _, next) + | Bind (_, _, _, _, next) + | Check (_, _, next) + | Compare (_, _, next) + | Filter (_, next) + | Backtrack (_, next) -> next + | _ -> failwith "illegal argument to continuation" + +(** Get the maximum register in code tree c, yields -1 if c uses no registers *) +let max_reg c = + let rec mr m = function + | Init (_, _, _, i, _, c) + | Check (i, _, c) + | ChooseTerm (_, i, c, _, _) -> mr (max m i) c + | Filter (_, c) + | Backtrack (_, c) -> mr m c + | Bind (i, _, j, _, c) + | Compare (i, j, c) -> mr (max (max m i) j) c + | Choose cs -> + List.fold_left mr m cs + | Yield (vs, _, _) -> IdMap.fold (fun _ -> max) vs m + in + mr (-1) c + +(** Make a choice tree out of trees c1 and + * Avoids the creation of nested choice nodes. *) +let mk_choose c1 c2 = + match c1, c2 with + | Choose cs1, Choose cs2 -> Choose (cs1 @ cs2) + | Choose cs1, _ -> Choose (cs1 @ [c2]) + | _, Choose cs2 -> Choose (c1 :: cs2) + | _ -> Choose [c1; c2] + +(** Work queue for pattern compilation *) +module WQ = PrioQueue.Make + (struct + type t = int + let compare = compare + end) + (struct + type t = term + let compare t1 t2 = + (* Rational for the following definition: + * Entries i -> x and i -> t where t is ground should be processed before entries i -> f(t_1, ..., t_n). + * This is to delay the creation of e-matching code that introduces backtracking points. *) + if is_ground_term t1 && not @@ is_ground_term t2 then -1 + else match t1, t2 with + | Var _, App _ -> -1 + | App _, Var _ -> 1 + | _ -> compare t1 t2 + end) + +(** Triggers of multi-patterns. *) +type trigger = term * filter list + +(** A multi-pattern for instantiating templates of generic type 'a. *) +type 'a pattern = 'a * trigger list + +(** Compile the list of multi-patterns [patterns] into an ematching code tree. *) +let compile (patterns: ('a pattern) list): 'a ematch_code = + (* do some heuristic analysis on the patterns so as to optimize the order + * of their triggers to faciliate sharing across patterns in the code tree *) + (* First count how often each trigger term occurs across all patterns *) + let occurs = + List.fold_left + (fun acc (_, pattern) -> + List.fold_left + (fun acc -> function + | App _ as t, _ -> + let curr = TermMap.find_opt t acc |> Opt.get_or_else 0 in + TermMap.add t (curr + 1) acc + | _ -> acc) + acc pattern) + TermMap.empty + patterns + in + let patterns = + let compare_triggers (t1, _) (t2, _) = + let get_count t = + TermMap.find_opt t occurs |> + Opt.get_or_else 0 + in + (* first prioritize terms that bind more variables as this helps to avoid back-tracking *) + let c1 = + - (compare + (IdSet.cardinal (fv_term t1)) + (IdSet.cardinal (fv_term t2))) + in + if c1 = 0 then + (* next, prioritize terms that occur more often across many patterns *) + let c2 = compare (get_count t2) (get_count t1) in + if c2 = 0 then compare t2 t1 + else c2 + else c1 + in + List.map (fun (tpl, pattern) -> + (tpl, List.sort compare_triggers pattern)) + patterns + in + let esymbol_of = function + | Var (_, srt) -> Either.first srt + | t -> Either.second (sorted_symbol_of t |> Opt.get) + in + let add_args_to_queue w o args = + List.fold_left + (fun (w', o') arg -> WQ.insert o' arg w', o' + 1) + (w, o) args + in + let prune args o = + let pl, _ = + List.fold_left + (fun (pl, o) -> function + | App (sym, _, _) as t when not @@ is_ground_term t -> + (o, sorted_symbol_of t |> Opt.get) :: pl, o + 1 + | _ -> pl, o + 1) + ([], o) args + in + pl + in + let update_yield_templates t_filters_opt fs gs template = + match t_filters_opt with + | Some (t, _) -> + let t_templates = TermMap.find_opt t fs |> Opt.get_or_else [] in + TermMap.add t (template :: t_templates) fs, gs + | None -> fs, template :: gs + in + (* Get shortest inverted path from some variable x bound + * in v that is a subterm of t, if such x exists. *) + let get_path v t = + let rec gp shortest path = function + | App (_, [], _) -> shortest + | Var (x, _) -> + let smaller = + shortest |> + Opt.fold (fun _ (_, p) -> List.length path < List.length p) true + in + if smaller then + IdMap.find_opt x v |> + Opt.map (fun i -> (i, path)) |> + Opt.lazy_or_else (fun () -> shortest) + else shortest + | App (_, args, _) as t -> + let sym = sorted_symbol_of t |> Opt.get in + let new_shortest, _ = + List.fold_left + (fun (ns, k) arg -> gp ns ((sym, k) :: path) arg, k + 1) + (shortest, 0) args + in + new_shortest + in + gp None [] t + in + let args_of = function + | Var _ as t -> [t] + | App (_, args, _) -> args + in + (* Compile a multi-pattern into a code tree. + * f: info about current term of the multi-pattern that is being processed. + * pattern: remaining trigger terms of the multi-pattern that still need to be processed. + * w: work queue of subterms of [f] that still need to be processed. + * v: accumulated map of variable to register bindings. + * o: index of next unused register. + *) + let rec compile f pattern w v o = + let rec c w v o = + if WQ.is_empty w then init f pattern v o else + let i, t, w' = WQ.extract_min w in + match t with + | Var (x, _) when not @@ IdMap.mem x v -> + c w' (IdMap.add x i v) o + | Var (x, _) -> + let next = c w' v o in + let j = IdMap.find x v in + if i = j then next + else Compare (i, j, next) + | t when is_ground_term t -> + Check (i, t, c w' v o) + | App (sym, args, _) -> + let w'', o' = add_args_to_queue w' o args in + let next = c w'' v o' in + let pl = prune args o in + Bind (i, sorted_symbol_of t |> Opt.get, o, TermMap.singleton t pl, next) + in + c w v o + and init f pattern v o : 'a ematch_code = + let template, t_filters_opt = f in + let code = + match pattern with + | [] -> + let fs, gs = update_yield_templates t_filters_opt TermMap.empty [] template in + Yield (v, fs, gs) + | (t', filters') :: pattern' -> + let args = args_of t' in + let w, o' = add_args_to_queue WQ.empty o args in + let next = compile (template, Some (t', filters')) pattern' w v o' in + let pl = prune args o in + let ts = match t_filters_opt with + | Some (t, _) -> Either.second [t, t'] + | None -> Either.first @@ TermSet.singleton t' + in + let pm = TermMap.singleton t' pl in + let esym = esymbol_of t' in + let path = get_path v t' in + Init (ts, esym, path, o, pm, next) + in + match t_filters_opt with + | Some (t, (_ :: _ as fs)) -> + let filters = TermMap.singleton t (v, fs) in + Filter (filters, code) + | _ -> code + in + let seq cs fchild = + let rec s = function + | Init (ts, sym, path, o, pm, c) :: cs -> Init (ts, sym, path, o, pm, s cs) + | Check (i, t, c) :: cs -> Check (i, t, s cs) + | Compare (i, j, c) :: cs -> Compare (i, j, s cs) + | Bind (i, sym, o, pm, c) :: cs -> Bind (i, sym, o, pm, s cs) + | Filter (filters, c) :: cs -> Filter (filters, s cs) + | [] -> fchild + | _ -> assert false + in + s (List.rev cs) + in + let branch f pattern comps fchild w v o = + seq comps (mk_choose (compile f pattern w v o) fchild) + in + let check_if_queue_processed w v = + WQ.fold + (fun i t (v', fully_processed) -> + match t with + | App _ -> v', false + | Var (x, _) -> + IdMap.add x i v', + fully_processed && (IdMap.find_opt x v' |> Opt.get_or_else i |> (=) i) + ) + w (v, true) + in + let _print_wq w = + WQ.fold (fun i t _ -> Printf.printf "%d -> %s, " i (string_of_term t)) w (); + print_newline (); + in + (* Check whether [(f, pattern, w, v)] is compatible with the first node of [code]. + * If yes, return updated values [(code', w', v', f', pattern')]. *) + let compatible f pattern w v code = + match code with + | Init (ts, sym, path, o, pm, next) -> + (* Has w been fully processed? *) + let v', fully_processed = check_if_queue_processed w v in + if fully_processed then + match pattern with + | (t', filters') :: pattern' when esymbol_of t' = sym && get_path v' t' = path -> + let args = args_of t' in + let w', _ = add_args_to_queue WQ.empty o args in + let f' = (fst f, Some (t', filters')) in + let ts' = + ts |> + Either.value_map + (fun ts -> TermSet.add t' ts |> Either.first) + (fun tts -> + snd f |> + Opt.map (fun (t, _) -> + (t, t') :: tts |> Util.remove_duplicates (=) |> Either.second) |> + Opt.get_or_else (TermSet.singleton t' |> Either.first)) + in + let pm' = TermMap.add t' (prune args o) pm in + let code' = Init (ts', sym, path, o, pm', next) in + Some (code', w', v', f', pattern') + | _ -> None + else None + | Filter (filters, next) -> + (match f with + | inst, Some (t, fs) -> + let v' = + WQ.fold + (fun i t v -> match t with + | Var (x, _) -> IdMap.add x i v + | _ -> v) + w v + in + let filters'_opt = + (* Make sure that there is no entry for t in filters, respectively, that the entry is compatible *) + match TermMap.find_opt t filters with + | None -> + Some (TermMap.add t (v', fs) filters) + | Some (v'', fs'') when Hashtbl.hash (v'', fs'') = Hashtbl.hash (v', fs) -> + Some (TermMap.add t (v', fs) filters) + | _ -> None + in + filters'_opt |> Opt.map (fun filters' -> Filter (filters', next), w, v, (inst, None), pattern) + | _, None -> None) + | Check (i, t, _) -> + (match WQ.find_opt i w with + | Some t' when t = t' -> Some (code, WQ.delete i w, v, f, pattern) + | _ -> None) + | Compare (i, j, _) -> + let ti_opt = WQ.find_opt i w in + (match ti_opt with + | Some (Var (x, _) as ti) -> + let v'_opt = match WQ.find_opt j w with + | Some tj when ti = tj -> Some (IdMap.add x j v) + | None when IdMap.find_opt x v = Some j -> Some v + | _ -> None + in + Opt.map (fun v' -> code, WQ.delete i w, v', f, pattern) v'_opt + | _ -> None) + | Bind (i, sym, o, pm, next) -> + (match WQ.find_opt i w with + | Some (App (_, args, _) as t) when sorted_symbol_of t = Some sym -> + let w', _ = add_args_to_queue w o args in + let pl = prune args o in + let code' = Bind (i, sym, o, TermMap.add t pl pm, next) in + Some (code', WQ.delete i w', v, f, pattern) + | _ -> None) + | _ -> None + in + let rec insert_code f pattern w v o comps incomps code: 'a ematch_code * int = + match code with + | Choose cs -> + if incomps = [] then + let code', score = insert_choose f pattern cs w v o in + seq comps code', score + List.length comps + else branch f pattern comps (seq incomps code) w v o, List.length comps + | Yield (v1, fs, gs) -> + let v2, fully_processed = check_if_queue_processed w v in + let v1_union_v2 = IdMap.union (fun x i j -> Some i) v1 v2 in + let v2_union_v1 = IdMap.union (fun x i j -> Some j) v1 v2 in + let filters = snd f |> Opt.map snd |> Opt.get_or_else [] in + if incomps = [] && pattern = [] && fully_processed && filters = [] && IdMap.equal (=) v1_union_v2 v2_union_v1 + then + let yield = + let fs', gs' = update_yield_templates (snd f) fs gs (fst f) in + Yield (v1_union_v2, fs', gs') + in + seq comps yield, List.length comps + 1 + else begin + (*Printf.printf "failed\n"; + print_ematch_code (fun ppf (f, _) -> pr_form ppf f) stdout code; + print_newline(); + if incomps <> [] then Printf.printf "incomps"; + if pattern <> [] then Printf.printf "pattern"; + if not fully_processed then Printf.printf "processed"; + if filters <> [] then Printf.printf "filters"; + print_newline();*) + branch f pattern comps (seq incomps code) w v o, List.length comps + end + | code -> + let next = continuation code in + compatible f pattern w v code |> + Opt.map (fun (code', w', v', f', pattern') -> + insert_code f' pattern' w' v' o (code' :: comps) incomps next) |> + Opt.lazy_get_or_else (fun () -> + match code with + | Init _ -> branch f pattern comps (seq incomps code) w v o, List.length comps + | _ -> insert_code f pattern w v o comps (code :: incomps) next) + and insert_choose f pattern cs w v o = + let cs', _, best = + List.fold_right + (fun code (cs', cs, best) -> + match insert_code f pattern w v o [] [] code with + | code', score when score > best -> + code' :: cs, code :: cs, score + | _ -> code :: cs', code :: cs, best + ) + cs ([], [], 0) + in + if best = 0 + then Choose (compile f pattern w v o :: cs), 0 + else Choose cs', best + in + List.fold_left + (fun code (f, pattern) -> + let code', _ = insert_code (f, None) pattern WQ.empty IdMap.empty (max_reg code + 1) [] [] code in + code' + ) + (Choose []) patterns + +(** Check whether term [t] and substitution [sm] satisfy the filters [filters]. *) +let filter_term filters t sm = + List.for_all + (fun f -> match f with + | FilterNotNull -> + (match t with + | App (Null, [], _) -> false + | _ -> true) + | FilterSymbolNotOccurs sym -> + let rec not_occurs = function + | App (EntPnt, _, _) -> sym <> EntPnt + | App (sym1, _, _) when sym1 = sym -> false + | App (_, ts, _) -> List.for_all not_occurs ts + | _ -> true + in not_occurs t + | FilterReadNotOccurs (name, (arg_srts, res_srt)) -> + let rec not_occurs = function + | App (EntPnt, _, _) -> true + | App ((Read | ArrayCells), (App (FreeSym (name1, _), arg_ts, res_srt1) :: _ as ts), _) -> + let ok = + try + name1 <> name || + res_srt1 <> res_srt || + List.fold_left2 (fun acc t1 srt -> acc || sort_of t1 <> srt) false arg_ts arg_srts + with Invalid_argument _ -> true + in ok && List.for_all not_occurs ts + | App (_, ts, _) -> List.for_all not_occurs ts + | _ -> true + in not_occurs t + | FilterGeneric fn -> fn sm t + ) + filters + + +(** The E-matching abstract machine. + * Runs the given E-matching code tree [code] on the E-graph [ccgraph]. + * Yields a hash table mapping templates of type 'a to the computed variable substitutions. + *) +let run ?(pr_tpl = (fun ppf _ -> ())) (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = + let egrapha, egraphp = CC.get_egraph ccgraph in + (* some utility functions for working with the e-graph *) + let get_apps sym = + CC.EGraphA.fold + (fun (n, sym') n_apps apps -> + if sym = sym' + then CC.NodeListSet.union n_apps apps + else apps) + egrapha CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + let get_reps srt = + CC.EGraphA.fold + (fun (n, sym) _ reps -> + if snd @@ snd sym = srt + then CC.NodeListSet.add [n] reps + else reps) + egrapha CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + let get_apps_of_node n sym = + CC.EGraphA.find_opt (n, sym) egrapha |> + Opt.get_or_else CC.NodeListSet.empty |> + CC.NodeListSet.elements + in + let get_parents n sym k = + (* Printf.printf "get_parents (%s, %s, %d)...\n" + (CC.term_of_rep ccgraph n |> string_of_term) + (fst sym |> string_of_symbol) + k + ;*) + CC.EGraphP.find_opt (n, sym, k) egraphp |> + Opt.get_or_else (CC.NodeListSet.empty, CC.NodeSet.empty) (*|> + (fun (ps, ns) -> print_list stdout pr_term (List.map (CC.term_of_rep ccgraph) (CC.NodeSet.elements ns)); (ps, ns))*) + in + (* initialize registers *) + let regs = Array.make (max_reg code + 1) (CC.rep_of_term ccgraph mk_true_term) in + (* create result table *) + let insts = Hashtbl.create 1000 in + let prune pm ts = + TermSet.filter (fun t -> + TermMap.find_opt t pm |> + Opt.map (fun pl -> + List.for_all (fun (i, sym) -> + let n = regs.(i) in + let funs_n = CC.funs_of_rep ccgraph n in + BloomFilter.mem sym funs_n) pl) |> + Opt.get_or_else true) ts + in + let take_step step ts = + step |> + Either.value_map + (fun ts' -> ts') + (fun tts -> + List.fold_left (fun ts' (t, t') -> + if TermSet.mem t ts then TermSet.add t' ts' + else ts') + TermSet.empty tts) + in + (* collect nodes containing one of the nodes in [terms] following the given inverted path *) + let rec collect (esym: esymbol) args terms = function + | [] -> + esym |> + Either.map + (fun _ -> CC.NodeSet.fold (fun n acc -> [n] :: acc) terms []) + (fun _ -> CC.NodeListSet.elements args) |> + Either.value + | (sym, k) :: path' -> + let args', terms' = + CC.NodeSet.fold + (fun n (args', terms') -> + let n_args, n_terms = get_parents n sym k in + CC.NodeListSet.union n_args args', + CC.NodeSet.union n_terms terms' + ) + terms (CC.NodeListSet.empty, CC.NodeSet.empty) + in + collect esym args' terms' path' + in + (* the actual machine *) + let rec run state = function + | Init (step, esym, path_opt, o, pm, next) :: stack -> + let ts = state in + let terms = + path_opt |> + Opt.map + (fun (i, path) -> + collect esym CC.NodeListSet.empty (CC.NodeSet.singleton regs.(i)) path) |> + Opt.lazy_get_or_else + (fun () -> esym |> Either.map get_reps get_apps |> Either.value) + in + let ts' = take_step step ts in + (*print_of_format (fun ppf esym -> fprintf ppf "init %s: [%a] -> %a\n" + (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym))) + pr_sorts + (esym |> Either.value_map (fun _ -> []) (fun sym -> fst @@ snd sym)) + pr_sort + (esym |> Either.value_map (fun srt -> srt) (fun sym -> snd @@ snd sym))) esym stdout + ; + Printf.printf "active terms: "; + print_list stdout pr_term (TermSet.elements ts'); + Printf.printf "\n"; + Printf.printf "choosing from: "; + print_list stdout (fun ppf ns -> fprintf ppf "[%a]" pr_term_list (List.map (CC.term_of_rep ccgraph) ns)) terms; + Printf.printf "\n";*) + run ts' (ChooseTerm (ts', o, next, pm, terms) :: stack) + | Bind (i, sym, o, pm, next) :: stack -> + (*Printf.printf "bind(%d, %s)\n" i (string_of_symbol @@ fst sym);*) + let apps = get_apps_of_node (regs.(i)) sym in + run state (ChooseTerm (state, o, next, pm, apps) :: stack) + | Check (i, t, next) :: stack -> + (*Printf.printf "check(%d, %s)\n" i (string_of_term t);*) + if CC.rep_of_term ccgraph t = regs.(i) + then run state (next :: stack) + else begin + (* let t' = CC.term_of_rep ccgraph regs.(i) in + Printf.printf " failed with %d = %s\n" i (string_of_term t');*) + run state stack + end + | Compare (i, j, next) :: stack -> + (* Printf.printf "compare(%d, %d)\n" i j;*) + if regs.(i) = regs.(j) + then run state (next :: stack) + else begin + (* let ti = CC.term_of_rep ccgraph regs.(i) in + let tj = CC.term_of_rep ccgraph regs.(j) in + Printf.printf " failed with %d = %s and %d = %s \n" i (string_of_term ti) j (string_of_term tj);*) + run state stack + end + | Choose cs :: stack -> + run state (List.map (fun c -> Backtrack (state, c)) cs @ stack) + | Yield (vs, fs, gs) :: stack -> + let ts = state in + let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in + (*Printf.printf "yield\n"; + print_list stdout (fun ppf (x, t) -> fprintf ppf "%s -> %a" (string_of_ident x) pr_term t) (IdMap.bindings sm); + print_newline ();*) + TermSet.iter (fun t -> + TermMap.find_opt t fs |> + Opt.iter (List.iter (fun f -> + (*print_of_format (fun ppf f -> fprintf ppf " for %a\n" pr_tpl f) f stdout;*) + Hashtbl.add insts f sm))) ts; + List.iter (fun f -> Hashtbl.add insts f sm) gs; + run state stack + | Filter (filters, next) :: stack -> + let ts = state in + let ts' = + TermSet.filter + (fun t -> + let t_filters = TermMap.find_opt t filters in + match t_filters with + | Some (vs, fs) -> + let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in + (*Printf.printf "Filtering %d, %s...\n" (IdMap.cardinal vs) (string_of_term (subst_term sm t));*) + filter_term fs (subst_term sm t) sm + | None -> true) + ts + in + (*Printf.printf "filter;\nactive terms: "; + print_list stdout pr_term (TermSet.elements ts'); + Printf.printf "\n";*) + if TermSet.is_empty ts' + then run state stack + else run ts' (next :: stack) + | ChooseTerm (state', o, next, pm, args :: apps) :: stack -> + (*Printf.printf "choosing app: "; + print_of_format (fun ppf args -> fprintf ppf "[%a]" pr_term_list (List.map (CC.term_of_rep ccgraph) args)) args stdout; + print_newline ();*) + let _ = + List.fold_left + (fun i arg -> + (*Printf.printf "%d -> %s, " i (string_of_term @@ CC.term_of_rep ccgraph arg);*) + regs.(i) <- arg; i + 1) + o args + in + let ts' = prune pm state' in + if TermSet.is_empty ts' + then run state (ChooseTerm (state', o, next, pm, apps) :: stack) + else run ts' (next :: ChooseTerm (state', o, next, pm, apps) :: stack) + | Backtrack (state', next) :: stack -> run state' (next :: stack) + | ChooseTerm (_, _, _, _, []) :: stack -> + run state stack + | [] -> insts + in + run TermSet.empty [code] + + +(** Generate an E-matching code tree from the given set of axioms. *) +let compile_axioms_to_ematch_code ?(force=false) ?(stratify=(!Config.stratify)) axioms : + (form * _) ematch_code * (form * _) pattern list = + (* *) + let epr_axioms, axioms = + List.partition + (fun f -> IdSrtSet.is_empty (vars_in_fun_terms f) || not !Config.instantiate && not force) + axioms + in + (*print_endline "EPR:"; + List.iter print_endline (List.map string_of_form epr_axioms);*) + (*let _ = print_endline "Candidate axioms for instantiation:" in + let _ = print_forms stdout axioms in*) + let generate_pattern f = + let fvars0 = sorted_free_vars f in + let fvars = IdSrtSet.inter fvars0 (vars_in_fun_terms f) in + (* filter out stratified variables *) + let fvars, strat_vars = + let merge_map k a b = match (a,b) with + | (Some a, Some b) -> Some (a @ b) + | (Some c, None) | (None, Some c) -> Some c + | (None, None) -> None + in + let rec gen_tpe acc t = match t with + | App (_, ts, srt) -> + List.fold_left + (IdMap.merge merge_map) + IdMap.empty + (List.map (gen_tpe (srt :: acc)) ts) + | Var (id, _) -> IdMap.add id acc IdMap.empty + in + let gen_map = + TermSet.fold + (fun t acc -> IdMap.merge merge_map (gen_tpe [] t) acc) + (fun_terms_with_vars f) + IdMap.empty + in + if stratify then + IdSrtSet.partition + (fun (id, srt) -> + try + let generating = IdMap.find id gen_map in + not (List.for_all (TypeStrat.is_stratified srt) generating) + with Not_found -> + begin + Debug.warn (fun () -> "BUG in stratification: " ^ (string_of_ident id) ^ "\n"); + true + end + ) + fvars + else fvars, IdSrtSet.empty + in + let strat_var_ids = IdSrtSet.fold (fun (id, _) acc -> IdSet.add id acc) strat_vars IdSet.empty in + (* collect all terms in which free variables appear below function symbols *) + let fun_terms, fun_vars, strat_terms = + let rec tt (fun_terms, fun_vars, strat_terms) t = + match t with + | App (sym, _ :: _, srt) when srt <> Bool -> + let tvs = fv_term t in + if IdSet.is_empty tvs then + fun_terms, fun_vars, strat_terms + else if IdSet.is_empty (IdSet.inter strat_var_ids tvs) + then TermSet.add t fun_terms, IdSet.union tvs fun_vars, strat_terms + else fun_terms, fun_vars, TermSet.add t strat_terms + | App (_, ts, _) -> + List.fold_left tt (fun_terms, fun_vars, strat_terms) ts + | _ -> fun_terms, fun_vars, strat_terms + in fold_terms tt (TermSet.empty, IdSet.empty, TermSet.empty) f + in + let unmatched_vars = + IdSrtSet.fold (fun (id, _) acc -> + if IdSet.mem id fun_vars then acc + else IdSet.add id acc) fvars IdSet.empty + in + let fun_terms, fun_vars = + TermSet.fold (fun t (fun_terms, fun_vars) -> + let vs = fv_term t in + if IdSet.is_empty (IdSet.inter unmatched_vars vs) + then fun_terms, fun_vars + else TermSet.add t fun_terms, IdSet.union vs fun_vars) + strat_terms (fun_terms, fun_vars) + in + let strat_vars1 = + IdSrtSet.filter (fun (id, _) -> not (IdSet.mem id fun_vars)) strat_vars + in + (* extract patterns separately to obtain auxilary filters *) + let patterns = extract_patterns f in + let fun_terms_with_filters = + TermSet.fold (fun t acc -> + try (t, List.assoc t patterns) :: acc + with Not_found -> (t, []) :: acc) + fun_terms [] + in + let f = mk_forall (IdSrtSet.elements strat_vars1) f in + (f, (fvars0, strat_vars1, unmatched_vars, fun_vars, fun_terms_with_filters)), fun_terms_with_filters + in + let patterns = [] in + let patterns = + List.fold_right + (fun f patterns -> ((f, (IdSrtSet.empty, IdSrtSet.empty, IdSet.empty, IdSet.empty, [])), []) :: patterns) + epr_axioms patterns + in + let patterns = + List.fold_right + (fun f patterns -> generate_pattern f :: patterns) axioms patterns + in + compile patterns, patterns + +(** Instantiate the axioms encoded in patterns [patterns] and code tree [code] for E-graph [ccgraph]. *) +let instantiate_axioms_from_code patterns code ccgraph : form list = + let print_debug ((f, (fvars, strat_vars, unmatched_vars, fun_vars, fun_terms_with_filters)), subst_maps) = + print_endline "--------------------"; + print_endline (string_of_form f); + Printf.printf "all vars: "; + print_list stdout pr_ident (List.map fst (IdSrtSet.elements fvars)); + print_newline (); + Printf.printf "strat vars: "; + print_list stdout pr_ident (List.map fst (IdSrtSet.elements strat_vars)); + print_newline (); + Printf.printf "unmatched vars: "; + print_list stdout pr_ident (IdSet.elements unmatched_vars); + print_newline (); + Printf.printf "inst vars: "; + print_list stdout pr_ident (IdSet.elements fun_vars); + print_newline (); + Printf.printf "fun terms: "; + print_list stdout pr_term (List.map fst fun_terms_with_filters); + print_newline (); + Printf.printf "# of insts: %d\n" (List.length subst_maps); + print_endline "subst_maps:"; + List.iter print_subst_map subst_maps; + print_endline "--------------------"; + in + let _ = + if Debug.is_debug 1 then CC.print_classes ccgraph + in + let instances = run code ccgraph in + let get_form (f, _) = f in + let grouped_instances = + List.rev_map + (fun (f, _) -> + let sms = Hashtbl.find_all instances f in + (f, sms)) + patterns + in + let _ = + if Debug.is_debug 1 then begin + grouped_instances |> + List.rev_map (fun (f, sms) -> (f, List.filter ((<>) IdMap.empty) sms)) |> + List.iter print_debug + end + in + grouped_instances |> + List.fold_left + (fun instances (f_aux, sms) -> + List.fold_left (fun instances sm -> + subst sm (get_form f_aux) :: instances) + instances sms) [] + +let instantiate_axioms_from_code patterns code ccgraph = + measure_call "EMatching.instantiate_axioms" (instantiate_axioms_from_code patterns code) ccgraph + +let instantiate_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms ccgraph = + let code, patterns = compile_axioms_to_ematch_code ~force:force ~stratify:stratify axioms in + instantiate_axioms_from_code patterns code ccgraph + +(** Compile term generators [generators] into an E-matching code tree. *) +let compile_term_generators_to_ematch_code generators = + let generators = + let remove_generic_filters (ms, ts) = + List.map (function Match (m, fs) -> (m, List.filter (function FilterGeneric _ -> false | _ -> true) fs)) ms, ts + in + remove_duplicates (fun g1 g2 -> remove_generic_filters g1 = remove_generic_filters g2) generators + in + let generate_pattern patterns (guards, ts) = + match ts with + | [] -> patterns + | _ -> + let triggers = List.map (function Match (t, filters) -> (t, filters)) guards in + (ts, triggers) :: patterns + in + let patterns = List.fold_left generate_pattern [] generators in + compile patterns + +(** Generate new terms according to term generators encoded in [code] and E-graph [ccgraph]. + * The new terms are added to the E-graph. Returns a Boolean indicating whether new terms + * were derived (modulo equality) and the new E-graph. *) +let generate_terms_from_code code ccgraph = + let rec round i has_mods ccgraph = + (*Printf.printf "Generating terms, round %d...\n" i; flush stdout; + CC.print_classes ccgraph;*) + (*let terms = CC.get_terms ccgraph in*) + let insts = run ~pr_tpl:pr_term_list code ccgraph in + let new_terms = + Hashtbl.fold (fun ts sm new_terms -> + List.fold_left (fun new_terms gen_term -> + let t = subst_term sm gen_term in + (*let _ = + if not @@ TermSet.mem t terms then + print_endline (" Adding generated term " ^ string_of_term gen_term ^ " -> " ^ string_of_term t); flush stdout + in*) + TermSet.add t new_terms) new_terms ts) + insts TermSet.empty + in + let ccgraph = CC.add_terms new_terms ccgraph in + if not @@ CC.has_mods ccgraph then has_mods, ccgraph + else round (i + 1) true (CC.reset ccgraph) + in + round 0 false ccgraph + +let generate_terms_from_code code ccgraph = + measure_call "EMatching.generate_terms_from_code" (generate_terms_from_code code) ccgraph diff --git a/src/prover/eMatching.ml b/src/prover/eMatching.ml index a2f8f45f..9d61f41a 100644 --- a/src/prover/eMatching.ml +++ b/src/prover/eMatching.ml @@ -65,7 +65,7 @@ let pr_ematch_code pr_inst ppf = in let rec pr_ematch_code ppf = function | Choose cs -> - fprintf ppf "@<-3>%s@ %a" "choose" pr_choose cs + fprintf ppf "%s@ %a" "choose" pr_choose cs | Init (step, sym, None, i, pm, next) -> fprintf ppf "init(@[%a,@ %d@])@\n%a@\n%a" pr_step step i diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index e070820e..a7b0b970 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -277,51 +277,51 @@ let get_read_propagators gts = let field_sorts = TermSet.fold (fun t srts -> match sort_of t with | (Loc ArrayCell _ | Map (_ :: _, _)) as srt -> SortSet.add srt srts - | Adt (_, _) as srt -> SortSet.add srt srts + | Adt (_, adts) -> + List.fold_left + (fun srts (id, _) -> SortSet.add (Adt (id, adts)) srts) + srts adts | _ -> srts) gts SortSet.empty in let read_propagators = SortSet.fold (function - | Adt (_, adts) -> fun propagators -> + | Adt (id, adts) -> fun propagators -> let s = fresh_ident "?s" in let t = fresh_ident "?t" in - List.fold_left - (fun propagators (id, cstrs) -> - let adt_srt = Adt (id, adts) in - let s = mk_var adt_srt s in - let t = mk_var adt_srt t in - let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in - let propagators = - List.fold_left - (fun propagators (destr, srt) -> - let srt = unfold_adts adts srt in - ([Match (mk_eq_term s t, []); - (* s == t, s.destr -> t.destr *) - Match (mk_destr srt destr s, [])], - [mk_destr srt destr t]) :: - ([Match (mk_eq_term s t, []); - (* s == t, t.destr -> s.destr *) - Match (mk_destr srt destr t, [])], - [mk_destr srt destr s]) :: propagators - ) - propagators destrs - in - List.fold_left (fun propagators (cid, destrs) -> - let args = - List.map (fun (destr, srt) -> - let srt = unfold_adts adts srt in - mk_var srt (fresh_ident "?v")) - destrs - in - let t = mk_constr adt_srt cid args in - let gen_terms = - List.map (fun (destr, srt) -> mk_destr srt destr t) destrs - in - ([Match (t, [])], gen_terms) :: propagators) - propagators cstrs - ) - propagators adts + let cstrs = List.assoc id adts in + let adt_srt = Adt (id, adts) in + let s = mk_var adt_srt s in + let t = mk_var adt_srt t in + let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in + let propagators = + List.fold_left + (fun propagators (destr, srt) -> + let srt = unfold_adts adts srt in + ([Match (mk_eq_term s t, []); + (* s == t, s.destr -> t.destr *) + Match (mk_destr srt destr s, [])], + [mk_destr srt destr t]) :: + ([Match (mk_eq_term s t, []); + (* s == t, t.destr -> s.destr *) + Match (mk_destr srt destr t, [])], + [mk_destr srt destr s]) :: propagators + ) + propagators destrs + in + List.fold_left (fun propagators (cid, destrs) -> + let args = + List.map (fun (destr, srt) -> + let srt = unfold_adts adts srt in + mk_var srt (fresh_ident "?v")) + destrs + in + let t = mk_constr adt_srt cid args in + let gen_terms = + List.map (fun (destr, srt) -> mk_destr (unfold_adts adts srt) destr t) destrs + in + ([Match (t, [])], gen_terms) :: propagators) + propagators cstrs | Loc (ArrayCell srt) -> fun propagators -> let f = fresh_ident "?f", field_sort (ArrayCell srt) srt in let fld = mk_var (snd f) (fst f) in From 1c6151f8b6fee3c878a2ab2af85e123d4ffaf200 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 24 Sep 2018 22:33:45 -0400 Subject: [PATCH 091/118] fix bug in CC --- src/prover/congruenceClosure.ml | 37 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/prover/congruenceClosure.ml b/src/prover/congruenceClosure.ml index 063a7891..557382ac 100644 --- a/src/prover/congruenceClosure.ml +++ b/src/prover/congruenceClosure.ml @@ -174,10 +174,27 @@ class dag = fun (terms: TermSet.t) -> match t with | Var (v, _) -> failwith ("CC: term not ground " ^ string_of_term t) (* create_and_add var (FreeSym v) []*) | App (_, args, _) as appl -> - let node_args = List.map convert_term args in + let has_mod_args, node_args = + List.fold_right (fun arg (has_mod_args, node_args) -> + let has_mod, n = convert_term arg in + has_mod || has_mod_args, n :: node_args) + args (false, []) + in let new_node = create_and_add appl (sorted_symbol_of appl |> Opt.get) node_args in - List.iter (fun n -> n#find#add_ccparent new_node) node_args; - new_node + List.iter (fun n -> n#find#add_ccparent new_node) node_args; + let arg_opt = List.nth_opt new_node#get_args 0 in + let has_mod = + arg_opt |> + Opt.fold + (fun _ arg -> + (*Printf.printf "Getting parents of %s\n" (string_of_term @@ self#get_term (arg#find));*) + NodeSet.exists (fun n' -> + (*Printf.printf "Checking congruence with: %s %b\n" + (string_of_term @@ self#get_term n') (n#congruent n');*) + n' <> new_node && new_node#congruent n' && n'#merge new_node) arg#ccpar) + true + in + has_mod_args || has_mod, new_node in let _ = TermSet.iter (fun t -> ignore (convert_term t)) terms in object (self) @@ -217,19 +234,7 @@ class dag = fun (terms: TermSet.t) -> method add_term t = (*Printf.printf "Adding term to cc: %s\n" (string_of_term t);*) - let n = convert_term t in - let arg_opt = List.nth_opt n#get_args 0 in - let has_mod = - arg_opt |> - Opt.fold - (fun _ arg -> - (*Printf.printf "Getting parents of %s\n" (string_of_term @@ self#get_term (arg#find));*) - NodeSet.exists (fun n' -> - (*Printf.printf "Checking congruence with: %s %b\n" - (string_of_term @@ self#get_term n') (n#congruent n');*) - n' <> n && n#congruent n' && n'#merge n) arg#ccpar) - true - in + let has_mod, n = convert_term t in _has_mods <- _has_mods || has_mod method add_eq t1 t2 = From 26ba46cdc118658e59592fb897ddca7f30de3dda Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 24 Sep 2018 22:33:57 -0400 Subject: [PATCH 092/118] more clean-up work --- src/prover/prover.ml | 65 ++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 7a263446..4d982a22 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -162,21 +162,6 @@ let instantiate_and_prove session fs = | _ -> gts) gts gts in - (*let generate_adt_terms gts = - TermSet.fold - (fun t gts -> match t with - | App (Constructor id, ts, Adt (ty_id, adts)) -> - let adt = List.assoc ty_id adts in - let destrs = List.assoc id adt in - List.fold_left2 - (fun gts arg (d_id, d_srt) -> - let d_srt = unfold_adts adts d_srt in - let d = GrassUtil.mk_app d_srt (Destructor d_id) [t] in - TermSet.add d gts) - (TermSet.add t gts) ts destrs - | t -> TermSet.add t gts - ) gts gts - in*) let fs1, generators = open_axioms isFunVar fs1 in let btwn_gen = btwn_field_generators fs in let gts = ground_terms ~include_atoms:true (mk_and fs) in @@ -193,14 +178,13 @@ let instantiate_and_prove session fs = CongruenceClosure.add_conjuncts fs in let _, cc_graph = EMatching.generate_terms_from_code tgcode cc_graph in - let round1 fs_inst gts_inst cc_graph = + let round1 fs_inst cc_graph = let equations = List.filter (fun f -> is_horn false [f]) fs_inst in let ground_fs = List.filter is_ground fs_inst in let code, patterns = EMatching.compile_axioms_to_ematch_code equations in let eqs = EMatching.instantiate_axioms_from_code patterns code cc_graph in (*let eqs = instantiate_with_terms equations (CongruenceClosure.get_classes cc_graph) in*) - let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and eqs)) gts_inst in - (* let gts1 = generate_adt_terms gts1 in*) + let gts1 = ground_terms ~include_atoms:true (mk_and eqs) in let eqs1 = List.filter (fun f -> IdSet.is_empty (fv f)) eqs in let cc_graph = cc_graph |> @@ -209,10 +193,11 @@ let instantiate_and_prove session fs = in let implied = CongruenceClosure.get_implied_equalities cc_graph in let gts1 = TermSet.union (ground_terms ~include_atoms:true (mk_and implied)) gts1 in - rev_concat [eqs; ground_fs; implied], gts1, cc_graph + let cc_graph = CongruenceClosure.add_terms gts1 cc_graph in + rev_concat [eqs; ground_fs; implied], cc_graph in - let round1 fs_inst gts_inst = measure_call "round1" (round1 fs_inst gts_inst) in - let round2 fs_inst gts_inst cc_graph = + let round1 fs_inst = measure_call "round1" (round1 fs_inst) in + let round2 fs_inst cc_graph = let fs_inst0 = fs_inst in let gts_known = generate_knowns gts in let core_terms = @@ -223,8 +208,7 @@ let instantiate_and_prove session fs = | _ -> acc) gts_a gts_known in - let gts_inst0 = TermSet.union gts_inst core_terms in - let cc_graph = CongruenceClosure.add_terms gts_inst0 cc_graph in + let cc_graph = CongruenceClosure.add_terms core_terms cc_graph in let fs1 = linearize fs1 in let code, patterns = EMatching.compile_axioms_to_ematch_code fs1 in (*let _ = @@ -233,51 +217,50 @@ let instantiate_and_prove session fs = (fun ppf (f, _) -> pr_form ppf f) stdout code; print_newline () in*) - let rec saturate i fs_inst gts_inst0 cc_graph = + let rec saturate i fs_inst cc_graph = (*Printf.printf "Saturate iteration %d\n" i; flush stdout;*) let has_mods2, cc_graph = cc_graph |> EMatching.generate_terms_from_code tgcode in let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in - let gts_inst = ground_terms_acc ~include_atoms:true gts_inst0 (mk_and fs_inst) in - (*let gts_inst = generate_adt_terms gts_inst in*) - let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in + let gts_inst = ground_terms ~include_atoms:true (mk_and fs_inst) in + (*let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in*) + (*let gts_inst = ground_terms_acc ~include_atoms:true gts_inst (mk_and implied_eqs) in*) (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) - let gts_inst = TermSet.union (ground_terms ~include_atoms:true (mk_and implied_eqs)) gts_inst in let cc_graph = cc_graph |> CongruenceClosure.add_terms gts_inst |> CongruenceClosure.add_conjuncts (rev_concat [fs_inst; fs]) in let has_mods1 = CongruenceClosure.has_mods cc_graph in - let gts_inst = CongruenceClosure.get_terms cc_graph in if not !Config.propagate_reads || not (has_mods1 || has_mods2) then - rev_concat [fs_inst; implied_eqs], gts_inst, cc_graph + rev_concat [fs_inst(*; implied_eqs*)], cc_graph else - saturate (i + 1) fs_inst gts_inst (CongruenceClosure.reset cc_graph) + saturate (i + 1) fs_inst (CongruenceClosure.reset cc_graph) in - let saturate i fs_inst gts_inst0 = measure_call "saturate" (saturate i fs_inst gts_inst0) in - let fs, gts_inst, cc_graph = saturate 1 fs_inst0 gts_inst0 cc_graph in + let saturate i fs_inst = measure_call "saturate" (saturate i fs_inst) in + let fs, cc_graph = saturate 1 fs_inst0 cc_graph in let _ = if Debug.is_debug 1 then begin + let gts_inst = CongruenceClosure.get_terms cc_graph in print_endline "ground terms:"; TermSet.iter (fun t -> print_endline (" " ^ (string_of_term t))) gts; print_endline "generated terms:"; TermSet.iter (fun t -> print_endline (" " ^ (string_of_term t))) (TermSet.diff gts_inst gts) end in - fs, gts_inst, cc_graph + fs, cc_graph in - let round2 fs_inst gts_inst = measure_call "round2" (round2 fs_inst gts_inst) in + let round2 fs_inst = measure_call "round2" (round2 fs_inst) in let do_rounds rounds = - let dr (k, result, fs_asserted, fs_inst, gts_inst, cc_graph) r = + let dr (k, result, fs_asserted, fs_inst, cc_graph) r = match result with | Some true | None -> - let fs_inst1, gts_inst1, classes1 = r fs_inst gts_inst cc_graph in + let fs_inst1, classes1 = r fs_inst cc_graph in let fs_inst1 = rename_forms fs_inst1 in let fs_asserted1, fs_inst1_assert = List.fold_left (fun (fs_asserted1, fs_inst1_assert) f -> @@ -292,11 +275,11 @@ let instantiate_and_prove session fs = Debug.debug (fun () -> Printf.sprintf "Calling SMT solver in instantiation round %d...\n" k); let result1 = measure_call "SmtLibSolver.is_sat" SmtLibSolver.is_sat session in Debug.debug (fun () -> "Solver done.\n"); - k + 1, result1, fs_asserted1, fs_inst1, gts_inst1, classes1 - | _ -> k, result, fs_asserted, fs_inst, gts_inst, cc_graph - in List.fold_left dr (1, None, FormSet.empty, fs1, cc_graph#get_terms, cc_graph) rounds + k + 1, result1, fs_asserted1, fs_inst1, classes1 + | _ -> k, result, fs_asserted, fs_inst, cc_graph + in List.fold_left dr (1, None, FormSet.empty, fs1, cc_graph) rounds in - let _, result, fs_asserted, fs_inst, _, _ = do_rounds [round1; round2] in + let _, result, fs_asserted, fs_inst, _ = do_rounds [round1; round2] in (match result with | Some true | None -> Debug.debugl 1 (fun () -> From 234cc5268ea8903852998dbd9ed301d5cf06f7e4 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 24 Sep 2018 22:51:37 -0400 Subject: [PATCH 093/118] clean-up --- src/prover/#eMatching.ml# | 943 -------------------------------------- 1 file changed, 943 deletions(-) delete mode 100644 src/prover/#eMatching.ml# diff --git a/src/prover/#eMatching.ml# b/src/prover/#eMatching.ml# deleted file mode 100644 index dac2d6cb..00000000 --- a/src/prover/#eMatching.ml# +++ /dev/null @@ -1,943 +0,0 @@ -(** E-Matching. - - Implements a variant of the E-matching technique presented in this paper: - Efficient E-Matching for SMT Solvers - Leonardo de Moura and Nikolaj Bjorner, CADE 2007 - -*) - -open Util -open Grass -open GrassUtil - -module CC = CongruenceClosure - - -(** Top-level symbol of current pattern to be matched. - * If we are matching a trivial pattern consisting only of - * a variable, we only record its sort, hence the Either type. - *) -type esymbol = (sort, sorted_symbol) Either.t - -(** Auxiliary state of the E-matching abstract machine. - * Stores the currently active patterns. - * Used for per-multi-pattern pruning and filtering. - *) -type state = TermSet.t - -(** Transition between two consecutive trigger terms in a multi-pattern. - * Used for updating/initializing the auxiliary state. - *) -type step = (TermSet.t, (term * term) list) Either.t - -(** Maps to keep track of register/symbol pairs that should be pruned after init/bind *) -type prune_map = (int * sorted_symbol) list TermMap.t - -(** Inverted paths of symbol/argument position pairs from subterms to parent terms *) -type inv_path = (sorted_symbol * int) list - -(** E-matching code trees for instantiating templates of some type 'a *) -type 'a ematch_code = - | Init of step * esymbol * (int * inv_path) option * int * prune_map * 'a ematch_code - | Bind of int * sorted_symbol * int * prune_map * 'a ematch_code - | Check of int * term * 'a ematch_code - | Compare of int * int * 'a ematch_code - | Choose of 'a ematch_code list - | Yield of int IdMap.t * ('a list) TermMap.t * 'a list - | Filter of (int IdMap.t * filter list) TermMap.t * 'a ematch_code - | Backtrack of state * 'a ematch_code - | ChooseTerm of state * int * 'a ematch_code * prune_map * CC.Node.t list list - -(** Pretty printing of E-matching code trees *) -open Format - -let pr_var_binding ppf (x, i) = - fprintf ppf "%a -> %d" pr_ident x i - -let pr_ematch_code pr_inst ppf = - let pr_step ppf = function - | Either.First ts -> pr_term_list ppf (TermSet.elements ts) - | Either.Second tts -> pr_list_comma (fun ppf (t1, t2) -> fprintf ppf "%a -> %a" pr_term t1 pr_term t2) ppf tts - in - let pr_prune ppf ppf (t, pl) = - fprintf ppf "prune(@[%a,@ [%a]@])" pr_term t - (pr_list_comma (fun ppf (i, sym) -> fprintf ppf "(%d,@ %a)" i pr_sym (fst sym))) pl - in - let rec pr_ematch_code ppf = function - | Choose cs -> - fprintf ppf "%s@ %a" "choose" pr_choose cs - | Init (step, sym, None, i, pm, next) -> - fprintf ppf "init(@[%a,@ %d@])@\n%a@\n%a" - pr_step step i - (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) - pr_ematch_code next - | Init (step, sym, Some (i, path), o, pm, next) -> - fprintf ppf "join(@[%a,@ %d,@ [%a],@ %d@])@\n%a@\n%a" - pr_step step - i - (pr_list_comma (fun ppf (sym, i) -> fprintf ppf "(%a,@ %d)" pr_sym (fst sym) i)) path - o - (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) - pr_ematch_code next - | Bind (i, sym, o, pm, next) -> - fprintf ppf "bind(@[%d,@ (%a, %d),@ %d@])@\n%a@\n%a" i pr_sym (fst sym) (List.length (fst @@ snd sym)) o - (pr_list 0 (fun ppf _ -> fprintf ppf "@\n") pr_prune) (TermMap.bindings pm) - pr_ematch_code next - | Check (i, t, next) -> - fprintf ppf "check(@[%d,@ %a@])@\n%a" i pr_term t pr_ematch_code next - | Compare (i, j, next) -> - fprintf ppf "compare(@[%d,@ %d@])@\n%a" i j pr_ematch_code next - | Yield (vs, fs, gs) -> - let pr_numbered_forms ppf fs = - (pr_list 0 - (fun ppf _ -> fprintf ppf ",@\n") - (fun i ppf f -> fprintf ppf "@[<2>%2d:@ @[%a@]@]" i pr_inst f)) ppf fs - in - fprintf ppf "@[<2>yield(@[[%a]@]) ->@\n%a@\n%a@]" - (pr_list_comma pr_var_binding) (IdMap.bindings vs) - (pr_list 0 - (fun ppf _ -> fprintf ppf ",@\n") - (fun _ ppf (t, fs) -> fprintf ppf "@[<2>%a:@\n%a@]" - pr_term t pr_numbered_forms fs)) (TermMap.bindings fs) - pr_numbered_forms gs - (*| Prune (i, sym, next) -> - fprintf ppf "prune(@[%d,@ %a@])@\n%a" i pr_sym (fst sym) pr_ematch_code next*) - | Filter (filters, next) -> - fprintf ppf "filter(@[%a@])@\n%a" - pr_term_list (filters |> TermMap.bindings |> List.map fst) - (*(pr_list_comma pr_var_binding) (IdMap.bindings vs) - pr_term t - pr_filter filters*) - pr_ematch_code next - | Backtrack (ts, next) -> - fprintf ppf "backtrack(@[[%a]@])@\n@%a" pr_term_list (TermSet.elements ts) pr_ematch_code next - | ChooseTerm (ts, i, next, _, _) -> - fprintf ppf "choose_term(@[%d@])@\n%a" i pr_ematch_code next - - and pr_choose ppf = function - | [] -> () - | [c] -> fprintf ppf "{@[<1>@\n%a@]@\n}" pr_ematch_code c - | c :: cs -> fprintf ppf "{@[<1>@\n%a@]@\n}@ @ @<-3>%s@ %a" pr_ematch_code c "or" pr_choose cs - in pr_ematch_code ppf - -let print_ematch_code string_of_inst out_ch code = - print_of_format (pr_ematch_code string_of_inst) code out_ch - -(** A few utility functions *) - -(** Get the continuation of the first command *) -let continuation = function - | Init (_, _, _, _, _, next) - | Bind (_, _, _, _, next) - | Check (_, _, next) - | Compare (_, _, next) - | Filter (_, next) - | Backtrack (_, next) -> next - | _ -> failwith "illegal argument to continuation" - -(** Get the maximum register in code tree c, yields -1 if c uses no registers *) -let max_reg c = - let rec mr m = function - | Init (_, _, _, i, _, c) - | Check (i, _, c) - | ChooseTerm (_, i, c, _, _) -> mr (max m i) c - | Filter (_, c) - | Backtrack (_, c) -> mr m c - | Bind (i, _, j, _, c) - | Compare (i, j, c) -> mr (max (max m i) j) c - | Choose cs -> - List.fold_left mr m cs - | Yield (vs, _, _) -> IdMap.fold (fun _ -> max) vs m - in - mr (-1) c - -(** Make a choice tree out of trees c1 and - * Avoids the creation of nested choice nodes. *) -let mk_choose c1 c2 = - match c1, c2 with - | Choose cs1, Choose cs2 -> Choose (cs1 @ cs2) - | Choose cs1, _ -> Choose (cs1 @ [c2]) - | _, Choose cs2 -> Choose (c1 :: cs2) - | _ -> Choose [c1; c2] - -(** Work queue for pattern compilation *) -module WQ = PrioQueue.Make - (struct - type t = int - let compare = compare - end) - (struct - type t = term - let compare t1 t2 = - (* Rational for the following definition: - * Entries i -> x and i -> t where t is ground should be processed before entries i -> f(t_1, ..., t_n). - * This is to delay the creation of e-matching code that introduces backtracking points. *) - if is_ground_term t1 && not @@ is_ground_term t2 then -1 - else match t1, t2 with - | Var _, App _ -> -1 - | App _, Var _ -> 1 - | _ -> compare t1 t2 - end) - -(** Triggers of multi-patterns. *) -type trigger = term * filter list - -(** A multi-pattern for instantiating templates of generic type 'a. *) -type 'a pattern = 'a * trigger list - -(** Compile the list of multi-patterns [patterns] into an ematching code tree. *) -let compile (patterns: ('a pattern) list): 'a ematch_code = - (* do some heuristic analysis on the patterns so as to optimize the order - * of their triggers to faciliate sharing across patterns in the code tree *) - (* First count how often each trigger term occurs across all patterns *) - let occurs = - List.fold_left - (fun acc (_, pattern) -> - List.fold_left - (fun acc -> function - | App _ as t, _ -> - let curr = TermMap.find_opt t acc |> Opt.get_or_else 0 in - TermMap.add t (curr + 1) acc - | _ -> acc) - acc pattern) - TermMap.empty - patterns - in - let patterns = - let compare_triggers (t1, _) (t2, _) = - let get_count t = - TermMap.find_opt t occurs |> - Opt.get_or_else 0 - in - (* first prioritize terms that bind more variables as this helps to avoid back-tracking *) - let c1 = - - (compare - (IdSet.cardinal (fv_term t1)) - (IdSet.cardinal (fv_term t2))) - in - if c1 = 0 then - (* next, prioritize terms that occur more often across many patterns *) - let c2 = compare (get_count t2) (get_count t1) in - if c2 = 0 then compare t2 t1 - else c2 - else c1 - in - List.map (fun (tpl, pattern) -> - (tpl, List.sort compare_triggers pattern)) - patterns - in - let esymbol_of = function - | Var (_, srt) -> Either.first srt - | t -> Either.second (sorted_symbol_of t |> Opt.get) - in - let add_args_to_queue w o args = - List.fold_left - (fun (w', o') arg -> WQ.insert o' arg w', o' + 1) - (w, o) args - in - let prune args o = - let pl, _ = - List.fold_left - (fun (pl, o) -> function - | App (sym, _, _) as t when not @@ is_ground_term t -> - (o, sorted_symbol_of t |> Opt.get) :: pl, o + 1 - | _ -> pl, o + 1) - ([], o) args - in - pl - in - let update_yield_templates t_filters_opt fs gs template = - match t_filters_opt with - | Some (t, _) -> - let t_templates = TermMap.find_opt t fs |> Opt.get_or_else [] in - TermMap.add t (template :: t_templates) fs, gs - | None -> fs, template :: gs - in - (* Get shortest inverted path from some variable x bound - * in v that is a subterm of t, if such x exists. *) - let get_path v t = - let rec gp shortest path = function - | App (_, [], _) -> shortest - | Var (x, _) -> - let smaller = - shortest |> - Opt.fold (fun _ (_, p) -> List.length path < List.length p) true - in - if smaller then - IdMap.find_opt x v |> - Opt.map (fun i -> (i, path)) |> - Opt.lazy_or_else (fun () -> shortest) - else shortest - | App (_, args, _) as t -> - let sym = sorted_symbol_of t |> Opt.get in - let new_shortest, _ = - List.fold_left - (fun (ns, k) arg -> gp ns ((sym, k) :: path) arg, k + 1) - (shortest, 0) args - in - new_shortest - in - gp None [] t - in - let args_of = function - | Var _ as t -> [t] - | App (_, args, _) -> args - in - (* Compile a multi-pattern into a code tree. - * f: info about current term of the multi-pattern that is being processed. - * pattern: remaining trigger terms of the multi-pattern that still need to be processed. - * w: work queue of subterms of [f] that still need to be processed. - * v: accumulated map of variable to register bindings. - * o: index of next unused register. - *) - let rec compile f pattern w v o = - let rec c w v o = - if WQ.is_empty w then init f pattern v o else - let i, t, w' = WQ.extract_min w in - match t with - | Var (x, _) when not @@ IdMap.mem x v -> - c w' (IdMap.add x i v) o - | Var (x, _) -> - let next = c w' v o in - let j = IdMap.find x v in - if i = j then next - else Compare (i, j, next) - | t when is_ground_term t -> - Check (i, t, c w' v o) - | App (sym, args, _) -> - let w'', o' = add_args_to_queue w' o args in - let next = c w'' v o' in - let pl = prune args o in - Bind (i, sorted_symbol_of t |> Opt.get, o, TermMap.singleton t pl, next) - in - c w v o - and init f pattern v o : 'a ematch_code = - let template, t_filters_opt = f in - let code = - match pattern with - | [] -> - let fs, gs = update_yield_templates t_filters_opt TermMap.empty [] template in - Yield (v, fs, gs) - | (t', filters') :: pattern' -> - let args = args_of t' in - let w, o' = add_args_to_queue WQ.empty o args in - let next = compile (template, Some (t', filters')) pattern' w v o' in - let pl = prune args o in - let ts = match t_filters_opt with - | Some (t, _) -> Either.second [t, t'] - | None -> Either.first @@ TermSet.singleton t' - in - let pm = TermMap.singleton t' pl in - let esym = esymbol_of t' in - let path = get_path v t' in - Init (ts, esym, path, o, pm, next) - in - match t_filters_opt with - | Some (t, (_ :: _ as fs)) -> - let filters = TermMap.singleton t (v, fs) in - Filter (filters, code) - | _ -> code - in - let seq cs fchild = - let rec s = function - | Init (ts, sym, path, o, pm, c) :: cs -> Init (ts, sym, path, o, pm, s cs) - | Check (i, t, c) :: cs -> Check (i, t, s cs) - | Compare (i, j, c) :: cs -> Compare (i, j, s cs) - | Bind (i, sym, o, pm, c) :: cs -> Bind (i, sym, o, pm, s cs) - | Filter (filters, c) :: cs -> Filter (filters, s cs) - | [] -> fchild - | _ -> assert false - in - s (List.rev cs) - in - let branch f pattern comps fchild w v o = - seq comps (mk_choose (compile f pattern w v o) fchild) - in - let check_if_queue_processed w v = - WQ.fold - (fun i t (v', fully_processed) -> - match t with - | App _ -> v', false - | Var (x, _) -> - IdMap.add x i v', - fully_processed && (IdMap.find_opt x v' |> Opt.get_or_else i |> (=) i) - ) - w (v, true) - in - let _print_wq w = - WQ.fold (fun i t _ -> Printf.printf "%d -> %s, " i (string_of_term t)) w (); - print_newline (); - in - (* Check whether [(f, pattern, w, v)] is compatible with the first node of [code]. - * If yes, return updated values [(code', w', v', f', pattern')]. *) - let compatible f pattern w v code = - match code with - | Init (ts, sym, path, o, pm, next) -> - (* Has w been fully processed? *) - let v', fully_processed = check_if_queue_processed w v in - if fully_processed then - match pattern with - | (t', filters') :: pattern' when esymbol_of t' = sym && get_path v' t' = path -> - let args = args_of t' in - let w', _ = add_args_to_queue WQ.empty o args in - let f' = (fst f, Some (t', filters')) in - let ts' = - ts |> - Either.value_map - (fun ts -> TermSet.add t' ts |> Either.first) - (fun tts -> - snd f |> - Opt.map (fun (t, _) -> - (t, t') :: tts |> Util.remove_duplicates (=) |> Either.second) |> - Opt.get_or_else (TermSet.singleton t' |> Either.first)) - in - let pm' = TermMap.add t' (prune args o) pm in - let code' = Init (ts', sym, path, o, pm', next) in - Some (code', w', v', f', pattern') - | _ -> None - else None - | Filter (filters, next) -> - (match f with - | inst, Some (t, fs) -> - let v' = - WQ.fold - (fun i t v -> match t with - | Var (x, _) -> IdMap.add x i v - | _ -> v) - w v - in - let filters'_opt = - (* Make sure that there is no entry for t in filters, respectively, that the entry is compatible *) - match TermMap.find_opt t filters with - | None -> - Some (TermMap.add t (v', fs) filters) - | Some (v'', fs'') when Hashtbl.hash (v'', fs'') = Hashtbl.hash (v', fs) -> - Some (TermMap.add t (v', fs) filters) - | _ -> None - in - filters'_opt |> Opt.map (fun filters' -> Filter (filters', next), w, v, (inst, None), pattern) - | _, None -> None) - | Check (i, t, _) -> - (match WQ.find_opt i w with - | Some t' when t = t' -> Some (code, WQ.delete i w, v, f, pattern) - | _ -> None) - | Compare (i, j, _) -> - let ti_opt = WQ.find_opt i w in - (match ti_opt with - | Some (Var (x, _) as ti) -> - let v'_opt = match WQ.find_opt j w with - | Some tj when ti = tj -> Some (IdMap.add x j v) - | None when IdMap.find_opt x v = Some j -> Some v - | _ -> None - in - Opt.map (fun v' -> code, WQ.delete i w, v', f, pattern) v'_opt - | _ -> None) - | Bind (i, sym, o, pm, next) -> - (match WQ.find_opt i w with - | Some (App (_, args, _) as t) when sorted_symbol_of t = Some sym -> - let w', _ = add_args_to_queue w o args in - let pl = prune args o in - let code' = Bind (i, sym, o, TermMap.add t pl pm, next) in - Some (code', WQ.delete i w', v, f, pattern) - | _ -> None) - | _ -> None - in - let rec insert_code f pattern w v o comps incomps code: 'a ematch_code * int = - match code with - | Choose cs -> - if incomps = [] then - let code', score = insert_choose f pattern cs w v o in - seq comps code', score + List.length comps - else branch f pattern comps (seq incomps code) w v o, List.length comps - | Yield (v1, fs, gs) -> - let v2, fully_processed = check_if_queue_processed w v in - let v1_union_v2 = IdMap.union (fun x i j -> Some i) v1 v2 in - let v2_union_v1 = IdMap.union (fun x i j -> Some j) v1 v2 in - let filters = snd f |> Opt.map snd |> Opt.get_or_else [] in - if incomps = [] && pattern = [] && fully_processed && filters = [] && IdMap.equal (=) v1_union_v2 v2_union_v1 - then - let yield = - let fs', gs' = update_yield_templates (snd f) fs gs (fst f) in - Yield (v1_union_v2, fs', gs') - in - seq comps yield, List.length comps + 1 - else begin - (*Printf.printf "failed\n"; - print_ematch_code (fun ppf (f, _) -> pr_form ppf f) stdout code; - print_newline(); - if incomps <> [] then Printf.printf "incomps"; - if pattern <> [] then Printf.printf "pattern"; - if not fully_processed then Printf.printf "processed"; - if filters <> [] then Printf.printf "filters"; - print_newline();*) - branch f pattern comps (seq incomps code) w v o, List.length comps - end - | code -> - let next = continuation code in - compatible f pattern w v code |> - Opt.map (fun (code', w', v', f', pattern') -> - insert_code f' pattern' w' v' o (code' :: comps) incomps next) |> - Opt.lazy_get_or_else (fun () -> - match code with - | Init _ -> branch f pattern comps (seq incomps code) w v o, List.length comps - | _ -> insert_code f pattern w v o comps (code :: incomps) next) - and insert_choose f pattern cs w v o = - let cs', _, best = - List.fold_right - (fun code (cs', cs, best) -> - match insert_code f pattern w v o [] [] code with - | code', score when score > best -> - code' :: cs, code :: cs, score - | _ -> code :: cs', code :: cs, best - ) - cs ([], [], 0) - in - if best = 0 - then Choose (compile f pattern w v o :: cs), 0 - else Choose cs', best - in - List.fold_left - (fun code (f, pattern) -> - let code', _ = insert_code (f, None) pattern WQ.empty IdMap.empty (max_reg code + 1) [] [] code in - code' - ) - (Choose []) patterns - -(** Check whether term [t] and substitution [sm] satisfy the filters [filters]. *) -let filter_term filters t sm = - List.for_all - (fun f -> match f with - | FilterNotNull -> - (match t with - | App (Null, [], _) -> false - | _ -> true) - | FilterSymbolNotOccurs sym -> - let rec not_occurs = function - | App (EntPnt, _, _) -> sym <> EntPnt - | App (sym1, _, _) when sym1 = sym -> false - | App (_, ts, _) -> List.for_all not_occurs ts - | _ -> true - in not_occurs t - | FilterReadNotOccurs (name, (arg_srts, res_srt)) -> - let rec not_occurs = function - | App (EntPnt, _, _) -> true - | App ((Read | ArrayCells), (App (FreeSym (name1, _), arg_ts, res_srt1) :: _ as ts), _) -> - let ok = - try - name1 <> name || - res_srt1 <> res_srt || - List.fold_left2 (fun acc t1 srt -> acc || sort_of t1 <> srt) false arg_ts arg_srts - with Invalid_argument _ -> true - in ok && List.for_all not_occurs ts - | App (_, ts, _) -> List.for_all not_occurs ts - | _ -> true - in not_occurs t - | FilterGeneric fn -> fn sm t - ) - filters - - -(** The E-matching abstract machine. - * Runs the given E-matching code tree [code] on the E-graph [ccgraph]. - * Yields a hash table mapping templates of type 'a to the computed variable substitutions. - *) -let run ?(pr_tpl = (fun ppf _ -> ())) (code: 'a ematch_code) ccgraph : ('a, subst_map) Hashtbl.t = - let egrapha, egraphp = CC.get_egraph ccgraph in - (* some utility functions for working with the e-graph *) - let get_apps sym = - CC.EGraphA.fold - (fun (n, sym') n_apps apps -> - if sym = sym' - then CC.NodeListSet.union n_apps apps - else apps) - egrapha CC.NodeListSet.empty |> - CC.NodeListSet.elements - in - let get_reps srt = - CC.EGraphA.fold - (fun (n, sym) _ reps -> - if snd @@ snd sym = srt - then CC.NodeListSet.add [n] reps - else reps) - egrapha CC.NodeListSet.empty |> - CC.NodeListSet.elements - in - let get_apps_of_node n sym = - CC.EGraphA.find_opt (n, sym) egrapha |> - Opt.get_or_else CC.NodeListSet.empty |> - CC.NodeListSet.elements - in - let get_parents n sym k = - (* Printf.printf "get_parents (%s, %s, %d)...\n" - (CC.term_of_rep ccgraph n |> string_of_term) - (fst sym |> string_of_symbol) - k - ;*) - CC.EGraphP.find_opt (n, sym, k) egraphp |> - Opt.get_or_else (CC.NodeListSet.empty, CC.NodeSet.empty) (*|> - (fun (ps, ns) -> print_list stdout pr_term (List.map (CC.term_of_rep ccgraph) (CC.NodeSet.elements ns)); (ps, ns))*) - in - (* initialize registers *) - let regs = Array.make (max_reg code + 1) (CC.rep_of_term ccgraph mk_true_term) in - (* create result table *) - let insts = Hashtbl.create 1000 in - let prune pm ts = - TermSet.filter (fun t -> - TermMap.find_opt t pm |> - Opt.map (fun pl -> - List.for_all (fun (i, sym) -> - let n = regs.(i) in - let funs_n = CC.funs_of_rep ccgraph n in - BloomFilter.mem sym funs_n) pl) |> - Opt.get_or_else true) ts - in - let take_step step ts = - step |> - Either.value_map - (fun ts' -> ts') - (fun tts -> - List.fold_left (fun ts' (t, t') -> - if TermSet.mem t ts then TermSet.add t' ts' - else ts') - TermSet.empty tts) - in - (* collect nodes containing one of the nodes in [terms] following the given inverted path *) - let rec collect (esym: esymbol) args terms = function - | [] -> - esym |> - Either.map - (fun _ -> CC.NodeSet.fold (fun n acc -> [n] :: acc) terms []) - (fun _ -> CC.NodeListSet.elements args) |> - Either.value - | (sym, k) :: path' -> - let args', terms' = - CC.NodeSet.fold - (fun n (args', terms') -> - let n_args, n_terms = get_parents n sym k in - CC.NodeListSet.union n_args args', - CC.NodeSet.union n_terms terms' - ) - terms (CC.NodeListSet.empty, CC.NodeSet.empty) - in - collect esym args' terms' path' - in - (* the actual machine *) - let rec run state = function - | Init (step, esym, path_opt, o, pm, next) :: stack -> - let ts = state in - let terms = - path_opt |> - Opt.map - (fun (i, path) -> - collect esym CC.NodeListSet.empty (CC.NodeSet.singleton regs.(i)) path) |> - Opt.lazy_get_or_else - (fun () -> esym |> Either.map get_reps get_apps |> Either.value) - in - let ts' = take_step step ts in - (*print_of_format (fun ppf esym -> fprintf ppf "init %s: [%a] -> %a\n" - (esym |> Either.value_map string_of_sort (fun sym -> string_of_symbol (fst sym))) - pr_sorts - (esym |> Either.value_map (fun _ -> []) (fun sym -> fst @@ snd sym)) - pr_sort - (esym |> Either.value_map (fun srt -> srt) (fun sym -> snd @@ snd sym))) esym stdout - ; - Printf.printf "active terms: "; - print_list stdout pr_term (TermSet.elements ts'); - Printf.printf "\n"; - Printf.printf "choosing from: "; - print_list stdout (fun ppf ns -> fprintf ppf "[%a]" pr_term_list (List.map (CC.term_of_rep ccgraph) ns)) terms; - Printf.printf "\n";*) - run ts' (ChooseTerm (ts', o, next, pm, terms) :: stack) - | Bind (i, sym, o, pm, next) :: stack -> - (*Printf.printf "bind(%d, %s)\n" i (string_of_symbol @@ fst sym);*) - let apps = get_apps_of_node (regs.(i)) sym in - run state (ChooseTerm (state, o, next, pm, apps) :: stack) - | Check (i, t, next) :: stack -> - (*Printf.printf "check(%d, %s)\n" i (string_of_term t);*) - if CC.rep_of_term ccgraph t = regs.(i) - then run state (next :: stack) - else begin - (* let t' = CC.term_of_rep ccgraph regs.(i) in - Printf.printf " failed with %d = %s\n" i (string_of_term t');*) - run state stack - end - | Compare (i, j, next) :: stack -> - (* Printf.printf "compare(%d, %d)\n" i j;*) - if regs.(i) = regs.(j) - then run state (next :: stack) - else begin - (* let ti = CC.term_of_rep ccgraph regs.(i) in - let tj = CC.term_of_rep ccgraph regs.(j) in - Printf.printf " failed with %d = %s and %d = %s \n" i (string_of_term ti) j (string_of_term tj);*) - run state stack - end - | Choose cs :: stack -> - run state (List.map (fun c -> Backtrack (state, c)) cs @ stack) - | Yield (vs, fs, gs) :: stack -> - let ts = state in - let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in - (*Printf.printf "yield\n"; - print_list stdout (fun ppf (x, t) -> fprintf ppf "%s -> %a" (string_of_ident x) pr_term t) (IdMap.bindings sm); - print_newline ();*) - TermSet.iter (fun t -> - TermMap.find_opt t fs |> - Opt.iter (List.iter (fun f -> - (*print_of_format (fun ppf f -> fprintf ppf " for %a\n" pr_tpl f) f stdout;*) - Hashtbl.add insts f sm))) ts; - List.iter (fun f -> Hashtbl.add insts f sm) gs; - run state stack - | Filter (filters, next) :: stack -> - let ts = state in - let ts' = - TermSet.filter - (fun t -> - let t_filters = TermMap.find_opt t filters in - match t_filters with - | Some (vs, fs) -> - let sm = IdMap.map (fun i -> CC.term_of_rep ccgraph regs.(i)) vs in - (*Printf.printf "Filtering %d, %s...\n" (IdMap.cardinal vs) (string_of_term (subst_term sm t));*) - filter_term fs (subst_term sm t) sm - | None -> true) - ts - in - (*Printf.printf "filter;\nactive terms: "; - print_list stdout pr_term (TermSet.elements ts'); - Printf.printf "\n";*) - if TermSet.is_empty ts' - then run state stack - else run ts' (next :: stack) - | ChooseTerm (state', o, next, pm, args :: apps) :: stack -> - (*Printf.printf "choosing app: "; - print_of_format (fun ppf args -> fprintf ppf "[%a]" pr_term_list (List.map (CC.term_of_rep ccgraph) args)) args stdout; - print_newline ();*) - let _ = - List.fold_left - (fun i arg -> - (*Printf.printf "%d -> %s, " i (string_of_term @@ CC.term_of_rep ccgraph arg);*) - regs.(i) <- arg; i + 1) - o args - in - let ts' = prune pm state' in - if TermSet.is_empty ts' - then run state (ChooseTerm (state', o, next, pm, apps) :: stack) - else run ts' (next :: ChooseTerm (state', o, next, pm, apps) :: stack) - | Backtrack (state', next) :: stack -> run state' (next :: stack) - | ChooseTerm (_, _, _, _, []) :: stack -> - run state stack - | [] -> insts - in - run TermSet.empty [code] - - -(** Generate an E-matching code tree from the given set of axioms. *) -let compile_axioms_to_ematch_code ?(force=false) ?(stratify=(!Config.stratify)) axioms : - (form * _) ematch_code * (form * _) pattern list = - (* *) - let epr_axioms, axioms = - List.partition - (fun f -> IdSrtSet.is_empty (vars_in_fun_terms f) || not !Config.instantiate && not force) - axioms - in - (*print_endline "EPR:"; - List.iter print_endline (List.map string_of_form epr_axioms);*) - (*let _ = print_endline "Candidate axioms for instantiation:" in - let _ = print_forms stdout axioms in*) - let generate_pattern f = - let fvars0 = sorted_free_vars f in - let fvars = IdSrtSet.inter fvars0 (vars_in_fun_terms f) in - (* filter out stratified variables *) - let fvars, strat_vars = - let merge_map k a b = match (a,b) with - | (Some a, Some b) -> Some (a @ b) - | (Some c, None) | (None, Some c) -> Some c - | (None, None) -> None - in - let rec gen_tpe acc t = match t with - | App (_, ts, srt) -> - List.fold_left - (IdMap.merge merge_map) - IdMap.empty - (List.map (gen_tpe (srt :: acc)) ts) - | Var (id, _) -> IdMap.add id acc IdMap.empty - in - let gen_map = - TermSet.fold - (fun t acc -> IdMap.merge merge_map (gen_tpe [] t) acc) - (fun_terms_with_vars f) - IdMap.empty - in - if stratify then - IdSrtSet.partition - (fun (id, srt) -> - try - let generating = IdMap.find id gen_map in - not (List.for_all (TypeStrat.is_stratified srt) generating) - with Not_found -> - begin - Debug.warn (fun () -> "BUG in stratification: " ^ (string_of_ident id) ^ "\n"); - true - end - ) - fvars - else fvars, IdSrtSet.empty - in - let strat_var_ids = IdSrtSet.fold (fun (id, _) acc -> IdSet.add id acc) strat_vars IdSet.empty in - (* collect all terms in which free variables appear below function symbols *) - let fun_terms, fun_vars, strat_terms = - let rec tt (fun_terms, fun_vars, strat_terms) t = - match t with - | App (sym, _ :: _, srt) when srt <> Bool -> - let tvs = fv_term t in - if IdSet.is_empty tvs then - fun_terms, fun_vars, strat_terms - else if IdSet.is_empty (IdSet.inter strat_var_ids tvs) - then TermSet.add t fun_terms, IdSet.union tvs fun_vars, strat_terms - else fun_terms, fun_vars, TermSet.add t strat_terms - | App (_, ts, _) -> - List.fold_left tt (fun_terms, fun_vars, strat_terms) ts - | _ -> fun_terms, fun_vars, strat_terms - in fold_terms tt (TermSet.empty, IdSet.empty, TermSet.empty) f - in - let unmatched_vars = - IdSrtSet.fold (fun (id, _) acc -> - if IdSet.mem id fun_vars then acc - else IdSet.add id acc) fvars IdSet.empty - in - let fun_terms, fun_vars = - TermSet.fold (fun t (fun_terms, fun_vars) -> - let vs = fv_term t in - if IdSet.is_empty (IdSet.inter unmatched_vars vs) - then fun_terms, fun_vars - else TermSet.add t fun_terms, IdSet.union vs fun_vars) - strat_terms (fun_terms, fun_vars) - in - let strat_vars1 = - IdSrtSet.filter (fun (id, _) -> not (IdSet.mem id fun_vars)) strat_vars - in - (* extract patterns separately to obtain auxilary filters *) - let patterns = extract_patterns f in - let fun_terms_with_filters = - TermSet.fold (fun t acc -> - try (t, List.assoc t patterns) :: acc - with Not_found -> (t, []) :: acc) - fun_terms [] - in - let f = mk_forall (IdSrtSet.elements strat_vars1) f in - (f, (fvars0, strat_vars1, unmatched_vars, fun_vars, fun_terms_with_filters)), fun_terms_with_filters - in - let patterns = [] in - let patterns = - List.fold_right - (fun f patterns -> ((f, (IdSrtSet.empty, IdSrtSet.empty, IdSet.empty, IdSet.empty, [])), []) :: patterns) - epr_axioms patterns - in - let patterns = - List.fold_right - (fun f patterns -> generate_pattern f :: patterns) axioms patterns - in - compile patterns, patterns - -(** Instantiate the axioms encoded in patterns [patterns] and code tree [code] for E-graph [ccgraph]. *) -let instantiate_axioms_from_code patterns code ccgraph : form list = - let print_debug ((f, (fvars, strat_vars, unmatched_vars, fun_vars, fun_terms_with_filters)), subst_maps) = - print_endline "--------------------"; - print_endline (string_of_form f); - Printf.printf "all vars: "; - print_list stdout pr_ident (List.map fst (IdSrtSet.elements fvars)); - print_newline (); - Printf.printf "strat vars: "; - print_list stdout pr_ident (List.map fst (IdSrtSet.elements strat_vars)); - print_newline (); - Printf.printf "unmatched vars: "; - print_list stdout pr_ident (IdSet.elements unmatched_vars); - print_newline (); - Printf.printf "inst vars: "; - print_list stdout pr_ident (IdSet.elements fun_vars); - print_newline (); - Printf.printf "fun terms: "; - print_list stdout pr_term (List.map fst fun_terms_with_filters); - print_newline (); - Printf.printf "# of insts: %d\n" (List.length subst_maps); - print_endline "subst_maps:"; - List.iter print_subst_map subst_maps; - print_endline "--------------------"; - in - let _ = - if Debug.is_debug 1 then CC.print_classes ccgraph - in - let instances = run code ccgraph in - let get_form (f, _) = f in - let grouped_instances = - List.rev_map - (fun (f, _) -> - let sms = Hashtbl.find_all instances f in - (f, sms)) - patterns - in - let _ = - if Debug.is_debug 1 then begin - grouped_instances |> - List.rev_map (fun (f, sms) -> (f, List.filter ((<>) IdMap.empty) sms)) |> - List.iter print_debug - end - in - grouped_instances |> - List.fold_left - (fun instances (f_aux, sms) -> - List.fold_left (fun instances sm -> - subst sm (get_form f_aux) :: instances) - instances sms) [] - -let instantiate_axioms_from_code patterns code ccgraph = - measure_call "EMatching.instantiate_axioms" (instantiate_axioms_from_code patterns code) ccgraph - -let instantiate_axioms ?(force=false) ?(stratify=(!Config.stratify)) axioms ccgraph = - let code, patterns = compile_axioms_to_ematch_code ~force:force ~stratify:stratify axioms in - instantiate_axioms_from_code patterns code ccgraph - -(** Compile term generators [generators] into an E-matching code tree. *) -let compile_term_generators_to_ematch_code generators = - let generators = - let remove_generic_filters (ms, ts) = - List.map (function Match (m, fs) -> (m, List.filter (function FilterGeneric _ -> false | _ -> true) fs)) ms, ts - in - remove_duplicates (fun g1 g2 -> remove_generic_filters g1 = remove_generic_filters g2) generators - in - let generate_pattern patterns (guards, ts) = - match ts with - | [] -> patterns - | _ -> - let triggers = List.map (function Match (t, filters) -> (t, filters)) guards in - (ts, triggers) :: patterns - in - let patterns = List.fold_left generate_pattern [] generators in - compile patterns - -(** Generate new terms according to term generators encoded in [code] and E-graph [ccgraph]. - * The new terms are added to the E-graph. Returns a Boolean indicating whether new terms - * were derived (modulo equality) and the new E-graph. *) -let generate_terms_from_code code ccgraph = - let rec round i has_mods ccgraph = - (*Printf.printf "Generating terms, round %d...\n" i; flush stdout; - CC.print_classes ccgraph;*) - (*let terms = CC.get_terms ccgraph in*) - let insts = run ~pr_tpl:pr_term_list code ccgraph in - let new_terms = - Hashtbl.fold (fun ts sm new_terms -> - List.fold_left (fun new_terms gen_term -> - let t = subst_term sm gen_term in - (*let _ = - if not @@ TermSet.mem t terms then - print_endline (" Adding generated term " ^ string_of_term gen_term ^ " -> " ^ string_of_term t); flush stdout - in*) - TermSet.add t new_terms) new_terms ts) - insts TermSet.empty - in - let ccgraph = CC.add_terms new_terms ccgraph in - if not @@ CC.has_mods ccgraph then has_mods, ccgraph - else round (i + 1) true (CC.reset ccgraph) - in - round 0 false ccgraph - -let generate_terms_from_code code ccgraph = - measure_call "EMatching.generate_terms_from_code" (generate_terms_from_code code) ccgraph From 768df483be1b2e989c6b18fc1d82ef93f0032f13 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 2 Nov 2018 18:12:29 -0400 Subject: [PATCH 094/118] Fixes #27 --- src/formulas/grassUtil.ml | 55 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index babb7e11..27a4dd05 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -1147,50 +1147,49 @@ let map_id_term fct t = (** Map all identifiers occuring in formula [f] to new identifiers according to function [fct] *) let map_id fct f = - let rec sub = function - | BoolOp (op, fs) -> BoolOp (op, List.map sub fs) - | Atom (t, a) -> Atom (map_id_term fct t, a) - | Binder (b, vs, f, a) -> Binder (b, vs, sub f, a) - in sub f - -(** Substitutes all identifiers in term [t] with other identifiers according to substitution map [subst_map] *) -let subst_id_term subst_map t = - let sub_id id = - try IdMap.find id subst_map with Not_found -> id - in - map_id_term sub_id t - -(** Substitutes all identifiers in formula [f] with other identifiers according to substitution map [subst_map]. - ** This operation is not capture avoiding. *) -let subst_id subst_map f = - let subt = subst_id_term subst_map in - let subf f = match f with + let mapt = map_id_term fct in + let mapf f = match f with | FilterSymbolNotOccurs (FreeSym id) -> - (try FilterSymbolNotOccurs (FreeSym (IdMap.find id subst_map)) + (try FilterSymbolNotOccurs (FreeSym (fct id)) with Not_found -> f) (*| FilterTermNotOccurs t -> FilterTermNotOccurs (subt t)*) | f -> f in - let subg g = match g with + let mapg g = match g with | Match (t, fs) -> - let t1 = subt t in - let fs1 = List.map subf fs in + let t1 = mapt t in + let fs1 = List.map mapf fs in Match (t1, fs1) in - let suba a = match a with + let mapa a = match a with | TermGenerator (guards, gen_terms) -> - TermGenerator (List.map subg guards, List.map subt gen_terms) - | Pattern (t, fs) -> Pattern (subt t, List.map subf fs) - | Label (pol, t) -> Label (pol, subt t) + TermGenerator (List.map mapg guards, List.map mapt gen_terms) + | Pattern (t, fs) -> Pattern (mapt t, List.map mapf fs) + | Label (pol, t) -> Label (pol, mapt t) | a -> a in let rec sub = function | BoolOp (op, fs) -> BoolOp (op, List.map sub fs) - | Atom (t, a) -> Atom (subt t, List.map suba a) - | Binder (b, vs, f, a) -> Binder (b, vs, sub f, List.map suba a) + | Atom (t, a) -> Atom (map_id_term fct t, List.map mapa a) + | Binder (b, vs, f, a) -> Binder (b, vs, sub f, List.map mapa a) in sub f +(** Substitutes all identifiers in term [t] with other identifiers according to substitution map [subst_map] *) +let subst_id_term subst_map t = + let sub_id id = + try IdMap.find id subst_map with Not_found -> id + in + map_id_term sub_id t + +(** Substitutes all identifiers in formula [f] with other identifiers according to substitution map [subst_map]. + ** This operation is not capture avoiding. *) +let subst_id subst_map f = + let sub_id id = + try IdMap.find id subst_map with Not_found -> id + in + map_id sub_id f + (** Substitutes all constants in term [t] with other terms according to substitution function [subst_fun]. *) let subst_consts_fun_term subst_fun t = let rec sub = function From 02e551052bfb0b4bc21987709320faf9154b9c29 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 2 Nov 2018 21:53:38 -0400 Subject: [PATCH 095/118] use find_opt when possible --- src/formulas/grassUtil.ml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index 27a4dd05..5a473fa9 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -185,8 +185,8 @@ let fresh_ident = let used_names = Hashtbl.create 0 in fun ?(id=0) (name : string) -> let last_index = - try Hashtbl.find used_names name - with Not_found -> -1 + Hashtbl.find_opt used_names name |> + Opt.get_or_else (-1) in let new_max = max (last_index + 1) id in Hashtbl.replace used_names name new_max; @@ -277,8 +277,8 @@ let eq_name id1 id2 = name id1 = name id2 let symbol_of_ident = let symbol_map = List.map (fun sym -> (string_of_symbol sym, sym)) symbols in fun ((name, _) as id) -> - try List.assoc name symbol_map - with Not_found -> FreeSym id + List.assoc_opt name symbol_map |> + Opt.get_or_else (FreeSym id) (** {6 (Smart) constructors} *) @@ -1121,7 +1121,7 @@ let sign f : signature = (** Extracts the signature of formula [f]. *) let overloaded_sign f : (arity list SymbolMap.t) = let add_to_sign sym tpe decls = - let old = try SymbolMap.find sym decls with Not_found -> [] in + let old = SymbolMap.find_opt sym decls |> Opt.get_or_else [] in if List.mem tpe old then decls else SymbolMap.add sym (tpe :: old) decls in @@ -1178,7 +1178,7 @@ let map_id fct f = (** Substitutes all identifiers in term [t] with other identifiers according to substitution map [subst_map] *) let subst_id_term subst_map t = let sub_id id = - try IdMap.find id subst_map with Not_found -> id + IdMap.find_opt id subst_map |> Opt.get_or_else id in map_id_term sub_id t @@ -1186,7 +1186,7 @@ let subst_id_term subst_map t = ** This operation is not capture avoiding. *) let subst_id subst_map f = let sub_id id = - try IdMap.find id subst_map with Not_found -> id + IdMap.find_opt id subst_map |> Opt.get_or_else id in map_id sub_id f @@ -1208,7 +1208,7 @@ let subst_consts_fun subst_fun f = (** Substitutes all constants in term [t] with other terms according to substitution map [subst_map]. *) let subst_consts_term subst_map t = let sub_id id t = - try IdMap.find id subst_map with Not_found -> t + IdMap.find_opt id subst_map |> Opt.get_or_else t in subst_consts_fun_term sub_id t @@ -1217,12 +1217,10 @@ let subst_consts_term subst_map t = let subst_consts subst_map f = let subst_filter f = match f with | FilterSymbolNotOccurs (FreeSym id) -> - (try - match IdMap.find id subst_map with - | App (FreeSym id1, [], _) -> + (match IdMap.find_opt id subst_map with + | Some (App (FreeSym id1, [], _)) -> FilterSymbolNotOccurs (FreeSym id1) - | _ -> f - with Not_found -> f) + | _ -> f) | f -> f in let subst_annot = function From 074e4256358a21a8c7c28fa1402ba7256a3bfd60 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 3 Nov 2018 12:33:34 -0400 Subject: [PATCH 096/118] don't check writes on loc maps that are not part of the state --- src/verifier/grassifier.ml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/verifier/grassifier.ml b/src/verifier/grassifier.ml index dc318c85..27e85505 100644 --- a/src/verifier/grassifier.ml +++ b/src/verifier/grassifier.ml @@ -1424,7 +1424,16 @@ let annotate_runtime_checks prog = | App (Length, [map], _) -> TermSet.add map acc | App (ArrayCells, [map], _) -> TermSet.add map acc | App (Write, fld :: loc :: ts, _) -> - List.fold_left checks (TermSet.add loc acc) (fld :: loc :: ts) + let acc1 = + match fld with + | App (FreeSym id, [], _) -> + (try + let _ = IdMap.mem id prog.prog_vars in + TermSet.add loc acc + with Not_found -> acc) + | _ -> acc + in + List.fold_left checks acc1 (fld :: loc :: ts) | App ((Div | Mod), [t1; t2], _) -> List.fold_left checks (TermSet.add t2 acc) [t1; t2] | App (_, ts, _) -> From 8a17c3ed9cab0cae94c3dbb5abc6a60fc0b7b899 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 3 Nov 2018 18:42:35 -0400 Subject: [PATCH 097/118] fixes #28 --- src/prover/reduction.ml | 118 ++++++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 35 deletions(-) diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index a7b0b970..2e7eb9d4 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -284,9 +284,8 @@ let get_read_propagators gts = | _ -> srts) gts SortSet.empty in - let read_propagators = - SortSet.fold (function - | Adt (id, adts) -> fun propagators -> + let add_propagators = function + | Adt (id, adts) -> fun propagators -> let s = fresh_ident "?s" in let t = fresh_ident "?t" in let cstrs = List.assoc id adts in @@ -352,22 +351,72 @@ let get_read_propagators gts = ([Match (mk_eq_term a b, []); Match (mk_read fld [mk_read (mk_array_cells b) [idx]], [])], [mk_read fld [mk_read (mk_array_cells a) [idx]]]) :: propagators - | Map (dsrts, srt) as fldsrt -> fun propagators -> - let f1 = fresh_ident "?f", fldsrt in - let fld1 = mk_var (snd f1) (fst f1) in - let f2 = fresh_ident "?g", fldsrt in - let fld2 = mk_var (snd f2) (fst f2) in + | Map (dsrts, srt) as mapsrt -> fun propagators -> + let f = fresh_ident "?f", mapsrt in + let fvar = mk_var (snd f) (fst f) in + let g = fresh_ident "?g", mapsrt in + let gvar = mk_var (snd g) (fst g) in let d = fresh_ident "?d" in let dvar = mk_var srt d in + let xvars = List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts in + let yvars = List.map (fun srt -> mk_var srt (fresh_ident "?j")) dsrts in + let propagators = + match srt with + | Adt (id, adts) -> + let cstrs = List.assoc id adts in + let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in + let propagators = + List.fold_left + (fun propagators -> function + | (destr, (Map ([dsrt2], _) as srt)) -> + let srt = unfold_adts adts srt in + let zvar = mk_var (unfold_adts adts dsrt2) (fresh_ident "?z") in + (* f[x := d], f[y].destr[z] -> f[x := d][y].destr[z] *) + ([Match (mk_write fvar xvars dvar, []); + Match (mk_read (mk_destr srt destr (mk_read fvar yvars)) [zvar], [])], + [mk_read (mk_destr srt destr (mk_read (mk_write fvar xvars dvar) yvars)) [zvar]]) :: + (* f[x := d].destr[z] -> f[y].destr[z] *) + ([Match (mk_read (mk_destr srt destr (mk_read (mk_write fvar xvars dvar) yvars)) [zvar], [])], + [mk_read (mk_destr srt destr (mk_read fvar yvars)) [zvar]]) + :: propagators + | _ -> propagators + ) + propagators destrs + in + propagators + | Map (dsrts2, Adt (id, adts)) -> + let cstrs = List.assoc id adts in + let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in + let uvars = List.map (fun srt -> mk_var srt (fresh_ident "?u")) dsrts2 in + let propagators = + List.fold_left + (fun propagators -> function + | (destr, (Map ([dsrt2], _) as srt)) -> + let srt = unfold_adts adts srt in + let zvar = mk_var (unfold_adts adts dsrt2) (fresh_ident "?z") in + (* f[x := d], f[y][u].destr[z] -> f[x := d][y][u].destr[z] *) + ([Match (mk_write fvar xvars dvar, []); + Match (mk_read (mk_destr srt destr (mk_read (mk_read fvar yvars) uvars)) [zvar], [])], + [mk_read (mk_destr srt destr (mk_read (mk_read (mk_write fvar xvars dvar) yvars) uvars)) [zvar]]) :: + (* f[x := d][u].destr[z], f[y] -> f[y][u].destr[z] *) + ([Match (mk_read fvar yvars, []); + Match (mk_read (mk_destr srt destr (mk_read (mk_read (mk_write fvar xvars dvar) yvars) uvars)) [zvar], [])], + [mk_read (mk_destr srt destr (mk_read (mk_read fvar yvars) uvars)) [zvar]]) + :: propagators + | _ -> propagators + ) + propagators destrs + in + propagators + | _ -> propagators + in let ssrt_opt = match dsrts with | Loc ssrt :: _ -> Some ssrt | _ -> None in - let ivars1 = List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts in - let ivars2 = List.map (fun srt -> mk_var srt (fresh_ident "?j")) dsrts in let match_ivar1 = ssrt_opt |> - Opt.map (fun _ -> Match (List.hd ivars1, [FilterNotNull])) |> + Opt.map (fun _ -> Match (List.hd xvars, [FilterNotNull])) |> Opt.to_list in let gen_frame wrap = @@ -376,50 +425,49 @@ let get_read_propagators gts = let set1 = Axioms.set1 ssrt in let set2 = Axioms.set2 ssrt in [(* Frame (x, a, f, g), y.g -> y.f *) - ([Match (mk_frame_term set1 set2 fld1 fld2, []); - Match (wrap (mk_read fld2 (ivars1)), [])] @ + ([Match (mk_frame_term set1 set2 fvar gvar, []); + Match (wrap (mk_read gvar (xvars)), [])] @ match_ivar1, - [wrap (mk_read fld1 (ivars1))]); + [wrap (mk_read fvar (xvars))]); (* Frame (x, a, f, g), y.f -> y.g *) - ([Match (mk_frame_term set1 set2 fld1 fld2, []); - Match (wrap (mk_read fld1 (ivars1)), [])], - [wrap (mk_read fld2 (ivars1))]) + ([Match (mk_frame_term set1 set2 fvar gvar, []); + Match (wrap (mk_read fvar (xvars)), [])], + [wrap (mk_read gvar (xvars))]) ]) |> Opt.get_or_else [] in let mk_generators wrap = - ([Match (mk_eq_term fld1 fld2, []); - Match (wrap (mk_read fld1 (ivars1)), []); + ([Match (mk_eq_term fvar gvar, []); + Match (wrap (mk_read fvar (xvars)), []); ], - [wrap (mk_read fld2 (ivars1))]) :: + [wrap (mk_read gvar (xvars))]) :: (* f == g, x.f -> x.g *) - ([Match (mk_eq_term fld1 fld2, []); - Match (wrap (mk_read fld1 (ivars1)), []) + ([Match (mk_eq_term fvar gvar, []); + Match (wrap (mk_read fvar (xvars)), []) ], - [wrap (mk_read fld2 (ivars1))]) :: + [wrap (mk_read gvar (xvars))]) :: (* f == g, x.g -> x.f *) - ([Match (mk_eq_term fld1 fld2, []); - Match (wrap (mk_read fld2 (ivars1)), [])] @ match_ivar1, - [wrap (mk_read fld1 (ivars1))]) :: + ([Match (mk_eq_term fvar gvar, []); + Match (wrap (mk_read gvar (xvars)), [])] @ match_ivar1, + [wrap (mk_read fvar (xvars))]) :: (* f [x := d], y.(f [x := d]) -> y.f *) - ([Match (mk_write fld1 ivars1 dvar, []); - Match (wrap (mk_read (mk_write fld1 ivars1 dvar) ivars2), []); + ([Match (mk_write fvar xvars dvar, []); + Match (wrap (mk_read (mk_write fvar xvars dvar) yvars), []); (*Match (loc1, [FilterNotNull]);*) (*Match (loc2, [FilterNotNull])*)], - [wrap (mk_read fld1 ivars2)]) :: + [wrap (mk_read fvar yvars)]) :: (* f [x := d], y.f -> y.(f [x := d]) *) - ([Match (mk_write fld1 ivars1 dvar, []); - Match (wrap (mk_read fld1 (ivars2)), []); + ([Match (mk_write fvar xvars dvar, []); + Match (wrap (mk_read fvar (yvars)), []); (*Match (loc1, [FilterNotNull]);*) (*Match (loc2, [FilterNotNull])*)], - [wrap (mk_read (mk_write fld1 (ivars1) dvar) (ivars2))]) :: + [wrap (mk_read (mk_write fvar (xvars) dvar) (yvars))]) :: gen_frame wrap in mk_generators (fun t -> t) (*@ mk_generators (fun t -> mk_known t)*) @ propagators - | _ -> fun propagators -> propagators) - field_sorts [] + | _ -> fun propagators -> propagators in - read_propagators + SortSet.fold add_propagators field_sorts [] let add_read_write_axioms fs = let gts = ground_terms ~include_atoms:true (mk_and fs) in From 84da5ca018fc722961a8aa23cd5f2e30c1fdb2af Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Sat, 3 Nov 2018 19:41:32 -0400 Subject: [PATCH 098/118] fix command line option to pass multiple procedures --- src/main/grasshopper.ml | 52 +++++++++++++++++++++------------------- src/verifier/verifier.ml | 10 ++------ 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/main/grasshopper.ml b/src/main/grasshopper.ml index e13e00d9..b1d81165 100644 --- a/src/main/grasshopper.ml +++ b/src/main/grasshopper.ml @@ -2,6 +2,7 @@ open Util open SplCompiler +open Grass let greeting = "GRASShopper version " ^ Config.version ^ "\n" @@ -25,7 +26,7 @@ let output_trace prog proc (pp, model) = let print_pos (pos, state) = Printf.fprintf trace_chan "{\"position\": {\"line_no\": %d, \"column_no_start\": %d, \"column_no_stop\": %d}, \"state\": " - pos.Grass.sp_start_line pos.Grass.sp_start_col pos.Grass.sp_end_col; + pos.sp_start_line pos.sp_start_col pos.sp_end_col; ModelPrinting.output_json trace_chan state; output_string trace_chan "}" in @@ -117,7 +118,15 @@ let parse_spl_program main_file = (** Check SPL program in main file [file] and procedure [proc] *) let check_spl_program spl_prog proc = let prog = SplTranslator.to_program spl_prog in - let simple_prog = Verifier.simplify proc prog in + let procs = (* Split proc string to get names of multiple procedures *) + match proc with + | Some p -> + p |> String.split_on_char ' ' + |> List.fold_left (fun ps s -> IdSet.add (s, 0) ps) IdSet.empty + | None -> + IdMap.fold (fun id _ -> IdSet.add id) prog.prog_procs IdSet.empty + in + let simple_prog = Verifier.simplify procs prog in let check simple_prog first proc = let errors = if !Config.typeonly then [] @@ -138,28 +147,23 @@ let check_spl_program spl_prog proc = ) first errors in - match proc with - | None -> Prog.fold_procs (check simple_prog) true simple_prog - | Some p -> - (* Split string to get names of multiple procedures *) - let procs = - p |> String.split_on_char ' ' - |> List.map (fun p -> - match Prog.find_proc_with_deps simple_prog (p, 0) with - | [] -> - let available = - Prog.fold_procs - (fun acc proc -> - let name = Prog.name_of_proc proc in - "\t" ^ Grass.string_of_ident name ^ "\n" ^ acc) - "" prog - in - failwith ("Could not find a procedure named " ^ p ^ - ". Available procedures are:\n" ^ available) - | ps -> ps) - |> List.concat |> List.sort_uniq compare - in - List.fold_left (check simple_prog) true procs + let procs = + IdSet.fold (fun p procs -> + match Prog.find_proc_with_deps simple_prog p with + | [] -> + let available = + Prog.fold_procs + (fun acc proc -> + let name = Prog.name_of_proc proc in + "\t" ^ string_of_ident name ^ "\n" ^ acc) + "" prog + in + failwith ("Could not find a procedure named " ^ (string_of_ident p) ^ + ". Available procedures are:\n" ^ available) + | ps -> ps :: procs) procs [] + |> List.concat |> List.sort_uniq compare + in + List.fold_left (check simple_prog) true procs (** Get current time *) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 8a107569..6da064b5 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -8,18 +8,12 @@ open Simplifier open Grassifier (** Simplify the given program [prog] by applying all transformation steps. *) -let simplify proc prog = +let simplify procs prog = let dump_if n prog = if !Config.dump_ghp == n then (print_prog stdout prog; prog) else prog in - let init_procs = - match proc with - | None -> - IdMap.fold (fun id _ -> IdSet.add id) prog.prog_procs IdSet.empty - | Some p -> IdSet.singleton (p, 0) - in let info msg prog = Debug.info (fun () -> msg); prog in prog |> dump_if 0 |> @@ -32,7 +26,7 @@ let simplify proc prog = info "Inferring accesses.\n" |> Analyzer.infer_accesses |> info "Pruning uncalled procedures and predicates.\n" |> - Simplifier.prune_uncalled init_procs |> + Simplifier.prune_uncalled procs |> info "Eliminating loops.\n" |> elim_loops |> info "Eliminating dependencies on global state.\n" |> From 97689893719aa66501d1f9ea00cb0f0cf7d0e97b Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 4 Nov 2018 13:48:19 -0500 Subject: [PATCH 099/118] fine-tune unfolding of predicate definitions --- src/verifier/verifier.ml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 6da064b5..2a782872 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -222,10 +222,16 @@ let add_pred_insts prog f = | App (FreeSym id, _ :: _, _) as t -> IdMap.find_opt id prog.prog_preds |> Opt.flat_map (fun decl -> + aux_match |> Opt.flat_map (fun (pid, _) -> - if IdSet.mem pid (accesses_pred decl) then None - else Some (mk_known t)) - aux_match) |> + (* Add generator for propagating known terms to force unfolding of predicate definitions *) + (* Only do this if id is not the entry point into an SCC in the predicate call graph *) + let pdecl = Prog.find_pred prog pid in + if pdecl.pred_contract.contr_name = decl.pred_contract.contr_name || + IdSet.mem pid (accesses_pred decl) && + not (contained_in_src_pos decl.pred_contract.contr_pos pdecl.pred_contract.contr_pos) + then None + else Some (mk_known t))) |> Opt.get_or_else t | t -> t) in From 2fe825ded7e39ecaafb13037272fe1819e597b8d Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 5 Nov 2018 00:14:22 -0500 Subject: [PATCH 100/118] fixes #29 --- src/verifier/verifier.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 2a782872..cb4a2501 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -227,9 +227,14 @@ let add_pred_insts prog f = (* Add generator for propagating known terms to force unfolding of predicate definitions *) (* Only do this if id is not the entry point into an SCC in the predicate call graph *) let pdecl = Prog.find_pred prog pid in + let ppos = + pdecl.pred_body |> + Opt.map (fun s -> s.spec_pos) |> + Opt.get_or_else dummy_position + in if pdecl.pred_contract.contr_name = decl.pred_contract.contr_name || IdSet.mem pid (accesses_pred decl) && - not (contained_in_src_pos decl.pred_contract.contr_pos pdecl.pred_contract.contr_pos) + not (contained_in_src_pos decl.pred_contract.contr_pos ppos) then None else Some (mk_known t))) |> Opt.get_or_else t From 3c69567b9462793cb86d5468f55e4074759b3117 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Fri, 9 Nov 2018 20:58:51 -0500 Subject: [PATCH 101/118] smtLib: also remove commas from assertion names --- src/backends/smtlib/smtLibSolver.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/smtlib/smtLibSolver.ml b/src/backends/smtlib/smtLibSolver.ml index 9c2b7c5c..5dbefab6 100644 --- a/src/backends/smtlib/smtLibSolver.ml +++ b/src/backends/smtlib/smtLibSolver.ml @@ -826,7 +826,7 @@ let extract_name ann = (function Name id -> string_of_ident id | _ -> "") ann in - Str.global_replace (Str.regexp " \\|(\\|)\\|<\\|>") "_" (String.concat "-" (List.rev names)) + Str.global_replace (Str.regexp " \\|,\\|(\\|)\\|<\\|>|") "_" (String.concat "-" (List.rev names)) let smtlib_form_of_grass_form solver_info signs f = let f = if !Config.use_bitvector then bitvectorize_grass_formula f else f in From bb81df08a0ce3369172840f9701349636ed4b15d Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Fri, 9 Nov 2018 20:59:03 -0500 Subject: [PATCH 102/118] macros: better error messages --- src/frontends/spl/splSyntax.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index b2b052d9..a59ddfe7 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -487,6 +487,9 @@ let replace_macros prog = (match IdMap.find_opt p prog.macro_decls with | Some macro -> let sm = + if List.length macro.m_args <> List.length es then + ProgError.error pos ("Wrong number of arguments to macro" + ^ (string_of_ident macro.m_name) ^ "."); List.combine macro.m_args es |> List.fold_left (fun sm (v, e) -> IdMap.add v e sm) IdMap.empty in From 7e129cb5ddcfab3e8d1d3db5b65e19b6b961f4b7 Mon Sep 17 00:00:00 2001 From: wies Date: Fri, 9 Nov 2018 23:01:28 -0500 Subject: [PATCH 103/118] adding missing implied equalities to VC --- src/prover/prover.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prover/prover.ml b/src/prover/prover.ml index 4d982a22..699d0b4a 100644 --- a/src/prover/prover.ml +++ b/src/prover/prover.ml @@ -225,7 +225,6 @@ let instantiate_and_prove session fs = in let fs_inst = EMatching.instantiate_axioms_from_code patterns code cc_graph in let gts_inst = ground_terms ~include_atoms:true (mk_and fs_inst) in - (*let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in*) (*let gts_inst = ground_terms_acc ~include_atoms:true gts_inst (mk_and implied_eqs) in*) (*print_endline "Implied equalities:"; print_endline (string_of_form (mk_and implied_eqs));*) @@ -237,7 +236,8 @@ let instantiate_and_prove session fs = let has_mods1 = CongruenceClosure.has_mods cc_graph in if not !Config.propagate_reads || not (has_mods1 || has_mods2) then - rev_concat [fs_inst(*; implied_eqs*)], cc_graph + let implied_eqs = CongruenceClosure.get_implied_equalities cc_graph in + rev_concat [fs_inst; implied_eqs], cc_graph else saturate (i + 1) fs_inst (CongruenceClosure.reset cc_graph) in From 0113f3b0497d7d46ef0c2de42e73dfd132966ba5 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 10 Nov 2018 12:55:41 -0500 Subject: [PATCH 104/118] Some heuristics for automatic term generators for dealing with combinations of maps and ADTs --- src/verifier/verifier.ml | 122 +++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 31 deletions(-) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index cb4a2501..6bf4dc3e 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -180,11 +180,17 @@ let add_match_filters = (** Expand predicate definitions for all predicates in formula [f] according to program [prog] *) let add_pred_insts prog f = - let add_generators aux_match f = + (* Create term generators for formula [f]. If [f] is the definition of a predicate, + then aux_match is is [Some (pid, m)] where [pid] is the name of the predicate and + and [m] is the associated matching trigger. + *) + let add_generators aux_match f = + (* if f defines a predicate, collect parameter variables of that predicate *) let aux_vs = - Opt.fold (fun aux_vs -> function (_, Match (t, _)) -> fv_term_acc aux_vs t) + Opt.fold (fun aux_vs -> function (pid, Match (t, _)) -> fv_term_acc aux_vs t) IdSet.empty aux_match in + (* collect all function terms in [f] that contain quantified variables and for which we want term generators *) let fun_terms bvs f = let rec ft acc = function | App (sym, (_ :: _ as ts), srt) as t @@ -202,43 +208,96 @@ let add_pred_insts prog f = then TermSet.add t acc else acc | App (_, ts, Bool) -> List.fold_left ft acc ts - | _ -> acc + | App (t, ts, _) -> + List.fold_left ft acc ts + | _ -> acc in fold_terms ft TermSet.empty f in + let read_vars = + aux_match |> + Opt.fold (fun _ -> function + | (_, Match (App (_, [App (_, _, Map (dsrts, _))], _), _)) -> + List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts + | _ -> []) [] + in + (* create term generators for the relevant function terms *) let rec ag bvs = function | BoolOp (op, fs) -> BoolOp (op, List.map (ag bvs) fs) | Binder (Forall, vs, f, ann) -> let bvs = List.fold_left (fun bvs (v, _) -> IdSet.add v bvs) bvs vs in - let ft = + let ft, read_gens = f |> fun_terms bvs |> TermSet.elements |> - List.map - (function - | App (Disjoint, [t1; t2], _) -> mk_inter [t1; t2] - | App (SubsetEq, [t1; t2], _) -> mk_union [t1; t2] - | App (FreeSym id, _ :: _, _) as t -> - IdMap.find_opt id prog.prog_preds |> - Opt.flat_map (fun decl -> - aux_match |> - Opt.flat_map (fun (pid, _) -> - (* Add generator for propagating known terms to force unfolding of predicate definitions *) - (* Only do this if id is not the entry point into an SCC in the predicate call graph *) - let pdecl = Prog.find_pred prog pid in - let ppos = - pdecl.pred_body |> - Opt.map (fun s -> s.spec_pos) |> - Opt.get_or_else dummy_position - in - if pdecl.pred_contract.contr_name = decl.pred_contract.contr_name || - IdSet.mem pid (accesses_pred decl) && - not (contained_in_src_pos decl.pred_contract.contr_pos ppos) - then None - else Some (mk_known t))) |> - Opt.get_or_else t - | t -> t) + (*List.map (fun t -> print_endline (string_of_term t); t) |>*) + List.map (function + | App (Disjoint, [t1; t2], _) -> mk_inter [t1; t2], [] + | App (SubsetEq, [t1; t2], _) -> mk_union [t1; t2], [] + | (App (Read, App (FreeSym id, _ :: _, _) :: _, srt) + | App (FreeSym id, _ :: _, srt)) as t -> + IdMap.find_opt id prog.prog_preds |> + Opt.flat_map (fun decl -> + aux_match |> + Opt.flat_map (function (pid, Match (pt, _)) -> + (* Add generator for propagating known terms to force unfolding of predicate definitions *) + (* Only do this if id is not the entry point into an SCC in the predicate call graph *) + let pdecl = Prog.find_pred prog pid in + let ppos = + pdecl.pred_body |> + Opt.map (fun s -> s.spec_pos) |> + Opt.get_or_else dummy_position + in + let read_gens = + match srt, pt with + | Map (dsrts, rsrt), App (Known, [App (_, _, Map (ret_dsrts, ret_rsrt)) as ptm], _) + when dsrts = ret_dsrts && rsrt = ret_rsrt -> + let read_pt = mk_read ptm read_vars in + let read_t = mk_read t read_vars in + [read_pt, read_t] + | Adt (tid, adts), App (Known, [App (_, _, Map (ret_dsrts, Adt (ret_tid, _))) as ptm], _) + when tid = ret_tid -> + let cstrs = List.assoc tid adts in + let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in + List.fold_left + (fun read_gens (destr, srt) -> + let srt = unfold_adts adts srt in + match srt with + | Map (dsrts, rsrt) -> + let read_vars = + List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts + in + let read_ret_vars = + List.map (fun srt -> mk_var srt (fresh_ident "?j")) ret_dsrts + in + let read_t = mk_read (mk_destr srt destr t) read_vars in + let read_pt = mk_read (mk_destr srt destr (mk_read ptm read_ret_vars)) read_vars in + (read_pt, read_t) :: read_gens + | _ -> read_gens + ) + [] destrs + | _ -> + [] + in + if pdecl.pred_contract.contr_name = decl.pred_contract.contr_name || + IdSet.mem pid (accesses_pred decl) && + not (contained_in_src_pos decl.pred_contract.contr_pos ppos) + then Some (t, read_gens) + else Some (mk_known t, read_gens))) |> + Opt.get_or_else (t, []) + | t -> t, []) |> + List.split + in + let read_gens = + List.flatten read_gens |> + List.map (fun (read_pt, read_t) -> + let ms = + IdMap.fold (fun id srt ms -> Match (mk_var srt id, []) :: ms) + (sorted_fv_term_acc IdMap.empty read_t) + [] + in + TermGenerator (Match (read_pt, []) :: ms, [read_t])) in let ms = IdMap.fold (fun id srt ms -> Match (mk_var srt id, []) :: ms) @@ -247,14 +306,15 @@ let add_pred_insts prog f = in let generators = (match ft with - | [] -> [] - | _ -> [TermGenerator (ms, ft)]) + | [] -> read_gens + | _ -> TermGenerator (ms, ft) :: read_gens) in Binder (Forall, vs, ag bvs f, generators @ ann) | f -> f in ag IdSet.empty f - in + (* end of add_generators *) + in (* Annotates term generators in predicate bodies *) let annotate_term_generators pred f = let locals = locals_of_pred pred in From 4b461a8bfc614ce4dda1796ebdd2facb36b03042 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 10 Nov 2018 17:31:43 -0500 Subject: [PATCH 105/118] fix incompleteness in term generator generation --- src/verifier/verifier.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index 6bf4dc3e..ba3608b8 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -196,6 +196,7 @@ let add_pred_insts prog f = | App (sym, (_ :: _ as ts), srt) as t when (sym <> Read || IdSet.subset (fv_term t) aux_vs) && (is_free_symbol sym || sym = Disjoint || sym = SubsetEq || srt <> Bool) -> + let acc = List.fold_left ft acc ts in let fvs = fv_term t in let sts = List.fold_left subterms_term_acc TermSet.empty ts in let no_var_reads = From 0c0b5bf31f5c9b9a436af760880b45db6de66710 Mon Sep 17 00:00:00 2001 From: wies Date: Sat, 10 Nov 2018 18:12:44 -0500 Subject: [PATCH 106/118] empty modifies sets for lemmas --- src/programs/prog.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/programs/prog.ml b/src/programs/prog.ml index 367e916a..3be7a74a 100644 --- a/src/programs/prog.ml +++ b/src/programs/prog.ml @@ -591,7 +591,8 @@ let modifies_proc prog proc = IdMap.fold (fun id _ mods -> IdSet.add id mods) prog.prog_vars IdSet.empty - | None -> + | None -> + if proc.proc_is_lemma then IdSet.empty else IdMap.fold (fun id _ acc -> IdSet.add id acc) prog.prog_vars IdSet.empty let modifies_basic_cmd = function From 91b91ae651b57c28f4ece33bf20268875256710c Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 11 Nov 2018 15:31:04 -0500 Subject: [PATCH 107/118] skolemize inequalities of curried maps --- src/prover/reduction.ml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/prover/reduction.ml b/src/prover/reduction.ml index 2e7eb9d4..49d6e4e4 100644 --- a/src/prover/reduction.ml +++ b/src/prover/reduction.ml @@ -18,9 +18,18 @@ let elim_exists = mk_exists [(e, srt)] (smk_or [smk_and [smk_elem ~ann:a ve t1; mk_not (smk_elem ~ann:a ve t2)]; smk_and [smk_elem ~ann:a ve t2; mk_not (smk_elem ~ann:a ve t1)]]) | Map (dsrts, rsrt) -> - let vs = List.map (fun srt -> fresh_ident "?i", srt) dsrts in - let vts = List.map (fun (v, srt) -> mk_var srt v) vs in - mk_exists vs (annotate (mk_neq (mk_read t1 vts) (mk_read t2 vts)) a) + let rec curried_domains doms = function + | Map (dsrts, rsrt) -> curried_domains (dsrts :: doms) rsrt + | _ -> doms + in + let doms = curried_domains [dsrts] rsrt in + let dom_vs = List.map (fun dsrts -> List.map (fun srt -> fresh_ident "?i", srt) dsrts) doms in + let dom_vts = List.map (fun vs -> List.map (fun (v, srt) -> mk_var srt v) vs) dom_vs in + let mk_reads t = List.fold_left (fun t_read vts -> mk_read t_read vts) t dom_vts in + let t1_read = mk_reads t1 in + let t2_read = mk_reads t2 in + let vs = List.flatten dom_vs in + elim_neq seen_adts bvs (mk_exists vs (annotate (mk_neq t1_read t2_read) a)) | Adt (id, adts) when not @@ IdSet.mem id seen_adts -> let cstrs = List.assoc id adts in let expand new_vs = function @@ -58,7 +67,8 @@ let elim_exists = let srt = element_sort_of_set s1 in let ve = mk_var srt e in mk_exists [(e, srt)] (annotate (smk_and [smk_elem ve s1; mk_not (smk_elem ve s2)]) a) - | BoolOp (op, fs) -> smk_op op (List.map (elim_neq IdSet.empty bvs) fs) + | BoolOp (op, fs) -> + smk_op op (List.map (elim_neq IdSet.empty bvs) fs) | Binder (Exists, vs, f, a) -> mk_exists ~ann:a vs (elim_neq seen_adts bvs f) | Binder (Forall, vs, f, a) -> From 44fec219841aba99c2f82fa57ca937d26f6fe420 Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 11 Nov 2018 15:31:26 -0500 Subject: [PATCH 108/118] Some heuristics for automatic term generators for dealing with combinations of maps and ADTs --- src/verifier/verifier.ml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index ba3608b8..bdd35297 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -257,6 +257,17 @@ let add_pred_insts prog f = let read_pt = mk_read ptm read_vars in let read_t = mk_read t read_vars in [read_pt, read_t] + | Map (dsrts, rsrt), App (Known, [App (_, _, Map (ret_dsrts, ret_rsrt)) as ptm], _) + when srt = ret_rsrt -> + let read_vars = + List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts + in + let read_ret_vars = + List.map (fun srt -> mk_var srt (fresh_ident "?j")) ret_dsrts + in + let read_t = mk_read t read_vars in + let read_pt = mk_read (mk_read ptm read_ret_vars) read_vars in + [read_pt, read_t] | Adt (tid, adts), App (Known, [App (_, _, Map (ret_dsrts, Adt (ret_tid, _))) as ptm], _) when tid = ret_tid -> let cstrs = List.assoc tid adts in From 57e290804b45f293d39deb1c8b4233fb2cc22908 Mon Sep 17 00:00:00 2001 From: wies Date: Sun, 11 Nov 2018 18:38:10 -0500 Subject: [PATCH 109/118] do not add Alloc variables for non-footprint sorts to modifies sets of procedures that don't have bodies --- src/programs/prog.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/programs/prog.ml b/src/programs/prog.ml index 3be7a74a..43e23f3a 100644 --- a/src/programs/prog.ml +++ b/src/programs/prog.ml @@ -593,7 +593,11 @@ let modifies_proc prog proc = prog.prog_vars IdSet.empty | None -> if proc.proc_is_lemma then IdSet.empty else - IdMap.fold (fun id _ acc -> IdSet.add id acc) prog.prog_vars IdSet.empty + IdMap.fold (fun id decl acc -> + match decl.var_sort with + | Set (Loc srt) when id = alloc_id srt && not @@ SortSet.mem srt proc.proc_contract.contr_footprint_sorts -> + acc + | _ -> IdSet.add id acc) prog.prog_vars IdSet.empty let modifies_basic_cmd = function | Assign ac -> id_set_of_list ac.assign_lhs From 7306915edb0dece1dc34f6b35ad6fbc1af34dfa4 Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 12 Nov 2018 19:46:27 -0500 Subject: [PATCH 110/118] make automatic term generator generation more robust --- src/formulas/grassUtil.ml | 24 ++++++------ src/verifier/verifier.ml | 82 ++++++++++++++++++--------------------- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/src/formulas/grassUtil.ml b/src/formulas/grassUtil.ml index 5a473fa9..7add90d9 100644 --- a/src/formulas/grassUtil.ml +++ b/src/formulas/grassUtil.ml @@ -357,17 +357,19 @@ let mk_ite c t e = mk_app (sort_of t) Ite [c; t; e] let mk_null id = mk_app (Loc id) Null [] -let mk_read map inds = - let dom_srts, ran_srt = match sort_of map with - | Map (ds,r) -> ds, r - | Loc (Array r) -> [Int], r - | s -> - failwith - ("tried to read from term " ^ - (string_of_term map) ^ " which is of sort " ^ (string_of_sort s) ^ ".\n" ^ - "Expected sort (Map X Y) for some sorts X, Y.") - in - mk_app ran_srt Read (map :: inds) +let mk_read map = function + | [] -> map + | inds -> + let dom_srts, ran_srt = match sort_of map with + | Map (ds,r) -> ds, r + | Loc (Array r) -> [Int], r + | s -> + failwith + ("tried to read from term " ^ + (string_of_term map) ^ " which is of sort " ^ (string_of_sort s) ^ ".\n" ^ + "Expected sort (Map X Y) for some sorts X, Y.") + in + mk_app ran_srt Read (map :: inds) let mk_read_form map ind = match sort_of map with diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index bdd35297..a10732fc 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -207,6 +207,8 @@ let add_pred_insts prog f = in if (no_var_reads || is_set_sort srt) && IdSet.subset fvs bvs && not @@ IdSet.is_empty fvs then TermSet.add t acc else acc + | App (sym, [], Adt _) as t -> + TermSet.add t acc | App (_, ts, Bool) -> List.fold_left ft acc ts | App (t, ts, _) -> @@ -215,13 +217,6 @@ let add_pred_insts prog f = in fold_terms ft TermSet.empty f in - let read_vars = - aux_match |> - Opt.fold (fun _ -> function - | (_, Match (App (_, [App (_, _, Map (dsrts, _))], _), _)) -> - List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts - | _ -> []) [] - in (* create term generators for the relevant function terms *) let rec ag bvs = function | BoolOp (op, fs) -> @@ -237,7 +232,7 @@ let add_pred_insts prog f = | App (Disjoint, [t1; t2], _) -> mk_inter [t1; t2], [] | App (SubsetEq, [t1; t2], _) -> mk_union [t1; t2], [] | (App (Read, App (FreeSym id, _ :: _, _) :: _, srt) - | App (FreeSym id, _ :: _, srt)) as t -> + | App (FreeSym id, _, srt)) as t -> IdMap.find_opt id prog.prog_preds |> Opt.flat_map (fun decl -> aux_match |> @@ -250,48 +245,45 @@ let add_pred_insts prog f = Opt.map (fun s -> s.spec_pos) |> Opt.get_or_else dummy_position in - let read_gens = - match srt, pt with - | Map (dsrts, rsrt), App (Known, [App (_, _, Map (ret_dsrts, ret_rsrt)) as ptm], _) - when dsrts = ret_dsrts && rsrt = ret_rsrt -> - let read_pt = mk_read ptm read_vars in - let read_t = mk_read t read_vars in - [read_pt, read_t] - | Map (dsrts, rsrt), App (Known, [App (_, _, Map (ret_dsrts, ret_rsrt)) as ptm], _) - when srt = ret_rsrt -> - let read_vars = - List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts - in - let read_ret_vars = - List.map (fun srt -> mk_var srt (fresh_ident "?j")) ret_dsrts - in - let read_t = mk_read t read_vars in - let read_pt = mk_read (mk_read ptm read_ret_vars) read_vars in - [read_pt, read_t] - | Adt (tid, adts), App (Known, [App (_, _, Map (ret_dsrts, Adt (ret_tid, _))) as ptm], _) - when tid = ret_tid -> + let mk_read_vars srts = + List.map (fun srt -> mk_var srt (fresh_ident "?i")) srts + in + let rec read_gens seen_adts gens t pt = + match sort_of t, sort_of pt with + | Adt (tid, adts), Adt (ret_tid, _) + when tid = ret_tid && not @@ IdSet.mem tid seen_adts -> let cstrs = List.assoc tid adts in let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in + let seen_adts1 = IdSet.add tid seen_adts in List.fold_left - (fun read_gens (destr, srt) -> + (fun gens (destr, srt) -> let srt = unfold_adts adts srt in - match srt with - | Map (dsrts, rsrt) -> - let read_vars = - List.map (fun srt -> mk_var srt (fresh_ident "?i")) dsrts - in - let read_ret_vars = - List.map (fun srt -> mk_var srt (fresh_ident "?j")) ret_dsrts - in - let read_t = mk_read (mk_destr srt destr t) read_vars in - let read_pt = mk_read (mk_destr srt destr (mk_read ptm read_ret_vars)) read_vars in - (read_pt, read_t) :: read_gens - | _ -> read_gens - ) - [] destrs - | _ -> - [] + let read_t = mk_destr srt destr t in + let read_pt = mk_destr srt destr pt in + read_gens seen_adts1 ((read_pt, read_t) :: gens) read_t read_pt + ) + gens destrs + | Map (dsrts, rsrt), Map (ret_dsrts, ret_rsrt) + when dsrts = ret_dsrts && rsrt = ret_rsrt -> + let read_vars = mk_read_vars dsrts in + let read_pt = mk_read pt read_vars in + let read_t = mk_read t read_vars in + read_gens seen_adts ((read_pt, read_t) :: gens) read_t read_pt + | (Map _ | Adt _), Map (ret_dsrts, ret_srt) -> + let read_vars = mk_read_vars ret_dsrts in + let pt = mk_read pt read_vars in + read_gens seen_adts gens t pt + | _, Pat -> + (match pt with + | App (_, [pt], _) -> read_gens seen_adts gens t pt + | _ -> gens) + | srt1, srt2 when srt1 = srt2 -> (pt, t) :: gens + | _ -> gens in + let read_gens = + read_gens IdSet.empty [] t pt |> + List.filter (fun (pt, t) -> pt <> t) + in if pdecl.pred_contract.contr_name = decl.pred_contract.contr_name || IdSet.mem pid (accesses_pred decl) && not (contained_in_src_pos decl.pred_contract.contr_pos ppos) From 28bc2e2828619eeb49ddf32b1bb91b23540c475b Mon Sep 17 00:00:00 2001 From: wies Date: Mon, 12 Nov 2018 20:11:20 -0500 Subject: [PATCH 111/118] some clean-up --- src/verifier/verifier.ml | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index a10732fc..d49cb8be 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -250,29 +250,35 @@ let add_pred_insts prog f = in let rec read_gens seen_adts gens t pt = match sort_of t, sort_of pt with - | Adt (tid, adts), Adt (ret_tid, _) - when tid = ret_tid && not @@ IdSet.mem tid seen_adts -> + | (Map _ | Adt _) as tsrt, Adt (tid, adts) + when not @@ IdSet.mem tid seen_adts -> let cstrs = List.assoc tid adts in let destrs = flat_map (fun (_, destrs) -> destrs) cstrs in let seen_adts1 = IdSet.add tid seen_adts in List.fold_left (fun gens (destr, srt) -> let srt = unfold_adts adts srt in - let read_t = mk_destr srt destr t in let read_pt = mk_destr srt destr pt in - read_gens seen_adts1 ((read_pt, read_t) :: gens) read_t read_pt + let gens1, read_t = match tsrt with + | Adt (tid1, _) when tid = tid1 -> + let read_t = mk_destr srt destr t in + (read_pt, read_t) :: gens, read_t + | _ -> gens, t + in + read_gens seen_adts1 gens1 read_t read_pt ) gens destrs - | Map (dsrts, rsrt), Map (ret_dsrts, ret_rsrt) - when dsrts = ret_dsrts && rsrt = ret_rsrt -> + | (Map _ | Adt _) as tsrt, Map (dsrts, rsrt) -> let read_vars = mk_read_vars dsrts in let read_pt = mk_read pt read_vars in - let read_t = mk_read t read_vars in - read_gens seen_adts ((read_pt, read_t) :: gens) read_t read_pt - | (Map _ | Adt _), Map (ret_dsrts, ret_srt) -> - let read_vars = mk_read_vars ret_dsrts in - let pt = mk_read pt read_vars in - read_gens seen_adts gens t pt + let gens1, read_t = match tsrt with + | Map (tdsrts, trsrt) + when tdsrts = dsrts && trsrt = rsrt -> + let read_t = mk_read t read_vars in + (read_pt, read_t) :: gens, read_t + | _ -> gens, t + in + read_gens seen_adts gens1 read_t read_pt | _, Pat -> (match pt with | App (_, [pt], _) -> read_gens seen_adts gens t pt From 282bcc0df7b824407e43d0a84a63d05dfe7ace84 Mon Sep 17 00:00:00 2001 From: wies Date: Tue, 13 Nov 2018 00:06:14 -0500 Subject: [PATCH 112/118] fixes #36 --- src/frontends/spl/splTypeChecker.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontends/spl/splTypeChecker.ml b/src/frontends/spl/splTypeChecker.ml index 590915dd..b0215426 100644 --- a/src/frontends/spl/splTypeChecker.ml +++ b/src/frontends/spl/splTypeChecker.ml @@ -408,12 +408,14 @@ let infer_types cu locals ty e = let arr_ety = match ty with | MapType (IntType, ety) -> ety + | ArrayType ety -> ety | _ -> AnyType in let arr, arr_ty = it locals (ArrayType arr_ety) arr in let arr_ety = match arr_ty with | MapType (IntType, ety) -> ety + | ArrayType ety -> ety | _ -> AnyType in UnaryOp (OpArrayMap, arr, pos), match_types pos ty (MapType (IntType, arr_ety)) From d26e7ea5ca766884c04c26eee8b0582493d82307 Mon Sep 17 00:00:00 2001 From: wies Date: Wed, 14 Nov 2018 21:43:24 -0500 Subject: [PATCH 113/118] fix issues with trigger loops --- src/verifier/verifier.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/verifier/verifier.ml b/src/verifier/verifier.ml index d49cb8be..6fb2b975 100644 --- a/src/verifier/verifier.ml +++ b/src/verifier/verifier.ml @@ -196,7 +196,6 @@ let add_pred_insts prog f = | App (sym, (_ :: _ as ts), srt) as t when (sym <> Read || IdSet.subset (fv_term t) aux_vs) && (is_free_symbol sym || sym = Disjoint || sym = SubsetEq || srt <> Bool) -> - let acc = List.fold_left ft acc ts in let fvs = fv_term t in let sts = List.fold_left subterms_term_acc TermSet.empty ts in let no_var_reads = @@ -206,7 +205,7 @@ let add_pred_insts prog f = | _ -> true) sts in if (no_var_reads || is_set_sort srt) && IdSet.subset fvs bvs && not @@ IdSet.is_empty fvs - then TermSet.add t acc else acc + then List.fold_left ft (TermSet.add t acc) ts else acc | App (sym, [], Adt _) as t -> TermSet.add t acc | App (_, ts, Bool) -> From 71f537ad1ef3355dd8e16933b4ed031272744164 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 15 Nov 2018 19:17:19 -0500 Subject: [PATCH 114/118] support for 'with' clauses in assertions --- emacs-mode/spl-mode.el | 2 +- src/frontends/spl/splLexer.mll | 1 + src/frontends/spl/splParser.mly | 36 ++++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/emacs-mode/spl-mode.el b/emacs-mode/spl-mode.el index cc13f2a8..39161dda 100644 --- a/emacs-mode/spl-mode.el +++ b/emacs-mode/spl-mode.el @@ -46,7 +46,7 @@ (defconst spl-builtins '("import" "yields" "exists" "forall" "Btwn" "Reach" "Disjoint" "Frame" "in" "old" "subsetof")) -(defconst spl-keywords '("havoc" "free" "choose" "else" "if" "new" "return" "while" "matching" "yields" "without" "pattern" "known")) +(defconst spl-keywords '("havoc" "free" "choose" "else" "if" "new" "return" "while" "matching" "yields" "with" "without" "pattern" "known")) ;(defconst dafny-all-keywords (cl-loop for source in '(dafny-defuns dafny-specifiers dafny-modifiers ; dafny-builtins dafny-keywords dafny-types) diff --git a/src/frontends/spl/splLexer.mll b/src/frontends/spl/splLexer.mll index 1a14edb6..11cafaf0 100644 --- a/src/frontends/spl/splLexer.mll +++ b/src/frontends/spl/splLexer.mll @@ -63,6 +63,7 @@ let _ = ("type", TYPE); ("var", VAR); ("while", WHILE); + ("with", WITH); ("without", WITHOUT); ("yields", YIELDS); ("axiom", AXIOM); diff --git a/src/frontends/spl/splParser.mly b/src/frontends/spl/splParser.mly index 342fe7e7..92eacbc8 100644 --- a/src/frontends/spl/splParser.mly +++ b/src/frontends/spl/splParser.mly @@ -68,7 +68,7 @@ type rhs_string_maybe = %token GHOST IMPLICIT VAR CONST STRUCT PURE LEMMA PROCEDURE INCLUDE OPTIONS AXIOM TYPE %token DEFINE DATATYPE OUTPUTS RETURNS REQUIRES ENSURES INVARIANT %token LOC INT BOOL BYTE SET MAP ARRAY ARRAYCELL -%token MATCHING YIELDS WITHOUT COMMENT PATTERN +%token MATCHING YIELDS WITHOUT WITH COMMENT PATTERN %token EOF %nonassoc COLONEQ @@ -571,8 +571,9 @@ stmt_wo_trailing_substmt: Assume ($3, fst $1, mk_position (if $1 <> (false, false) then 1 else 2) 4) } /* assert */ -| contract_mods ASSERT expr SEMICOLON { - Assert ($3, fst $1, mk_position (if $1 <> (false, false) then 1 else 2) 4) +| contract_mods ASSERT expr with_clause { + $4 (fst $1) $3 (mk_position (if $1 <> (false, false) then 1 else 2) 4) + (*Assert ($3, fst $1, mk_position (if $1 <> (false, false) then 1 else 2) 4)*) } /* split */ | SPLIT expr SEMICOLON { @@ -584,6 +585,35 @@ stmt_wo_trailing_substmt: } ; +with_clause: +| SEMICOLON { + fun pure e pos -> Assert (e, pure, pos) +} +| WITH LBRACE block RBRACE { + fun pure e pos -> + let vs, e1, pos1 = match e with + | Binder (Forall, vars, e1, pos1) when pure -> + let vs = + List.fold_right (fun bv vs -> + match bv with + | UnguardedVar v -> + v :: vs + | GuardedVar (x, e) -> + ProgError.syntax_error (pos_of_expr e) + (Some "no guarded variables allowed in 'with' clauses")) + vars [] + in + vs, e1, pos1 + | _ -> [], e, pos + in + let checks = + LocalVars (vs, None, pos) :: + List.append $3 [Assert (e1, true, pos1); Assume (BoolVal (false, pos), true, pos)] + in + Choice ([Assume (e, true, pos); Block (checks, pos)], pos) +} + + assign_lhs_list: | assign_lhs COMMA assign_lhs_list { $1 :: $3 } | assign_lhs { [$1] } From e3e10e79a302350b1e2eb7c79e8aa0d5801155e8 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Thu, 15 Nov 2018 19:33:40 -0500 Subject: [PATCH 115/118] macro error: fix typo --- src/frontends/spl/splSyntax.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontends/spl/splSyntax.ml b/src/frontends/spl/splSyntax.ml index a59ddfe7..f2fcdfc3 100644 --- a/src/frontends/spl/splSyntax.ml +++ b/src/frontends/spl/splSyntax.ml @@ -488,7 +488,7 @@ let replace_macros prog = | Some macro -> let sm = if List.length macro.m_args <> List.length es then - ProgError.error pos ("Wrong number of arguments to macro" + ProgError.error pos ("Wrong number of arguments to macro " ^ (string_of_ident macro.m_name) ^ "."); List.combine macro.m_args es |> List.fold_left (fun sm (v, e) -> IdMap.add v e sm) IdMap.empty From 80113813058ce8f95a833b855724be5a54e4e043 Mon Sep 17 00:00:00 2001 From: Siddharth Krishna Date: Wed, 21 Nov 2018 11:22:21 +0000 Subject: [PATCH 116/118] fix bug in smt clause names; fixes #38 --- src/backends/smtlib/smtLibSolver.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/smtlib/smtLibSolver.ml b/src/backends/smtlib/smtLibSolver.ml index 5dbefab6..1a5edc10 100644 --- a/src/backends/smtlib/smtLibSolver.ml +++ b/src/backends/smtlib/smtLibSolver.ml @@ -826,7 +826,7 @@ let extract_name ann = (function Name id -> string_of_ident id | _ -> "") ann in - Str.global_replace (Str.regexp " \\|,\\|(\\|)\\|<\\|>|") "_" (String.concat "-" (List.rev names)) + Str.global_replace (Str.regexp " \\|,\\|(\\|)\\|<\\|>") "_" (String.concat "-" (List.rev names)) let smtlib_form_of_grass_form solver_info signs f = let f = if !Config.use_bitvector then bitvectorize_grass_formula f else f in From c599b7b9551b88e4612a1d70a1ad194746eca509 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 7 Feb 2019 19:04:14 -0500 Subject: [PATCH 117/118] update version --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index efcdc0c2..74519e9d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2018 +Copyright (c) 2013-2019 Thomas Wies (wies@cs.nyu.edu) Damien Zufferey (zufferey@csail.mit.edu) Siddharth Krishna (siddharth@cs.nyu.edu) diff --git a/README.md b/README.md index a990e463..466a798c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ GRASShopper ======= -![Version 0.5 pre](https://img.shields.io/badge/version-0.5_pre-green.svg) +![Version 0.5](https://img.shields.io/badge/version-0.5-green.svg) [![BSD licensed](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/wies/grasshopper/master/LICENSE) [![Build Status](https://travis-ci.org/wies/grasshopper.svg?branch=master)](https://travis-ci.org/wies/grasshopper) From 4de1357934d762d38fcbd551639f022026dcab17 Mon Sep 17 00:00:00 2001 From: wies Date: Thu, 7 Feb 2019 19:10:23 -0500 Subject: [PATCH 118/118] update license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 74519e9d..c0209098 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Copyright (c) 2013-2019 Thomas Wies (wies@cs.nyu.edu) - Damien Zufferey (zufferey@csail.mit.edu) + Damien Zufferey (zufferey@mpi-sws.org) Siddharth Krishna (siddharth@cs.nyu.edu) Ari Holtzman (ah2637@nyu.edu) All rights reserved.