Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
wp_eauto.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
(******************************************************************************) (* This file is part of Waterproof-lib. *) (* *) (* Waterproof-lib is free software: you can redistribute it and/or modify *) (* it under the terms of the GNU General Public License as published by *) (* the Free Software Foundation, either version 3 of the License, or *) (* (at your option) any later version. *) (* *) (* Waterproof-lib is distributed in the hope that it will be useful, *) (* but WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) (* GNU General Public License for more details. *) (* *) (* You should have received a copy of the GNU General Public License *) (* along with Waterproof-lib. If not, see <https://www.gnu.org/licenses/>. *) (* *) (******************************************************************************) open Auto open Context.Named open EConstr open Hints open Names open Pp open Proofview open Proofview.Notations open Tactics open Termops open Util open Backtracking open Proofutils open Wp_auto (* All the definitions below come from coq-core hidden library (i.e not visible in the API) *) let eauto_unif_flags: Unification.unify_flags = auto_flags_of_state TransparentState.full let e_give_exact ?(flags: Unification.unify_flags = eauto_unif_flags) (c: types): unit tactic = Goal.enter begin fun gl -> let sigma, t1 = Tacmach.pf_type_of gl c in let t2 = Tacmach.pf_concl gl in if occur_existential sigma t1 || occur_existential sigma t2 then Tacticals.tclTHENLIST [ Unsafe.tclEVARS sigma; Clenv.unify ~cv_pb:CUMUL ~flags t1; exact_no_check c ] else exact_check c end let e_assumption: trace tactic = TraceTactics.typedGoalEnter begin fun gl -> let hyps = Goal.hyps gl in let sigma = Goal.sigma gl in let concl = Tacmach.pf_concl gl in if List.is_empty hyps then Tacticals.tclZEROMSG (str "No applicable tactic.") else let not_ground = occur_existential sigma concl in let map (decl: (_, types, _) Declaration.pt): trace tactic = let id = Declaration.get_id decl in let t = Declaration.get_type decl in begin if not_ground || occur_existential sigma t then Clenv.unify ~cv_pb:CUMUL ~flags:eauto_unif_flags t <*> exact_no_check (mkVar id) else exact_check (mkVar id) end <*> tclUNIT @@ singleton_trace true (str "eassumption") (str "") in tclTraceFirst (List.map map hyps) end let unify_e_resolve (flags: Unification.unify_flags) (h: hint): unit tactic = Hints.hint_res_pf ~with_evars:true ~with_classes:true ~flags h let e_exact (h: hint): unit tactic = Goal.enter begin fun gl -> let env = Goal.env gl in let sigma = Goal.sigma gl in let sigma, c = Hints.fresh_hint env sigma h in Unsafe.tclEVARS sigma <*> e_give_exact c end (* All the definitions below are inspired by the coq-core hidden library (i.e not visible in the API) but modified for Waterproof *) (** Cost to solve a hint *) type cost = { cost_priority : int; cost_subgoals : int; } (** Type alias containing functions that will return a [Hints.hint_db] *) type delayed_db = Environ.env -> Evd.evar_map -> hint_db (** State of the search *) type search_state = { depth: int; (** Depth remaining before failing *) tactics_resolution: (Proofview_monad.goal_with_state * delayed_db) list; trace: trace; (** Current trace *) } let search_tactics_factory (initial_state: search_state): (module Mergeable with type elt = search_state) = ( module struct type elt = search_state let empty = initial_state let merge state1 state2 = { state1 with trace = merge_traces state1.trace state2.trace } end: Mergeable with type elt = search_state ) (** Creates an initial state *) let initial_state (log: bool) (evk: Proofview_monad.goal_with_state) (local_db: delayed_db) (n: int): search_state = { depth = n; tactics_resolution = [(evk, local_db)]; trace = new_trace log } (** Prints a debug header *) let pr_dbg_header () = Feedback.msg_notice (str "(* info wp_eauto: *)") let tclTraceComplete (t: trace tactic): trace tactic = t >>= fun res -> ( tclINDEPENDENT (let info = Exninfo.reify () in tclZERO ~info (SearchBound no_trace)) ) <*> tclUNIT res let rec e_trivial_fail_db (db_list: hint_db list) (local_db: hint_db) (forbidden_tactics: Pp.t list): trace tactic = let next = TraceTactics.typedGoalEnter begin fun gl -> let d = Declaration.get_id @@ Tacmach.pf_last_hyp gl in let local_db = push_resolve_hyp (Tacmach.pf_env gl) (Tacmach.project gl) d local_db in e_trivial_fail_db db_list local_db forbidden_tactics end in TraceTactics.typedGoalEnter begin fun gl -> let secvars = compute_secvars gl in let tacl = e_assumption :: (Tactics.intro <*> next) :: (e_trivial_resolve (Tacmach.pf_env gl) (Tacmach.project gl) db_list local_db secvars (Tacmach.pf_concl gl) forbidden_tactics) in tclTraceFirst (List.map tclTraceComplete tacl) end and esearch_find (env: Environ.env) (sigma: Evd.evar_map) (db_list: hint_db list) (local_db: hint_db) (secvars: Id.Pred.t) (concl: types) (forbidden_tactics: Pp.t list): (trace tactic * cost * Pp.t) list = let hint_of_db: hint_db -> FullHint.t list = hintmap_of env sigma secvars concl in let flagged_hints: (Unification.unify_flags * FullHint.t) list = List.map_append (fun db -> let flags = auto_flags_of_state (Hint_db.transparent_state db) in List.map (fun x -> (flags, x)) (hint_of_db db) ) (local_db::db_list) in (* Returns a tactic, its priority (which is an approximation of the cost), and its representation from the current state and a [Hints.FullHint.t] *) let tac_of_hint ((state, hint): (Unification.unify_flags * FullHint.t)): trace tactic * cost * Pp.t = let priority = match FullHint.repr hint with | Unfold_nth _ -> 1 | _ -> FullHint.priority hint in let tac: hint hint_ast -> trace tactic = function | Res_pf h -> unify_e_resolve state h <*> tclUNIT @@ no_trace | ERes_pf h -> unify_e_resolve state h <*> tclUNIT @@ no_trace | Give_exact h -> e_exact h <*> tclUNIT @@ singleton_trace true (str "exact") (str "") | Res_pf_THEN_trivial_fail h -> (unify_e_resolve state h) <*> (e_trivial_fail_db db_list local_db forbidden_tactics) | Unfold_nth c -> TraceTactics.typedGoalEnter begin fun gl -> if exists_evaluable_reference (Goal.env gl) c then Tacticals.tclPROGRESS (reduce (Genredexpr.Unfold [Locus.AllOccurrences, c]) Locusops.onConcl) <*> tclUNIT @@ singleton_trace true (str "unfold") (str "") else let info = Exninfo.reify () in Tacticals.tclFAIL ~info (str"Unbound reference") end | Extern (pat, tacast) -> conclPattern concl pat tacast <*> tclUNIT @@ singleton_trace true (str "") (str "extern") in let subgoals = match FullHint.subgoals hint with | Some subgoals -> subgoals | None -> priority in let cost = { cost_priority = priority; cost_subgoals = subgoals } in let pr_hint (h: FullHint.t) (env: Environ.env) (sigma: Evd.evar_map): t * t = let origin = match FullHint.database h with | None -> mt () | Some n -> str n in (Proofutils.pr_hint env sigma h, origin) in (* We cannot determine statically the cost of subgoals of an Extern hint, so approximate it by the hint's priority. *) let tactic = tclLOG (pr_hint hint) (FullHint.run hint tac) forbidden_tactics in (tactic, cost, FullHint.print env sigma hint) in (List.map tac_of_hint flagged_hints) and e_trivial_resolve (env: Environ.env) (sigma: Evd.evar_map) (db_list: hint_db list) (local_db: hint_db) (secvars: Id.Pred.t) (gl: types) (forbidden_tactics: Pp.t list): trace tactic list = let filter (tac, pr, _) = if pr.cost_priority = 0 then Some tac else None in try List.map_filter filter (esearch_find env sigma db_list local_db secvars gl forbidden_tactics) with Not_found -> [] (** The goal is solved if the cost of solving is null *) let is_solved (cost: cost): bool = cost.cost_subgoals = 0 (* Solved comes first *) let solve_order (c1: cost) (c2: cost): int = match (is_solved c1, is_solved c2) with | (true, true) | (false, false) -> 0 | (false, true) -> 1 | (true, false) -> -1 (** Compare two states Ordering of states is lexicographic: 1. tactics known to solve the goal 2. priority 3. number of generated goals *) let compare ((_, c1, _): (trace tactic * cost * Pp.t)) ((_, c2, _): (trace tactic * cost * Pp.t)) = let solve_ordering = solve_order c1 c2 in let priority_ordering = Int.compare c1.cost_priority c2.cost_priority in if solve_ordering != 0 then solve_ordering else if priority_ordering != 0 then priority_ordering else Int.compare c1.cost_subgoals c2.cost_subgoals (** Returns the list of tactics that will be tried for the proof search It always begins with [assumption] and [intros] (not exactly them but equivalent with evar support), then uses the hint databases *) let branching (n: int) (delayed_database: delayed_db) (dblist: hint_db list) (local_lemmas: Tactypes.delayed_open_constr list) (forbidden_tactics: Pp.t list): (bool * delayed_db * trace tactic * Pp.t) list tactic = Goal.enter_one begin fun gl -> let env = Goal.env gl in let sigma = Goal.sigma gl in let concl = Goal.concl gl in let hyps = EConstr.named_context env in let db = delayed_database env sigma in let secvars = secvars_of_hyps hyps in (* Construction of tactics equivalent to [assumption] *) let assumption_tacs : (bool * delayed_db * trace tactic * Pp.t) list = (* Ensure that no goal is generated *) let mkdb (env: Environ.env) (sigma: Evd.evar_map): 'a = assert false in let map_assum (id: variable): (bool * delayed_db * trace tactic * Pp.t) = let hint = str "exact" ++ str " " ++ Id.print id in (false, mkdb, tclLOG (fun _ _ -> (hint, str "")) (e_give_exact (mkVar id) <*> tclUNIT no_trace) forbidden_tactics, hint) in List.map map_assum (ids_of_named_context hyps) in (* Construction of tactic equivalent to [intros] *) let intro_tac: (bool * delayed_db * trace tactic * Pp.t) = let mkdb (env: Environ.env) (sigma: Evd.evar_map): hint_db = push_resolve_hyp env sigma (Declaration.get_id (List.hd (EConstr.named_context env))) db in (false, mkdb, tclLOG (fun _ _ -> (str "intro", str "")) (Tactics.intro <*> tclUNIT no_trace) forbidden_tactics, str "intro") in (* Construction of tactics derivated from hint databases *) let rec_tacs: (bool * delayed_db * trace tactic * Pp.t) list tactic = let mkdb (env: Environ.env) (sigma: Evd.evar_map): hint_db = let hyps' = EConstr.named_context env in if hyps' == hyps then db else make_local_hint_db env sigma ~ts:TransparentState.full true local_lemmas in try esearch_find env sigma dblist db secvars concl forbidden_tactics |> List.sort compare |> List.map (fun (tac, _, pp) -> (true, mkdb, tac, pp)) |> tclUNIT with Not_found -> tclUNIT [] in rec_tacs >>= fun rec_tacs -> tclUNIT (assumption_tacs @ intro_tac :: rec_tacs) end (** Actual search function *) let resolve_esearch (max_depth: int) (dblist: hint_db list) (local_lemmas: Tactypes.delayed_open_constr list) (state: search_state) (must_use_tactics: Pp.t list) (forbidden_tactics: Pp.t list): search_state tactic = let rec explore (state: search_state) (previous_envs: (EConstr.named_context * EConstr.constr) list) = if state.depth = 0 then tclZERO (SearchBound no_trace) else match state.tactics_resolution with | [] -> tclUNIT state | (gl, db) :: rest -> tclEVARMAP >>= fun sigma -> match Unsafe.undefined sigma [gl] with | [] -> explore { state with tactics_resolution = rest; trace = incr_trace_depth state.trace } previous_envs | gl :: _ -> Unsafe.tclSETGOALS [gl] <*> branching state.depth db dblist local_lemmas forbidden_tactics >>= fun tacs -> let cast ((isrec, mkdb, tac, pp): (bool * delayed_db * trace tactic * Pp.t)): search_state tactic = tclONCE tac >>= fun trace -> Unsafe.tclGETGOALS >>= fun lgls -> tclEVARMAP >>= fun sigma -> let join (gl: Proofview_monad.goal_with_state): (Proofview_monad.goal_with_state * delayed_db) = (gl, mkdb) in let depth = if isrec && not @@ List.is_empty lgls then pred state.depth else state.depth in tclUNIT { depth; tactics_resolution = List.map join lgls @ rest; trace = merge_traces state.trace trace } in tacs |> List.map cast |> explore_many previous_envs >>= fun s -> if state.depth = max_depth then trace_check_used must_use_tactics s.trace >>= fun trace -> tclUNIT s else tclUNIT s and explore_many (previous_envs: (EConstr.named_context * EConstr.constr) list) (tactic_list: search_state tactic list): search_state tactic = match tactic_list with | [] -> tclZERO (SearchBound no_trace) | tac :: l -> tclORELSE (tac >>= fun state -> let module SearchState = (val (search_tactics_factory {state with tactics_resolution = []})) in let module StateTactics = TypedTactics(SearchState) in let search_goal_enter: (Goal.t -> search_state tactic) -> search_state tactic = StateTactics.typedGoalEnter in search_goal_enter @@ fun goal -> if List.mem (Goal.hyps goal, Goal.concl goal) previous_envs then tclZERO (SearchBound no_trace) else explore state @@ (Goal.hyps goal, Goal.concl goal)::previous_envs ) ( fun (e, _) -> match e with | SearchBound trace -> explore_many previous_envs l | _ -> explore_many previous_envs l ) in explore state [] (** Searches a sequence of at most [n] tactics within [db_list] and [lems] that solves the goal The goal can contain evars *) let esearch (log: bool) (depth: int) (lems: Tactypes.delayed_open_constr list) (db_list: hint_db list) (must_use_tactics: Pp.t list) (forbidden_tactics: Pp.t list): trace tactic = TraceTactics.typedGoalEnter begin fun gl -> let local_db env sigma = make_local_hint_db env sigma ~ts:TransparentState.full true lems in let tac (s: search_state): search_state tactic = resolve_esearch depth db_list lems s must_use_tactics forbidden_tactics in tclORELSE begin let evk = goal_with_state (Goal.goal gl) (Goal.state gl) in tac (initial_state log evk local_db depth) >>= fun s -> assert (List.is_empty s.tactics_resolution); Unsafe.tclSETGOALS [] <*> tclUNIT s.trace end begin fun (exn, info) -> match exn with | SearchBound trace -> tclUNIT trace | _ -> tclZERO ~info exn end end (** Generates the {! wp_eauto} function *) let gen_wp_eauto (log: bool) ?(n: int = 5) (lems: Tactypes.delayed_open_constr list) (dbnames: hint_db_name list option) (must_use_tactics: Pp.t list) (forbidden_tactics: Pp.t list): trace tactic = wrap_hint_warning @@ TraceTactics.typedGoalEnter begin fun gl -> let db_list = match dbnames with | Some dbnames -> make_db_list dbnames | None -> current_pure_db () in tclTryDbg pr_dbg_header @@ TraceTactics.typedThen (tclUNIT @@ new_trace log) @@ esearch log n lems db_list must_use_tactics forbidden_tactics end (** Waterproof eauto This function is a rewrite around {! Eauto.eauto} with the same arguments to be able to retrieve which hints have been used in case of success. The code structure has been rearranged to match the one of [wp_auto.wp_auto]. *) let wp_eauto (log: bool) (n: int) (lems: Tactypes.delayed_open_constr list) (db_names: hint_db_name list): trace tactic = gen_wp_eauto log ~n lems (Some db_names) [] [] (** Restricted Waterproof eauto This function acts the same as {! wp_eauto} but will fail if all proof found contain at least one must-use lemma that is unused or one hint that is in the [forbidden] list. *) let rwp_eauto (log: bool) (n: int) (lems: Tactypes.delayed_open_constr list) (dbnames: hint_db_name list) (must_use_tactics: Pp.t list) (forbidden_tactics: Pp.t list): trace tactic = gen_wp_eauto log ~n lems (Some dbnames) must_use_tactics forbidden_tactics