From 5b7eafd0f00a16d78f99a27f5c7d5a0de77dc7e6 Mon Sep 17 00:00:00 2001 From: Stephane Glondu Date: Wed, 21 Jul 2010 09:46:51 +0200 Subject: Imported Upstream snapshot 8.3~beta0+13298 --- tactics/auto.ml | 774 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 412 insertions(+), 362 deletions(-) (limited to 'tactics/auto.ml') diff --git a/tactics/auto.ml b/tactics/auto.ml index 36136a6c..99630417 100644 --- a/tactics/auto.ml +++ b/tactics/auto.ml @@ -6,14 +6,16 @@ (* * GNU Lesser General Public License Version 2.1 *) (************************************************************************) -(* $Id: auto.ml 12187 2009-06-13 19:36:59Z msozeau $ *) +(* $Id$ *) open Pp open Util open Names open Nameops +open Namegen open Term open Termops +open Inductiveops open Sign open Environ open Inductive @@ -34,6 +36,7 @@ open Clenv open Hiddentac open Libnames open Nametab +open Smartlocate open Libobject open Library open Printer @@ -45,15 +48,15 @@ open Mod_subst (* The Type of Constructions Autotactic Hints *) (****************************************************************************) -type auto_tactic = +type auto_tactic = | Res_pf of constr * clausenv (* Hint Apply *) | ERes_pf of constr * clausenv (* Hint EApply *) - | Give_exact of constr + | Give_exact of constr | Res_pf_THEN_trivial_fail of constr * clausenv (* Hint Immediate *) | Unfold_nth of evaluable_global_reference (* Hint Unfold *) - | Extern of glob_tactic_expr (* Hint Extern *) + | Extern of glob_tactic_expr (* Hint Extern *) -type pri_auto_tactic = { +type pri_auto_tactic = { pri : int; (* A number between 0 and 4, 4 = lower priority *) pat : constr_pattern option; (* A pattern for the concl of the Goal *) code : auto_tactic (* the tactic to apply when the concl matches pat *) @@ -61,19 +64,17 @@ type pri_auto_tactic = { type hint_entry = global_reference option * pri_auto_tactic -let pri_ord {pri=pri1} {pri=pri2} = pri1 - pri2 - let pri_order {pri=pri1} {pri=pri2} = pri1 <= pri2 -let insert v l = +let insert v l = let rec insrec = function | [] -> [v] | h::tl -> if pri_order v h then v::h::tl else h::(insrec tl) - in + in insrec l (* Nov 98 -- Papageno *) -(* Les Hints sont ré-organisés en plusieurs databases. +(* Les Hints sont ré-organisés en plusieurs databases. La table impérative "searchtable", de type "hint_db_table", associe une database (hint_db) à chaque nom. @@ -89,150 +90,162 @@ let insert v l = type stored_data = pri_auto_tactic -type search_entry = stored_data list * stored_data list * stored_data Btermdn.t - -let empty_se = ([],[],Btermdn.create ()) +module Bounded_net = Btermdn.Make(struct + type t = stored_data + let compare = Pervasives.compare + end) + +type search_entry = stored_data list * stored_data list * Bounded_net.t + +let empty_se = ([],[],Bounded_net.create ()) + +let eq_pri_auto_tactic x y = + if x.pri = y.pri && x.pat = y.pat then + match x.code,y.code with + | Res_pf(cstr,_),Res_pf(cstr1,_) -> + eq_constr cstr cstr1 + | ERes_pf(cstr,_),ERes_pf(cstr1,_) -> + eq_constr cstr cstr1 + | Give_exact cstr,Give_exact cstr1 -> + eq_constr cstr cstr1 + | Res_pf_THEN_trivial_fail(cstr,_) + ,Res_pf_THEN_trivial_fail(cstr1,_) -> + eq_constr cstr cstr1 + | _,_ -> false + else + false let add_tac pat t st (l,l',dn) = match pat with - | None -> if not (List.mem t l) then (insert t l, l', dn) else (l, l', dn) - | Some pat -> if not (List.mem t l') then (l, insert t l', Btermdn.add st dn (pat,t)) else (l, l', dn) + | None -> if not (List.exists (eq_pri_auto_tactic t) l) then (insert t l, l', dn) else (l, l', dn) + | Some pat -> if not (List.exists (eq_pri_auto_tactic t) l') then (l, insert t l', Bounded_net.add st dn (pat,t)) else (l, l', dn) let rebuild_dn st (l,l',dn) = - (l, l', List.fold_left (fun dn t -> Btermdn.add (Some st) dn (Option.get t.pat, t)) - (Btermdn.create ()) l') - + (l, l', List.fold_left (fun dn t -> Bounded_net.add (Some st) dn (Option.get t.pat, t)) + (Bounded_net.create ()) l') + let lookup_tacs (hdc,c) st (l,l',dn) = - let l' = List.map snd (Btermdn.lookup st dn c) in + let l' = List.map snd (Bounded_net.lookup st dn c) in let sl' = Sort.list pri_order l' in Sort.merge pri_order l sl' -module Constr_map = Map.Make(struct - type t = global_reference - let compare = Pervasives.compare - end) +module Constr_map = Map.Make(RefOrdered) let is_transparent_gr (ids, csts) = function | VarRef id -> Idpred.mem id ids | ConstRef cst -> Cpred.mem cst csts | IndRef _ | ConstructRef _ -> false - -let fmt_autotactic = - function - | Res_pf (c,clenv) -> (str"apply " ++ pr_lconstr c) - | ERes_pf (c,clenv) -> (str"eapply " ++ pr_lconstr c) - | Give_exact c -> (str"exact " ++ pr_lconstr c) - | Res_pf_THEN_trivial_fail (c,clenv) -> - (str"apply " ++ pr_lconstr c ++ str" ; trivial") - | Unfold_nth c -> (str"unfold " ++ pr_evaluable_reference c) - | Extern tac -> - (str "(external) " ++ Pptactic.pr_glob_tactic (Global.env()) tac) - -let pr_autotactic = fmt_autotactic module Hint_db = struct - type t = { + type t = { hintdb_state : Names.transparent_state; + hintdb_unfolds : Idset.t * Cset.t; use_dn : bool; hintdb_map : search_entry Constr_map.t; (* A list of unindexed entries starting with an unfoldable constant or with no associated pattern. *) - hintdb_nopat : stored_data list + hintdb_nopat : (global_reference option * stored_data) list } let empty st use_dn = { hintdb_state = st; + hintdb_unfolds = (Idset.empty, Cset.empty); use_dn = use_dn; hintdb_map = Constr_map.empty; hintdb_nopat = [] } - + let find key db = try Constr_map.find key db.hintdb_map with Not_found -> empty_se - - let map_none db = - Sort.merge pri_order db.hintdb_nopat [] - + + let map_none db = + Sort.merge pri_order (List.map snd db.hintdb_nopat) [] + let map_all k db = let (l,l',_) = find k db in - Sort.merge pri_order (db.hintdb_nopat @ l) l' + Sort.merge pri_order (List.map snd db.hintdb_nopat @ l) l' let map_auto (k,c) db = let st = if db.use_dn then Some db.hintdb_state else None in let l' = lookup_tacs (k,c) st (find k db) in - Sort.merge pri_order db.hintdb_nopat l' - - let is_exact = function + Sort.merge pri_order (List.map snd db.hintdb_nopat) l' + + let is_exact = function | Give_exact _ -> true | _ -> false - let rebuild_db st' db = - { db with hintdb_map = Constr_map.map (rebuild_dn st') db.hintdb_map } - - let add_one (k,v) db = - let st',rebuild = - match v.code with - | Unfold_nth egr -> - let (ids,csts) = db.hintdb_state in - (match egr with - | EvalVarRef id -> (Idpred.add id ids, csts) - | EvalConstRef cst -> (ids, Cpred.add cst csts)), true - | _ -> db.hintdb_state, false - in - let dnst, db, k = - if db.use_dn then - let db', k' = - if rebuild then rebuild_db st' db, k - else (* not an unfold *) - (match k with - | Some gr -> db, if is_transparent_gr st' gr then None else k - | None -> db, None) - in - (Some st', db', k') - else None, db, k + let addkv gr v db = + let k = match gr with + | Some gr -> if db.use_dn && is_transparent_gr db.hintdb_state gr then None else Some gr + | None -> None in + let dnst = if db.use_dn then Some db.hintdb_state else None in let pat = if not db.use_dn && is_exact v.code then None else v.pat in match k with | None -> - if not (List.mem v db.hintdb_nopat) then - { db with hintdb_nopat = v :: db.hintdb_nopat } + if not (List.exists (fun (_, v') -> v = v') db.hintdb_nopat) then + { db with hintdb_nopat = (gr,v) :: db.hintdb_nopat } else db | Some gr -> let oval = find gr db in - { db with hintdb_map = Constr_map.add gr (add_tac pat v dnst oval) db.hintdb_map; - hintdb_state = st' } - + { db with hintdb_map = Constr_map.add gr (add_tac pat v dnst oval) db.hintdb_map } + + let rebuild_db st' db = + let db' = + { db with hintdb_map = Constr_map.map (rebuild_dn st') db.hintdb_map; + hintdb_state = st'; hintdb_nopat = [] } + in + List.fold_left (fun db (gr,v) -> addkv gr v db) db' db.hintdb_nopat + + let add_one (k,v) db = + let st',db,rebuild = + match v.code with + | Unfold_nth egr -> + let addunf (ids,csts) (ids',csts') = + match egr with + | EvalVarRef id -> (Idpred.add id ids, csts), (Idset.add id ids', csts') + | EvalConstRef cst -> (ids, Cpred.add cst csts), (ids', Cset.add cst csts') + in + let state, unfs = addunf db.hintdb_state db.hintdb_unfolds in + state, { db with hintdb_unfolds = unfs }, true + | _ -> db.hintdb_state, db, false + in + let db = if db.use_dn && rebuild then rebuild_db st' db else db + in addkv k v db + let add_list l db = List.fold_right add_one l db - - let iter f db = - f None db.hintdb_nopat; + + let iter f db = + f None (List.map snd db.hintdb_nopat); Constr_map.iter (fun k (l,l',_) -> f (Some k) (l@l')) db.hintdb_map - + let transparent_state db = db.hintdb_state let set_transparent_state db st = - let db = if db.use_dn then rebuild_db st db else db in - { db with hintdb_state = st } + if db.use_dn then rebuild_db st db + else { db with hintdb_state = st } + + let unfolds db = db.hintdb_unfolds let use_dn db = db.use_dn - + end module Hintdbmap = Gmap type hint_db = Hint_db.t -type frozen_hint_db_table = (string,hint_db) Hintdbmap.t +type frozen_hint_db_table = (string,hint_db) Hintdbmap.t type hint_db_table = (string,hint_db) Hintdbmap.t ref type hint_db_name = string let searchtable = (ref Hintdbmap.empty : hint_db_table) - -let searchtable_map name = + +let searchtable_map name = Hintdbmap.find name !searchtable -let searchtable_add (name,db) = +let searchtable_add (name,db) = searchtable := Hintdbmap.add name db !searchtable let current_db_names () = Hintdbmap.dom !searchtable @@ -242,7 +255,7 @@ let current_db_names () = (**************************************************************************) let auto_init : (unit -> unit) ref = ref (fun () -> ()) - + let init () = searchtable := Hintdbmap.empty; !auto_init () let freeze () = !searchtable let unfreeze fs = searchtable := fs @@ -250,52 +263,51 @@ let unfreeze fs = searchtable := fs let _ = Summary.declare_summary "search" { Summary.freeze_function = freeze; Summary.unfreeze_function = unfreeze; - Summary.init_function = init; - Summary.survive_module = false; - Summary.survive_section = false } + Summary.init_function = init } + - (**************************************************************************) (* Auxiliary functions to prepare AUTOHINT objects *) (**************************************************************************) let rec nb_hyp c = match kind_of_term c with | Prod(_,_,c2) -> if noccurn 1 c2 then 1+(nb_hyp c2) else nb_hyp c2 - | _ -> 0 + | _ -> 0 (* adding and removing tactics in the search table *) -let try_head_pattern c = +let try_head_pattern c = try head_pattern_bound c with BoundPattern -> error "Bound head variable." -let dummy_goal = +let dummy_goal = {it = make_evar empty_named_context_val mkProp; sigma = empty} -let make_exact_entry pri (c,cty) = +let make_exact_entry sigma pri (c,cty) = let cty = strip_outer_cast cty in - match kind_of_term cty with - | Prod (_,_,_) -> - failwith "make_exact_entry" + match kind_of_term cty with + | Prod _ -> failwith "make_exact_entry" | _ -> - let ce = mk_clenv_from dummy_goal (c,cty) in - let c' = clenv_type ce in - let pat = Pattern.pattern_of_constr c' in - (Some (head_of_constr_reference (fst (head_constr cty))), - { pri=(match pri with Some pri -> pri | None -> 0); pat=Some pat; code=Give_exact c }) + let pat = snd (Pattern.pattern_of_constr sigma cty) in + let head = + try head_of_constr_reference (fst (head_constr cty)) + with _ -> failwith "make_exact_entry" + in + (Some head, + { pri=(match pri with Some pri -> pri | None -> 0); pat=Some pat; code=Give_exact c }) let make_apply_entry env sigma (eapply,hnf,verbose) pri (c,cty) = let cty = if hnf then hnf_constr env sigma cty else cty in match kind_of_term cty with | Prod _ -> let ce = mk_clenv_from dummy_goal (c,cty) in - let c' = clenv_type ce in - let pat = Pattern.pattern_of_constr c' in + let c' = clenv_type (* ~reduce:false *) ce in + let pat = snd (Pattern.pattern_of_constr sigma c') in let hd = (try head_pattern_bound pat with BoundPattern -> failwith "make_apply_entry") in let nmiss = List.length (clenv_missing ce) in - if nmiss = 0 then + if nmiss = 0 then (Some hd, { pri = (match pri with None -> nb_hyp cty | Some p -> p); pat = Some pat; @@ -311,43 +323,43 @@ let make_apply_entry env sigma (eapply,hnf,verbose) pri (c,cty) = code = ERes_pf(c,{ce with env=empty_env}) }) end | _ -> failwith "make_apply_entry" - -(* flags is (e,h,v) with e=true if eapply and h=true if hnf and v=true if verbose + +(* flags is (e,h,v) with e=true if eapply and h=true if hnf and v=true if verbose c is a constr cty is the type of constr *) let make_resolves env sigma flags pri c = let cty = type_of env sigma c in - let ents = - map_succeed - (fun f -> f (c,cty)) - [make_exact_entry pri; make_apply_entry env sigma flags pri] - in + let ents = + map_succeed + (fun f -> f (c,cty)) + [make_exact_entry sigma pri; make_apply_entry env sigma flags pri] + in if ents = [] then - errorlabstrm "Hint" - (pr_lconstr c ++ spc() ++ + errorlabstrm "Hint" + (pr_lconstr c ++ spc() ++ (if pi1 flags then str"cannot be used as a hint." else str "can be used as a hint only for eauto.")); ents (* used to add an hypothesis to the local hint database *) -let make_resolve_hyp env sigma (hname,_,htyp) = +let make_resolve_hyp env sigma (hname,_,htyp) = try [make_apply_entry env sigma (true, true, false) None (mkVar hname, htyp)] - with + with | Failure _ -> [] | e when Logic.catchable_exception e -> anomaly "make_resolve_hyp" (* REM : in most cases hintname = id *) -let make_unfold (ref, eref) = - (Some ref, +let make_unfold eref = + (Some (global_of_evaluable_reference eref), { pri = 4; pat = None; code = Unfold_nth eref }) -let make_extern pri pat tacast = - let hdconstr = Option.map try_head_pattern pat in +let make_extern pri pat tacast = + let hdconstr = Option.map try_head_pattern pat in (hdconstr, { pri=pri; pat = pat; @@ -358,7 +370,7 @@ let make_trivial env sigma c = let hd = head_of_constr_reference (fst (head_constr t)) in let ce = mk_clenv_from dummy_goal (c,t) in (Some hd, { pri=1; - pat = Some (Pattern.pattern_of_constr (clenv_type ce)); + pat = Some (snd (Pattern.pattern_of_constr sigma (clenv_type ce))); code=Res_pf_THEN_trivial_fail(c,{ce with env=empty_env}) }) open Vernacexpr @@ -369,52 +381,47 @@ open Vernacexpr (* If the database does not exist, it is created *) (* TODO: should a warning be printed in this case ?? *) + +let get_db dbname = + try searchtable_map dbname + with Not_found -> Hint_db.empty empty_transparent_state false + let add_hint dbname hintlist = - try - let db = searchtable_map dbname in - let db' = Hint_db.add_list hintlist db in + let db = get_db dbname in + let db' = Hint_db.add_list hintlist db in searchtable_add (dbname,db') - with Not_found -> - let db = Hint_db.add_list hintlist (Hint_db.empty empty_transparent_state false) in - searchtable_add (dbname,db) let add_transparency dbname grs b = - let db = searchtable_map dbname in + let db = get_db dbname in let st = Hint_db.transparent_state db in - let st' = - List.fold_left (fun (ids, csts) gr -> + let st' = + List.fold_left (fun (ids, csts) gr -> match gr with | EvalConstRef c -> (ids, (if b then Cpred.add else Cpred.remove) c csts) | EvalVarRef v -> (if b then Idpred.add else Idpred.remove) v ids, csts) st grs in searchtable_add (dbname, Hint_db.set_transparent_state db st') - + type hint_action = | CreateDB of bool * transparent_state | AddTransparency of evaluable_global_reference list * bool | AddTactic of (global_reference option * pri_auto_tactic) list -let cache_autohint (_,(local,name,hints)) = +let cache_autohint (_,(local,name,hints)) = match hints with | CreateDB (b, st) -> searchtable_add (name, Hint_db.empty st b) | AddTransparency (grs, b) -> add_transparency name grs b | AddTactic hints -> add_hint name hints -let forward_subst_tactic = +let forward_subst_tactic = ref (fun _ -> failwith "subst_tactic is not installed for auto") let set_extern_subst_tactic f = forward_subst_tactic := f -let subst_autohint (_,subst,(local,name,hintlist as obj)) = +let subst_autohint (subst,(local,name,hintlist as obj)) = let trans_clenv clenv = Clenv.subst_clenv subst clenv in - let trans_data data code = - { data with - pat = Option.smartmap (subst_pattern subst) data.pat ; - code = code ; - } - in let subst_key gr = let (lab'', elab') = subst_global subst gr in - let gr' = + let gr' = (try head_of_constr_reference (fst (head_constr_bound elab')) with Tactics.Bound -> lab'') in if gr' == gr then gr else gr' @@ -424,61 +431,87 @@ let subst_autohint (_,subst,(local,name,hintlist as obj)) = let data' = match data.code with | Res_pf (c, clenv) -> let c' = subst_mps subst c in - if c==c' then data else - trans_data data (Res_pf (c', trans_clenv clenv)) + let clenv' = trans_clenv clenv in + let pat' = Option.smartmap (subst_pattern subst) data.pat in + if c==c' && clenv'==clenv && pat'==data.pat then data else + {data with + pat=pat'; + code=Res_pf (c', clenv')} | ERes_pf (c, clenv) -> let c' = subst_mps subst c in - if c==c' then data else - trans_data data (ERes_pf (c', trans_clenv clenv)) + let clenv' = trans_clenv clenv in + let pat' = Option.smartmap (subst_pattern subst) data.pat in + if c==c' && clenv'==clenv && pat'==data.pat then data else + {data with + pat=pat'; + code=ERes_pf (c', clenv')} | Give_exact c -> let c' = subst_mps subst c in - if c==c' then data else - trans_data data (Give_exact c') + let pat' = Option.smartmap (subst_pattern subst) data.pat in + if c==c' && pat'==data.pat then data else + {data with + pat=pat'; + code=(Give_exact c')} | Res_pf_THEN_trivial_fail (c, clenv) -> let c' = subst_mps subst c in - if c==c' then data else - let code' = Res_pf_THEN_trivial_fail (c', trans_clenv clenv) in - trans_data data code' - | Unfold_nth ref -> + let clenv' = trans_clenv clenv in + let pat' = Option.smartmap (subst_pattern subst) data.pat in + if c==c' && clenv'==clenv && pat'==data.pat then data else + {data with + pat=pat'; + code=Res_pf_THEN_trivial_fail (c',clenv')} + | Unfold_nth ref -> let ref' = subst_evaluable_reference subst ref in - if ref==ref' then data else - trans_data data (Unfold_nth ref') + let pat' = Option.smartmap (subst_pattern subst) data.pat in + if ref==ref' && pat'==data.pat then data else + {data with + pat=pat'; + code=(Unfold_nth ref')} | Extern tac -> let tac' = !forward_subst_tactic subst tac in - if tac==tac' then data else - trans_data data (Extern tac') + let pat' = Option.smartmap (subst_pattern subst) data.pat in + if tac==tac' && pat'==data.pat then data else + {data with + pat=pat'; + code=(Extern tac')} in if k' == k && data' == data then hint else (k',data') in match hintlist with | CreateDB _ -> obj - | AddTransparency (grs, b) -> + | AddTransparency (grs, b) -> let grs' = list_smartmap (subst_evaluable_reference subst) grs in if grs==grs' then obj else (local, name, AddTransparency (grs', b)) | AddTactic hintlist -> let hintlist' = list_smartmap subst_hint hintlist in if hintlist' == hintlist then obj else (local,name,AddTactic hintlist') - -let classify_autohint (_,((local,name,hintlist) as obj)) = - if local or hintlist = (AddTactic []) then Dispose else Substitute obj -let export_autohint ((local,name,hintlist) as obj) = - if local then None else Some obj +let classify_autohint ((local,name,hintlist) as obj) = + if local or hintlist = (AddTactic []) then Dispose else Substitute obj -let (inAutoHint,outAutoHint) = +let discharge_autohint (_,(local,name,hintlist as obj)) = + if local then None else + match hintlist with + | CreateDB _ -> + (* We assume that the transparent state is either empty or full *) + Some obj + | AddTransparency _ | AddTactic _ -> + (* Needs the adequate code here to support Global Hints in sections *) + None + +let (inAutoHint,_) = declare_object {(default_object "AUTOHINT") with cache_function = cache_autohint; load_function = (fun _ -> cache_autohint); subst_function = subst_autohint; - classify_function = classify_autohint; - export_function = export_autohint } + classify_function = classify_autohint } -let create_hint_db l n st b = +let create_hint_db l n st b = Lib.add_anonymous_leaf (inAutoHint (l,n,CreateDB (b, st))) - + (**************************************************************************) (* The "Hint" vernacular command *) (**************************************************************************) @@ -494,14 +527,14 @@ let add_resolves env sigma clist local dbnames = let add_unfolds l local dbnames = - List.iter - (fun dbname -> Lib.add_anonymous_leaf + List.iter + (fun dbname -> Lib.add_anonymous_leaf (inAutoHint (local,dbname, AddTactic (List.map make_unfold l)))) dbnames let add_transparency l b local dbnames = - List.iter - (fun dbname -> Lib.add_anonymous_leaf + List.iter + (fun dbname -> Lib.add_anonymous_leaf (inAutoHint (local,dbname, AddTransparency (l, b)))) dbnames @@ -513,16 +546,16 @@ let add_extern pri pat tacast local dbname = | Some (patmetas,pat) -> (match (list_subtract tacmetas patmetas) with | i::_ -> - errorlabstrm "add_extern" - (str "The meta-variable ?" ++ pr_patvar i ++ str" is not bound.") + errorlabstrm "add_extern" + (str "The meta-variable ?" ++ Ppconstr.pr_patvar i ++ str" is not bound.") | [] -> Lib.add_anonymous_leaf (inAutoHint(local,dbname, AddTactic [make_extern pri (Some pat) tacast]))) - | None -> + | None -> Lib.add_anonymous_leaf (inAutoHint(local,dbname, AddTactic [make_extern pri None tacast])) -let add_externs pri pat tacast local dbnames = +let add_externs pri pat tacast local dbnames = List.iter (add_extern pri pat tacast local) dbnames let add_trivials env sigma l local dbnames = @@ -532,62 +565,61 @@ let add_trivials env sigma l local dbnames = inAutoHint(local,dbname, AddTactic (List.map (make_trivial env sigma) l)))) dbnames -let forward_intern_tac = +let forward_intern_tac = ref (fun _ -> failwith "intern_tac is not installed for auto") let set_extern_intern_tac f = forward_intern_tac := f -let add_hints local dbnames0 h = - let dbnames = if dbnames0 = [] then ["core"] else dbnames0 in - let env = Global.env() and sigma = Evd.empty in - let f = Constrintern.interp_constr sigma env in +type hints_entry = + | HintsResolveEntry of (int option * bool * constr) list + | HintsImmediateEntry of constr list + | HintsUnfoldEntry of evaluable_global_reference list + | HintsTransparencyEntry of evaluable_global_reference list * bool + | HintsExternEntry of + int * (patvar list * constr_pattern) option * glob_tactic_expr + | HintsDestructEntry of identifier * int * (bool,unit) location * + (patvar list * constr_pattern) * glob_tactic_expr + +let interp_hints h = + let f = Constrintern.interp_constr Evd.empty (Global.env()) in + let fr r = + let gr = global_with_alias r in + let r' = evaluable_of_global_reference (Global.env()) gr in + Dumpglob.add_glob (loc_of_reference r) gr; + r' in + let fp = Constrintern.intern_constr_pattern Evd.empty (Global.env()) in match h with - | HintsResolve lhints -> - add_resolves env sigma (List.map (fun (pri, b, x) -> pri, b, f x) lhints) local dbnames - | HintsImmediate lhints -> - add_trivials env sigma (List.map f lhints) local dbnames - | HintsUnfold lhints -> - let f r = - let gr = Syntax_def.global_with_alias r in - let r' = match gr with - | ConstRef c -> EvalConstRef c - | VarRef c -> EvalVarRef c - | _ -> - errorlabstrm "evalref_of_ref" - (str "Cannot coerce" ++ spc () ++ pr_global gr ++ spc () ++ - str "to an evaluable reference.") - in - Dumpglob.add_glob (loc_of_reference r) gr; - (gr,r') in - add_unfolds (List.map f lhints) local dbnames - | HintsTransparency (lhints, b) -> - let f r = - let gr = Syntax_def.global_with_alias r in - let r' = match gr with - | ConstRef c -> EvalConstRef c - | VarRef c -> EvalVarRef c - | _ -> - errorlabstrm "evalref_of_ref" - (str "Cannot coerce" ++ spc () ++ pr_global gr ++ spc () ++ - str "to an evaluable reference.") - in - Dumpglob.add_glob (loc_of_reference r) gr; - r' in - add_transparency (List.map f lhints) b local dbnames + | HintsResolve lhints -> HintsResolveEntry (List.map (on_pi3 f) lhints) + | HintsImmediate lhints -> HintsImmediateEntry (List.map f lhints) + | HintsUnfold lhints -> HintsUnfoldEntry (List.map fr lhints) + | HintsTransparency (lhints, b) -> + HintsTransparencyEntry (List.map fr lhints, b) | HintsConstructors lqid -> - let add_one qid = - let env = Global.env() and sigma = Evd.empty in - let isp = inductive_of_reference qid in - let consnames = (snd (Global.lookup_inductive isp)).mind_consnames in - let lcons = list_tabulate - (fun i -> None, true, mkConstruct (isp,i+1)) (Array.length consnames) in - add_resolves env sigma lcons local dbnames in - List.iter add_one lqid + let constr_hints_of_ind qid = + let ind = global_inductive_with_alias qid in + list_tabulate (fun i -> None, true, mkConstruct (ind,i+1)) + (nconstructors ind) in + HintsResolveEntry (List.flatten (List.map constr_hints_of_ind lqid)) | HintsExtern (pri, patcom, tacexp) -> - let pat = Option.map (Constrintern.intern_constr_pattern Evd.empty (Global.env())) patcom in + let pat = Option.map fp patcom in let tacexp = !forward_intern_tac (match pat with None -> [] | Some (l, _) -> l) tacexp in - add_externs pri pat tacexp local dbnames + HintsExternEntry (pri, pat, tacexp) | HintsDestruct(na,pri,loc,pat,code) -> + let (l,_ as pat) = fp pat in + HintsDestructEntry (na,pri,loc,pat,!forward_intern_tac l code) + +let add_hints local dbnames0 h = + let dbnames = if dbnames0 = [] then ["core"] else dbnames0 in + let env = Global.env() and sigma = Evd.empty in + match h with + | HintsResolveEntry lhints -> add_resolves env sigma lhints local dbnames + | HintsImmediateEntry lhints -> add_trivials env sigma lhints local dbnames + | HintsUnfoldEntry lhints -> add_unfolds lhints local dbnames + | HintsTransparencyEntry (lhints, b) -> + add_transparency lhints b local dbnames + | HintsExternEntry (pri, pat, tacexp) -> + add_externs pri pat tacexp local dbnames + | HintsDestructEntry (na,pri,loc,pat,code) -> if dbnames0<>[] then warn (str"Database selection not implemented for destruct hints"); Dhyp.add_destructor_hint local na loc pat pri code @@ -601,10 +633,10 @@ let pr_autotactic = | Res_pf (c,clenv) -> (str"apply " ++ pr_lconstr c) | ERes_pf (c,clenv) -> (str"eapply " ++ pr_lconstr c) | Give_exact c -> (str"exact " ++ pr_lconstr c) - | Res_pf_THEN_trivial_fail (c,clenv) -> + | Res_pf_THEN_trivial_fail (c,clenv) -> (str"apply " ++ pr_lconstr c ++ str" ; trivial") | Unfold_nth c -> (str"unfold " ++ pr_evaluable_reference c) - | Extern tac -> + | Extern tac -> (str "(external) " ++ Pptactic.pr_glob_tactic (Global.env()) tac) let pr_hint v = @@ -619,17 +651,17 @@ let pr_hints_db (name,db,hintlist) = else (fnl () ++ pr_hint_list hintlist)) (* Print all hints associated to head c in any database *) -let pr_hint_list_for_head c = +let pr_hint_list_for_head c = let dbs = Hintdbmap.to_list !searchtable in - let valid_dbs = - map_succeed - (fun (name,db) -> (name,db,Hint_db.map_all c db)) - dbs + let valid_dbs = + map_succeed + (fun (name,db) -> (name,db,Hint_db.map_all c db)) + dbs in - if valid_dbs = [] then + if valid_dbs = [] then (str "No hint declared for :" ++ pr_global c) - else - hov 0 + else + hov 0 (str"For " ++ pr_global c ++ str" -> " ++ fnl () ++ hov 0 (prlist pr_hints_db valid_dbs)) @@ -638,11 +670,11 @@ let pr_hint_ref ref = pr_hint_list_for_head ref (* Print all hints associated to head id in any database *) let print_hint_ref ref = ppnl(pr_hint_ref ref) -let pr_hint_term cl = - try +let pr_hint_term cl = + try let dbs = Hintdbmap.to_list !searchtable in - let valid_dbs = - let fn = try + let valid_dbs = + let fn = try let (hdc,args) = head_constr_bound cl in let hd = head_of_constr_reference hdc in if occur_existential cl then @@ -652,50 +684,53 @@ let pr_hint_term cl = in map_succeed (fun (name, db) -> (name, db, fn db)) dbs in - if valid_dbs = [] then + if valid_dbs = [] then (str "No hint applicable for current goal") else (str "Applicable Hints :" ++ fnl () ++ hov 0 (prlist pr_hints_db valid_dbs)) - with Match_failure _ | Failure _ -> + with Match_failure _ | Failure _ -> (str "No hint applicable for current goal") let error_no_such_hint_database x = error ("No such Hint database: "^x^".") - + let print_hint_term cl = ppnl (pr_hint_term cl) (* print all hints that apply to the concl of the current goal *) -let print_applicable_hint () = - let pts = get_pftreestate () in - let gl = nth_goal_of_pftreestate 1 pts in +let print_applicable_hint () = + let pts = get_pftreestate () in + let gl = nth_goal_of_pftreestate 1 pts in print_hint_term (pf_concl gl) - + (* displays the whole hint database db *) let print_hint_db db = let (ids, csts) = Hint_db.transparent_state db in + msg (hov 0 + ((if Hint_db.use_dn db then str"Discriminated database" + else str"Non-discriminated database") ++ fnl ())); msg (hov 0 (str"Unfoldable variable definitions: " ++ pr_idpred ids ++ fnl () ++ str"Unfoldable constant definitions: " ++ pr_cpred csts ++ fnl ())); - Hint_db.iter + Hint_db.iter (fun head hintlist -> match head with | Some head -> - msg (hov 0 + msg (hov 0 (str "For " ++ pr_global head ++ str " -> " ++ pr_hint_list hintlist)) | None -> - msg (hov 0 + msg (hov 0 (str "For any goal -> " ++ pr_hint_list hintlist))) db let print_hint_db_by_name dbname = - try + try let db = searchtable_map dbname in print_hint_db db - with Not_found -> + with Not_found -> error_no_such_hint_database dbname - + (* displays all the hints of all databases *) let print_searchtable () = Hintdbmap.iter @@ -714,41 +749,55 @@ let print_searchtable () = let priority l = List.filter (fun (_,hint) -> hint.pri = 0) l -let select_unfold_extern = - List.filter (function (_,{code = (Unfold_nth _ | Extern _)}) -> true | _ -> false) - (* tell auto not to reuse already instantiated metas in unification (for compatibility, since otherwise, apply succeeds oftener) *) open Unification let auto_unif_flags = { - modulo_conv_on_closed_terms = Some full_transparent_state; + modulo_conv_on_closed_terms = Some full_transparent_state; use_metas_eagerly = false; modulo_delta = empty_transparent_state; + resolve_evars = true; + use_evars_pattern_unification = false; } (* Try unification with the precompiled clause, then use registered Apply *) -let unify_resolve_nodelta (c,clenv) gl = +let h_clenv_refine ev c clenv = + Refiner.abstract_tactic (TacApply (true,ev,[c,NoBindings],None)) + (Clenvtac.clenv_refine ev clenv) + +let unify_resolve_nodelta (c,clenv) gl = let clenv' = connect_clenv gl clenv in - let _ = clenv_unique_resolver false ~flags:auto_unif_flags clenv' gl in - h_simplest_apply c gl + let clenv'' = clenv_unique_resolver false ~flags:auto_unif_flags clenv' gl in + h_clenv_refine false c clenv'' gl -let unify_resolve flags (c,clenv) gl = +let unify_resolve flags (c,clenv) gl = let clenv' = connect_clenv gl clenv in - let _ = clenv_unique_resolver false ~flags clenv' gl in - h_apply true false [inj_open c,NoBindings] gl + let clenv'' = clenv_unique_resolver false ~flags clenv' gl in + h_clenv_refine false c clenv'' gl let unify_resolve_gen = function | None -> unify_resolve_nodelta | Some flags -> unify_resolve flags +(* Util *) + +let expand_constructor_hints lems = + list_map_append (fun lem -> + match kind_of_term lem with + | Ind ind -> + list_tabulate (fun i -> mkConstruct (ind,i+1)) (nconstructors ind) + | _ -> + [lem]) lems + (* builds a hint database from a constr signature *) (* typically used with (lid, ltyp) = pf_hyps_types *) let add_hint_lemmas eapply lems hint_db gl = - let hintlist' = + let lems = expand_constructor_hints lems in + let hintlist' = list_map_append (pf_apply make_resolves gl (eapply,true,false) None) lems in Hint_db.add_list hintlist' hint_db @@ -763,21 +812,21 @@ let make_local_hint_db eapply lems gl = terme pour l'affichage ? (HH) *) (* Si on enlève le dernier argument (gl) conclPattern est calculé une -fois pour toutes : en particulier si Pattern.somatch produit une UserError +fois pour toutes : en particulier si Pattern.somatch produit une UserError Ce qui fait que si la conclusion ne matche pas le pattern, Auto échoue, même si après Intros la conclusion matche le pattern. *) (* conclPattern doit échouer avec error car il est rattraper par tclFIRST *) -let forward_interp_tactic = +let forward_interp_tactic = ref (fun _ -> failwith "interp_tactic is not installed for auto") let set_extern_interp f = forward_interp_tactic := f let conclPattern concl pat tac gl = - let constr_bindings = - match pat with + let constr_bindings = + match pat with | None -> [] | Some pat -> try matches pat concl @@ -793,7 +842,7 @@ let conclPattern concl pat tac gl = de Hint impérative a été remplacée par plusieurs bases fonctionnelles *) let flags_of_state st = - {auto_unif_flags with + {auto_unif_flags with modulo_conv_on_closed_terms = Some st; modulo_delta = st} let hintmap_of hdc concl = @@ -802,34 +851,38 @@ let hintmap_of hdc concl = | Some hdc -> if occur_existential concl then Hint_db.map_all hdc else Hint_db.map_auto (hdc,concl) - + +let exists_evaluable_reference env = function + | EvalConstRef _ -> true + | EvalVarRef v -> try ignore(lookup_named v env); true with Not_found -> false + let rec trivial_fail_db mod_delta db_list local_db gl = - let intro_tac = - tclTHEN intro + let intro_tac = + tclTHEN intro (fun g'-> let hintl = make_resolve_hyp (pf_env g') (project g') (pf_last_hyp g') in trivial_fail_db mod_delta db_list (Hint_db.add_list hintl local_db) g') in - tclFIRST + tclFIRST (assumption::intro_tac:: - (List.map tclCOMPLETE + (List.map tclCOMPLETE (trivial_resolve mod_delta db_list local_db (pf_concl gl)))) gl and my_find_search_nodelta db_list local_db hdc concl = - List.map (fun hint -> (None,hint)) + List.map (fun hint -> (None,hint)) (list_map_append (hintmap_of hdc concl) (local_db::db_list)) and my_find_search mod_delta = if mod_delta then my_find_search_delta else my_find_search_nodelta - + and my_find_search_delta db_list local_db hdc concl = let flags = {auto_unif_flags with use_metas_eagerly = true} in let f = hintmap_of hdc concl in - if occur_existential concl then + if occur_existential concl then list_map_append - (fun db -> - if Hint_db.use_dn db then + (fun db -> + if Hint_db.use_dn db then let flags = flags_of_state (Hint_db.transparent_state db) in List.map (fun x -> (Some flags,x)) (f db) else @@ -837,8 +890,8 @@ and my_find_search_delta db_list local_db hdc concl = List.map (fun x -> (Some flags,x)) (f db)) (local_db::db_list) else - list_map_append (fun db -> - if Hint_db.use_dn db then + list_map_append (fun db -> + if Hint_db.use_dn db then let flags = flags_of_state (Hint_db.transparent_state db) in List.map (fun x -> (Some flags, x)) (f db) else @@ -859,37 +912,40 @@ and tac_of_hint db_list local_db concl (flags, {pat=p; code=t}) = | Res_pf (term,cl) -> unify_resolve_gen flags (term,cl) | ERes_pf (_,c) -> (fun gl -> error "eres_pf") | Give_exact c -> exact_check c - | Res_pf_THEN_trivial_fail (term,cl) -> - tclTHEN + | Res_pf_THEN_trivial_fail (term,cl) -> + tclTHEN (unify_resolve_gen flags (term,cl)) (trivial_fail_db (flags <> None) db_list local_db) - | Unfold_nth c -> unfold_in_concl [all_occurrences,c] + | Unfold_nth c -> (fun gl -> + if exists_evaluable_reference (pf_env gl) c then + tclPROGRESS (h_reduce (Unfold [all_occurrences_expr,c]) onConcl) gl + else tclFAIL 0 (str"Unbound reference") gl) | Extern tacast -> conclPattern concl p tacast - -and trivial_resolve mod_delta db_list local_db cl = - try - let head = + +and trivial_resolve mod_delta db_list local_db cl = + try + let head = try let hdconstr,_ = head_constr_bound cl in Some (head_of_constr_reference hdconstr) with Bound -> None in List.map (tac_of_hint db_list local_db cl) - (priority + (priority (my_find_search mod_delta db_list local_db head cl)) with Not_found -> [] let trivial lems dbnames gl = - let db_list = + let db_list = List.map - (fun x -> - try + (fun x -> + try searchtable_map x - with Not_found -> + with Not_found -> error_no_such_hint_database x) - ("core"::dbnames) + ("core"::dbnames) in - tclTRY (trivial_fail_db false db_list (make_local_hint_db false lems gl)) gl - + tclTRY (trivial_fail_db false db_list (make_local_hint_db false lems gl)) gl + let full_trivial lems gl = let dbnames = Hintdbmap.dom !searchtable in let dbnames = list_subtract dbnames ["v62"] in @@ -903,7 +959,7 @@ let gen_trivial lems = function let inj_open c = (Evd.empty,c) let h_trivial lems l = - Refiner.abstract_tactic (TacTrivial (List.map inj_open lems,l)) + Refiner.abstract_tactic (TacTrivial (lems,l)) (gen_trivial lems l) (**************************************************************************) @@ -911,8 +967,8 @@ let h_trivial lems l = (**************************************************************************) let possible_resolve mod_delta db_list local_db cl = - try - let head = + try + let head = try let hdconstr,_ = head_constr_bound cl in Some (head_of_constr_reference hdconstr) with Bound -> None @@ -931,19 +987,19 @@ let decomp_unary_term_then (id,_,typc) kont1 kont2 gl = kont2 gl with UserError _ -> kont2 gl -let decomp_empty_term (id,_,typc) gl = - if Hipattern.is_empty_type typc then - simplest_case (mkVar id) gl - else +let decomp_empty_term (id,_,typc) gl = + if Hipattern.is_empty_type typc then + simplest_case (mkVar id) gl + else errorlabstrm "Auto.decomp_empty_term" (str "Not an empty type.") let extend_local_db gl decl db = Hint_db.add_list (make_resolve_hyp (pf_env gl) (project gl) decl) db -(* Try to decompose hypothesis [decl] into atomic components of a - conjunction with maximum depth [p] (or solve the goal from an - empty type) then call the continuation tactic with hint db extended - with the obtappined not-further-decomposable hypotheses *) +(* Try to decompose hypothesis [decl] into atomic components of a + conjunction with maximum depth [p] (or solve the goal from an + empty type) then call the continuation tactic with hint db extended + with the obtained not-further-decomposable hypotheses *) let rec decomp_and_register_decl p kont (id,_,_ as decl) db gl = if p = 0 then @@ -962,7 +1018,7 @@ and intros_decomp p kont decls db n = if n = 0 then decomp_and_register_decls p kont decls db else - tclTHEN intro (tclLAST_DECL (fun d -> + tclTHEN intro (onLastDecl (fun d -> (intros_decomp p kont (d::decls) db (n-1)))) (* Decompose hypotheses [hyps] with maximum depth [p] and @@ -973,21 +1029,21 @@ and decomp_and_register_decls p kont decls = List.fold_left (decomp_and_register_decl p) kont decls -(* decomp is an natural number giving an indication on decomposition +(* decomp is an natural number giving an indication on decomposition of conjunction in hypotheses, 0 corresponds to no decomposition *) (* n is the max depth of search *) (* local_db contains the local Hypotheses *) exception Uplift of tactic list -let rec search_gen p n mod_delta db_list local_db = - let rec search n local_db gl = - if n=0 then error "BOUND 2"; - tclFIRST - (assumption :: - intros_decomp p (search n) [] local_db 1 :: - List.map (fun ntac -> tclTHEN ntac (search (n-1) local_db)) - (possible_resolve mod_delta db_list local_db (pf_concl gl))) gl +let search_gen p n mod_delta db_list local_db = + let rec search n local_db = + if n=0 then (fun gl -> error "BOUND 2") else + tclORELSE0 assumption + (tclORELSE0 (intros_decomp p (search n) [] local_db 1) + (fun gl -> tclFIRST + (List.map (fun ntac -> tclTHEN ntac (search (n-1) local_db)) + (possible_resolve mod_delta db_list local_db (pf_concl gl))) gl)) in search n local_db @@ -996,14 +1052,14 @@ let search = search_gen 0 let default_search_depth = ref 5 let delta_auto mod_delta n lems dbnames gl = - let db_list = + let db_list = List.map - (fun x -> - try + (fun x -> + try searchtable_map x - with Not_found -> + with Not_found -> error_no_such_hint_database x) - ("core"::dbnames) + ("core"::dbnames) in tclTRY (search n mod_delta db_list (make_local_hint_db false lems gl)) gl @@ -1013,7 +1069,7 @@ let new_auto = delta_auto true let default_auto = auto !default_search_depth [] [] -let delta_full_auto mod_delta n lems gl = +let delta_full_auto mod_delta n lems gl = let dbnames = Hintdbmap.dom !searchtable in let dbnames = list_subtract dbnames ["v62"] in let db_list = List.map (fun x -> searchtable_map x) dbnames in @@ -1033,25 +1089,25 @@ let gen_auto n lems dbnames = let inj_or_var = Option.map (fun n -> ArgArg n) let h_auto n lems l = - Refiner.abstract_tactic (TacAuto (inj_or_var n,List.map inj_open lems,l)) + Refiner.abstract_tactic (TacAuto (inj_or_var n,lems,l)) (gen_auto n lems l) (**************************************************************************) (* The "destructing Auto" from Eduardo *) (**************************************************************************) -(* Depth of search after decomposition of hypothesis, by default - one look for an immediate solution *) +(* Depth of search after decomposition of hypothesis, by default + one look for an immediate solution *) let default_search_decomp = ref 20 -let destruct_auto p lems n gl = +let destruct_auto p lems n gl = decomp_and_register_decls p (fun local_db gl -> search_gen p n false (List.map searchtable_map ["core";"extcore"]) (add_hint_lemmas false lems local_db gl) gl) (pf_hyps gl) (Hint_db.empty empty_transparent_state false) gl - + let dautomatic des_opt lems n = tclTRY (destruct_auto des_opt lems n) let dauto (n,p) lems = @@ -1062,7 +1118,7 @@ let dauto (n,p) lems = let default_dauto = dauto (None,None) [] let h_dauto (n,p) lems = - Refiner.abstract_tactic (TacDAuto (inj_or_var n,p,List.map inj_open lems)) + Refiner.abstract_tactic (TacDAuto (inj_or_var n,p,lems)) (dauto (n,p) lems) (***************************************) @@ -1070,41 +1126,37 @@ let h_dauto (n,p) lems = (***************************************) let make_resolve_any_hyp env sigma (id,_,ty) = - let ents = + let ents = map_succeed - (fun f -> f (mkVar id,ty)) - [make_exact_entry None; make_apply_entry env sigma (true,true,false) None] - in + (fun f -> f (mkVar id,ty)) + [make_exact_entry sigma None; make_apply_entry env sigma (true,true,false) None] + in ents type autoArguments = - | UsingTDB - | Destructing - -let keepAfter tac1 tac2 = - (tclTHEN tac1 - (function g -> tac2 [pf_last_hyp g] g)) + | UsingTDB + | Destructing let compileAutoArg contac = function - | Destructing -> - (function g -> - let ctx = pf_hyps g in - tclFIRST - (List.map - (fun (id,_,typ) -> - let cl = snd (decompose_prod typ) in + | Destructing -> + (function g -> + let ctx = pf_hyps g in + tclFIRST + (List.map + (fun (id,_,typ) -> + let cl = (strip_prod_assum typ) in if Hipattern.is_conjunction cl - then - tclTHENSEQ [simplest_elim (mkVar id); clear [id]; contac] - else + then + tclTHENSEQ [simplest_elim (mkVar id); clear [id]; contac] + else tclFAIL 0 (pr_id id ++ str" is not a conjunction")) ctx) g) - | UsingTDB -> - (tclTHEN - (Tacticals.tryAllClauses - (function - | Some ((_,id),_) -> Dhyp.h_destructHyp false id - | None -> Dhyp.h_destructConcl)) + | UsingTDB -> + (tclTHEN + (Tacticals.tryAllHypsAndConcl + (function + | Some id -> Dhyp.h_destructHyp false id + | None -> Dhyp.h_destructConcl)) contac) let compileAutoArgList contac = List.map (compileAutoArg contac) @@ -1114,20 +1166,20 @@ let rec super_search n db_list local_db argl gl = tclFIRST (assumption :: - tclTHEN intro - (fun g -> + tclTHEN intro + (fun g -> let hintl = pf_apply make_resolve_any_hyp g (pf_last_hyp g) in super_search n db_list (Hint_db.add_list hintl local_db) argl g) :: - List.map (fun ntac -> - tclTHEN ntac + List.map (fun ntac -> + tclTHEN ntac (super_search (n-1) db_list local_db argl)) (possible_resolve false db_list local_db (pf_concl gl)) @ compileAutoArgList (super_search (n-1) db_list local_db argl) argl) gl -let search_superauto n to_add argl g = +let search_superauto n to_add argl g = let sigma = List.fold_right (fun (id,c) -> add_named_decl (id, None, pf_type_of g c)) @@ -1136,14 +1188,12 @@ let search_superauto n to_add argl g = let db = Hint_db.add_list db0 (make_local_hint_db false [] g) in super_search n [Hintdbmap.find "core" !searchtable] db argl g -let superauto n to_add argl = +let superauto n to_add argl = tclTRY (tclCOMPLETE (search_superauto n to_add argl)) -let default_superauto g = superauto !default_search_depth [] [] g - let interp_to_add gl r = - let r = Syntax_def.locate_global_with_alias (qualid_of_reference r) in - let id = id_of_global r in + let r = locate_global_with_alias (qualid_of_reference r) in + let id = basename_of_global r in (next_ident_away id (pf_ids_of_hyps gl), constr_of_global r) let gen_superauto nopt l a b gl = -- cgit v1.2.3