(************************************************************************) (* * The Rocq Prover / The Rocq Development Team *) (* v * INRIA, CNRS and contributors - Copyright 1999-2019 *) (* <O___,, * (see CREDITS file for the list of authors) *) (* \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) *) (************************************************************************)
open Util open Names open Constr open Context open Termops open Univ open Environ open EConstr open Vars open Context.Rel.Declaration
(** This module implements a call by name reduction used by the cbn tactic.
(** Machinery to custom the behavior of the reduction *)
module ReductionBehaviour = Reductionops.ReductionBehaviour
type volatile = { volatile : bool } [@@unboxed]
(** Machinery about stack of unfolded constants *)
module Cst_stack = struct open EConstr
(** constant * params * args
-constantappliedtoparams=terminheadappliedtoargs -thereisatmostoneargumentswithanemptylistofargs,itmustbethefirst.
- in args, the int represents the indice of the first arg to consider *) type t = (constr * constr list * (int * constr array) list * volatile) list
let empty = [] let all_volatile = CList.for_all (fun (_,_,_,{volatile}) -> volatile)
let drop_useless = function
| _ :: ((_,_,[],_)::_ as q) -> q
| l -> l
let add_param h cst_l = let append2cst = function
| (c,params,[],vol) -> (c, h::params, [], vol)
| (c,params,((i,t)::q),vol) when i = pred (Array.length t) ->
(c, params, q, vol)
| (c,params,(i,t)::q, vol) ->
(c, params, (succ i,t)::q, vol) in
drop_useless (List.map append2cst cst_l)
let add_args cl = List.map (fun (a,b,args,vol) -> (a,b,(0,cl)::args,vol))
let add_cst ?(volatile=false) cst = function
| (_,_,[],_) :: q as l -> l
| l -> (cst,[],[],{volatile})::l
let best_cst = function
| (cst,params,[],_)::_ -> Some(cst,params)
| _ -> None
let reference sigma t = match best_cst t with
| Some (c, params) when isConst sigma c -> Some (fst (destConst sigma c), params)
| _ -> None
(** [best_replace d cst_l c] makes the best replacement for [d]
by [cst_l] in [c] *) let best_replace sigma d cst_l c = let reconstruct_head = List.fold_left
(fun t (i,args) -> mkApp (t,Array.sub args i (Array.length args - i))) in List.fold_right
(fun (cst,params,args,_) t -> Termops.replace_term sigma
(reconstruct_head d args)
(applist (cst, List.rev params))
t) cst_l c
let pr env sigma l = letopen Pp in let p_c c = Termops.Internal.print_constr_env env sigma c in
prlist_with_sep pr_semicolon
(fun (c,params,args,{volatile}) ->
hov 1 (str"(" ++ p_c c ++ str ")" ++ spc () ++ pr_sequence p_c params ++ spc () ++ str "(args:" ++
pr_sequence (fun (i,el) -> prvect_with_sep spc p_c (Array.sub el i (Array.length el - i))) args ++
str ")" ++
(if volatile then str " (volatile)"else mt()))) l end
(** The type of (machine) stacks (= lambda-bar-calculus' contexts) *)
module Stack : sig open EConstr type'a app_node
type cst_member =
| Cst_const of pconstant
| Cst_proj of Projection.t * ERelevance.t
type'a case_stk =
case_info * EInstance.t * 'a array * ('a,ERelevance.t) pcase_return * 'a pcase_invert * ('a,ERelevance.t) pcase_branch array type'a member =
| Appof'a app_node
| Caseof'a case_stk * Cst_stack.t
| Proj of Projection.t * ERelevance.t * Cst_stack.t
| Fix of ('a, 'a, ERelevance.t) pfixpoint * 'a t * Cst_stack.t
| Primitive of CPrimitives.t * (Constant.t * EInstance.t) * 'a t * CPrimitives.args_red * Cst_stack.t
| Cst of { const : cst_member;
curr : int;
remains : int list;
params : 'a t;
volatile : bool;
cst_l : Cst_stack.t;
}
and'a t = 'a member list
val pr : ('a -> Pp.t) -> 'a t -> Pp.t val empty : 'a t val append_app : 'a array -> 'a t -> 'a t val decomp : 'a t -> ('a * 'a t) option val equal : env -> ('a -> 'a -> bool) -> (('a, 'a, ERelevance.t) pfixpoint -> ('a, 'a, ERelevance.t) pfixpoint -> bool)
-> ('a case_stk -> 'a case_stk -> bool) -> 'a t -> 'a t -> bool val strip_app : 'a t -> 'a t * 'a t val strip_n_app : int -> 'a t -> ('a t * 'a * 'a t) option val will_expose_iota : 'a t -> bool val list_of_app_stack : constr t -> constr listoption val args_size : 'a t -> int val tail : int -> 'a t -> 'a t val nth : 'a t -> int -> 'a val best_state : Evd.evar_map -> constr * constr t -> Cst_stack.t -> constr * constr t val zip : ?refold:bool -> Evd.evar_map -> constr * constr t -> constr val check_native_args : CPrimitives.t -> 'a t -> bool val get_next_primitive_args : CPrimitives.args_red -> 'a t -> CPrimitives.args_red * ('a t * 'a * 'a t) option end = struct open EConstr type'a app_node = int * 'a array * int (* first releavnt position, arguments, last relevant position *)
let pr_app_node pr (i,a,j) = letopen Pp in surround (
prvect_with_sep pr_comma pr (Array.sub a i (j - i + 1))
)
type cst_member =
| Cst_const of pconstant
| Cst_proj of Projection.t * ERelevance.t
type'a case_stk =
case_info * EInstance.t * 'a array * ('a,ERelevance.t) pcase_return * 'a pcase_invert * ('a,ERelevance.t) pcase_branch array type'a member =
| Appof'a app_node
| Caseof'a case_stk * Cst_stack.t
| Proj of Projection.t * ERelevance.t * Cst_stack.t
| Fix of ('a, 'a, ERelevance.t) pfixpoint * 'a t * Cst_stack.t
| Primitive of CPrimitives.t * (Constant.t * EInstance.t) * 'a t * CPrimitives.args_red * Cst_stack.t
| Cst of { const : cst_member;
curr : int;
remains : int list;
params : 'a t;
volatile : bool;
cst_l : Cst_stack.t;
}
and'a t = 'a member list
(* Debugging printer *) let rec pr_member pr_c member = letopen Pp in let pr_c x = hov 1 (pr_c x) in match member with
| Appapp -> str "ZApp" ++ pr_app_node pr_c app
| Case ((_,_,_,_,_,br),cst) ->
str "ZCase(" ++
prvect_with_sep (pr_bar) (fun (_, b) -> pr_c b) br
++ str ")"
| Proj (p,_,cst) ->
str "ZProj(" ++ Projection.debug_print p ++ str ")"
| Fix (f,args,cst) ->
str "ZFix(" ++ Constr.debug_print_fix pr_c f
++ pr_comma () ++ pr pr_c args ++ str ")"
| Primitive (p,c,args,kargs,cst_l) ->
str "ZPrimitive(" ++ str (CPrimitives.to_string p)
++ pr_comma () ++ pr pr_c args ++ str ")"
| Cst {const=mem;curr;remains;params;cst_l} ->
str "ZCst(" ++ pr_cst_member pr_c mem ++ pr_comma () ++ int curr
++ pr_comma () ++
prlist_with_sep pr_semicolon int remains ++
pr_comma () ++ pr pr_c params ++ str ")" and pr pr_c l = letopen Pp in
prlist_with_sep pr_semicolon (fun x -> hov 1 (pr_member pr_c x)) l
and pr_cst_member pr_c c = letopen Pp in match c with
| Cst_const (c, u) -> if UVars.Instance.is_empty u then Constant.debug_print c else str"(" ++ Constant.debug_print c ++ str ", " ++
UVars.Instance.pr Sorts.QVar.raw_pr Univ.Level.raw_pr u ++ str")"
| Cst_proj (p,r) ->
str".(" ++ Projection.debug_print p ++ str")"
let empty = []
let append_app v s = let le = Array.length v in if Int.equal le 0then s elseApp (0,v,pred le) :: s
let decomp_node (i,l,j) sk = if i < j then (l.(i), App (succ i,l,j) :: sk) else (l.(i), sk)
let decomp = function
| App node::s -> Some (decomp_node node s)
| _ -> None
let decomp_node_last (i,l,j) sk = if i < j then (l.(j), App (i,l,pred j) :: sk) else (l.(j), sk)
let equal env f f_fix f_case sk1 sk2 = let equal_cst_member x y = match x, y with
| Cst_const (c1,u1), Cst_const (c2, u2) ->
QConstant.equal env c1 c2 && UVars.Instance.equal u1 u2
| Cst_proj (p1,_), Cst_proj (p2,_) -> QProjection.Repr.equal env (Projection.repr p1) (Projection.repr p2)
| _, _ -> false in let rec equal_rec sk1 sk2 = match sk1,sk2 with
| [],[] -> true
| App a1 :: s1, App a2 :: s2 -> let t1,s1' = decomp_node_last a1 s1 in let t2,s2' = decomp_node_last a2 s2 in
(f t1 t2) && (equal_rec s1' s2')
| Case ((ci1,pms1,p1,t1,iv1,a1),_) :: s1, Case ((ci2,pms2,p2,iv2,t2,a2),_) :: s2 ->
f_case (ci1,pms1,p1,t1,iv1,a1) (ci2,pms2,p2,iv2,t2,a2) && equal_rec s1 s2
| (Proj (p,_,_)::s1, Proj(p2,_,_)::s2) ->
QProjection.Repr.equal env (Projection.repr p) (Projection.repr p2)
&& equal_rec s1 s2
| Fix (f1,s1,_) :: s1', Fix (f2,s2,_) :: s2' ->
f_fix f1 f2
&& equal_rec (List.rev s1) (List.rev s2)
&& equal_rec s1' s2'
| Cst c1::s1', Cst c2::s2' ->
equal_cst_member c1.const c2.const
&& equal_rec (List.rev c1.params) (List.rev c2.params)
&& equal_rec s1' s2'
| ((App _|Case _|Proj _|Fix _|Cst _|Primitive _)::_|[]), _ -> false in equal_rec (List.rev sk1) (List.rev sk2)
let append_app_list l s = let a = Array.of_list l in
append_app a s
let rec args_size = function
| App (i,_,j)::s -> j + 1 - i + args_size s
| (Case _|Fix _|Proj _|Cst _|Primitive _)::_ | [] -> 0
let strip_app s = let rec aux out = function
| ( App _ as e) :: s -> aux (e :: out) s
| s -> List.rev out,s in aux [] s let strip_n_app n s = let rec aux n out = function
| App (i,a,j) as e :: s -> let nb = j - i + 1in if n >= nb then
aux (n - nb) (e::out) s else let p = i+n in
Some (CList.rev
(if Int.equal n 0then out elseApp (i,a,p-1) :: out),
a.(p), if j > p thenApp(succ p,a,j)::s else s)
| s -> None in aux n [] s
let will_expose_iota args = List.exists
(function (Fix (_,_,l) | Case (_,l) |
Proj (_,_,l) | Cst {cst_l=l}) when Cst_stack.all_volatile l -> true | _ -> false)
args
let list_of_app_stack s = let rec aux = function
| App (i,a,j) :: s -> let (args',s') = aux s in let a' = Array.sub a i (j - i + 1) in
(Array.fold_right (fun x y -> x::y) a' args', s')
| s -> ([],s) in let (out,s') = aux s in let init = match s' with [] -> true | _ -> false in Option.init init out
let tail n0 s0 = let rec aux n s = if Int.equal n 0then s else match s with
| App (i,a,j) :: s -> let nb = j - i + 1in if n >= nb then
aux (n - nb) s else let p = i+n in if j >= p thenApp(p,a,j)::s else s
| _ -> raise (Invalid_argument "Reductionops.Stack.tail") in aux n0 s0
let nth s p = match strip_n_app p s with
| Some (_,el,_) -> el
| None -> raise Not_found
(** This function breaks the abstraction of Cst_stack ! *) let best_state sigma (_,sk as s) l = let rec aux sk def = function
|(_,_,_,{volatile=true}) -> def
|(cst, params, [], _) -> (cst, append_app_list (List.rev params) sk)
|(cst, params, (i,t)::q, vol) -> match decomp sk with
| Some (el,sk') when EConstr.eq_constr sigma el t.(i) -> if i = pred (Array.length t) then aux sk' def (cst, params, q, vol) else aux sk' def (cst, params, (succ i,t)::q, vol)
| _ -> def inList.fold_left (aux sk) s l
let constr_of_cst_member f sk = match f with
| Cst_const (c, u) -> mkConstU (c, EInstance.make u), sk
| Cst_proj (p,r) -> match decomp sk with
| Some (hd, sk) -> mkProj (p, r, hd), sk
| None -> assert false
let zip ?(refold=false) sigma s = let rec zip = function
| f, [] -> f
| f, (App (i,a,j) :: s) -> let a' = if Int.equal i 0 && Int.equal j (Array.length a - 1) then a else Array.sub a i (j - i + 1) in
zip (mkApp (f, a'), s)
| f, (Case ((ci,u,pms,rt,iv,br),cst_l)::s) when refold ->
zip (best_state sigma (mkCase (ci,u,pms,rt,iv,f,br), s) cst_l)
| f, (Case ((ci,u,pms,rt,iv,br),_)::s) -> zip (mkCase (ci,u,pms,rt,iv,f,br), s)
| f, (Fix (fix,st,cst_l)::s) when refold ->
zip (best_state sigma (mkFix fix, st @ (append_app [|f|] s)) cst_l)
| f, (Fix (fix,st,_)::s) -> zip
(mkFix fix, st @ (append_app [|f|] s))
| f, (Cst {const;params;cst_l}::s) when refold ->
zip (best_state sigma (constr_of_cst_member const (params @ (append_app [|f|] s))) cst_l)
| f, (Cst {const;params}::s) ->
zip (constr_of_cst_member const (params @ (append_app [|f|] s)))
| f, (Proj (p,r,cst_l)::s) when refold ->
zip (best_state sigma (mkProj (p,r,f),s) cst_l)
| f, (Proj (p,r,_)::s) -> zip (mkProj (p,r,f),s)
| f, (Primitive (p,c,args,kargs,cst_l)::s) ->
zip (mkConstU c, args @ append_app [|f|] s) in
zip s
(* Check if there is enough arguments on [stk] w.r.t. arity of [op] *) let check_native_args op stk = let nargs = CPrimitives.arity op in let rargs = args_size stk in
nargs <= rargs
let get_next_primitive_args kargs stk = let rec nargs = function
| [] -> 0
| CPrimitives.Kwhnf :: _ -> 0
| _ :: s -> 1 + nargs s in let n = nargs kargs in
(List.skipn (n+1) kargs, strip_n_app n stk)
end
(** The type of (machine) states (= lambda-bar-calculus' cuts) *)
(*************************************) (*** Reduction using bindingss ***) (*************************************)
(* Beta Reduction tools *)
let apply_subst env sigma cst_l t stack = let rec aux env cst_l t stack = match (Stack.decomp stack, EConstr.kind sigma t) with
| Some (h,stacktl), Lambda (_,_,c) -> let cst_l' = Cst_stack.add_param h cst_l in
aux (h::env) cst_l' c stacktl
| _ -> (cst_l, (substl env t, stack)) in
aux env cst_l t stack
(* Iota reduction tools *)
(** @return c if there is a constant c whose body is bd @returnbdelse.
EvenmorefragilethatwecouldhopebecausedoModuleM.Fixpoint fx:=t.EndM.Definitionf:=u.andsaygoodbyetoanyhope ofrefoldingM.fthisway...
*) let magically_constant_of_fixbody env sigma (reference, params) bd = function
| Name.Anonymous -> bd
| Name.Name id -> letopen UnivProblem in let cst = Constant.change_label reference (Label.of_id id) in ifnot (Environ.mem_constant cst env) then bd else let (cst, u), ctx = UnivGen.fresh_constant_instance env cst in match constant_opt_value_in env (cst,u) with
| None -> bd
| Some t -> let csts = EConstr.eq_constr_universes env sigma (Reductionops.beta_applist sigma (EConstr.of_constr t, params)) bd in beginmatch csts with
| Some csts -> let addqs l r (qs,us) = Sorts.QVar.Map.add l r qs, us in let addus l r (qs,us) = qs, Univ.Level.Map.add l r us in let subst = Set.fold (fun cst acc -> match cst with
| QEq (a,b) | QLeq (a,b) -> let a = match a with
| QVar q -> q
| _ -> assert false in
addqs a b acc
| ULub (u, v) | UWeak (u, v) -> addus u v acc
| UEq (u, v) | ULe (u, v) -> (* XXX add something when qsort? *) let get u = match u with
| Sorts.SProp | Sorts.Prop -> assert false
| Sorts.Set -> Level.set
| Sorts.Type u | Sorts.QSort (_, u) -> Option.get (Universe.level u) in
addus (get u) (get v) acc)
csts UVars.empty_sort_subst in let inst = UVars.subst_sort_level_instance subst u in
applist (mkConstU (cst, EInstance.make inst), params)
| None -> bd end
let contract_cofix env sigma ?reference (bodynum,(names,types,bodies as typedbodies)) = let nbodies = Array.length bodies in let make_Fi j = let ind = nbodies-j-1in if Int.equal bodynum ind then mkCoFix (ind,typedbodies) else let bd = mkCoFix (ind,typedbodies) in match reference with
| None -> bd
| Some r -> magically_constant_of_fixbody env sigma r bd names.(ind).binder_name in let closure = List.init nbodies make_Fi in
substl closure bodies.(bodynum)
(** Similar to the "fix" case below *) let reduce_and_refold_cofix recfun env sigma cst_l cofix sk = let raw_answer =
contract_cofix env sigma ?reference:(Cst_stack.reference sigma cst_l) cofix in let (x, (t, sk')) = apply_subst [] sigma Cst_stack.empty raw_answer sk in let t' = Cst_stack.best_replace sigma (mkCoFix cofix) cst_l t in
recfun x (t', sk')
(* contracts fix==FIX[nl;i](A1...Ak;[F1...Fk]{B1....Bk}) to produce
Bi[Fj --> FIX[nl;j](A1...Ak;[F1...Fk]{B1...Bk})] *)
let contract_fix env sigma ?reference ((recindices,bodynum),(names,types,bodies as typedbodies)) = let nbodies = Array.length recindices in let make_Fi j = let ind = nbodies-j-1in if Int.equal bodynum ind then mkFix ((recindices,ind),typedbodies) else let bd = mkFix ((recindices,ind),typedbodies) in match reference with
| None -> bd
| Some r -> magically_constant_of_fixbody env sigma r bd names.(ind).binder_name in let closure = List.init nbodies make_Fi in
substl closure bodies.(bodynum)
(** First we substitute the Rel bodynum by the fixpoint and then we try to replacethefixpointbythebestconstantfrom[cst_l] Otherrelsaredirectlysubstitutedbyconstants"magicallyfoundfromthe
context" in contract_fix *) let reduce_and_refold_fix recfun env sigma cst_l fix sk = let raw_answer =
contract_fix env sigma ?reference:(Cst_stack.reference sigma cst_l) fix in let (x, (t, sk')) = apply_subst [] sigma Cst_stack.empty raw_answer sk in let t' = Cst_stack.best_replace sigma (mkFix fix) cst_l t in
recfun x (t', sk')
let equal_stacks env sigma (x, l) (y, l') = let f_equal x y = eq_constr sigma x y in let eq_fix a b = f_equal (mkFix a) (mkFix b) in let eq_case (ci1, u1, pms1, (p1,_), _, br1) (ci2, u2, pms2, (p2,_), _, br2) =
Array.equal f_equal pms1 pms2 &&
f_equal (snd p1) (snd p2) &&
Array.equal (fun (_, c1) (_, c2) -> f_equal c1 c2) br1 br2 in
Stack.equal env f_equal eq_fix eq_case l l' && f_equal x y
let apply_branch env sigma (ind, i) args (ci, u, pms, iv, r, lf) = let args = Stack.tail ci.ci_npar args in let args = Option.get (Stack.list_of_app_stack args) in let br = lf.(i - 1) in let subst = if Int.equal ci.ci_cstr_nargs.(i - 1) ci.ci_cstr_ndecls.(i - 1) then (* No let-bindings *) List.rev args else let ctx = expand_branch env sigma u pms (ind, i) br in
subst_of_rel_context_instance_list ctx args in
Vars.substl subst (snd br)
exception PatternFailure
let match_einstance sigma pu u psubst = match UVars.Instance.pattern_match pu (EInstance.kind sigma u) psubst with
| Some psubst -> psubst
| None -> raise PatternFailure
let match_sort ps s psubst = match Sorts.pattern_match ps s psubst with
| Some psubst -> psubst
| None -> raise PatternFailure
let rec match_arg_pattern whrec env sigma ctx psubst p t = letopen Declarations in let t' = EConstr.it_mkLambda_or_LetIn t ctx in match p with
| EHole i -> Partial_subst.add_term i t' psubst
| EHoleIgnored -> psubst
| ERigid (ph, es) -> let (t, stk), _ = whrec Cst_stack.empty (t, Stack.empty) in let psubst = match_rigid_arg_pattern whrec env sigma ctx psubst ph t in let psubst, stk = apply_rule whrec env sigma ctx psubst es stk in match stk with
| [] -> psubst
| _ :: _ -> raise PatternFailure
and match_rigid_arg_pattern whrec env sigma ctx psubst p t = match [@ocaml.warning "-4"] p, EConstr.kind sigma t with
| PHInd (ind, pu), Ind (ind', u) -> if QInd.equal env ind ind' then match_einstance sigma pu u psubst else raise PatternFailure
| PHConstr (constr, pu), Construct (constr', u) -> if QConstruct.equal env constr constr' then match_einstance sigma pu u psubst else raise PatternFailure
| PHRel i, Rel n when i = n -> psubst
| PHSort ps, Sort s -> match_sort ps (ESorts.kind sigma s) psubst
| PHSymbol (c, pu), Const (c', u) -> if QConstant.equal env c c' then match_einstance sigma pu u psubst else raise PatternFailure
| PHInt i, Int i' -> if Uint63.equal i i' then psubst else raise PatternFailure
| PHFloat f, Float f' -> if Float64.equal f f' then psubst else raise PatternFailure
| PHString s, String s' -> if Pstring.equal s s' then psubst else raise PatternFailure
| PHLambda (ptys, pbod), _ -> let ntys, _ = EConstr.decompose_lambda sigma t in let na = List.length ntys and np = Array.length ptys in if np > na thenraise PatternFailure; let ntys, body = EConstr.decompose_lambda_n sigma np t in let ctx' = List.map (fun (n, ty) -> Context.Rel.Declaration.LocalAssum (n, ty)) ntys in let tys = Array.of_list @@ List.rev_map snd ntys in let na = Array.length tys in let contexts_upto = Array.init na (fun i -> List.skipn (na - i) ctx' @ ctx) in let psubst = Array.fold_left3 (fun psubst ctx -> match_arg_pattern whrec env sigma ctx psubst) psubst contexts_upto ptys tys in let psubst = match_arg_pattern whrec env sigma (ctx' @ ctx) psubst pbod body in
psubst
| PHProd (ptys, pbod), _ -> let ntys, _ = EConstr.decompose_prod sigma t in let na = List.length ntys and np = Array.length ptys in if np > na thenraise PatternFailure; let ntys, body = EConstr.decompose_prod_n sigma np t in let ctx' = List.map (fun (n, ty) -> Context.Rel.Declaration.LocalAssum (n, ty)) ntys in let tys = Array.of_list @@ List.rev_map snd ntys in let na = Array.length tys in let contexts_upto = Array.init na (fun i -> List.skipn (na - i) ctx' @ ctx) in let psubst = Array.fold_left3 (fun psubst ctx -> match_arg_pattern whrec env sigma ctx psubst) psubst contexts_upto ptys tys in let psubst = match_arg_pattern whrec env sigma (ctx' @ ctx) psubst pbod body in
psubst
| (PHInd _ | PHConstr _ | PHRel _ | PHInt _ | PHFloat _ | PHString _ | PHSort _ | PHSymbol _), _ -> raise PatternFailure
and extract_n_stack args n s = if n = 0thenList.rev args, s else match Stack.decomp s with
| Some (arg, rest) -> extract_n_stack (arg :: args) (n-1) rest
| None -> raise PatternFailure
and apply_rule whrec env sigma ctx psubst es stk = match [@ocaml.warning "-4"] es, stk with
| [], _ -> psubst, stk
| Declarations.PEApp pargs :: e, s -> let np = Array.length pargs in let pargs = Array.to_list pargs in let args, s = extract_n_stack [] np s in let psubst = List.fold_left2 (match_arg_pattern whrec env sigma ctx) psubst pargs args in
apply_rule whrec env sigma ctx psubst e s
| Declarations.PECase (pind, pu, pret, pbrs) :: e, Stack.Case ((ci, u, pms, p, iv, brs), cst_l) :: s -> ifnot @@ QInd.equal env pind ci.ci_ind thenraise PatternFailure; let dummy = mkProp in let psubst = match_einstance sigma pu u psubst in let (_, _, _, ((ntys_ret, ret), _), _, _, brs) = EConstr.annotate_case env sigma (ci, u, pms, p, NoInvert, dummy, brs) in let psubst = match_arg_pattern whrec env sigma (ntys_ret @ ctx) psubst pret ret in let psubst = Array.fold_left2 (fun psubst pat (ctx', br) -> match_arg_pattern whrec env sigma (ctx' @ ctx) psubst pat br) psubst pbrs brs in
apply_rule whrec env sigma ctx psubst e s
| Declarations.PEProj proj :: e, Stack.Proj (proj', r, cst_l') :: s -> ifnot @@ QProjection.Repr.equal env proj (Projection.repr proj') then raise PatternFailure;
apply_rule whrec env sigma ctx psubst e s
| _, _ -> raise PatternFailure
let rec apply_rules whrec env sigma u r stk = letopen Declarations in match r with
| [] -> raise PatternFailure
| { lhs_pat = (pu, elims); nvars; rhs } :: rs -> try let psubst = Partial_subst.make nvars in let psubst = match_einstance sigma pu u psubst in let psubst, stk = apply_rule whrec env sigma [] psubst elims stk in let subst, qsubst, usubst = Partial_subst.to_arrays psubst in let usubst = UVars.Instance.of_array (qsubst, usubst) in let rhsu = subst_instance_constr (EConstr.EInstance.make usubst) (EConstr.of_constr rhs) in let rhs' = substl (Array.to_list subst) rhsu in
(rhs', stk) with PatternFailure -> apply_rules whrec env sigma u rs stk
let whd_state_gen ?csts flags env sigma = letopen Context.Named.Declaration in letopen ReductionBehaviour in let rec whrec cst_l (x, stack) = let () = debug_RAKAM (fun () -> letopen Pp in let pr c = Termops.Internal.print_constr_env env sigma c in
(h (str "<<" ++ pr x ++
str "|" ++ cut () ++ Cst_stack.pr env sigma cst_l ++
str "|" ++ cut () ++ Stack.pr pr stack ++
str ">>"))) in let c0 = EConstr.kind sigma x in let fold () = let () = debug_RAKAM (fun () ->
Pp.(str "<><><><><>")) in
((EConstr.of_kind c0, stack),cst_l) in match c0 with
| Rel n when RedFlags.red_set flags RedFlags.fDELTA ->
(match lookup_rel n env with
| LocalDef (_,body,_) -> whrec Cst_stack.empty (lift n body, stack)
| _ -> fold ())
| Var id when RedFlags.red_set flags (RedFlags.fVAR id) ->
(match lookup_named id env with
| LocalDef (_,body,_) ->
whrec (Cst_stack.add_cst (mkVar id) cst_l) (body, stack)
| _ -> fold ())
| Evar _ | Meta _ -> fold ()
| Const (c,u as const) ->
Reductionops.reduction_effect_hook env sigma c
(lazy (EConstr.to_constr sigma (Stack.zip sigma (x,fst (Stack.strip_app stack))))); if RedFlags.red_set flags (RedFlags.fCONST c) then let u' = EInstance.kind sigma u in match constant_value_in env (c, u') with
| body -> begin let body = EConstr.of_constr body in (* Looks for ReductionBehaviour *) match ReductionBehaviour.get c with
| None -> whrec (Cst_stack.add_cst (mkConstU const) cst_l) (body, stack)
| Some behavior -> beginmatch behavior with
| NeverUnfold -> fold ()
| (UnfoldWhen { nargs = Some n } |
UnfoldWhenNoMatch { nargs = Some n } )
when Stack.args_size stack < n ->
fold ()
| UnfoldWhenNoMatch { recargs; nargs } -> (* maybe unfolds *) let app_sk,sk = Stack.strip_app stack in let volatile = Option.has_some nargs in let (tm',sk'),cst_l' = match recargs with
| [] ->
whrec (Cst_stack.add_cst ~volatile (mkConstU const) cst_l) (body, app_sk)
| curr :: remains -> match Stack.strip_n_app curr app_sk with
| None -> (x,app_sk), cst_l
| Some (bef,arg,app_sk') -> let cst_l = Stack.Cst
{ const = Stack.Cst_const (fst const, u');
volatile;
curr; remains; params=bef; cst_l;
} in
whrec Cst_stack.empty (arg,cst_l :: app_sk') in let rec is_case x = match EConstr.kind sigma x with
| Lambda (_,_, x) | LetIn (_,_,_, x) | Cast (x, _,_) -> is_case x
| App (hd, _) -> is_case hd
| Case _ -> true
| _ -> falsein if equal_stacks env sigma (x, app_sk) (tm', sk')
|| Stack.will_expose_iota sk'
|| is_case tm' then fold () else whrec cst_l' (tm', sk' @ sk)
| UnfoldWhen { recargs; nargs } -> (* maybe unfolds *) beginmatch recargs with
| [] -> (* if nargs has been specified *) (* CAUTION : the constant is NEVER refolded (even when it hides a (co)fix) *)
whrec cst_l (body, stack)
| curr :: remains -> match Stack.strip_n_app curr stack with
| None -> fold ()
| Some (bef,arg,s') -> let cst_l = Stack.Cst
{ const = Stack.Cst_const (fst const, u');
volatile = Option.has_some nargs;
curr; remains; params=bef; cst_l;
} in
whrec Cst_stack.empty (arg,cst_l::s') end end end
| exception NotEvaluableConst (IsPrimitive (u,p)) when Stack.check_native_args p stack -> let kargs = CPrimitives.kind p in let (kargs,o) = Stack.get_next_primitive_args kargs stack in (* Should not fail thanks to [check_native_args] *) let (before,a,after) = Option.get o in
whrec Cst_stack.empty (a,Stack.Primitive(p,const,before,kargs,cst_l)::after)
| exception NotEvaluableConst (HasRules (u', b, r)) -> begintry let rhs_stack = apply_rules whrec env sigma u r stack in
whrec Cst_stack.empty rhs_stack with PatternFailure -> ifnot b then fold () else match Stack.strip_app stack with
| args, (Stack.Fix (f,s',cst_l)::s'') when RedFlags.red_set flags RedFlags.fFIX -> let x' = Stack.zip sigma (x, args) in let out_sk = s' @ (Stack.append_app [|x'|] s'') in
reduce_and_refold_fix whrec env sigma cst_l f out_sk
| _ -> fold () end
| exception NotEvaluableConst _ -> fold () else fold ()
| Proj (p, r, c) when RedFlags.red_projection flags p ->
(let npars = Projection.npars p in match ReductionBehaviour.get (Projection.constant p) with
| None -> let stack' = (c, Stack.Proj (p, r, cst_l) :: stack) in let stack'', csts = whrec Cst_stack.empty stack' in if equal_stacks env sigma stack' stack'' then fold () else stack'', csts
| Some behavior -> beginmatch behavior with
| NeverUnfold -> fold ()
| (UnfoldWhen { nargs = Some n }
| UnfoldWhenNoMatch { nargs = Some n })
when Stack.args_size stack < n - (npars + 1) -> fold ()
| UnfoldWhen { recargs }
| UnfoldWhenNoMatch { recargs }-> (* maybe unfolds *) let recargs = List.map_filter (fun x -> let idx = x - npars in if idx < 0then None else Some idx) recargs in let volatile = match behavior with
| UnfoldWhen {nargs} -> Option.has_some nargs
| UnfoldWhenNoMatch _ | NeverUnfold -> false in match recargs with
| [] -> (* if nargs has been specified *) (* CAUTION : the constant is NEVER refold
(even when it hides a (co)fix) *) let stack' = (c, Stack.Proj (p, r, cst_l) :: stack) in
whrec Cst_stack.empty(* cst_l *) stack'
| curr :: remains -> match Stack.strip_n_app curr (Stack.append_app [|c|] stack) with
| None -> fold ()
| Some (bef,arg,s') -> let cst_l = Stack.Cst
{ const=Stack.Cst_proj (p,r);
curr;
remains;
volatile;
params=bef;
cst_l;
} in
whrec Cst_stack.empty (arg,cst_l::s') end)
| LetIn (_,b,_,c) when RedFlags.red_set flags RedFlags.fZETA -> let (cst_l, p) = apply_subst [b] sigma cst_l c stack in
whrec cst_l p
| Cast (c,_,_) -> whrec cst_l (c, stack)
| App (f,cl) ->
whrec
(Cst_stack.add_args cl cst_l)
(f, Stack.append_app cl stack)
| Lambda (na,t,c) ->
(match Stack.decomp stack with
| Some _ when RedFlags.red_set flags RedFlags.fBETA -> let (cst_l, p) = apply_subst [] sigma cst_l x stack in
whrec cst_l p
| _ -> fold ())
| Construct (cstr ,u) -> let use_match = RedFlags.red_set flags RedFlags.fMATCH in let use_fix = RedFlags.red_set flags RedFlags.fFIX in if use_match || use_fix then match Stack.strip_app stack with
|args, (Stack.Case(case,_)::s') when use_match -> let r = apply_branch env sigma cstr args casein
whrec Cst_stack.empty (r, s')
|args, (Stack.Proj (p,_,_)::s') when use_match ->
whrec Cst_stack.empty (Stack.nth args (Projection.npars p + Projection.arg p), s')
|args, (Stack.Fix (f,s',cst_l)::s'') when use_fix -> let x' = Stack.zip sigma (x, args) in let out_sk = s' @ (Stack.append_app [|x'|] s'') in
reduce_and_refold_fix whrec env sigma cst_l f out_sk
|args, (Stack.Cst {const;curr;remains;volatile;params=s';cst_l} :: s'') -> let x' = Stack.zip sigma (x, args) in beginmatch remains with
| [] ->
(matchconstwith
| Stack.Cst_const const ->
(match constant_opt_value_in env constwith
| None -> fold ()
| Some body -> letconst = (fst const, EInstance.make (snd const)) in let body = EConstr.of_constr body in let cst_l = Cst_stack.add_cst ~volatile (mkConstU const) cst_l in
whrec cst_l (body, s' @ (Stack.append_app [|x'|] s'')))
| Stack.Cst_proj (p,r) -> let stack = s' @ (Stack.append_app [|x'|] s'') in match Stack.strip_n_app 0 stack with
| None -> assert false
| Some (_,arg,s'') ->
whrec Cst_stack.empty (arg, Stack.Proj (p,r,cst_l) :: s''))
| next :: remains' -> match Stack.strip_n_app (next-curr-1) s'' with
| None -> fold ()
| Some (bef,arg,s''') -> let cst_l = Stack.Cst
{ const;
curr=next;
volatile;
remains=remains';
params=s' @ (Stack.append_app [|x'|] bef);
cst_l;
} in
whrec Cst_stack.empty (arg, cst_l :: s''') end
|_, (Stack.App _)::_ -> assert false
|_, _ -> fold () else fold ()
| CoFix cofix -> if RedFlags.red_set flags RedFlags.fCOFIX then match Stack.strip_app stack with
|args, ((Stack.Case _ |Stack.Proj _)::s') ->
reduce_and_refold_cofix whrec env sigma cst_l cofix stack
|_ -> fold () else fold ()
| Int _ | Float _ | String _ | Array _ -> beginmatch Stack.strip_app stack with
| (_, Stack.Primitive(p,(_,u as kn),rargs,kargs,cst_l')::s) -> let more_to_reduce = List.exists (fun k -> CPrimitives.Kwhnf = k) kargs in if more_to_reduce then let (kargs,o) = Stack.get_next_primitive_args kargs s in (* Should not fail because Primitive is put on the stack only if fully applied *) let (before,a,after) = Option.get o in
whrec Cst_stack.empty (a,Stack.Primitive(p,kn,rargs @ Stack.append_app [|x|] before,kargs,cst_l')::after) else let n = List.length kargs in let (args,s) = Stack.strip_app s in let (args,extra_args) = tryList.chop n args withList.IndexOutOfRange -> (args,[]) (* FIXME probably useless *) in let s = extra_args @ s in let args = Array.of_list (Option.get (Stack.list_of_app_stack (rargs @ Stack.append_app [|x|] args))) in beginmatch CredNative.red_prim env sigma p u args with
| Some t -> whrec cst_l' (t,s)
| None -> ((mkApp (mkConstU kn, args), s), cst_l) end
|args, (Stack.Cst {const;curr;remains;volatile;params=s';cst_l} :: s'') -> let x' = Stack.zip sigma (x, args) in beginmatch remains with
| [] ->
(matchconstwith
| Stack.Cst_const const ->
(match constant_opt_value_in env constwith
| None -> fold ()
| Some body -> letconst = (fst const, EInstance.make (snd const)) in let body = EConstr.of_constr body in let cst_l = Cst_stack.add_cst ~volatile (mkConstU const) cst_l in
whrec cst_l (body, s' @ (Stack.append_app [|x'|] s'')))
| Stack.Cst_proj (p,r) -> let stack = s' @ (Stack.append_app [|x'|] s'') in match Stack.strip_n_app 0 stack with
| None -> assert false
| Some (_,arg,s'') ->
whrec Cst_stack.empty (arg, Stack.Proj (p,r,cst_l) :: s''))
| next :: remains' -> match Stack.strip_n_app (next-curr-1) s'' with
| None -> fold ()
| Some (bef,arg,s''') -> let cst_l = Stack.Cst
{ const;
curr=next;
volatile;
remains=remains';
params=s' @ (Stack.append_app [|x'|] bef);
cst_l;
} in
whrec Cst_stack.empty (arg, cst_l :: s''') end
| _ -> fold () end
| Rel _ | Var _ | LetIn _ | Proj _ -> fold ()
| Sort _ | Ind _ | Prod _ -> fold () in fun xs -> let (s,cst_l as res) = whrec (Option.default Cst_stack.empty csts) xs in
(Stack.best_state sigma s cst_l)
let whd_cbn flags env sigma t = let state = whd_state_gen flags env sigma (t, Stack.empty) in
Stack.zip ~refold:true sigma state
let norm_cbn flags env sigma t = let push_rel_check_zeta d env = letopen RedFlags in let d = match d with
| LocalDef (na,c,t) when not (red_set flags fZETA) -> LocalAssum (na,t)
| d -> d in
push_rel d env in let rec strongrec env t =
map_constr_with_full_binders env sigma
push_rel_check_zeta strongrec env (whd_cbn flags env sigma t) in
strongrec env t
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-09-28)
¤
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 und die Messung sind noch experimentell.