(************************************************************************) (* * The Rocq Prover / The Rocq Development Team *) (* v * Copyright INRIA, CNRS and contributors *) (* <O___,, * (see version control and CREDITS file for authors & dates) *) (* \VV/ **************************************************************) (* // * This file is distributed under the terms of the *) (* * GNU Lesser General Public License Version 2.1 *) (* * (see LICENSE file for the text of the license) *) (************************************************************************)
(* This file is (C) Copyright 2006-2015 Microsoft Corporation and Inria. *)
open Util open Names open Evd open Constr open Context open Termops open Printer open Locusops
open Ltac_plugin open Proofview.Notations open Libnames open Ssrmatching_plugin open Ssrmatching open Ssrast open Ssrprinters
(* The Ltac API does have a type for assumptions but it is level-dependent *) (* and therefore impractical to use for complex arguments, so we substitute *) (* our own to have a uniform representation. Also, we refuse to intern *) (* idents that match global/section constants, since this would lead to *) (* fragile Ltac scripts. *)
let hyp_id (SsrHyp (_, id)) = id
let hyp_err ?loc msg id =
CErrors.user_err ?loc Pp.(str msg ++ Id.print id)
let not_section_id id = not (Termops.is_section_variable (Global.env ()) id)
let hyps_ids = List.map hyp_id
let rec check_hyps_uniq ids = function
| SsrHyp (loc, id) :: _ when List.mem id ids ->
hyp_err ?loc "Duplicate assumption " id
| SsrHyp (_, id) :: hyps -> check_hyps_uniq (id :: ids) hyps
| [] -> ()
let rec pf_check_hyps_uniq ids = function
| SsrHyp (loc, id) :: _ when List.mem id ids ->
Tacticals.tclZEROMSG ?loc Pp.(str "Duplicate assumption " ++ Id.print id)
| SsrHyp (_, id) :: hyps -> pf_check_hyps_uniq (id :: ids) hyps
| [] -> Proofview.tclUNIT ()
let check_hyp_exists hyps (SsrHyp(_, id)) = try ignore(Context.Named.lookup id hyps) with Not_found -> errorstrm Pp.(str"No assumption is named " ++ Id.print id)
let test_hyp_exists hyps (SsrHyp(_, id)) = try ignore(Context.Named.lookup id hyps); true with Not_found -> false
let hoik f = function Hyp x -> f x | Id x -> f x let hoi_id = hoik hyp_id
let mk_hint tac = false, [Some tac] let mk_orhint tacs = true, tacs let nullhint = true, [] let nohint = false, []
open Pp
let errorstrm x = CErrors.user_err x let anomaly s = CErrors.anomaly (str s)
(* Tentative patch from util.ml *)
let array_fold_right_from n f v a = let rec fold n = if n >= Array.length v then a else f v.(n) (fold (succ n)) in
fold n
let array_app_tl v l = if Array.length v = 0 then invalid_arg "array_app_tl";
array_fold_right_from 1 (fun e l -> e::l) v l
let array_list_of_tl v = if Array.length v = 0 then invalid_arg "array_list_of_tl";
array_fold_right_from 1 (fun e l -> e::l) v []
(* end patch *)
let option_assert_get o msg = match o with
| None -> CErrors.anomaly msg
| Some x -> x
(** Constructors for rawconstr *) open Glob_term
let mkRHole = DAst.make @@ GHole (GInternalHole)
let rec mkRHoles n = if n > 0 then mkRHole :: mkRHoles (n - 1) else [] let rec isRHoles cl = match cl with
| [] -> true
| c :: l -> match DAst.get c with GHole _ -> isRHoles l | _ -> false let mkRApp f args = if args = [] then f else DAst.make @@ GApp (f, args) let mkRVar id = DAst.make @@ GRef (GlobRef.VarRef id,None) let mkRltacVar id = DAst.make @@ GVar (id) let mkRCast rc rt = DAst.make @@ GCast (rc, Some DEFAULTcast, rt) let mkRType = DAst.make @@ GSort Glob_ops.glob_Type_sort let mkRProp = DAst.make @@ GSort Glob_ops.glob_Prop_sort let mkRArrow rt1 rt2 = DAst.make @@ GProd (Anonymous, None, Explicit, rt1, rt2) let mkRConstruct c = DAst.make @@ GRef (GlobRef.ConstructRef c,None) let mkRInd mind = DAst.make @@ GRef (GlobRef.IndRef mind,None) let mkRLambda n s t = DAst.make @@ GLambda (n, None, Explicit, s, t)
let rec mkRnat n = if n <= 0 then DAst.make @@ GRef (Rocqlib.lib_ref "num.nat.O", None) else
mkRApp (DAst.make @@ GRef (Rocqlib.lib_ref "num.nat.S", None)) [mkRnat (n - 1)]
let glob_constr ist genv = function
| _, Some ce -> let vars = Id.Map.fold (fun x _ accu -> Id.Set.add x accu) ist.Tacinterp.lfun Id.Set.empty in let ltacvars = {
Constrintern.empty_ltac_sign with Constrintern.ltac_vars = vars } in
Constrintern.intern_gen Pretyping.WithoutTypeConstraint ~ltacvars genv Evd.(from_env genv) ce
| rc, None -> rc
let intern_term ist env (_, c) = glob_constr ist env c
(* Estimate a bound on the number of arguments of a raw constr. *) (* This is not perfect, because the unifier may fail to *) (* typecheck the partial application, so we use a minimum of 5. *) (* Also, we don't handle delayed or iterated coercions to *) (* FUNCLASS, which is probably just as well since these can *) (* lead to infinite arities. *)
let splay_open_constr env (sigma, c) = let t = Retyping.get_type_of env sigma c in
Reductionops.whd_decompose_prod env sigma t
let isAppInd env sigma c = let c = Reductionops.clos_whd_flags RedFlags.all env sigma c in let c, _ = EConstr.decompose_app sigma c in
EConstr.isInd sigma c
let interp_refine env sigma ist ~concl rc = let constrvars = Tacinterp.extract_ltac_constr_values ist env in let vars = { Glob_ops.empty_lvar with
Ltac_pretype.ltac_constrs = constrvars; ltac_genargs = ist.Tacinterp.lfun
} in let kind = Pretyping.OfType concl in let flags = Pretyping.{
use_coercions = true;
use_typeclasses = UseTC;
solve_unification_constraints = true;
fail_evar = false;
expand_evars = true;
program_mode = false;
polymorphic = false;
undeclared_evars_patvars = false;
patvars_abstract = false;
unconstrained_sorts = false;
} in let sigma, c = Pretyping.understand_ltac flags env sigma vars kind rc in (* ppdebug(lazy(str"sigma@interp_refine=" ++ pr_evar_map None sigma)); *)
debug_ssr (fun () -> str"c@interp_refine=" ++ Printer.pr_econstr_env env sigma c);
(sigma, c)
let interp_open_constr env sigma ist gc = let (sigma, (c, _)) = Tacinterp.interp_open_constr_with_bindings ist env sigma (gc, Tactypes.NoBindings) in
(sigma, c)
let interp_term env sigma ist (_, c) = interp_open_constr env sigma ist c
let interp_hyp ist env sigma (SsrHyp (loc, id)) = let id' = Tacinterp.interp_hyp ist env sigma CAst.(make ?loc id) in if not_section_id id' then SsrHyp (loc, id') else
hyp_err ?loc "Can't clear section hypothesis " id'
let interp_hyps ist env sigma ghyps = let hyps = List.map (interp_hyp ist env sigma) ghyps in
check_hyps_uniq [] hyps; hyps
(* Old terms *) let mk_term k c = k, (mkRHole, Some c) let mk_lterm c = mk_term NoFlag c
(* New terms *)
let mk_ast_closure_term a t = {
annotation = a;
body = t;
interp_env = None;
glob_env = None;
}
let glob_ast_closure_term (ist : Genintern.glob_sign) t = let ist = {
ast_ltacvars = ist.Genintern.ltacvars;
ast_intern_sign = ist.Genintern.intern_sign;
ast_extra = ist.Genintern.extra;
} in
{ t with glob_env = Some ist } let subst_ast_closure_term (_s : Mod_subst.substitution) t = (* _s makes sense only for glob constr *)
t let interp_ast_closure_term (ist : Geninterp.interp_sign) env sigma t = (* sigma is only useful if we want to interp *now*, later we have
* a potentially different gl.sigma *)
{ t with interp_env = Some ist }
let ssrterm_of_ast_closure_term { body; annotation } = let c = match annotation with
| `Parens -> InParens
| `At -> WithAt
| _ -> NoFlag in
mk_term c body
let nbargs_open_constr env oc = let pl, _ = splay_open_constr env oc inList.length pl
let pf_nbargs env sigma c = nbargs_open_constr env (sigma, c)
let internal_names = ref [] let add_internal_name pt = internal_names := pt :: !internal_names let is_internal_name s = List.exists (fun p -> p s) !internal_names
let mk_internal_id s = let s' = Printf.sprintf "_%s_" s in let s' = String.map (fun c -> if c = '' then '_' else c) s'in
add_internal_name ((=) s'); Id.of_string s'
let same_prefix s t n = let rec loop i = i = n || s.[i] = t.[i] && loop (i + 1) in loop 0
let skip_digits s = let n = String.length s in let rec loop i = if i < n && is_digit s.[i] then loop (i + 1) else i in loop
let mk_tagged_id t i = Id.of_string (Printf.sprintf "%s%d_" t i) let is_tagged t s = let n = String.length s - 1 and m = String.length t in
m < n && s.[n] = '_' && same_prefix s t m && skip_digits s m = n
let evar_tag = "_evar_" let _ = add_internal_name (is_tagged evar_tag) let mk_evar_name n = Name (mk_tagged_id evar_tag n)
let ssr_anon_hyp = "Hyp"
let wildcard_tag = "_the_" let wildcard_post = "_wildcard_" let has_wildcard_tag s = let n = String.length s inlet m = String.length wildcard_tag in let m' = String.length wildcard_post in
n < m + m' + 2 && same_prefix s wildcard_tag m && String.sub s (n - m') m' = wildcard_post &&
skip_digits s m = n - m' - 2 let _ = add_internal_name has_wildcard_tag
let discharged_tag = "_discharged_" let mk_discharged_id id =
Id.of_string (Printf.sprintf "%s%s_" discharged_tag (Id.to_string id)) let has_discharged_tag s = let m = String.length discharged_tag and n = String.length s - 1 in
m < n && s.[n] = '_' && same_prefix s discharged_tag m let _ = add_internal_name has_discharged_tag let is_discharged_id id = has_discharged_tag (Id.to_string id)
let max_suffix m (t, j0 as tj0) id = let s = Id.to_string id inlet n = String.length s - 1 in let dn = String.length t - 1 - n inlet i0 = j0 - dn in ifnot (i0 >= m && s.[n] = '_' && same_prefix s t m) then tj0 else let rec loop i = if i < i0 && s.[i] = '0'then loop (i + 1) else if (if i < i0 then skip_digits s i = n else le_s_t i) then s, i else tj0 and le_s_t i = let ds = s.[i] and dt = t.[i + dn] in if ds = dt then i = n || le_s_t (i + 1) else
dt < ds && skip_digits s i = n in
loop m
(** creates a fresh (w.r.t. `gl_ids` and internal names) inaccessible name of the form _tXX_ *) let mk_anon_id t gl_ids = let m, si0, id0 = let s = ref (Printf.sprintf "_%s_" t) in if is_internal_name !s then s := "_" ^ !s; let n = String.length !s - 1 in let rec loop i j = let d = !s.[i] inifnot (is_digit d) then i + 1, j else
loop (i - 1) (if d = '0'then j else i) in let m, j = loop (n - 1) n in m, (!s, j), Id.of_string_soft !s in ifnot (List.mem id0 gl_ids) then id0 else let s, i = List.fold_left (max_suffix m) si0 gl_ids in letopen Bytes in let s = of_string s in let n = length s - 1 in let rec loop i = if get s i = '9'then (set s i '0'; loop (i - 1)) else if i < m then (set s n '0'; set s m '1'; cat s (of_string "_")) else
(set s i (Char.chr (Char.code (get s i) + 1)); s) in
Id.of_string_soft (Bytes.to_string (loop (n - 1)))
let convert_concl_no_check t = Tactics.convert_concl ~cast:false ~check:false t DEFAULTcast let convert_concl ~check t = Tactics.convert_concl ~cast:false ~check t DEFAULTcast
(* Reduction that preserves the Prod/Let spine of the "in" tactical. *)
let inc_safe n = if n = 0 then n else n + 1 let rec safe_depth s c = match EConstr.kind s c with
| LetIn ({binder_name=Name x}, _, _, c') when is_discharged_id x -> safe_depth s c' + 1
| LetIn (_, _, _, c') | Prod (_, _, c') -> inc_safe (safe_depth s c')
| _ -> 0
let red_safe (r : Reductionops.reduction_function) e s c0 = let rec red_to e c n = match EConstr.kind s c with
| Prod (x, t, c') when n > 0 -> let t' = r e s t in let e' = EConstr.push_rel (RelDecl.LocalAssum (x, t')) e in
EConstr.mkProd (x, t', red_to e' c' (n - 1))
| LetIn (x, b, t, c') when n > 0 -> let t' = r e s t in let e' = EConstr.push_rel (RelDecl.LocalAssum (x, t')) e in
EConstr.mkLetIn (x, r e s b, t', red_to e' c' (n - 1))
| _ -> r e s c in
red_to e c0 (safe_depth s c0)
let is_id_constr sigma c = match EConstr.kind sigma c with
| Lambda(_,_,c) when EConstr.isRel sigma c -> 1 = EConstr.destRel sigma c
| _ -> false
let red_product_skip_id env sigma c = match EConstr.kind sigma c with
| App(hd,args) when Array.length args = 1 && is_id_constr sigma hd -> args.(0)
| _ -> match Tacred.red_product env sigma c with Some c -> c | None -> c
let ssrevaltac ist gtac = Tacinterp.tactic_of_value ist gtac
(** Open term to lambda-term coercion *)(* {{{ ************************************)
(* This operation takes a goal gl and an open term (sigma, t), and *) (* returns a term t' where all the new evars in sigma are abstracted *) (* with the mkAbs argument, i.e., for mkAbs = mkLambda then there is *) (* some duplicate-free array args of evars of sigma such that the *) (* term mkApp (t', args) is convertible to t. *) (* This makes a useful shorthand for local definitions in proofs, *) (* i.e., pose succ := _ + 1 means pose succ := fun n : nat => n + 1, *) (* and, in context of the 4CT library, pose mid := maps id means *) (* pose mid := fun d : detaSet => @maps d d (@id (datum d)) *) (* Note that this facility does not extend to set, which tries *) (* instead to fill holes by matching a goal subterm. *) (* The argument to "have" et al. uses product abstraction, e.g. *) (* have Hmid: forall s, (maps id s) = s. *) (* stands for *) (* have Hmid: forall (d : dataSet) (s : seq d), (maps id s) = s. *) (* We also use this feature for rewrite rules, so that, e.g., *) (* rewrite: (plus_assoc _ 3). *) (* will execute as *) (* rewrite (fun n => plus_assoc n 3) *) (* i.e., it will rewrite some subterm .. + (3 + ..) to .. + 3 + ... *) (* The convention is also used for the argument of the congr tactic, *) (* e.g., congr (x + _ * 1). *)
(* Replace new evars with lambda variables, retaining local dependencies *) (* but stripping global ones. We use the variable names to encode the *) (* the number of dependencies, so that the transformation is reversible. *)
let env_size env = List.length (Environ.named_context env)
let resolve_typeclasses env sigma ~where ~fail = letfilter = let evset = Evarutil.undefined_evars_of_term sigma where in fun k _ -> Evar.Set.mem k evset in
Typeclasses.resolve_typeclasses ~filter ~fail env sigma
let abs_evars env sigma0 ?(rigid = []) (sigma, c0) = let c0 = Evarutil.nf_evar sigma c0 in let sigma0, ucst = sigma0, Evd.ustate sigma in let nenv = env_size env in let abs_evar n k = letopen EConstr in let evi = Evd.find_undefined sigma k in let concl = Evd.evar_concl evi in let dc = CList.firstn n (evar_filtered_context evi) in let abs_dc c = function
| NamedDecl.LocalDef (x,b,t) -> mkNamedLetIn sigma x b t (mkArrow t x.binder_relevance c)
| NamedDecl.LocalAssum (x,t) -> mkNamedProd sigma x t c in let t = Context.Named.fold_inside abs_dc ~init:concl dc in
Evarutil.nf_evar sigma t in let rec put evlist c = match EConstr.kind sigma c with
| Evar (k, a) -> ifList.mem_assoc k evlist || Evd.mem sigma0 k || List.mem k rigid then evlist else let n = max 0 (SList.length a - nenv) in let t = abs_evar n k in (k, (n, t)) :: put evlist t
| _ -> EConstr.fold sigma put evlist c in let evlist = put [] c0 in ifList.is_empty evlist then
c0, [], ucst else letopen EConstr in let rec lookup k i = function
| [] -> 0, 0
| (k', (n, _)) :: evl -> if k = k'then i, n else lookup k (i + 1) evl in let rec get i c = match EConstr.kind sigma c with
| Evar (ev, a) -> let j, n = lookup ev i evlist in if j = 0 then EConstr.map sigma (get i) c elseif n = 0 then mkRel j else let a = Array.of_list @@ Evd.expand_existential sigma (ev, a) in
mkApp (mkRel j, Array.init n (fun k -> get i a.(n - 1 - k)))
| _ -> EConstr.map_with_binders sigma ((+) 1) get i c in let rec loop c i = function
| (_, (n, t)) :: evl ->
loop (mkLambda (make_annot (mk_evar_name n) ERelevance.relevant, get (i - 1) t, c)) (i - 1) evl
| [] -> c in
loop (get 1 c0) 1 evlist, List.map fst evlist, ucst
(* As before but if (?i : T(?j)) and (?j : P : Prop), then the lambda for i * looks like (fun evar_i : (forall pi : P. T(pi))) thanks to "loopP" and all * occurrences of evar_i are replaced by (evar_i evar_j) thanks to "app". * * If P can be solved by ssrautoprop (that defaults to trivial), then * the corresponding lambda looks like (fun evar_i : T(c)) where c is * the solution found by ssrautoprop.
*) let ssrautoprop_tac = ref (Proofview.Goal.enter (fun gl -> assert false))
(* Thanks to Arnaud Spiwack for this snippet *) let call_on_evar env sigma tac e = try let tac = Proofview.Unsafe.tclSETGOALS [Proofview.with_empty_state e] <*> tac in let _, init = Proofview.init sigma [] in let name = Names.Id.of_string "legacy_pe"in let (_, final, _, _, _) = Proofview.apply ~name ~poly:false env tac init in let (gs, final) = Proofview.proofview final in let () = if (gs <> []) then errorstrm (str "Should we tell the user?") in
final with Logic_monad.TacticFailure e as src -> let (_, info) = Exninfo.capture src in
Exninfo.iraise (e, info)
open Pp
module Intset = Evar.Set
let abs_evars_pirrel env sigma0 (sigma, c0) = let c0 = Evarutil.nf_evar sigma c0 in let nenv = env_size env in let abs_evar n k = letopen EConstr in let evi = Evd.find_undefined sigma k in let concl = Evd.evar_concl evi in let dc = CList.firstn n (evar_filtered_context evi) in let abs_dc c = function
| NamedDecl.LocalDef (x,b,t) -> mkNamedLetIn sigma x b t (mkArrow t x.binder_relevance c)
| NamedDecl.LocalAssum (x,t) -> mkNamedProd sigma x t c in let t = Context.Named.fold_inside abs_dc ~init:concl dc in
Evarutil.nf_evar sigma t in let rec put evlist c = match EConstr.kind sigma c with
| Evar (k, a) -> ifList.mem_assoc k evlist || Evd.mem sigma0 k then evlist else let n = max 0 (SList.length a - nenv) in (* FIXME? this is not the right environment in general *) let k_ty = Retyping.get_sort_quality_of env sigma (Evd.evar_concl (Evd.find_undefined sigma k)) in let is_prop = UnivGen.QualityOrSet.is_prop k_ty in let t = abs_evar n k in
(k, (n, t, is_prop)) :: put evlist t
| _ -> EConstr.fold sigma put evlist c in let evlist = put [] c0 in if evlist = [] then 0, c0 else let evplist = let depev = List.fold_left (fun evs (_,(_,t,_)) ->
Intset.union evs (Evarutil.undefined_evars_of_term sigma t)) Intset.empty evlist in List.filter (fun (i,(_,_,b)) -> b && Intset.mem i depev) evlist in let evlist, evplist, sigma = if evplist = [] then evlist, [], sigma else List.fold_left (fun (ev, evp, sigma) (i, (_,t,_) as p) -> try let sigma = call_on_evar env sigma !ssrautoprop_tac i in List.filter (fun (j,_) -> j <> i) ev, evp, sigma with e when CErrors.noncritical e -> ev, p::evp, sigma) (evlist, [], sigma) (List.rev evplist) in let c0 = Evarutil.nf_evar sigma c0 in let nf (k, (n, t, p)) = (k, (n, Evarutil.nf_evar sigma t, p)) in let evlist = List.map nf evlist in let evplist = List.map nf evplist in let rec lookup k i = function
| [] -> 0, 0
| (k', (n,_,_)) :: evl -> if k = k'then i,n else lookup k (i + 1) evl in letopen EConstr in let rec get evlist i c = match EConstr.kind sigma c with
| Evar (ev, a) -> let j, n = lookup ev i evlist in if j = 0 then EConstr.map sigma (get evlist i) c elseif n = 0 then mkRel j else let a = Array.of_list @@ Evd.expand_existential sigma (ev, a) in
mkApp (mkRel j, Array.init n (fun k -> get evlist i a.(n - 1 - k)))
| _ -> EConstr.map_with_binders sigma ((+) 1) (get evlist) i c in let rec app extra_args i c = match decompose_app_list sigma c with
| hd, args when isRel sigma hd && destRel sigma hd = i -> let j = destRel sigma hd in
mkApp (mkRel j, Array.of_list (List.map (Vars.lift (i-1)) extra_args @ args))
| _ -> EConstr.map_with_binders sigma ((+) 1) (app extra_args) i c in let rec loopP evlist accu i = function
| [] -> List.rev accu
| (_, (n, t, _)) :: evl -> let t = get evlist (i - 1) t in let n = Name (Id.of_string (ssr_anon_hyp ^ string_of_int n)) in
loopP evlist (RelDecl.LocalAssum (make_annot n ERelevance.relevant, t) :: accu) (i - 1) evl in let rec loop c i = function
| (_, (n, t, _)) :: evl -> let evs = Evarutil.undefined_evars_of_term sigma t in let t_evplist = List.filter (fun (k,_) -> Intset.mem k evs) evplist in let ctx_t = loopP t_evplist [] 1 t_evplist in let t = EConstr.it_mkProd_or_LetIn (get t_evplist 1 t) ctx_t in let t = get evlist (i - 1) t in let extra_args = List.rev_map (fun (k,_) -> mkRel (fst (lookup k i evlist))) t_evplist in let c = if extra_args = [] then c elseapp extra_args 1 c in
loop (mkLambda (make_annot (mk_evar_name n) ERelevance.relevant, t, c)) (i - 1) evl
| [] -> c in let res = loop (get evlist 1 c0) 1 evlist in List.length evlist, res
(* Strip all non-essential dependencies from an abstracted term, generating *) (* standard names for the abstracted holes. *)
let nb_evar_deps = function
| Name id -> let s = Id.to_string id in ifnot (is_tagged evar_tag s) then 0 else let m = String.length evar_tag in
(try int_of_string (String.sub s m (String.length s - 1 - m)) with e when CErrors.noncritical e -> 0)
| _ -> 0
let type_id env sigma t = Id.of_string (Namegen.hdchar env sigma t) let pfe_type_relevance_of env sigma t = let sigma, ty = Typing.type_of env sigma t in
sigma, ty, Retyping.relevance_of_term env sigma t
let abs_cterm env sigma n c0 = letopen EConstr in if n <= 0 then c0 else let noargs = [|0|] in let eva = Array.make n noargs in let rec strip i c = match EConstr.kind sigma c with
| App (f, a) when isRel sigma f -> let j = i - destRel sigma f in if j >= n || eva.(j) = noargs then mkApp (f, Array.map (strip i) a) else let dp = eva.(j) in let nd = Array.length dp - 1 in let mkarg k = strip i a.(if k < nd then dp.(k + 1) - j else k + dp.(0)) in
mkApp (f, Array.init (Array.length a - dp.(0)) mkarg)
| _ -> EConstr.map_with_binders sigma ((+) 1) strip i c in let rec strip_ndeps j i c = match EConstr.kind sigma c with
| Prod (x, t, c1) when i < j -> let dl, c2 = strip_ndeps j (i + 1) c1 in if Vars.noccurn sigma 1 c2 then dl, Vars.lift (-1) c2 else
i :: dl, mkProd (x, strip i t, c2)
| LetIn (x, b, t, c1) when i < j -> let _, _, c1' = destProd sigma c1 in let dl, c2 = strip_ndeps j (i + 1) c1' in if Vars.noccurn sigma 1 c2 then dl, Vars.lift (-1) c2 else
i :: dl, mkLetIn (x, strip i b, strip i t, c2)
| _ -> [], strip i c in let rec strip_evars i c = match EConstr.kind sigma c with
| Lambda (x, t1, c1) when i < n -> let na = nb_evar_deps x.binder_name in let dl, t2 = strip_ndeps (i + na) i t1 in let na' = List.length dl in
eva.(i) <- Array.of_list (na - na' :: dl); let x' = if na' = 0 then Name (type_id env sigma t2) else mk_evar_name na'in
mkLambda ({x with binder_name=x'}, t2, strip_evars (i + 1) c1) (* if noccurn 1 c2 then lift (-1) c2 else
mkLambda (Name (pf_type_id gl t2), t2, c2) *)
| _ -> strip i c in
strip_evars 0 c0
(* }}} *)
let rec constr_name sigma c = match EConstr.kind sigma c with
| Var id -> Name id
| Cast (c', _, _) -> constr_name sigma c'
| Const (cn,_) -> Name (Label.to_id (Constant.label cn))
| App (c', _) -> constr_name sigma c'
| _ -> Anonymous
let pf_mkprod env sigma c ?(name=constr_name sigma c) cl = let sigma, t, r = pfe_type_relevance_of env sigma c in if name <> Anonymous || EConstr.Vars.noccurn sigma 1 cl then
sigma, EConstr.mkProd (make_annot name r, t, cl) else
sigma, EConstr.mkProd (make_annot (Name (type_id env sigma t)) r, t, cl)
(** look up a name in the ssreflect internals module *) let ssrdirpath = DirPath.make [Id.of_string "ssreflect"] let ssrqid name = Libnames.make_qualid ssrdirpath (Id.of_string name) let mkSsrRef name = let qn = Format.sprintf "plugins.ssreflect.%s" name in if Rocqlib.has_ref qn then Rocqlib.lib_ref qn else
CErrors.user_err Pp.(str "Small scale reflection library not loaded (" ++ str name ++ str ")") let mkSsrRRef name = (DAst.make @@ GRef (mkSsrRef name,None)), None let mkSsrConst env sigma name =
EConstr.fresh_global env sigma (mkSsrRef name)
let mkProt env sigma t c = let sigma, prot = mkSsrConst env sigma "protect_term"in
sigma, EConstr.mkApp (prot, [|t; c|])
let mkEtaApp c n imin = letopen EConstr in if n = 0 then c else let nargs, mkarg = if n < 0 then -n, (fun i -> mkRel (imin + i)) else let imax = imin + n - 1 in n, (fun i -> mkRel (imax - i)) in
mkApp (c, Array.init nargs mkarg)
let mkRefl env sigma t c = let (sigma, refl) = EConstr.fresh_global env sigma Rocqlib.(lib_ref "core.eq.refl") in
sigma, EConstr.mkApp (refl, [|t; c|])
let discharge_hyp (id', (id, mode)) = letopen EConstr in letopen Tacmach in
Proofview.Goal.enter beginfun gl -> let sigma = Proofview.Goal.sigma gl in let cl' = Vars.subst_var sigma id (Tacmach.pf_concl gl) in let decl = pf_get_hyp id gl in match decl, mode with
| NamedDecl.LocalAssum _, _ | NamedDecl.LocalDef _, "(" -> let id' = {(NamedDecl.get_annot decl) with binder_name = Name id'} in
Tactics.apply_type ~typecheck:true
(mkProd (id', NamedDecl.get_type decl, cl')) [mkVar id]
| NamedDecl.LocalDef (_, v, t), _ -> let id' = {(NamedDecl.get_annot decl) with binder_name = Name id'} in
convert_concl ~check:true (mkLetIn (id', v, t, cl')) end
let view_error s gv =
Tacticals.tclZEROMSG (str ("Cannot " ^ s ^ " view ") ++ pr_term gv)
open Locus (****************************** tactics ***********************************)
let rewritetac ?(under=false) dir c = (* Due to the new optional arg ?tac, application shouldn't be too partial *) letopen Proofview.Notations in
Proofview.Goal.enter beginfun _ ->
Equality.general_rewrite ~where:None ~l2r:(dir = L2R) AllOccurrences ~freeze:true ~dep:false ~with_evars:false (c, Tactypes.NoBindings) <*> if under then Proofview.cycle 1 else Proofview.tclUNIT () end
type name_hint = (int * EConstr.types array) optionref
let abs_ssrterm ?(resolve_typeclasses=false) ist env sigma t = let sigma0 = sigma in let sigma, ct = interp_term env sigma ist t in let t = ifnot resolve_typeclasses then (sigma, ct) else let sigma = Typeclasses.resolve_typeclasses ~fail:false env sigma in
sigma, Evarutil.nf_evar sigma ct in let c, abstracted_away, ucst = abs_evars env sigma0 t in let n = List.length abstracted_away in let sigma = Evd.merge_universe_context sigma0 ucst in let t = abs_cterm env sigma n c in
sigma, t, n
let top_id = mk_internal_id "top assumption"
let ssr_n_tac seed n =
Proofview.Goal.enter beginfun gl -> let name = if n = -1 then seed else ("ssr" ^ seed ^ string_of_int n) in let fail msg = CErrors.user_err (Pp.str msg) in let tacname = try Tacenv.locate_tactic (Libnames.qualid_of_ident (Id.of_string name)) with Not_found -> try Tacenv.locate_tactic (ssrqid name) with Not_found -> if n = -1 then fail "The ssreflect library was not loaded" else fail ("The tactic "^name^" was not found") in let tacexpr = Tacexpr.Reference (ArgArg (Loc.tag @@ tacname)) in
Tacinterp.eval_tactic @@ CAst.make (Tacexpr.TacArg tacexpr) end
let donetac n = ssr_n_tac "done" n
open Constrexpr open Util
(** Constructors for constr_expr *) let mkCProp loc = CAst.make ?loc @@ CSort Constrexpr_ops.expr_Prop_sort let mkCType loc = CAst.make ?loc @@ CSort Constrexpr_ops.expr_Type_sort let mkCVar ?loc id = CAst.make ?loc @@ CRef (qualid_of_ident ?loc id, None) let rec mkCHoles ?loc n = if n <= 0 then [] else (CAst.make ?loc @@ CHole (None)) :: mkCHoles ?loc (n - 1) let mkCHole loc = CAst.make ?loc @@ CHole (None) let mkCLambda ?loc name ty t = CAst.make ?loc @@
CLambdaN ([CLocalAssum([CAst.make ?loc name], None, Default Explicit, ty)], t) let mkCArrow ?loc ty t = CAst.make ?loc @@
CProdN ([CLocalAssum([CAst.make Anonymous], None, Default Explicit, ty)], t) let mkCCast ?loc t ty = CAst.make ?loc @@ CCast (t, Some DEFAULTcast, ty)
let pf_interp_ty ?(resolve_typeclasses=false) env sigma0 ist ty = let n_binders = ref 0 in let ty = match ty with
| a, (t, None) -> let rec force_type ty = DAst.(map (function
| GProd (x, r, k, s, t) -> incr n_binders; GProd (x, r, k, s, force_type t)
| GLetIn (x, r, v, oty, t) -> incr n_binders; GLetIn (x, r, v, oty, force_type t)
| _ -> DAst.get (mkRCast ty mkRType))) ty in
a, (force_type t, None)
| _, (_, Some ty) -> let rec force_type ty = CAst.(map (function
| CProdN (abs, t) ->
n_binders := !n_binders + List.length (List.flatten (List.map (function CLocalAssum (nal,_,_,_) -> nal | CLocalDef (na,_,_,_) -> [na] | CLocalPattern _ -> (* We count a 'pat for 1; TO BE CHECKED *) [CAst.make Name.Anonymous]) abs));
CProdN (abs, force_type t)
| CLetIn (n, v, oty, t) -> incr n_binders; CLetIn (n, v, oty, force_type t)
| _ -> (mkCCast ty (mkCType None)).v)) ty in
mk_term NoFlag (force_type ty) in let strip_cast (sigma, t) = letopen EConstr in let rec aux t = match kind_of_type sigma t with
| CastType (t, ty) when !n_binders = 0 && isSort sigma ty -> t
| ProdType(n,s,t) -> decr n_binders; mkProd (n, s, aux t)
| LetInType(n,v,ty,t) -> decr n_binders; mkLetIn (n, v, ty, aux t)
| _ -> anomaly "pf_interp_ty: ssr Type cast deleted by typecheck"in
sigma, aux t in let sigma, cty as ty = strip_cast (interp_term env sigma0 ist ty) in let ty = ifnot resolve_typeclasses then ty else let sigma = Typeclasses.resolve_typeclasses ~fail:false env sigma in
sigma, Evarutil.nf_evar sigma cty in let c, evs, ucst = abs_evars env sigma0 ty in let n = List.length evs in let sigma0 = Evd.merge_universe_context sigma0 ucst in let lam_c = abs_cterm env sigma0 n c in let ctx, c = EConstr.decompose_lambda_n_assum sigma n lam_c in
sigma0, n, EConstr.it_mkProd_or_LetIn c ctx, lam_c
(* TASSI: given (c : ty), generates (c ??? : ty[???/...]) with m evars *)
exception NotEnoughProducts let saturate ?(beta=false) ?(bi_types=false) env sigma c ?ty m = let sigma, ty = match ty with
| Some ty -> sigma, ty
| None -> if Termops.is_template_polymorphic_ref env sigma c then beginmatch EConstr.kind sigma c with
| Ind (ind,_) -> let sigma, univs = Typing.get_template_parameters env sigma ind ~refresh_all:true [||] in let specif = Inductive.lookup_mind_specif env ind in let typ, csts = Inductive.type_of_inductive_knowing_parameters
(specif,UVars.Instance.empty)
univs in let sigma = Evd.add_constraints sigma csts in
sigma, EConstr.of_constr typ
| Construct ((ind,_ as ctor),_) -> let sigma, univs = Typing.get_template_parameters env sigma ind ~refresh_all:true [||] in let specif = Inductive.lookup_mind_specif env ind in let typ, csts = Inductive.type_of_constructor_knowing_parameters
(ctor,UVars.Instance.empty)
specif
univs in let sigma = Evd.add_constraints sigma csts in
sigma, EConstr.of_constr typ
| _ -> assert false end else sigma, Retyping.get_type_of env sigma c in let rec loop ty args sigma n = letopen EConstr in if n = 0 then let args = List.rev args in
(if beta then Reductionops.whd_beta env sigma elsefun x -> x)
(EConstr.mkApp (c, Array.of_list (List.map pi2 args))), ty, args, sigma elsematch kind_of_type sigma ty with
| ProdType (_, src, tgt) -> let sigma = create_evar_defs sigma in let argty = if bi_types then Reductionops.nf_betaiota env sigma src else src in let typeclass_candidate = Typeclasses.is_maybe_class_type sigma argty in let (sigma, x) = Evarutil.new_evar ~typeclass_candidate env sigma argty in
loop (EConstr.Vars.subst1 x tgt) ((m - n,x,argty) :: args) sigma (n-1)
| CastType (t, _) -> loop t args sigma n
| LetInType (_, v, _, t) -> loop (EConstr.Vars.subst1 v t) args sigma n
| SortType _ -> raise NotEnoughProducts
| AtomicType _ -> let ty = (* FIXME *)
(Reductionops.whd_all env sigma) ty in match kind_of_type sigma ty with
| ProdType _ -> loop ty args sigma n
| _ -> raise NotEnoughProducts in
loop ty [] sigma m
let dependent_apply_error =
CErrors.UserError (Pp.str "Could not fill dependent hole in \"apply\"")
(* TASSI: Sometimes Rocq's apply fails. According to my experience it may be * related to goals that are products and with beta redexes. In that case it * guesses the wrong number of implicit arguments for your lemma. What follows * is just like apply, but with a user-provided number n of implicits. * * Refine.refine function that handles type classes and evars but fails to * handle "dependently typed higher order evars". * * Assumes that the type of [t] is a telescope with [n] leading product types. * This is always the case as the only caller of applyn uses it with the output * of abs_evars_pirrel which starts with n lambdas generated by abstraction. * It also assumes [t] starts with [n] Lambda nodes. * * Refiner.refiner that does not handle metas with a non ground type but works
* with dependently typed higher order metas. *) let applyn ?(beta=false) ~with_evars ?(first_goes_last=false) n t =
Proofview.Goal.enter beginfun gl -> let sigma = Proofview.Goal.sigma gl in let env = Proofview.Goal.env gl in let concl = Proofview.Goal.concl gl in if with_evars then let refine =
Refine.refine ~typecheck:falsebeginfun sigma -> let rec saturate c args sigma n = if n = 0 then args, sigma elsematch EConstr.kind sigma c with
| Lambda (_, argty, c) -> let argty = Reductionops.nf_betaiota env sigma (EConstr.Vars.substl args argty) in let typeclass_candidate = Typeclasses.is_maybe_class_type sigma argty in let (sigma, x) = Evarutil.new_evar ~typeclass_candidate env sigma argty in
saturate c (x :: args) sigma (n-1)
| _ -> assert false in let _, ty = EConstr.decompose_prod_n_decls sigma n (Retyping.get_type_of env sigma t) in let args, sigma = saturate t [] sigma n in let ty = EConstr.Vars.substl args ty in let args = Array.rev_of_list args in let t = EConstr.mkApp (t, args) in let t = if beta then Reductionops.whd_beta env sigma t else t in let sigma = unify_HO env sigma ty concl in (* Set our own set of goals. In theory saturate generates them in the right order, so we could just return sigma directly, but explicit is
better than implicit. *) let sigma = Evd.push_future_goals (snd @@ Evd.pop_future_goals sigma) in let fold sigma e = match EConstr.kind sigma e with
| Evar (evk, _) -> Evd.declare_future_goal evk sigma
| _ -> sigma in let sigma = Array.fold_left fold sigma args in
(sigma, t) end in
Tacticals.tclTHENLIST [
refine;
Proofview.shelve_unifiable;
Proofview.(if first_goes_last then cycle 1 else tclUNIT ())
] else let sigma = Evd.push_future_goals sigma in let hyps = Environ.named_context_val env in let inst = EConstr.identity_subst_val hyps in let t, args, sigma = let rec loop sigma bo args = function (* saturate with metas *)
| 0 -> (t, args, sigma)
| n -> match EConstr.kind sigma bo with
| Lambda (na, ty, bo) -> let () = ifnot (EConstr.Vars.closed0 sigma ty) thenraise dependent_apply_error in let ty = Reductionops.nf_betaiota env sigma ty in let relevance = na.binder_relevance in let (sigma, evk) = Evarutil.new_pure_evar ~typeclass_candidate:false hyps sigma ~relevance ty in
loop sigma bo (evk :: args) (n - 1)
| _ -> assert false in
loop sigma t [] n in let _, sigma = Evd.pop_future_goals sigma in
letmap evk = Proofview.goal_with_state evk (Proofview.Goal.state gl) in let sgl = List.rev_map map args in let ans = EConstr.applist (t, List.rev_map (fun evk -> EConstr.mkEvar (evk, inst)) args) in let evk = Proofview.Goal.goal gl in let _ = ifnot (Evarutil.occur_evar_upto sigma evk ans) then () else Pretype_errors.error_occur_check env sigma evk ans in let sigma = Evd.define evk ans sigma in
Tacticals.tclTHENLIST [
Proofview.Unsafe.tclEVARS sigma;
Proofview.Unsafe.tclSETGOALS sgl;
Proofview.(if first_goes_last then cycle 1 else tclUNIT ())
] end
let refine_with ?(first_goes_last=false) ?beta ?(with_evars=true) oc = letopen Proofview.Notations in
Proofview.Goal.enter beginfun gl -> let env = Proofview.Goal.env gl in let sigma = Proofview.Goal.sigma gl in let uct = Evd.ustate (fst oc) in let n, oc = abs_evars_pirrel env sigma oc in
Proofview.Unsafe.tclEVARS (Evd.set_universe_context sigma uct) <*>
Proofview.tclORELSE (applyn ~with_evars ~first_goes_last ?beta n oc)
(fun _ -> Proofview.tclZERO dependent_apply_error) end
(** Basic tactics *)
let rec fst_prod red tac = Proofview.Goal.enter beginfun gl -> let concl = Proofview.Goal.concl gl in match EConstr.kind (Proofview.Goal.sigma gl) concl with
| Prod (id,_,tgt) | LetIn(id,_,_,tgt) -> tac id.binder_name
| _ -> if red then Tacticals.tclZEROMSG (str"No product even after head-reduction.") else Tacticals.tclTHEN Tactics.hnf_in_concl (fst_prod true tac) end
let introid ?(orig=ref Anonymous) name = letopen Proofview.Notations in
Proofview.Goal.enter beginfun gl -> let env = Proofview.Goal.env gl in let sigma = Proofview.Goal.sigma gl in let g = Proofview.Goal.concl gl in match EConstr.kind sigma g with
| App (hd, _) when EConstr.isLambda sigma hd ->
convert_concl_no_check (Reductionops.whd_beta env sigma g)
| _ -> Tacticals.tclIDTAC end <*>
(fst_prod false (fun id -> orig := id; Tactics.intro_mustbe_force name))
let anontac decl =
Proofview.Goal.enter beginfun gl -> let id = match RelDecl.get_name decl with
| Name id -> if is_discharged_id id then id else mk_anon_id (Id.to_string id) (Tacmach.pf_ids_of_hyps gl)
| _ -> mk_anon_id ssr_anon_hyp (Tacmach.pf_ids_of_hyps gl) in
introid id end
let rec intro_anon () = letopen Tacmach in letopen Proofview.Notations in
Proofview.Goal.enter beginfun gl -> let d = List.hd (fst (EConstr.decompose_prod_n_decls (project gl) 1 (pf_concl gl))) in
Proofview.tclORELSE (anontac d)
(fun (err0, info) -> Proofview.tclORELSE
(Tactics.red_in_concl <*> intro_anon ()) (fun _ -> Proofview.tclZERO ~info err0)) end
let intro_anon = intro_anon ()
let is_pf_var sigma c =
EConstr.isVar sigma c && not_section_id (EConstr.destVar sigma c)
let hyp_of_var sigma v = SsrHyp (Loc.tag @@ EConstr.destVar sigma v)
let interp_clr sigma = function
| Some clr, (k, c)
when (k = NoFlag || k = WithAt) && is_pf_var sigma c ->
hyp_of_var sigma c :: clr
| Some clr, _ -> clr
| None, _ -> []
let tclDOTRY n tac = letopen Tacticals in if n <= 0 then tclIDTAC else let rec loop i = if i = n then tclTRY tac else
tclTRY (tclTHEN tac (loop (i + 1))) in
loop 1
let tclDO n tac = let prefix i = str"At iteration " ++ int i ++ str": "in let tac_err_at i =
Proofview.Goal.enter beginfun gl ->
Proofview.tclORELSE tac begin function
| (CErrors.UserError s, info) -> let e' = CErrors.UserError (prefix i ++ s) in
Proofview.tclZERO ~info e'
| (e, info) -> Proofview.tclZERO ~info e end end in let rec loop i =
Proofview.Goal.enter beginfun gl -> if i = n then tac_err_at i else
Tacticals.tclTHEN (tac_err_at i) (loop (i + 1)) endin
loop 1
let tclAT_LEAST_ONCE t = letopen Tacticals in
tclTHEN t (tclREPEAT t)
let tclMULT = function
| 0, May -> Tacticals.tclREPEAT
| 1, May -> Tacticals.tclTRY
| n, May -> tclDOTRY n
| 0, Must -> tclAT_LEAST_ONCE
| n, Must when n > 1 -> tclDO n
| _ -> tclID
let get_hyp env sigma id = try EConstr.of_named_decl (Environ.lookup_named id env) with Not_found -> raise (Logic.RefinerError (env, sigma, Logic.NoSuchHyp id))
(** Generalize tactic *)
(* XXX the k of the redex should percolate out *) let pf_interp_gen_aux env sigma ~concl to_ind ((oclr, occ), t) = let pat = interp_cpattern env sigma t None in(* UGLY API *) let sigma = Evd.merge_universe_context sigma (Evd.ustate @@ pat.pat_sigma) in let sigma, c, cl = fill_rel_occ_pattern env sigma concl pat occ in let clr = interp_clr sigma (oclr, (tag_of_cpattern t, c)) in ifnot(occur_existential sigma c) then if tag_of_cpattern t = WithAt then ifnot (EConstr.isVar sigma c) then
errorstrm (str "@ can be used with variables only") elsematch get_hyp env sigma (EConstr.destVar sigma c) with
| NamedDecl.LocalAssum _ -> errorstrm (str "@ can be used with let-ins only")
| NamedDecl.LocalDef (name, b, ty) -> true, pat, EConstr.mkLetIn (map_annot Name.mk_name name,b,ty,cl),c,clr, sigma elselet sigma, ccl = pf_mkprod env sigma c cl infalse, pat, ccl, c, clr, sigma elseif to_ind && occ = None then let p, evs, ucst' = abs_evars env sigma (pat.pat_sigma, c) in let sigma = Evd.merge_universe_context sigma ucst' in ifList.is_empty evs then anomaly "occur_existential but no evars"else let sigma, pty, rp = pfe_type_relevance_of env sigma p in false, pat, EConstr.mkProd (make_annot (constr_name sigma c) rp, pty, concl), p, clr, sigma else CErrors.user_err ?loc:(loc_of_cpattern t) (str "generalized term didn't match")
let genclrtac cl cs clr = letopen Proofview.Notations in (* apply_type may give a type error, but the useful message is * the one of clear. You type "move: x" and you get * "x is used in hyp H" instead of
* "The term H has type T x but is expected to have type T x0". *)
(Proofview.tclORELSE
(Tactics.apply_type ~typecheck:true cl cs)
(fun (type_err, info) ->
(Tactics.exfalso) <*>
(cleartac clr) <*>
(Proofview.tclZERO ~info type_err)))
<*>
(cleartac clr)
let gentac gen =
Proofview.Goal.enter beginfun gl -> let env = Proofview.Goal.env gl in let sigma = Proofview.Goal.sigma gl in let concl = Proofview.Goal.concl gl in (* ppdebug(lazy(str"sigma@gentac=" ++ pr_evar_map None (project gl))); *) let conv, _, cl, c, clr, sigma = pf_interp_gen_aux env sigma ~concl false gen in
debug_ssr (fun () -> str"c@gentac=" ++ pr_econstr_env env sigma c);
Proofview.Unsafe.tclEVARS sigma <*> if conv then Tacticals.tclTHEN (convert_concl ~check:true cl) (cleartac clr) else genclrtac cl [c] clr end
let interp_gen env sigma ~concl to_ind gen = let _, _, a, b, c, sigma = pf_interp_gen_aux env sigma ~concl to_ind gen in
sigma, (a, b ,c)
let is_protect hd env sigma = let protectC = mkSsrRef "protect_term"in
EConstr.isRefX env sigma protectC hd
let abs_wgen env sigma keep_let f gen (args,c) = let evar_closed t p = if occur_existential sigma t then
CErrors.user_err ?loc:(loc_of_cpattern p)
(pr_econstr_pat env sigma t ++
str" contains holes and matches no subterm of the goal.") in match gen with
| _, Some ((x, mode), None) when mode = "@" || (mode = " " && keep_let) -> let x = hoi_id x in let decl = get_hyp env sigma x in
sigma,
(if NamedDecl.is_local_def decl then args else EConstr.mkVar x :: args),
EConstr.mkProd_or_LetIn (decl |> NamedDecl.to_rel_decl |> RelDecl.set_name (Name (f x)))
(EConstr.Vars.subst_var sigma x c)
| _, Some ((x, _), None) -> let x = hoi_id x in let hyp = get_hyp env sigma x in let x' = make_annot (Name (f x)) (NamedDecl.get_relevance hyp) in let prod = EConstr.mkProd (x', NamedDecl.get_type hyp, EConstr.Vars.subst_var sigma x c) in
sigma, EConstr.mkVar x :: args, prod
| _, Some ((x, "@"), Some p) -> let x = hoi_id x in let cp = interp_cpattern env sigma p None in let sigma = Evd.merge_universe_context sigma (Evd.ustate cp.pat_sigma) in let sigma, t, c = fill_rel_occ_pattern env sigma c cp None in
evar_closed t p; let ut = red_product_skip_id env sigma t in let sigma, ty, r = pfe_type_relevance_of env sigma t in
sigma, args, EConstr.mkLetIn(make_annot (Name (f x)) r, ut, ty, c)
| _, Some ((x, _), Some p) -> let x = hoi_id x in let cp = interp_cpattern env sigma p None in let sigma = Evd.merge_universe_context sigma (Evd.ustate cp.pat_sigma) in let sigma, t, c = fill_rel_occ_pattern env sigma c cp None in
evar_closed t p; let sigma, ty, r = pfe_type_relevance_of env sigma t in
sigma, t :: args, EConstr.mkProd(make_annot (Name (f x)) r, ty, c)
| _ -> sigma, args, c
let clr_of_wgen gen clrs = match gen with
| clr, Some ((x, _), None) -> let x = hoi_id x in
cleartac clr :: cleartac [SsrHyp(Loc.tag x)] :: clrs
| clr, _ -> cleartac clr :: clrs
let reduct_in_concl ~check t = Tactics.reduct_in_concl ~cast:false ~check (t, DEFAULTcast) let unfold cl =
Proofview.tclEVARMAP >>= fun sigma -> let module R = Reductionops inlet module F = RedFlags in let flags = F.mkflags [F.fBETA; F.fMATCH; F.fFIX; F.fCOFIX; F.fDELTA] in let fold accu c = F.red_add accu (F.fCONST (fst (EConstr.destConst sigma c))) in let flags = List.fold_left fold flags cl in
reduct_in_concl ~check:false (R.clos_norm_flags flags)
open Proofview open Notations
let pf_apply f = Proofview.Goal.enter_one ~__LOC__ beginfun gl ->
f (Proofview.Goal.env gl) (Proofview.Goal.sigma gl) end
let tclINTERP_AST_CLOSURE_TERM_AS_CONSTR c =
tclINDEPENDENTL @@ pf_apply beginfun env sigma -> let old_ssrterm = mkRHole, Some c.Ssrast.body in let ist =
option_assert_get c.Ssrast.interp_env
Pp.(str "tclINTERP_AST_CLOSURE_TERM_AS_CONSTR: term with no ist") in let sigma, t = Tacinterp.interp_constr_gen Pretyping.WithoutTypeConstraint ist env sigma old_ssrterm in
Unsafe.tclEVARS sigma <*>
tclUNIT t end
let tacEVAL_TO_QUANTIFIED_IND ty =
pf_apply beginfun env sigma -> try tclUNIT (Tacred.eval_to_quantified_ind env sigma ty) with e when CErrors.noncritical e -> tclZERO e end
let tacTYPEOF c = Goal.enter_one ~__LOC__ (fun g -> let sigma, env = Goal.sigma g, Goal.env g in let sigma, ty = Typing.type_of env sigma c in
Unsafe.tclEVARS sigma <*> tclUNIT ty)
(** This tactic creates a partial proof realizing the introduction rule, but
does not check anything. *) let unsafe_intro env decl ~relevance b = letopen Context.Named.Declaration in
Refine.refine_with_principal ~typecheck:falsebeginfun sigma -> let ctx = Environ.named_context_val env in let nctx = EConstr.push_named_context_val decl ctx in let inst = EConstr.identity_subst_val (Environ.named_context_val env) in let ninst = SList.cons (EConstr.mkRel 1) inst in let nb = EConstr.Vars.subst1 (EConstr.mkVar (get_id decl)) b in let sigma, ev = Evarutil.new_pure_evar nctx sigma ~relevance nb in
sigma, EConstr.mkNamedLambda_or_LetIn sigma decl (EConstr.mkEvar (ev, ninst)), Some ev end
let set_decl_id id = letopen Context in function
| Rel.Declaration.LocalAssum(name,ty) -> Named.Declaration.LocalAssum({name with binder_name=id},ty)
| Rel.Declaration.LocalDef(name,ty,t) -> Named.Declaration.LocalDef({name with binder_name=id},ty,t)
let rec decompose_assum env sigma orig_goal = letopen Context in match EConstr.kind sigma orig_goal with
| Prod(name,ty,t) ->
Rel.Declaration.LocalAssum(name,ty), t, true
| LetIn(name,ty,t1,t2) -> Rel.Declaration.LocalDef(name, ty, t1), t2, true
| _ -> let goal = Reductionops.whd_allnolet env sigma orig_goal in match EConstr.kind sigma goal with
| Prod(name,ty,t) -> Rel.Declaration.LocalAssum(name,ty), t, false
| LetIn(name,ty,t1,t2) -> Rel.Declaration.LocalDef(name,ty,t1), t2, false
| App(hd,args) when EConstr.isLetIn sigma hd -> (* hack *) let _,v,_,b = EConstr.destLetIn sigma hd in let ctx, t, _ =
decompose_assum env sigma
(EConstr.mkApp (EConstr.Vars.subst1 v b, args)) in
ctx, t, false
| _ -> CErrors.user_err
Pp.(str "No assumption in " ++ Printer.pr_econstr_env env sigma goal)
let tclFULL_BETAIOTA = Goal.enter beginfun gl -> let r, _ = Redexpr.reduction_of_red_expr (Goal.env gl)
Genredexpr.(Lazy {
rBeta=true; rMatch=true; rFix=true; rCofix=true;
rZeta=false; rDelta=false; rConst=[]; rStrength=Norm}) in
Tactics.e_reduct_in_concl ~cast:false ~check:false (r,Constr.DEFAULTcast) end
type intro_id =
| Anon
| Id of Id.t
| Seed ofstring
(** [intro id k] introduces the first premise (product or let-in) of the goal under the name [id], reducing the head of the goal (using beta, iota, delta but not zeta) if necessary. If [id] is None, a name is generated, that will not be user accessible. If the goal does not start with a product or a let-in even after reduction, it fails. In case of success, the original name
and final id are passed to the continuation [k] which gets evaluated. *) let tclINTRO ~id ~conclusion:k = Goal.enter beginfun gl -> letopen Context in let env, sigma, g, relevance = Goal.(env gl, sigma gl, concl gl, relevance gl) in let decl, t, no_red = decompose_assum env sigma g in let original_name = Rel.Declaration.get_name decl in let already_used = Tacmach.pf_ids_of_hyps gl in let id = match id, original_name with
| Id id, _ -> id
| Seed id, _ -> mk_anon_id id already_used
| Anon, Name id -> if is_discharged_id id then id else mk_anon_id (Id.to_string id) already_used
| Anon, Anonymous -> let ids = Tacmach.pf_ids_of_hyps gl in
mk_anon_id ssr_anon_hyp ids in ifList.mem id already_used then
errorstrm Pp.(Id.print id ++ str" already used");
unsafe_intro env (set_decl_id id decl) ~relevance t <*>
(if no_red then tclUNIT () else tclFULL_BETAIOTA) <*>
k ~orig_name:original_name ~new_name:id end
let return ~orig_name:_ ~new_name:_ = tclUNIT ()
let tclINTRO_ID id = tclINTRO ~id:(Id id) ~conclusion:return let tclINTRO_ANON ?seed () = match seed with
| None -> tclINTRO ~id:Anon ~conclusion:return
| Some seed -> tclINTRO ~id:(Seed seed) ~conclusion:return
let tclRENAME_HD_PROD name = Goal.enter beginfun gl -> let concl = Goal.concl gl in let sigma = Goal.sigma gl in match EConstr.kind sigma concl with
| Prod(x,src,tgt) ->
convert_concl_no_check EConstr.(mkProd ({x with binder_name = name},src,tgt))
| _ -> CErrors.anomaly (Pp.str "rename_hd_prod: no head product") end
let tcl0G ~default tac =
numgoals >>= fun ng -> if ng = 0 then tclUNIT default else tac
let rec tclFIRSTi tac n = if n < 0 then Tacticals.tclZEROMSG Pp.(str "tclFIRSTi") else tclORELSE (tclFIRSTi tac (n-1)) (fun _ -> tac n)
let tacCONSTR_NAME ?name c = match name with
| Some n -> tclUNIT n
| None ->
Goal.enter_one ~__LOC__ (fun g -> let sigma = Goal.sigma g in
tclUNIT (constr_name sigma c))
let tacMKPROD c ?name cl =
tacTYPEOF c >>= fun t ->
tacCONSTR_NAME ?name c >>= fun name ->
Goal.enter_one ~__LOC__ beginfun g -> let sigma, env = Goal.sigma g, Goal.env g in let r = Retyping.relevance_of_term env sigma c in if name <> Names.Name.Anonymous || EConstr.Vars.noccurn sigma 1 cl then tclUNIT (EConstr.mkProd (make_annot name r, t, cl)) else let name = Names.Id.of_string (Namegen.hdchar env sigma t) in
tclUNIT (EConstr.mkProd (make_annot (Name.Name name) r, t, cl)) end
let tacINTERP_CPATTERN cp =
pf_apply beginfun env sigma ->
tclUNIT (Ssrmatching.interp_cpattern env sigma cp None) end
let tacUNIFY a b =
pf_apply beginfun env sigma -> let sigma = Ssrmatching.unify_HO env sigma a b in
Unsafe.tclEVARS sigma end
let tclOPTION o d = match o with
| None -> d >>= tclUNIT
| Some x -> tclUNIT x
let tacIS_INJECTION_CASE ?ty t = begin
tclOPTION ty (tacTYPEOF t) >>= fun ty ->
tacEVAL_TO_QUANTIFIED_IND ty >>= fun (mind,_) ->
tclUNIT (Rocqlib.check_ref "core.eq.type" (GlobRef.IndRef mind)) end
let tclWITHTOP tac = Goal.enter beginfun gl -> let top =
mk_anon_id "top_assumption" (Tacmach.pf_ids_of_hyps gl) in
tclINTRO_ID top <*>
tac (EConstr.mkVar top) <*>
Tactics.clear [top] end
let tacMK_SSR_CONST name =
Proofview.tclENV >>= fun env ->
Proofview.tclEVARMAP >>= fun sigma -> match mkSsrConst env sigma name with
| sigma, c -> Unsafe.tclEVARS sigma <*> tclUNIT c
| exception e when CErrors.noncritical e ->
tclLIFT (Proofview.NonLogical.raise (e, Exninfo.null))
let tacDEST_CONST c =
Proofview.tclEVARMAP >>= fun sigma -> match EConstr.destConst sigma c with
| c, _ -> tclUNIT c
| exception e when CErrors.noncritical e ->
tclLIFT (Proofview.NonLogical.raise (e, Exninfo.null))
(* TASSI: This version of unprotects inlines the unfold tactic definition, * since we don't want to wipe out let-ins, and it seems there is no flag
* to change that behaviour in the standard unfold code *) let unprotecttac =
tacMK_SSR_CONST "protect_term" >>= tacDEST_CONST >>= fun prot -> letopen RedFlags in let flags = red_add_transparent allnolet TransparentState.empty in let flags = red_add flags (fCONST prot) in
Tacticals.onClause (fun idopt -> let hyploc = Option.map (fun id -> id, InHyp) idopt in
Tactics.reduct_option ~check:false
(Reductionops.clos_norm_flags flags, DEFAULTcast) hyploc)
allHypsAndConcl
module type StateType = sig type state val init : state val name : string end
module MakeState(S : StateType) = struct
let state_field : S.state Proofview_monad.StateStore.field =
Proofview_monad.StateStore.field S.name
(* FIXME: should not inject fresh_state, but initialize it at the beginning *) let lift_upd_state upd s = letopen Proofview_monad.StateStore in let old_state = Option.default S.init (get s state_field) in
upd old_state >>= fun new_state ->
tclUNIT (set s state_field new_state)
let tacUPDATE upd = Goal.enter beginfun gl -> let s0 = Goal.state gl in
Goal.enter_one ~__LOC__ (fun _ -> lift_upd_state upd s0) >>= fun s ->
Unsafe.tclGETGOALS >>= fun gls -> let gls = List.map (fun gs -> let g = Proofview_monad.drop_state gs in
Proofview_monad.goal_with_state g s) gls in
Unsafe.tclSETGOALS gls end
let tclGET k = Goal.enter beginfun gl -> letopen Proofview_monad.StateStore in
k (Option.default S.init (get (Goal.state gl) state_field)) end
let tclGET1 k = Goal.enter_one beginfun gl -> letopen Proofview_monad.StateStore in
k (Option.default S.init (get (Goal.state gl) state_field)) end
let tclSET new_s = letopen Proofview_monad.StateStore in
Unsafe.tclGETGOALS >>= fun gls -> let gls = List.map (fun gs -> let g = Proofview_monad.drop_state gs in let s = Proofview_monad.get_state gs in
Proofview_monad.goal_with_state g (set s state_field new_s)) gls in
Unsafe.tclSETGOALS gls
let get g = Option.default S.init
(Proofview_monad.StateStore.get (Goal.state g) state_field)
end
let is_construct_ref env sigma c r =
EConstr.isConstruct sigma c && Environ.QGlobRef.equal env (GlobRef.ConstructRef (fst(EConstr.destConstruct sigma c))) r let is_ind_ref env sigma c r = EConstr.isInd sigma c && Environ.QGlobRef.equal env (GlobRef.IndRef (fst(EConstr.destInd sigma c))) r let is_const_ref env sigma c r =
EConstr.isConst sigma c && Environ.QGlobRef.equal env (GlobRef.ConstRef (fst(EConstr.destConst sigma c))) r
(* vim: set filetype=ocaml foldmethod=marker: *)
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.