(************************************************************************) (* * 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) *) (************************************************************************)
(* Created under Benjamin Werner account by Bruno Barras to implement a call-by-value conversion algorithm and a lazy reduction machine
with sharing, Nov 1996 *) (* Addition of zeta-reduction (let-in contraction) by Hugo Herbelin, Oct 2000 *) (* Irreversibility of opacity by Bruno Barras *) (* Cleaning and lightening of the kernel by Bruno Barras, Nov 2001 *) (* Equal inductive types by Jacek Chrzaszcz as part of the module
system, Aug 2002 *)
open CErrors open Util open Names open Constr open Declarations open Environ open CClosure open Esubst
let rec is_empty_stack = function
[] -> true
| Zupdate _::s -> is_empty_stack s
| Zshift _::s -> is_empty_stack s
| _ -> false
(* Compute the lift to be performed on a term placed in a given stack *) let el_stack el stk = let n = List.fold_left
(fun i z -> match z with
Zshift n -> i+n
| _ -> i)
0
stk in
el_shft n el
type lft_constr_stack_elt =
Zlapp of (lift * fconstr) array
| Zlproj of Projection.Repr.t * lift
| Zlfix of (lift * fconstr) * lft_constr_stack
| Zlcase of case_info * lift * UVars.Instance.t * constr array * case_return * case_branch array * usubs
| Zlprimitive of
CPrimitives.t * pconstant * lft_fconstr list * lft_fconstr next_native_args and lft_constr_stack = lft_constr_stack_elt list
let rec zlapp v = function
Zlapp v2 :: s -> zlapp (Array.append v v2) s
| s -> Zlapp v :: s
(** Hand-unrolling of the map function to bypass the call to the generic array allocation. Type annotation is required to tell OCaml that the array does
not contain floats. *) let map_lift (l : lift) (v : fconstr array) = match v with
| [||] -> assert false
| [|c0|] -> [|(l, c0)|]
| [|c0; c1|] -> [|(l, c0); (l, c1)|]
| [|c0; c1; c2|] -> [|(l, c0); (l, c1); (l, c2)|]
| [|c0; c1; c2; c3|] -> [|(l, c0); (l, c1); (l, c2); (l, c3)|]
| v -> Array.Fun1.map (fun l t -> (l, t)) l v
let pure_stack lfts stk = let rec pure_rec lfts stk = match stk with
[] -> (lfts,[])
| zi::s ->
(match (zi,pure_rec lfts s) with
(Zupdate _,lpstk) -> lpstk
| (Zshift n,(l,pstk)) -> (el_shft n l, pstk)
| (Zapp a, (l,pstk)) ->
(l,zlapp (map_lift l a) pstk)
| (Zproj (p,_), (l,pstk)) ->
(l, Zlproj (p,l)::pstk)
| (Zfix(fx,a),(l,pstk)) -> let (lfx,pa) = pure_rec l a in
(l, Zlfix((lfx,fx),pa)::pstk)
| (ZcaseT(ci,u,pms,p,br,e),(l,pstk)) ->
(l,Zlcase(ci,l,u,pms,p,br,e)::pstk)
| (Zprimitive(op,c,rargs,kargs),(l,pstk)) ->
(l,Zlprimitive(op,c,List.map (fun t -> (l,t)) rargs, List.map (fun (k,t) -> (k,(l,t))) kargs)::pstk)) in
snd (pure_rec lfts stk)
(* functions of this type are called from the kernel *) type'a kernel_conversion_function = env -> 'a -> 'a -> (unit, unit) result
(* functions of this type can be called from outside the kernel *) type'a extended_conversion_function =
?l2r:bool -> ?reds:TransparentState.t -> env ->
?evars:evar_handler -> 'a -> 'a -> (unit, unit) result
type payload = ..
exception NotConvertible
exception NotConvertibleTrace of payload
(* Convertibility of sorts *)
(* The sort cumulativity is
Prop <= Set <= Type 1 <= ... <= Type i <= ...
and this holds whatever Set is predicative or impredicative
*)
type ('a, 'err) universe_state = 'a * ('a, 'err) universe_compare
type ('a, 'err) generic_conversion_function = ('a, 'err) universe_state -> constr -> constr -> ('a, 'err option) result
let sort_cmp_universes env pb s0 s1 (u, check) =
(check.compare_sorts env pb s0 s1 u, check)
(* [flex] should be true for constants, false for inductive types and
constructors. *) let convert_instances ~flex u u' (s, check) =
(check.compare_instances ~flex u u' s, check)
exception MustExpand
let convert_instances_cumul pb var u u' (s, check) =
(check.compare_cumul_instances pb var u u' s, check)
let get_cumulativity_constraints cv_pb variance u u' = match cv_pb with
| CONV ->
UVars.enforce_eq_variance_instances variance u u' Sorts.QUConstraints.empty
| CUMUL ->
UVars.enforce_leq_variance_instances variance u u' Sorts.QUConstraints.empty
let inductive_cumulativity_arguments (mind,ind) =
mind.Declarations.mind_nparams +
mind.Declarations.mind_packets.(ind).Declarations.mind_nrealargs
let convert_inductives_gen cmp_instances cmp_cumul cv_pb (mind,ind) nargs u1 u2 s = match mind.Declarations.mind_variance with
| None -> cmp_instances u1 u2 s
| Some variances -> let num_param_arity = inductive_cumulativity_arguments (mind,ind) in ifnot (Int.equal num_param_arity nargs) then (* shortcut, not sure if worth doing, could use perf data *) if UVars.Instance.equal u1 u2 then Result.Ok s elseraise MustExpand else
cmp_cumul cv_pb variances u1 u2 s
type'e conv_tab = {
cnv_inf : clos_infos;
cnv_typ : bool; (* true if the input terms were well-typed *)
lft_tab : clos_tab;
rgt_tab : clos_tab;
err_ret : 'e -> payload;
} (** Invariant: for any tl ∈ lft_tab and tr ∈ rgt_tab, there is no mutable memory
location contained both in tl and in tr. *)
let fail_check (infos : 'err conv_tab) (state, check) = match state with
| Result.Ok state -> (state, check)
| Result.Error None -> raise NotConvertible
| Result.Error (Some err) -> raise (NotConvertibleTrace (infos.err_ret err))
let convert_inductives cv_pb ind nargs u1 u2 (s, check) =
convert_inductives_gen (check.compare_instances ~flex:false) check.compare_cumul_instances
cv_pb ind nargs u1 u2 s, check
let convert_constructors_gen cmp_instances cmp_cumul (mind, ind, cns) nargs u1 u2 s = match mind.Declarations.mind_variance with
| None -> cmp_instances u1 u2 s
| Some _ -> let num_cnstr_args = constructor_cumulativity_arguments (mind,ind,cns) in ifnot (Int.equal num_cnstr_args nargs) then if UVars.Instance.equal u1 u2 then Result.Ok s elseraise MustExpand else (** By invariant, both constructors have a common supertype,
so they are convertible _at that type_. *) (* NB: no variance for qualities *) let variance = Array.make (snd (UVars.Instance.length u1)) UVars.Variance.Irrelevant in
cmp_cumul CONV variance u1 u2 s
let convert_constructors ctor nargs u1 u2 (s, check) =
convert_constructors_gen (check.compare_instances ~flex:false) check.compare_cumul_instances
ctor nargs u1 u2 s, check
let conv_table_key infos ~nargs k1 k2 cuniv = if k1 == k2 then cuniv else match k1, k2 with
| ConstKey (cst, u), ConstKey (cst', u') when Constant.CanOrd.equal cst cst' -> if UVars.Instance.equal u u' then cuniv elseif Int.equal nargs 1 && is_array_type (info_env infos.cnv_inf) cst then cuniv else let flex = evaluable_constant cst (info_env infos.cnv_inf)
&& RedFlags.red_set (info_flags infos.cnv_inf) (RedFlags.fCONST cst) in fail_check infos @@ convert_instances ~flex u u' cuniv
| VarKey id, VarKey id' when Id.equal id id' -> cuniv
| RelKey n, RelKey n' when Int.equal n n' -> cuniv
| _ -> raise NotConvertible
let same_args_size sk1 sk2 = let n = CClosure.stack_args_size sk1 in if Int.equal n (CClosure.stack_args_size sk2) then n elseraise NotConvertible
(** The same heap separation invariant must hold for the fconstr arguments
passed to each respective side of the conversion function below. *)
let push_relevance infos r =
{ infos with cnv_inf = CClosure.push_relevance infos.cnv_inf r }
let push_relevances infos nas =
{ infos with cnv_inf = CClosure.push_relevances infos.cnv_inf nas }
let identity_of_ctx (ctx:Constr.rel_context) =
Context.Rel.instance mkRel 0 ctx
(* ind -> fun args => ind args *) let eta_expand_ind env (ind,u as pind) = let mib = Environ.lookup_mind (fst ind) env in let mip = mib.mind_packets.(snd ind) in let ctx = Vars.subst_instance_context u mip.mind_arity_ctxt in let args = identity_of_ctx ctx in let c = mkApp (mkIndU pind, args) in let c = Term.it_mkLambda_or_LetIn c ctx in
inject c
let eta_expand_constructor env ((ind,ctor),u as pctor) = let mib = Environ.lookup_mind (fst ind) env in let mip = mib.mind_packets.(snd ind) in let ctx = Vars.subst_instance_context u (fst mip.mind_nf_lc.(ctor-1)) in let args = identity_of_ctx ctx in let c = mkApp (mkConstructU pctor, args) in let c = Term.it_mkLambda_or_LetIn c ctx in
inject c
let esubst_of_context ctx u args e = let rec aux lft e args ctx = match ctx with
| [] -> lft, e
| None :: ctx -> aux (lft + 1) (usubs_lift e) (usubs_lift args) ctx
| Some c :: ctx -> let c = Vars.subst_instance_constr u c in let c = mk_clos args c in
aux lft (usubs_cons c e) (usubs_cons c args) ctx in
aux 0 e args (List.rev ctx)
let irr_flex infos = function
| ConstKey (con,u) -> is_irrelevant infos @@ UVars.subst_instance_relevance u @@ Environ.constant_relevance con (info_env infos)
| VarKey x -> is_irrelevant infos @@ Context.Named.Declaration.get_relevance (Environ.lookup_named x (info_env infos))
| RelKey x -> is_irrelevant infos @@ Context.Rel.Declaration.get_relevance (Environ.lookup_rel x (info_env infos))
let eq_universes (_,e1) (_,e2) u1 u2 = let subst e u = if UVars.Instance.is_empty e then u else UVars.subst_instance_instance e u in
UVars.Instance.equal (subst e1 u1) (subst e2 u2)
let rec compare_under e1 c1 e2 c2 = match Constr.kind c1, Constr.kind c2 with
| Cast (c1, _, _), _ -> compare_under e1 c1 e2 c2
| _, Cast (c2, _, _) -> compare_under e1 c1 e2 c2
| Rel i, Rel j -> beginmatch Esubst.expand_rel i (fst e1) with
| Inl _ -> false
| Inr (k, _) -> beginmatch Esubst.expand_rel j (fst e2) with
| Inl _ -> false
| Inr (k', _) -> Int.equal k k' end end
| Meta m1, Meta m2 -> Int.equal m1 m2
| Var id1, Var id2 -> Id.equal id1 id2
| Int i1, Int i2 -> Uint63.equal i1 i2
| Float f1, Float f2 -> Float64.equal f1 f2
| String s1, String s2 -> Pstring.equal s1 s2
| Sort s1, Sort s2 -> let subst_instance_sort u s = if UVars.Instance.is_empty u then s else UVars.subst_instance_sort u s in let s1 = subst_instance_sort (snd e1) s1 and s2 = subst_instance_sort (snd e2) s2 in
Sorts.equal s1 s2
| Prod (_,t1,c1), Prod (_,t2,c2) ->
compare_under e1 t1 e2 t2
&& compare_under (usubs_lift e1) c1 (usubs_lift e2) c2
| Lambda (_,t1,c1), Lambda (_,t2,c2) ->
compare_under e1 t1 e2 t2
&& compare_under (usubs_lift e1) c1 (usubs_lift e2) c2
| LetIn (_,b1,_,c1), LetIn (_,b2,_,c2) -> (* don't care about types when bodies are equal *)
compare_under e1 b1 e2 b2
&& compare_under (usubs_lift e1) c1 (usubs_lift e2) c2
| App (c1, l1), App (c2, l2) -> let len = Array.length l1 in
Int.equal len (Array.length l2)
&& compare_under e1 c1 e2 c2
&& Array.equal_norefl (fun c1 c2 -> compare_under e1 c1 e2 c2) l1 l2
| Proj (p1,_,c1), Proj (p2,_,c2) ->
Projection.CanOrd.equal p1 p2 && compare_under e1 c1 e2 c2
| Evar _, Evar _ -> false
| Const (c1,u1), Const (c2,u2) -> (* The args length currently isn't used but may as well pass it. *)
Constant.CanOrd.equal c1 c2 && eq_universes e1 e2 u1 u2
| Ind (c1,u1), Ind (c2,u2) -> Ind.CanOrd.equal c1 c2 && eq_universes e1 e2 u1 u2
| Construct (c1,u1), Construct (c2,u2) ->
Construct.CanOrd.equal c1 c2 && eq_universes e1 e2 u1 u2
| Case _, Case _ | Fix _, Fix _ | CoFix _, CoFix _ -> false(* todo some other time *)
| Array(_,t1,def1,ty1), Array(_,t2,def2,ty2) ->
Array.equal_norefl (fun c1 c2 -> compare_under e1 c1 e2 c2) t1 t2
&& compare_under e1 def1 e2 def2
&& compare_under e1 ty1 e2 ty2
| (Rel _ | Meta _ | Var _ | Sort _ | Prod _ | Lambda _ | LetIn _ | App _
| Proj _ | Evar _ | Const _ | Ind _ | Construct _ | Case _ | Fix _
| CoFix _ | Int _ | Float _ | String _ | Array _), _ -> false
let assert_reduced_constructor s = ifnot @@ CList.is_empty s then
CErrors.anomaly Pp.(str "conversion was given unreduced term (FConstruct).")
(* Conversion between [lft1]term1 and [lft2]term2 *) let rec ccnv cv_pb l2r infos lft1 lft2 term1 term2 cuniv = let fast = fast_test lft1 term1 lft2 term2 in if fast then cuniv else
eqappr cv_pb l2r infos (lft1, (term1,[])) (lft2, (term2,[])) cuniv
(* Conversion between [lft1](hd1 v1) and [lft2](hd2 v2) *) and eqappr cv_pb l2r infos (lft1,st1) (lft2,st2) cuniv =
Control.check_for_interrupt (); (* First head reduce both terms *) let ninfos = infos_with_reds infos.cnv_inf RedFlags.betaiotazeta in let appr1 = whd_stack ninfos infos.lft_tab (fst st1) (snd st1) in let appr2 = whd_stack ninfos infos.rgt_tab (fst st2) (snd st2) in
eqwhnf cv_pb l2r infos (lft1, appr1) (lft2, appr2) cuniv
(* assumes that appr1 and appr2 are in whnf *) and eqwhnf cv_pb l2r infos (lft1, (hd1, v1) as appr1) (lft2, (hd2, v2) as appr2) cuniv = (** We delay the computation of the lifts that apply to the head of the term
with [el_stack] inside the branches where they are actually used. *) (** Irrelevant terms are guaranteed to be [FIrrelevant], except for [FFlex],
[FRel] and [FLambda]. Those ones are handled specifically below. *) match (fterm_of hd1, fterm_of hd2) with (* case of leaves *)
| (FAtom a1, FAtom a2) ->
(match kind a1, kind a2 with
| (Sort s1, Sort s2) -> ifnot (is_empty_stack v1 && is_empty_stack v2) then (* May happen because we convert application right to left *) raise NotConvertible;
fail_check infos @@ sort_cmp_universes (info_env infos.cnv_inf) cv_pb s1 s2 cuniv
| (Meta n, Meta m) -> if Int.equal n m then convert_stacks l2r infos lft1 lft2 v1 v2 cuniv elseraise NotConvertible
| _ -> raise NotConvertible)
| (FEvar (ev1, args1, env1, _), FEvar (ev2, args2, env2, _)) -> if Evar.equal ev1 ev2 then let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let cuniv = convert_stacks l2r infos lft1 lft2 v1 v2 cuniv in
convert_list l2r infos el1 el2
(List.map (mk_clos env1) args1)
(List.map (mk_clos env2) args2) cuniv elseraise NotConvertible
(* 2 index known to be bound to no constant *)
| (FRel n, FRel m) -> let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let n = reloc_rel n el1 in let m = reloc_rel m el2 in let rn = Range.get (info_relevances infos.cnv_inf) (n - 1) in let rm = Range.get (info_relevances infos.cnv_inf) (m - 1) in if is_irrelevant infos.cnv_inf rn && is_irrelevant infos.cnv_inf rm then let v1 = CClosure.skip_irrelevant_stack infos.cnv_inf v2 in let v2 = CClosure.skip_irrelevant_stack infos.cnv_inf v2 in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv elseif Int.equal n m then
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv else raise NotConvertible
(* 2 constants, 2 local defined vars or 2 defined rels *)
| (FFlex fl1, FFlex fl2) ->
(try let nargs = same_args_size v1 v2 in let cuniv = conv_table_key infos ~nargs fl1 fl2 cuniv in let () = if irr_flex infos.cnv_inf fl1 thenraise NotConvertible (* trigger the fallback *) in let mask = if infos.cnv_typ thenmatch fl1 with
| ConstKey _ -> get_ref_mask infos.cnv_inf infos.lft_tab fl1
| RelKey _ | VarKey _ -> [||] else [||] in
convert_stacks ~mask l2r infos lft1 lft2 v1 v2 cuniv with NotConvertible | NotConvertibleTrace _ -> let r1 = unfold_ref_with_args infos.cnv_inf infos.lft_tab fl1 v1 in let r2 = unfold_ref_with_args infos.cnv_inf infos.rgt_tab fl2 v2 in match r1, r2 with
| None, None -> raise NotConvertible
| Some (t1, v1), Some (t2, v2) -> (* else the oracle tells which constant is to be expanded *) let oracle = CClosure.oracle_of_infos infos.cnv_inf in let to_er fl = match fl with
| ConstKey (c, _) -> Some (Conv_oracle.EvalConstRef c)
| VarKey id -> Some (Conv_oracle.EvalVarRef id)
| RelKey _ -> None in let ninfos = infos_with_reds infos.cnv_inf RedFlags.betaiotazeta in let () = Control.check_for_interrupt () in if Conv_oracle.oracle_order oracle l2r (to_er fl1) (to_er fl2) then let appr1 = whd_stack ninfos infos.lft_tab t1 v1 in
eqwhnf cv_pb l2r infos (lft1, appr1) appr2 cuniv else let appr2 = whd_stack ninfos infos.rgt_tab t2 v2 in
eqwhnf cv_pb l2r infos appr1 (lft2, appr2) cuniv
| Some (t1, v1), None -> letall = RedFlags.(red_add_transparent all (red_transparent (info_flags infos.cnv_inf))) in let t1 = whd_stack (infos_with_reds infos.cnv_inf all) infos.lft_tab t1 v1 in
eqwhnf cv_pb l2r infos (lft1, t1) appr2 cuniv
| None, Some (t2, v2) -> letall = RedFlags.(red_add_transparent all (red_transparent (info_flags infos.cnv_inf))) in let t2 = whd_stack (infos_with_reds infos.cnv_inf all) infos.rgt_tab t2 v2 in
eqwhnf cv_pb l2r infos appr1 (lft2, t2) cuniv
)
| (FProj (p1,r1,c1), FProj (p2, r2, c2)) -> (* Projections: prefer unfolding to first-order unification, which will happen naturally if the terms c1, c2 are not in constructor
form *)
(match unfold_projection infos.cnv_inf p1 r1 with
| Some s1 ->
eqappr cv_pb l2r infos (lft1, (c1, (s1 :: v1))) appr2 cuniv
| None -> match unfold_projection infos.cnv_inf p2 r2 with
| Some s2 ->
eqappr cv_pb l2r infos appr1 (lft2, (c2, (s2 :: v2))) cuniv
| None -> if Projection.Repr.CanOrd.equal (Projection.repr p1) (Projection.repr p2)
&& compare_stack_shape v1 v2 then let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let u1 = ccnv CONV l2r infos el1 el2 c1 c2 cuniv in
convert_stacks l2r infos lft1 lft2 v1 v2 u1 else(* Two projections in WHNF: unfold *) raise NotConvertible)
| (FProj (p1,r1,c1), t2) -> beginmatch unfold_projection infos.cnv_inf p1 r1 with
| Some s1 ->
eqappr cv_pb l2r infos (lft1, (c1, (s1 :: v1))) appr2 cuniv
| None -> beginmatch t2 with
| FFlex fl2 -> beginmatch unfold_ref_with_args infos.cnv_inf infos.rgt_tab fl2 v2 with
| Some t2 ->
eqappr cv_pb l2r infos appr1 (lft2, t2) cuniv
| None -> raise NotConvertible end
| _ -> raise NotConvertible end end
| (t1, FProj (p2,r2,c2)) -> beginmatch unfold_projection infos.cnv_inf p2 r2 with
| Some s2 ->
eqappr cv_pb l2r infos appr1 (lft2, (c2, (s2 :: v2))) cuniv
| None -> beginmatch t1 with
| FFlex fl1 -> beginmatch unfold_ref_with_args infos.cnv_inf infos.lft_tab fl1 v1 with
| Some t1 ->
eqappr cv_pb l2r infos (lft1, t1) appr2 cuniv
| None -> raise NotConvertible end
| _ -> raise NotConvertible end end
(* other constructors *)
| (FLambda _, FLambda _) -> (* Inconsistency: we tolerate that v1, v2 contain shift and update but
we throw them away *) ifnot (is_empty_stack v1 && is_empty_stack v2) then
anomaly (Pp.str "conversion was given ill-typed terms (FLambda)."); let (x1,ty1,bd1) = destFLambda mk_clos hd1 in let (_,ty2,bd2) = destFLambda mk_clos hd2 in let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let cuniv = ccnv CONV l2r infos el1 el2 ty1 ty2 cuniv in
ccnv CONV l2r (push_relevance infos x1) (el_lift el1) (el_lift el2) bd1 bd2 cuniv
| (FProd (x1, c1, c2, e), FProd (_, c'1, c'2, e')) -> ifnot (is_empty_stack v1 && is_empty_stack v2) then (* May happen because we convert application right to left *) raise NotConvertible; (* Luo's system *) let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let cuniv = ccnv CONV l2r infos el1 el2 c1 c'1 cuniv in let x1 = usubst_binder e x1 in
ccnv cv_pb l2r (push_relevance infos x1) (el_lift el1) (el_lift el2) (mk_clos (usubs_lift e) c2) (mk_clos (usubs_lift e') c'2) cuniv
(* Eta-expansion on the fly *)
| (FLambda _, _) -> let () = match v1 with
| [] -> ()
| _ ->
anomaly (Pp.str "conversion was given unreduced term (FLambda).") in let (x1,_ty1,bd1) = destFLambda mk_clos hd1 in let infos = push_relevance infos x1 in
eqappr CONV l2r infos
(el_lift lft1, (bd1, [])) (el_lift lft2, (hd2, eta_expand_stack infos.cnv_inf x1 v2)) cuniv
| (_, FLambda _) -> let () = match v2 with
| [] -> ()
| _ ->
anomaly (Pp.str "conversion was given unreduced term (FLambda).") in let (x2,_ty2,bd2) = destFLambda mk_clos hd2 in let infos = push_relevance infos x2 in
eqappr CONV l2r infos
(el_lift lft1, (hd1, eta_expand_stack infos.cnv_inf x2 v1)) (el_lift lft2, (bd2, [])) cuniv
(* only one constant, defined var or defined rel *)
| (FFlex fl1, c2) -> beginmatch unfold_ref_with_args infos.cnv_inf infos.lft_tab fl1 v1 with
| Some (def1,v1) -> (** By virtue of the previous case analyses, we know [c2] is rigid. Conversion check to rigid terms eventually implies full weak-head reduction, so instead of repeatedly performing small-step
unfoldings, we perform reduction with all flags on. *) letall = RedFlags.(red_add_transparent all (red_transparent (info_flags infos.cnv_inf))) in let r1 = whd_stack (infos_with_reds infos.cnv_inf all) infos.lft_tab def1 v1 in
eqwhnf cv_pb l2r infos (lft1, r1) appr2 cuniv
| None ->
(match c2 with
| FConstruct (((ind2,1),u2),_) -> let () = assert_reduced_constructor v2 in
(try let v2, v1 =
eta_expand_ind_stack (info_env infos.cnv_inf) (ind2,u2) hd2 (snd appr1) in convert_stacks l2r infos lft1 lft2 v1 v2 cuniv with Not_found -> raise NotConvertible)
| _ -> raise NotConvertible) end
| (c1, FFlex fl2) -> beginmatch unfold_ref_with_args infos.cnv_inf infos.rgt_tab fl2 v2 with
| Some (def2, v2) -> (** Symmetrical case of above. *) letall = RedFlags.(red_add_transparent all (red_transparent (info_flags infos.cnv_inf))) in let r2 = whd_stack (infos_with_reds infos.cnv_inf all) infos.rgt_tab def2 v2 in
eqwhnf cv_pb l2r infos appr1 (lft2, r2) cuniv
| None -> match c1 with
| FConstruct (((ind1,1),u1),_) -> let () = assert_reduced_constructor v1 in
(trylet v1, v2 =
eta_expand_ind_stack (info_env infos.cnv_inf) (ind1,u1) hd1 (snd appr2) in convert_stacks l2r infos lft1 lft2 v1 v2 cuniv with Not_found -> raise NotConvertible)
| _ -> raise NotConvertible end
(* Inductive types: MutInd MutConstruct Fix Cofix *)
| (FInd (ind1,u1 as pind1), FInd (ind2,u2 as pind2)) -> if Ind.CanOrd.equal ind1 ind2 then if UVars.Instance.is_empty u1 || UVars.Instance.is_empty u2 then let cuniv = fail_check infos @@ convert_instances ~flex:false u1 u2 cuniv in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv else let mind = Environ.lookup_mind (fst ind1) (info_env infos.cnv_inf) in let nargs = same_args_size v1 v2 in match fail_check infos @@ convert_inductives cv_pb (mind, snd ind1) nargs u1 u2 cuniv with
| cuniv -> convert_stacks l2r infos lft1 lft2 v1 v2 cuniv
| exception MustExpand -> let env = info_env infos.cnv_inf in let hd1 = eta_expand_ind env pind1 in let hd2 = eta_expand_ind env pind2 in
eqappr cv_pb l2r infos (lft1,(hd1,v1)) (lft2,(hd2,v2)) cuniv elseraise NotConvertible
| (FConstruct (((ind1,j1),u1 as pctor1,args1)), FConstruct (((ind2,j2),u2 as pctor2),args2)) -> let () = assert_reduced_constructor v1 in let () = assert_reduced_constructor v2 in let nargs = Array.length args1 in let () = ifnot @@ Int.equal nargs (Array.length args2) thenraise NotConvertible in let v1 = append_stack args1 v1 in let v2 = append_stack args2 v2 in if Int.equal j1 j2 && Ind.CanOrd.equal ind1 ind2 then if UVars.Instance.is_empty u1 || UVars.Instance.is_empty u2 then let cuniv = fail_check infos @@ convert_instances ~flex:false u1 u2 cuniv in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv else let mind = Environ.lookup_mind (fst ind1) (info_env infos.cnv_inf) in match fail_check infos @@ convert_constructors (mind, snd ind1, j1) nargs u1 u2 cuniv with
| cuniv -> convert_stacks l2r infos lft1 lft2 v1 v2 cuniv
| exception MustExpand -> let env = info_env infos.cnv_inf in let hd1 = eta_expand_constructor env pctor1 in let hd2 = eta_expand_constructor env pctor2 in
eqappr cv_pb l2r infos (lft1,(hd1,v1)) (lft2,(hd2,v2)) cuniv elseraise NotConvertible
(* Eta expansion of records *)
| (FConstruct (((ind1,j1),u1), _),_) -> let () = assert_reduced_constructor v1 in (* records only have 1 constructor *) let () = ifnot @@ Int.equal j1 1 thenraise NotConvertible in
(try let v1, v2 =
eta_expand_ind_stack (info_env infos.cnv_inf) (ind1,u1) hd1 (snd appr2) in convert_stacks l2r infos lft1 lft2 v1 v2 cuniv with Not_found -> raise NotConvertible)
| (_, FConstruct (((ind2,j2),u2),_)) -> let () = assert_reduced_constructor v2 in (* records only have 1 constructor *) let () = ifnot @@ Int.equal j2 1 thenraise NotConvertible in
(try let v2, v1 =
eta_expand_ind_stack (info_env infos.cnv_inf) (ind2,u2) hd2 (snd appr1) in convert_stacks l2r infos lft1 lft2 v1 v2 cuniv with Not_found -> raise NotConvertible)
| (FFix (((op1, i1),(na1,tys1,cl1)),e1), FFix(((op2, i2),(_,tys2,cl2)),e2)) -> if Int.equal i1 i2 && Array.equal Int.equal op1 op2 then let n = Array.length cl1 in let fty1 = Array.map (mk_clos e1) tys1 in let fty2 = Array.map (mk_clos e2) tys2 in let fcl1 = Array.map (mk_clos (usubs_liftn n e1)) cl1 in let fcl2 = Array.map (mk_clos (usubs_liftn n e2)) cl2 in let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let cuniv = convert_vect l2r infos el1 el2 fty1 fty2 cuniv in let cuniv = let na1 = Array.map (usubst_binder e1) na1 in let infos = push_relevances infos na1 in
convert_vect l2r infos
(el_liftn n el1) (el_liftn n el2) fcl1 fcl2 cuniv in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv elseraise NotConvertible
| (FCoFix ((op1,(na1,tys1,cl1)),e1), FCoFix((op2,(_,tys2,cl2)),e2)) -> if Int.equal op1 op2 then let n = Array.length cl1 in let fty1 = Array.map (mk_clos e1) tys1 in let fty2 = Array.map (mk_clos e2) tys2 in let fcl1 = Array.map (mk_clos (usubs_liftn n e1)) cl1 in let fcl2 = Array.map (mk_clos (usubs_liftn n e2)) cl2 in let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let cuniv = convert_vect l2r infos el1 el2 fty1 fty2 cuniv in let cuniv = let na1 = Array.map (usubst_binder e1) na1 in let infos = push_relevances infos na1 in
convert_vect l2r infos
(el_liftn n el1) (el_liftn n el2) fcl1 fcl2 cuniv in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv elseraise NotConvertible
| FCaseInvert (ci1,u1,pms1,p1,iv1,_,br1,e1), FCaseInvert (ci2,u2,pms2,p2,iv2,_,br2,e2) ->
(ifnot (Ind.CanOrd.equal ci1.ci_ind ci2.ci_ind) thenraise NotConvertible); let el1 = el_stack lft1 v1 and el2 = el_stack lft2 v2 in let fold c1 c2 cuniv = ccnv CONV l2r infos el1 el2 c1 c2 cuniv in (** FIXME: cache the presence of let-bindings in the case_info *) let mind = Environ.lookup_mind (fst ci1.ci_ind) (info_env infos.cnv_inf) in let mip = mind.Declarations.mind_packets.(snd ci1.ci_ind) in let cuniv = let ind = (mind,snd ci1.ci_ind) in let nargs = inductive_cumulativity_arguments ind in let u1 = CClosure.usubst_instance e1 u1 in let u2 = CClosure.usubst_instance e2 u2 in
fail_check infos @@ convert_inductives CONV ind nargs u1 u2 cuniv in let pms1 = mk_clos_vect e1 pms1 in let pms2 = mk_clos_vect e2 pms2 in let cuniv = Array.fold_right2 fold pms1 pms2 cuniv in let cuniv = Array.fold_right2 fold (get_invert iv1) (get_invert iv2) cuniv in let cuniv = convert_return_clause mind mip l2r infos e1 e2 el1 el2 u1 u2 pms1 pms2 p1 p2 cuniv in
convert_branches mind mip l2r infos e1 e2 el1 el2 u1 u2 pms1 pms2 br1 br2 cuniv
| FArray (u1,t1,ty1), FArray (u2,t2,ty2) -> let len = Parray.length_int t1 in ifnot (Int.equal len (Parray.length_int t2)) thenraise NotConvertible; let cuniv = fail_check infos @@ convert_instances_cumul CONV [|UVars.Variance.Irrelevant|] u1 u2 cuniv in let el1 = el_stack lft1 v1 in let el2 = el_stack lft2 v2 in let cuniv = ccnv CONV l2r infos el1 el2 ty1 ty2 cuniv in let cuniv = Parray.fold_left2 (fun u v1 v2 -> ccnv CONV l2r infos el1 el2 v1 v2 u) cuniv t1 t2 in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv
| (FRel n1, FIrrelevant) -> let n1 = reloc_rel n1 (el_stack lft1 v1) in let r1 = Range.get (info_relevances infos.cnv_inf) (n1 - 1) in if is_irrelevant infos.cnv_inf r1 then let v1 = CClosure.skip_irrelevant_stack infos.cnv_inf v1 in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv elseraise NotConvertible
| (FIrrelevant, FRel n2) -> let n2 = reloc_rel n2 (el_stack lft2 v2) in let r2 = Range.get (info_relevances infos.cnv_inf) (n2 - 1) in if is_irrelevant infos.cnv_inf r2 then let v2 = CClosure.skip_irrelevant_stack infos.cnv_inf v2 in
convert_stacks l2r infos lft1 lft2 v1 v2 cuniv elseraise NotConvertible
and convert_stacks ?(mask = [||]) l2r infos lft1 lft2 stk1 stk2 cuniv = let f (l1, t1) (l2, t2) cuniv = ccnv CONV l2r infos l1 l2 t1 t2 cuniv in let rec cmp_rec nargs pstk1 pstk2 cuniv = match (pstk1,pstk2) with
| (z1::s1, z2::s2) -> (* Stacks are known to have the same argument size *) let rnargs = match z1 with
| Zlapp a -> if nargs < 0 then -1 else nargs + Array.length a
| Zlproj _ | Zlfix _ | Zlcase _ | Zlprimitive _ -> -1 in let cu1 = cmp_rec rnargs s1 s2 cuniv in
(match (z1,z2) with
| (Zlapp a1,Zlapp a2) -> if nargs < 0 then
Array.fold_right2 f a1 a2 cu1 else let rec fold i cu = if i < 0 then cu elseif nargs + i < Array.length mask && not mask.(nargs + i) then
fold (i - 1) cu (* skip runtime irrelevant argument *) else let cu = f a1.(i) a2.(i) cu in
fold (i - 1) cu in
fold (Array.length a1 - 1) cu1
| (Zlproj (c1,_l1),Zlproj (c2,_l2)) -> ifnot (Projection.Repr.CanOrd.equal c1 c2) then raise NotConvertible else cu1
| (Zlfix(fx1,a1),Zlfix(fx2,a2)) -> let cu2 = f fx1 fx2 cu1 in
cmp_rec (-1) a1 a2 cu2
| (Zlcase(ci1,l1,u1,pms1,p1,br1,e1),Zlcase(ci2,l2,u2,pms2,p2,br2,e2)) -> ifnot (Ind.CanOrd.equal ci1.ci_ind ci2.ci_ind) then raise NotConvertible; let cu = cu1 in (** FIXME: cache the presence of let-bindings in the case_info *) let mind = Environ.lookup_mind (fst ci1.ci_ind) (info_env infos.cnv_inf) in let mip = mind.Declarations.mind_packets.(snd ci1.ci_ind) in let cu = if UVars.Instance.is_empty u1 || UVars.Instance.is_empty u2 then
convert_instances ~flex:false u1 u2 cu else let u1 = CClosure.usubst_instance e1 u1 in let u2 = CClosure.usubst_instance e2 u2 in match mind.Declarations.mind_variance with
| None -> convert_instances ~flex:false u1 u2 cu
| Some variances -> convert_instances_cumul CONV variances u1 u2 cu in let cu = fail_check infos cu in let pms1 = mk_clos_vect e1 pms1 in let pms2 = mk_clos_vect e2 pms2 in let fold_params c1 c2 accu = f (l1, c1) (l2, c2) accu in let cu = Array.fold_right2 fold_params pms1 pms2 cu in let cu = convert_return_clause mind mip l2r infos e1 e2 l1 l2 u1 u2 pms1 pms2 p1 p2 cu in
convert_branches mind mip l2r infos e1 e2 l1 l2 u1 u2 pms1 pms2 br1 br2 cu
| (Zlprimitive(op1,_,rargs1,kargs1),Zlprimitive(op2,_,rargs2,kargs2)) -> ifnot (CPrimitives.equal op1 op2) thenraise NotConvertible else let cu2 = List.fold_right2 f rargs1 rargs2 cu1 in let fk (_,a1) (_,a2) cu = f a1 a2 cu in List.fold_right2 fk kargs1 kargs2 cu2
| ((Zlapp _ | Zlproj _ | Zlfix _| Zlcase _| Zlprimitive _), _) -> assert false)
| _ -> cuniv in if compare_stack_shape stk1 stk2 then let nargs = if Array.is_empty mask then -1 else 0 in
cmp_rec nargs (pure_stack lft1 stk1) (pure_stack lft2 stk2) cuniv elseraise NotConvertible
and convert_vect l2r infos lft1 lft2 v1 v2 cuniv = let lv1 = Array.length v1 in let lv2 = Array.length v2 in if Int.equal lv1 lv2 then let rec fold n cuniv = if n >= lv1 then cuniv else let cuniv = ccnv CONV l2r infos lft1 lft2 v1.(n) v2.(n) cuniv in
fold (n+1) cuniv in
fold 0 cuniv elseraise NotConvertible
and convert_under_context l2r infos e1 e2 lft1 lft2 ctx (nas1, c1) (nas2, c2) cu = let n = Array.length nas1 in let () = assert (Int.equal n (Array.length nas2)) in let n, e1, e2 = match ctx with
| None -> (* nolet *) let e1 = usubs_liftn n e1 in let e2 = usubs_liftn n e2 in
(n, e1, e2)
| Some (ctx, u1, u2, args1, args2) -> let n1, e1 = esubst_of_context ctx u1 args1 e1 in let n2, e2 = esubst_of_context ctx u2 args2 e2 in let () = assert (Int.equal n1 n2) in
n1, e1, e2 in let lft1 = el_liftn n lft1 in let lft2 = el_liftn n lft2 in let infos = push_relevances infos (Array.map (usubst_binder e1) nas1) in
ccnv CONV l2r infos lft1 lft2 (mk_clos e1 c1) (mk_clos e2 c2) cu
and convert_return_clause mib mip l2r infos e1 e2 l1 l2 u1 u2 pms1 pms2 p1 p2 cu = let ctx = if Int.equal mip.mind_nrealargs mip.mind_nrealdecls then None else let ctx, _ = List.chop mip.mind_nrealdecls mip.mind_arity_ctxt in let pms1 = inductive_subst mib u1 pms1 in let pms2 = inductive_subst mib u1 pms2 in letopen Context.Rel.Declaration in (* Add the inductive binder *) let ctx = None :: List.map get_value ctx in
Some (ctx, u1, u2, pms1, pms2) in
convert_under_context l2r infos e1 e2 l1 l2 ctx (fst p1) (fst p2) cu
and convert_branches mib mip l2r infos e1 e2 lft1 lft2 u1 u2 pms1 pms2 br1 br2 cuniv = let fold i (ctx, _) cuniv = let ctx = if Int.equal mip.mind_consnrealdecls.(i) mip.mind_consnrealargs.(i) then None else let ctx, _ = List.chop mip.mind_consnrealdecls.(i) ctx in let ctx = List.map Context.Rel.Declaration.get_value ctx in let pms1 = inductive_subst mib u1 pms1 in let pms2 = inductive_subst mib u2 pms2 in
Some (ctx, u1, u2, pms1, pms2) in let c1 = br1.(i) in let c2 = br2.(i) in
convert_under_context l2r infos e1 e2 lft1 lft2 ctx c1 c2 cuniv in
Array.fold_right_i fold mip.mind_nf_lc cuniv
let clos_gen_conv (type err) ~typed trans cv_pb l2r evars env graph univs t1 t2 =
NewProfile.profile "Conversion"beginfun () -> let reds = RedFlags.red_add_transparent RedFlags.betaiotazeta trans in let infos = create_conv_infos ~univs:graph ~evars reds env in let module Error = structtype payload += Error of err endin let box e = Error.Error e in let infos = {
cnv_inf = infos;
cnv_typ = typed;
lft_tab = create_tab ();
rgt_tab = create_tab ();
err_ret = box;
} in try Result.Ok (ccnv cv_pb l2r infos el_id el_id (inject t1) (inject t2) univs) with
| NotConvertible -> Result.Error None
| NotConvertibleTrace (Error.Error e) -> Result.Error (Some e)
| NotConvertibleTrace _ -> assert false end ()
let check_eq univs u u' = if UGraph.check_eq_sort univs u u' then Result.Ok univs else Result.Error None
let check_leq univs u u' = if UGraph.check_leq_sort univs u u' then Result.Ok univs else Result.Error None
let checked_sort_cmp_universes _env pb s0 s1 univs = match pb with
| CUMUL -> check_leq univs s0 s1
| CONV -> check_eq univs s0 s1
let check_convert_instances ~flex:_ u u' univs = if UGraph.check_eq_instances univs u u' then Result.Ok univs else Result.Error None
(* general conversion and inference functions *) let check_inductive_instances cv_pb variance u1 u2 univs = let qcsts, ucsts = get_cumulativity_constraints cv_pb variance u1 u2 in if Sorts.QConstraints.trivial qcsts && (UGraph.check_constraints ucsts univs) then Result.Ok univs else Result.Error None
let () = let conv infos tab a b = try let box = Empty.abort in let univs = info_univs infos in let infos = { cnv_inf = infos; cnv_typ = true; lft_tab = tab; rgt_tab = tab; err_ret = box } in let univs', _ = ccnv CONV false infos el_id el_id a b
(univs, checked_universes) in
assert (univs==univs'); true with
| NotConvertible -> false
| NotConvertibleTrace _ -> assert false in
CClosure.set_conv conv
let gen_conv ~typed cv_pb ?(l2r=false) ?(reds=TransparentState.full) env ?(evars=default_evar_handler env) t1 t2 = let univs = Environ.universes env in let b = if cv_pb = CUMUL then leq_constr_univs univs t1 t2 else eq_constr_univs univs t1 t2 in if b then Result.Ok () elsematch clos_gen_conv ~typed reds cv_pb l2r evars env univs (univs, checked_universes) t1 t2 with
| Result.Ok (_ : UGraph.t * (UGraph.t, Empty.t) universe_compare)-> Result.Ok ()
| Result.Error None -> Result.Error ()
| Result.Error (Some e) -> Empty.abort e
let conv = gen_conv ~typed:false CONV let conv_leq = gen_conv ~typed:false CUMUL
let generic_conv cv_pb ~l2r reds env ?(evars=default_evar_handler env) univs t1 t2 = let graph = Environ.universes env in match clos_gen_conv ~typed:false reds cv_pb l2r evars env graph univs t1 t2 with
| Result.Ok (s, _) -> Result.Ok s
| Result.Error e -> Result.Error e
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.