diff options
| author | msozeau | 2006-04-07 15:08:12 +0000 |
|---|---|---|
| committer | msozeau | 2006-04-07 15:08:12 +0000 |
| commit | 26ca22424b286f5ff22a1fa97c38d15e224b3dc2 (patch) | |
| tree | 190e12acf505e47d3a81ef0fd625a405ff782c04 | |
| parent | 5f9b04da0f3c72f4b582cd094edae721b1bc9a9e (diff) | |
- Documentation of the Program tactics.
- Fixes to the subtac implementation, utility tactic to apply existentials to a function and build a dependent sum out of name, constr lists.
Also defined a Utils coq module for tactics related to subsets and the projections for ex in Prop.
- Enhancements to inference algorithm added but not used in the default version as there are some remaining bugs.
git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/coq/trunk@8688 85f007b7-540e-0410-9357-904b9bb8a0f7
33 files changed, 1575 insertions, 442 deletions
@@ -1014,7 +1014,7 @@ JPROVERVO= CCVO= -SUBTACVO=contrib/subtac/FixSub.vo +SUBTACVO=contrib/subtac/FixSub.vo contrib/subtac/Utils.vo RTAUTOVO = \ contrib/rtauto/Bintree.vo contrib/rtauto/Rtauto.vo diff --git a/contrib/subtac/FixSub.v b/contrib/subtac/FixSub.v index 6face72d11..a9a22ca76e 100644 --- a/contrib/subtac/FixSub.v +++ b/contrib/subtac/FixSub.v @@ -21,8 +21,10 @@ End FixPoint. End Well_founded. -(*Check Fix_sub.*) - Notation "'forall' { x : A | P } , Q" := - (forall x:{x:A|P}, (fun x => Q) (proj1_sig x)) - (at level 200, x ident, right associativity).
\ No newline at end of file + (forall x:{x:A|P}, Q) + (at level 200, x ident, right associativity). + +Notation "'fun' { x : A | P } => Q" := + (fun x:{x:A|P} => Q) + (at level 200, x ident, right associativity). diff --git a/contrib/subtac/Utils.v b/contrib/subtac/Utils.v new file mode 100644 index 0000000000..ceb8279fc2 --- /dev/null +++ b/contrib/subtac/Utils.v @@ -0,0 +1,22 @@ +Set Implicit Arguments. +Lemma subset_simpl : forall (A : Set) (P : A -> Prop) + (t : sig P), P (proj1_sig t). +Proof. +intros. +induction t. + simpl ; auto. +Qed. + +Definition ex_pi1 (A : Prop) (P : A -> Prop) (t : ex P) : A. +intros. +induction t. +exact x. +Defined. + +Lemma ex_pi2 : forall (A : Prop) (P : A -> Prop) (t : ex P), + P (ex_pi1 t). +intros A P. +dependent inversion t. +simpl. +exact p. +Defined. diff --git a/contrib/subtac/eterm.ml b/contrib/subtac/eterm.ml index ced1756f13..5703c0efc1 100644 --- a/contrib/subtac/eterm.ml +++ b/contrib/subtac/eterm.ml @@ -10,10 +10,14 @@ open Names open Evd open List open Pp +open Util let reverse_array arr = Array.of_list (List.rev (Array.to_list arr)) +let trace s = + if !Options.debug then msgnl s + else () (** Utilities to find indices in lists *) let list_index x l = @@ -37,14 +41,14 @@ let subst_evars evs n t = | [] -> raise Not_found in let (idx, hyps, v) = aux 0 evs in - idx + 1, hyps + n + idx + 1, hyps in let rec substrec depth c = match kind_of_term c with | Evar (k, args) -> (try let index, hyps = evar_info k in - msgnl (str "Evar " ++ int k ++ str " found, applied to " ++ int (Array.length args) ++ str "arguments," ++ - int (List.length hyps) ++ str " hypotheses"); + trace (str "Evar " ++ int k ++ str " found, applied to " ++ int (Array.length args) ++ str "arguments," ++ + int (List.length hyps) ++ str " hypotheses"); let ex = mkRel (index + depth) in (* Evar arguments are created in inverse order, @@ -63,8 +67,7 @@ let subst_evars evs n t = in mkApp (ex, Array.of_list args) with Not_found -> - msgnl (str "Evar " ++ int k ++ str " not found!!!"); - c) + anomaly ("eterm: existential variable " ^ string_of_int k ^ " not found")) | _ -> map_constr_with_binders succ substrec depth c in substrec 0 t @@ -100,29 +103,33 @@ let etype_of_evar evs ev hyps = open Tacticals -let eterm evm t = +let eterm_term evm t tycon = (* 'Serialize' the evars, we assume that the types of the existentials refer to previous existentials in the list only *) let evl = to_list evm in let evts = (* Remove existential variables in types and build the corresponding products *) - fold_left - (fun l (id, ev) -> + fold_right + (fun (id, ev) l -> let hyps = Environ.named_context_of_val ev.evar_hyps in let y' = (id, hyps, etype_of_evar l ev hyps) in y' :: l) - [] evl + evl [] in let t' = (* Substitute evar refs in the term by De Bruijn indices *) subst_evars evts 0 t in - let t'' = - (* Make the lambdas 'generalizing' the existential variables *) - fold_left - (fun acc (id, _, c) -> - mkLambda (Name (id_of_string ("Evar" ^ string_of_int id)), - c, acc)) - t' evts + let evar_names = + List.map (fun (id, _, c) -> (id_of_string ("Evar" ^ string_of_int id)), c) evts + in + let evar_bl = + List.map (fun (id, c) -> Name id, None, c) evar_names + in + let anon_evar_bl = List.map (fun (_, x, y) -> (Anonymous, x, y)) evar_bl in + (* Generalize over the existential variables *) + let t'' = Termops.it_mkLambda_or_LetIn t' evar_bl + and tycon = option_app + (fun typ -> Termops.it_mkProd_wo_LetIn typ anon_evar_bl) tycon in let _declare_evar (id, c) = let id = id_of_string ("Evar" ^ string_of_int id) in @@ -133,12 +140,29 @@ let eterm evm t = let id = id_of_string ("Evar" ^ string_of_int id) in tclTHEN acc (Tactics.assert_tac false (Name id) c) in - msgnl (str "Term given to eterm" ++ spc () ++ + trace (str "Term given to eterm" ++ spc () ++ Termops.print_constr_env (Global.env ()) t); - msgnl (str "Term constructed in eterm" ++ spc () ++ + trace (str "Term constructed in eterm" ++ spc () ++ Termops.print_constr_env (Global.env ()) t''); - Tactics.apply_term t'' (List.map (fun _ -> Evarutil.mk_new_meta ()) evts) + ignore(option_app + (fun typ -> + trace (str "Type :" ++ spc () ++ + Termops.print_constr_env (Global.env ()) typ)) + tycon); + t'', tycon, evar_names + +let mkMetas n = + let rec aux i acc = + if i > 0 then aux (pred i) (Evarutil.mk_new_meta () :: acc) + else acc + in aux n [] + +let eterm evm t (tycon : types option) = + let t, tycon, evs = eterm_term evm t tycon in + match tycon with + Some typ -> Tactics.apply_term (mkCast (t, DEFAULTcast, typ)) [] + | None -> Tactics.apply_term t (mkMetas (List.length evs)) open Tacmach -let etermtac (evm, t) = eterm evm t +let etermtac (evm, t) = eterm evm t None diff --git a/contrib/subtac/eterm.mli b/contrib/subtac/eterm.mli index bcb9b4c56d..d6f058ebda 100644 --- a/contrib/subtac/eterm.mli +++ b/contrib/subtac/eterm.mli @@ -9,5 +9,12 @@ (*i $Id$ i*) open Tacmach +open Term +open Evd +open Names -val etermtac : Evd.open_constr -> tactic +val mkMetas : int -> constr list + +val eterm_term : evar_map -> constr -> types option -> constr * types option * (identifier * types) list + +val etermtac : open_constr -> tactic diff --git a/contrib/subtac/g_subtac.ml4 b/contrib/subtac/g_subtac.ml4 index d9c7a8c023..2f47608c10 100644 --- a/contrib/subtac/g_subtac.ml4 +++ b/contrib/subtac/g_subtac.ml4 @@ -49,13 +49,6 @@ GEXTEND Gram ; END -(* type wf_proof_type_argtype = (Subtac_utils.wf_proof_type, constr_expr, Tacexpr.raw_tactic_expr) Genarg.abstract_argument_type *) - -(* let (wit_subtac_wf_proof_type : wf_proof_type_argtype), *) -(* (globwit_subtac_wf_proof_type : wf_proof_type_argtype), *) -(* (rawwit_subtac_wf_proof_type : wf_proof_type_argtype) = *) -(* Genarg.create_arg "subtac_wf_proof_type" *) - type gallina_loc_argtype = (Vernacexpr.vernac_expr located, constr_expr, Tacexpr.raw_tactic_expr) Genarg.abstract_argument_type let (wit_subtac_gallina_loc : gallina_loc_argtype), @@ -63,18 +56,6 @@ let (wit_subtac_gallina_loc : gallina_loc_argtype), (rawwit_subtac_gallina_loc : gallina_loc_argtype) = Genarg.create_arg "subtac_gallina_loc" -(* type subtac_recdef_argtype = (Subtac_utils.recursion_order option, constr_expr, Tacexpr.raw_tactic_expr) Genarg.abstract_argument_type *) - -(* let (wit_subtac_recdef : subtac_recdef_argtype), *) -(* (globwit_subtac_recdef : subtac_recdef_argtype), *) -(* (rawwit_subtac_recdef : subtac_recdef_argtype) = *) -(* Genarg.create_arg "subtac_recdef" *) - -(* VERNAC COMMAND EXTEND SubtacRec *) -(* [ "Recursive" "program" ident(id) subtac_binders(l) subtac_recdef(f) ] -> *) -(* [ Interp.subtac id l Environ.empty_env f ] *) -(* END *) - VERNAC COMMAND EXTEND Subtac [ "Program" subtac_gallina_loc(g) ] -> [ Subtac.subtac g ] diff --git a/contrib/subtac/subtac.ml b/contrib/subtac/subtac.ml index 01c959a47d..8b59b7eff5 100644 --- a/contrib/subtac/subtac.ml +++ b/contrib/subtac/subtac.ml @@ -119,6 +119,7 @@ let subtac (loc, command) = check_required_library ["Coq";"Init";"Datatypes"]; check_required_library ["Coq";"Init";"Specif"]; require_library "Coq.subtac.FixSub"; + require_library "Coq.subtac.Utils"; try match command with VernacDefinition (defkind, (locid, id), expr, hook) -> diff --git a/contrib/subtac/subtac_coercion.ml b/contrib/subtac/subtac_coercion.ml index c605314018..4b4a25e718 100644 --- a/contrib/subtac/subtac_coercion.ml +++ b/contrib/subtac/subtac_coercion.ml @@ -35,7 +35,7 @@ let make_name s = Name (id_of_string s) module Coercion = struct - exception NoCoercion + exception NoSubtacCoercion let rec disc_subset x = match kind_of_term x with @@ -86,6 +86,8 @@ module Coercion = struct | Type _, Prop Null -> Prop Null | _, Type _ -> s2 + let hnf env isevars c = whd_betadeltaiota env (evars_of !isevars) c + let rec mu env isevars t = let rec aux v = match disc_subset v with @@ -101,17 +103,22 @@ module Coercion = struct and coerce loc env isevars (x : Term.constr) (y : Term.constr) : (Term.constr -> Term.constr) option = - trace (str "Coerce called for " ++ (my_print_constr env x) ++ - str " and "++ my_print_constr env y); - + let x = nf_evar (evars_of !isevars) x and y = nf_evar (evars_of !isevars) y in + trace (str "Coerce called for " ++ (my_print_constr env x) ++ + str " and "++ my_print_constr env y ++ + str " with evars: " ++ spc () ++ + my_print_evardefs !isevars); let rec coerce_unify env x y = - if e_cumul env isevars x y then ( + trace (str "coerce_unify from " ++ (my_print_constr env x) ++ + str " to "++ my_print_constr env y); + try + isevars := the_conv_x_leq env x y !isevars; trace (str "Unified " ++ (my_print_constr env x) ++ - str " and "++ my_print_constr env y); + str " and "++ my_print_constr env y); None - ) else coerce' env x y (* head recutions needed *) + with Reduction.NotConvertible -> coerce' env (hnf env isevars x) (hnf env isevars y) and coerce' env x y : (Term.constr -> Term.constr) option = - let subco () = subset_coerce env x y in + let subco () = subset_coerce env isevars x y in trace (str "coerce' from " ++ (my_print_constr env x) ++ str " to "++ my_print_constr env y); match (kind_of_term x, kind_of_term y) with @@ -137,43 +144,78 @@ module Coercion = struct | App (c, l), App (c', l') -> (match kind_of_term c, kind_of_term c' with - Ind i, Ind i' -> + Ind i, Ind i' -> (* Sigma types *) let len = Array.length l in let existS = Lazy.force existS in - if len = Array.length l' && len = 2 - && i = i' && i = Term.destInd existS.typ - then - begin (* Sigma types *) - debug 1 (str "In coerce sigma types"); - let (a, pb), (a', pb') = - pair_of_array l, pair_of_array l' - in - let c1 = coerce_unify env a a' in - let remove_head c = - let (_, _, x) = Term.destProd c in - x - in - let b, b' = remove_head pb, remove_head pb' in - let env' = push_rel (make_name "x", None, a) env in - let c2 = coerce_unify env' b b' in - match c1, c2 with - None, None -> None - | _, _ -> - Some - (fun x -> - let x, y = - app_opt c1 (mkApp (existS.proj1, - [| a; pb; x |])), - app_opt c2 (mkApp (existS.proj2, - [| a; pb'; x |])) - in - mkApp (existS.intro, [| x ; y |])) - end + let prod = Lazy.force prod in + if len = Array.length l' && len = 2 && i = i' + then + if i = Term.destInd existS.typ + then + begin + debug 1 (str "In coerce sigma types"); + let (a, pb), (a', pb') = + pair_of_array l, pair_of_array l' + in + let c1 = coerce_unify env a a' in + let rec remove_head a c = + match kind_of_term c with + | Lambda (n, t, t') -> c, t' + (*| Prod (n, t, t') -> t'*) + | Evar (k, args) -> + let (evs, t) = Evarutil.define_evar_as_lambda !isevars (k,args) in + isevars := evs; + let (n, dom, rng) = destLambda t in + let (domk, args) = destEvar dom in + isevars := evar_define domk a !isevars; + t, rng + | _ -> raise NoSubtacCoercion + in + let (pb, b), (pb', b') = remove_head a pb, remove_head a' pb' in + let env' = push_rel (make_name "x", None, a) env in + let c2 = coerce_unify env' b b' in + match c1, c2 with + None, None -> + trace (str "No coercion needed"); + None + | _, _ -> + Some + (fun x -> + let x, y = + app_opt c1 (mkApp (existS.proj1, + [| a; pb; x |])), + app_opt c2 (mkApp (existS.proj2, + [| a; pb; x |])) + in + mkApp (existS.intro, [| a'; pb'; x ; y |])) + end + else if i = Term.destInd prod.typ then + begin + debug 1 (str "In coerce prod types"); + let (a, b), (a', b') = + pair_of_array l, pair_of_array l' + in + let c1 = coerce_unify env a a' in + let c2 = coerce_unify env b b' in + match c1, c2 with + None, None -> None + | _, _ -> + Some + (fun x -> + let x, y = + app_opt c1 (mkApp (prod.proj1, + [| a; b; x |])), + app_opt c2 (mkApp (prod.proj2, + [| a; b; x |])) + in + mkApp (prod.intro, [| a'; b'; x ; y |])) + end + else subco () else subco () | _ -> subco ()) | _, _ -> subco () - and subset_coerce env x y = + and subset_coerce env isevars x y = match disc_subset x with Some (u, p) -> let c = coerce_unify env u y in @@ -193,15 +235,16 @@ module Coercion = struct (mkApp ((Lazy.force sig_).intro, [| u; p; cx; evar |]))) - | None -> raise NoCoercion + | None -> + raise NoSubtacCoercion + (*isevars := Evd.add_conv_pb (Reduction.CONV, x, y) !isevars; + None*) in coerce_unify env x y - let coerce_itf loc env isevars hj c1 = - let {uj_val = v; uj_type = t} = hj in + let coerce_itf loc env isevars v t c1 = let evars = ref isevars in let coercion = coerce loc env evars t c1 in - !evars, {uj_val = app_opt coercion v; - uj_type = t} + !evars, option_app (app_opt coercion) v, t (* Taken from pretyping/coercion.ml *) @@ -224,9 +267,8 @@ module Coercion = struct in apply_rec [] funj.uj_type argl - exception NoCoercion - (* appliquer le chemin de coercions de patterns p *) + exception NoCoercion let apply_pattern_coercion loc pat p = List.fold_left @@ -275,7 +317,7 @@ module Coercion = struct try let coercef, t = mu env isevars t in (isevars, { uj_val = app_opt coercef j.uj_val; uj_type = t }) - with NoCoercion -> + with NoSubtacCoercion | NoCoercion -> (isevars,j)) let inh_tosort_force loc env isevars j = @@ -297,79 +339,141 @@ module Coercion = struct | _ -> inh_tosort_force loc env isevars j - let inh_coerce_to_fail env isevars c1 hj = - let hj' = + let inh_coerce_to_fail env isevars c1 v t = + let v', t' = try let t1,i1 = class_of1 env (evars_of isevars) c1 in - let t2,i2 = class_of1 env (evars_of isevars) hj.uj_type in + let t2,i2 = class_of1 env (evars_of isevars) t in let p = lookup_path_between (i2,i1) in - apply_coercion env p hj t2 + match v with + Some v -> + let j = apply_coercion env p {uj_val = v; uj_type = t} t2 in + Some j.uj_val, j.uj_type + | None -> None, t with Not_found -> raise NoCoercion in - try (the_conv_x_leq env hj'.uj_type c1 isevars, hj') + try (the_conv_x_leq env t' c1 isevars, v', t') with Reduction.NotConvertible -> raise NoCoercion - let rec inh_conv_coerce_to_fail env isevars hj c1 = - let {uj_val = v; uj_type = t} = hj in - try (the_conv_x_leq env t c1 isevars, hj) - with Reduction.NotConvertible -> - (try - inh_coerce_to_fail env isevars c1 hj + let rec inh_conv_coerce_to_fail loc env isevars v t c1 = + (try + trace (str "inh_conv_coerce_to_fail called for " ++ + Termops.print_constr_env env t ++ str " and "++ spc () ++ + Termops.print_constr_env env c1 ++ str " with evars: " ++ spc () ++ + Evd.pr_evar_defs isevars ++ str " in env: " ++ spc () ++ + Termops.print_env env); + with _ -> ()); + try (the_conv_x_leq env t c1 isevars, v, t) + with Reduction.NotConvertible -> + (try + inh_coerce_to_fail env isevars c1 v t with NoCoercion -> (match kind_of_term (whd_betadeltaiota env (evars_of isevars) t), kind_of_term (whd_betadeltaiota env (evars_of isevars) c1) with | Prod (_,t1,t2), Prod (name,u1,u2) -> - let v' = whd_betadeltaiota env (evars_of isevars) v in + let v' = option_app (whd_betadeltaiota env (evars_of isevars)) v in let (evd',b) = - match kind_of_term v' with - | Lambda (_,v1,v2) -> - (try the_conv_x env v1 u1 isevars, true (* leq v1 u1? *) - with Reduction.NotConvertible -> (isevars, false)) - | _ -> (isevars,false) in - if b - then - let (x,v1,v2) = destLambda v' in - let env1 = push_rel (x,None,v1) env in - let (evd'',h2) = inh_conv_coerce_to_fail env1 evd' - {uj_val = v2; uj_type = t2 } u2 in - (evd'',{ uj_val = mkLambda (x, v1, h2.uj_val); - uj_type = mkProd (x, v1, h2.uj_type) }) - else - (* Mismatch on t1 and u1 or not a lambda: we eta-expand *) - (* we look for a coercion c:u1->t1 s.t. [name:u1](v' (c x)) *) - (* has type (name:u1)u2 (with v' recursively obtained) *) - let name = (match name with - | Anonymous -> Name (id_of_string "x") - | _ -> name) in - let env1 = push_rel (name,None,u1) env in - let (evd',h1) = - inh_conv_coerce_to_fail env1 isevars - {uj_val = mkRel 1; uj_type = (lift 1 u1) } - (lift 1 t1) in - let (evd'',h2) = inh_conv_coerce_to_fail env1 evd' - { uj_val = mkApp (lift 1 v, [|h1.uj_val|]); - uj_type = subst1 h1.uj_val t2 } - u2 - in - (evd'', - { uj_val = mkLambda (name, u1, h2.uj_val); - uj_type = mkProd (name, u1, h2.uj_type) }) + match v' with + Some v' -> + (match kind_of_term v' with + | Lambda (x,v1,v2) -> + (try the_conv_x env v1 u1 isevars, Some (x, v1, v2) (* leq v1 u1? *) + with Reduction.NotConvertible -> (isevars, None)) + | _ -> (isevars, None)) + | None -> (isevars, None) + in + (match b with + Some (x, v1, v2) -> + let env1 = push_rel (x,None,v1) env in + let (evd'', v2', t2') = inh_conv_coerce_to_fail loc env1 evd' + (Some v2) t2 u2 in + (evd'', option_app (fun v2' -> mkLambda (x, v1, v2')) v2', + mkProd (x, v1, t2')) + | None -> + (* Mismatch on t1 and u1 or not a lambda: we eta-expand *) + (* we look for a coercion c:u1->t1 s.t. [name:u1](v' (c x)) *) + (* has type (name:u1)u2 (with v' recursively obtained) *) + let name = (match name with + | Anonymous -> Name (id_of_string "x") + | _ -> name) in + let env1 = push_rel (name,None,u1) env in + let (evd', v1', t1') = + inh_conv_coerce_to_fail loc env1 isevars + (Some (mkRel 1)) (lift 1 u1) (lift 1 t1) + in + let (evd'', v2', t2') = + let v2 = + match v with + Some v -> option_app (fun v1' -> mkApp (lift 1 v, [|v1'|])) v1' + | None -> None + and evd', t2 = + match v1' with + Some v1' -> evd', subst1 v1' t2 + | None -> + let evd', ev = new_evar evd' env ~src:(loc, InternalHole) t1' in + evd', subst1 ev t2 + in + inh_conv_coerce_to_fail loc env1 evd' v2 t2 u2 + in + (evd'', option_app (fun v2' -> mkLambda (name, u1, v2')) v2', + mkProd (name, u1, t2'))) | _ -> raise NoCoercion)) + (* Look for cj' obtained from cj by inserting coercions, s.t. cj'.typ = t *) - let inh_conv_coerce_to loc env isevars cj t = - trace (str "inh_conv_coerce_to called for " ++ (my_print_constr env cj.uj_type) ++ - str " and "++ my_print_constr env t); - let (evd',cj') = - try - inh_conv_coerce_to_fail env isevars cj t - with NoCoercion -> - try - coerce_itf loc env isevars cj t + let inh_conv_coerce_to loc env isevars cj ((n, t) as tycon) = + (try + trace (str "inh_conv_coerce_to called for " ++ + Termops.print_constr_env env cj.uj_type ++ str " and "++ spc () ++ + Evarutil.pr_tycon_type env tycon ++ str " with evars: " ++ spc () ++ + Evd.pr_evar_defs isevars ++ str " in env: " ++ spc () ++ + Termops.print_env env); + with _ -> ()); + if n = 0 then + let (evd', val', type') = + try + inh_conv_coerce_to_fail loc env isevars (Some cj.uj_val) cj.uj_type t with NoCoercion -> let sigma = evars_of isevars in - debug 2 (str "No coercion found"); - error_actual_type_loc loc env sigma cj t + try + coerce_itf loc env isevars (Some cj.uj_val) cj.uj_type t + with NoSubtacCoercion -> + error_actual_type_loc loc env sigma cj t + in + let val' = match val' with Some v -> v | None -> assert(false) in + (evd',{ uj_val = val'; uj_type = t }) + else + (isevars, cj) + + let inh_conv_coerces_to loc env isevars t ((abs, t') as tycon) = + (try + trace (str "inh_conv_coerces_to called for " ++ + Termops.print_constr_env env t ++ str " and "++ spc () ++ + Evarutil.pr_tycon_type env tycon ++ str " with evars: " ++ spc () ++ + Evd.pr_evar_defs isevars ++ str " in env: " ++ spc () ++ + Termops.print_env env); + with _ -> ()); + let (rels, rng) = + (* a little more effort to get products is needed *) + try decompose_prod_n abs t + with _ -> + trace (str "decompose_prod_n failed"); + raise (Invalid_argument "Subtac_coercion.inh_conv_coerces_to") in - (evd',{ uj_val = cj'.uj_val; uj_type = t }) + (* The final range free variables must have been replaced by evars, we accept only that evars + in rng are applied to free vars. *) + if noccur_with_meta 0 (succ abs) rng then ( + trace (str "No occur between 0 and " ++ int (succ abs)); + let env', t, t' = + let env' = List.fold_right (fun (n, t) env -> push_rel (n, None, t) env) rels env in + env', rng, lift abs t' + in + try + pi1 (try inh_conv_coerce_to_fail loc env' isevars None t t' + with NoCoercion -> + coerce_itf loc env' isevars None t t') + with NoSubtacCoercion -> + let sigma = evars_of isevars in + error_cannot_coerce env' sigma (t, t')) + else isevars end diff --git a/contrib/subtac/subtac_command.ml b/contrib/subtac/subtac_command.ml index 5659950bc9..94f2d70ac4 100644 --- a/contrib/subtac/subtac_command.ml +++ b/contrib/subtac/subtac_command.ml @@ -1,4 +1,3 @@ - open Closure open RedFlags open Declarations @@ -34,7 +33,9 @@ open Mod_subst open Printer open Inductiveops open Syntax_def - +open Environ +open Tactics +open Tacticals open Tacinterp open Vernacexpr open Notation @@ -46,13 +47,17 @@ open Pretyping (*********************************************************************) (* Functions to parse and interpret constructions *) +let evar_nf isevars c = + isevars := Evarutil.nf_evar_defs !isevars; + Evarutil.nf_isevar !isevars c + let interp_gen kind isevars env ?(impls=([],[])) ?(allow_soapp=false) ?(ltacvars=([],[])) c = let c' = Constrintern.intern_gen (kind=IsType) ~impls ~allow_soapp ~ltacvars (Evd.evars_of !isevars) env c in let c' = Subtac_interp_fixpoint.rewrite_cases env c' in let c' = SPretyping.pretype_gen isevars env ([],[]) kind c' in - non_instanciated_map env isevars, c' + evar_nf isevars c' let interp_constr isevars env c = interp_gen (OfType None) isevars env c @@ -64,14 +69,47 @@ let interp_casted_constr isevars env ?(impls=([],[])) c typ = interp_gen (OfType (Some typ)) isevars env ~impls c let interp_open_constr isevars env c = - SPretyping.pretype_gen isevars env ([], []) (OfType None) - (Constrintern.intern_constr (Evd.evars_of !isevars) env c) + let c = SPretyping.pretype_gen isevars env ([], []) (OfType None) + (Constrintern.intern_constr (Evd.evars_of !isevars) env c) in + evar_nf isevars c let interp_constr_judgment isevars env c = - let s, j = SPretyping.understand_judgment_tcc (Evd.evars_of !isevars) env - (Constrintern.intern_constr (Evd.evars_of !isevars) env c) - in - Evd.create_evar_defs s, j + let j = + SPretyping.understand_judgment_tcc isevars env + (Constrintern.intern_constr (Evd.evars_of !isevars) env c) + in + { uj_val = evar_nf isevars j.uj_val; uj_type = evar_nf isevars j.uj_type } + +let locate_if_isevar loc na = function + | RHole _ -> + (try match na with + | Name id -> Reserve.find_reserved_type id + | Anonymous -> raise Not_found + with Not_found -> RHole (loc, Evd.BinderType na)) + | x -> x + +let interp_binder sigma env na t = + let t = Constrintern.intern_gen true (Evd.evars_of !sigma) env t in + SPretyping.understand_type (Evd.evars_of !sigma) env (locate_if_isevar (loc_of_rawconstr t) na t) + + +let interp_context sigma env params = + List.fold_left + (fun (env,params) d -> match d with + | LocalRawAssum ([_,na],(CHole _ as t)) -> + let t = interp_binder sigma env na t in + let d = (na,None,t) in + (push_rel d env, d::params) + | LocalRawAssum (nal,t) -> + let t = interp_type sigma env t in + let ctx = list_map_i (fun i (_,na) -> (na,None,lift i t)) 0 nal in + let ctx = List.rev ctx in + (push_rel_context ctx env, ctx@params) + | LocalRawDef ((_,na),c) -> + let c = interp_constr_judgment sigma env c in + let d = (na, Some c.uj_val, c.uj_type) in + (push_rel d env,d::params)) + (env,[]) params (* try to find non recursive definitions *) @@ -85,7 +123,7 @@ let collect_non_rec env = let i = list_try_find_i (fun i f -> - if List.for_all (fun (_, def) -> not (occur_var env f def)) ldefrec + if List.for_all (fun (_, _, def) -> not (occur_var env f def)) ldefrec then i else failwith "try_find_i") 0 lnamerec in @@ -108,6 +146,9 @@ let collect_non_rec env = in searchrec [] +let definition_message id = + Options.if_verbose message ((string_of_id id) ^ " is defined") + let recursive_message v = match Array.length v with | 0 -> error "no recursive definition" @@ -115,13 +156,27 @@ let recursive_message v = | _ -> hov 0 (prvect_with_sep pr_coma Printer.pr_global v ++ spc () ++ str "are recursively defined") +let filter_map f l = + let rec aux acc = function + hd :: tl -> (match f hd with Some t -> aux (t :: acc) tl + | None -> aux acc tl) + | [] -> List.rev acc + in aux [] l + +let list_of_local_binders l = + let rec aux acc = function + Topconstr.LocalRawDef (n, c) :: tl -> aux ((n, Some c, None) :: acc) tl + | Topconstr.LocalRawAssum (nl, c) :: tl -> + aux (List.fold_left (fun acc n -> (n, None, Some c) :: acc) acc nl) tl + | [] -> List.rev acc + in aux [] l + let build_recursive (lnameargsardef:(fixpoint_expr * decl_notation) list) boxed = let sigma = Evd.empty and env0 = Global.env() - and protos = List.map (fun ((f, n, _, _, _),_) -> f,n) lnameargsardef in - let lnameargsardef = - List.map (fun (f, d) -> Subtac_interp_fixpoint.rewrite_fixpoint env0 protos (f, d)) + let lnameargsardef = + (*List.map (fun (f, d) -> Subtac_interp_fixpoint.rewrite_fixpoint env0 protos (f, d))*) lnameargsardef in let lrecnames = List.map (fun ((f,_,_,_,_),_) -> f) lnameargsardef @@ -130,16 +185,56 @@ let build_recursive (lnameargsardef:(fixpoint_expr * decl_notation) list) boxed (* Build the recursive context and notations for the recursive types *) let (rec_sign,rec_impls,arityl) = List.fold_left - (fun (env,impls,arl) ((recname,_,bl,arityc,_),_) -> - let arityc = Command.generalize_constr_expr arityc bl in - let isevars = ref (Evd.create_evar_defs sigma) in - let isevars, arity = interp_type isevars env0 arityc in - let impl = - if Impargs.is_implicit_args() - then Impargs.compute_implicits env0 arity - else [] in - let impls' =(recname,([],impl,compute_arguments_scope arity))::impls in - (Environ.push_named (recname,None,arity) env, impls', (isevars, arity)::arl)) + (fun (env,impls,arl) ((recname,(n, ro),bl,arityc,body),_) -> + let isevars = ref (Evd.create_evar_defs sigma) in + match ro with + CStructRec -> + let arityc = Command.generalize_constr_expr arityc bl in + let arity = interp_type isevars env0 arityc in + let impl = + if Impargs.is_implicit_args() + then Impargs.compute_implicits env0 arity + else [] in + let impls' =(recname,([],impl,compute_arguments_scope arity))::impls in + (Environ.push_named (recname,None,arity) env, impls', (isevars, None, arity)::arl) + | CWfRec r -> + let _ = trace (str "Rewriting fixpoint: " ++ Ppconstr.pr_id recname ++ + Ppconstr.pr_binders bl ++ str " : " ++ + Ppconstr.pr_constr_expr arityc ++ str " := " ++ spc () ++ + Ppconstr.pr_constr_expr body) + in + let env', binders_rel = interp_context isevars env0 bl in + let before, ((argname, _, argtyp) as arg), after = list_chop_hd n binders_rel in + let argid = match argname with Name n -> n | _ -> assert(false) in + let after' = List.map (fun (n, c, t) -> (n, option_app (lift 1) c, lift 1 t)) after in + let envwf = push_rel_context before env0 in + let wf_rel = interp_constr isevars envwf r in + let accarg_id = id_of_string ("Acc_" ^ string_of_id argid) in + let accarg = (Name accarg_id, None, mkApp (Lazy.force acc_inv, [| wf_rel; mkRel 1 |])) in + let argid' = id_of_string (string_of_id argid ^ "'") in + let before_length, after_length = List.length before, List.length after in + let full_length = before_length + 1 + after_length in + let wfarg = (Name argid, None, + mkSubset (Name argid') argtyp + (mkApp (wf_rel, [|mkRel 1; mkRel (full_length + 1)|]))) + in + let new_bl = before @ (arg :: accarg :: after') + and intern_bl = before @ (wfarg :: after) + in + let intern_env = push_rel_context intern_bl env0 in + let env' = push_rel_context new_bl env0 in + let arity = interp_type isevars intern_env arityc in + let intern_arity = it_mkProd_or_LetIn arity intern_bl in + let arity' = interp_type isevars env' arityc in + let arity' = it_mkProd_or_LetIn arity' new_bl in + let fun_bl = before @ (arg :: (Name recname, None, arity) :: after') in + let impl = + if Impargs.is_implicit_args() + then Impargs.compute_implicits intern_env arity' + else [] in + let impls' = (recname,([],impl,compute_arguments_scope arity'))::impls in + (Environ.push_named (recname,None,arity') env, impls', + (isevars, Some (full_length - n, argtyp, wf_rel, fun_bl, intern_bl, intern_arity), arity')::arl)) (env0,[],[]) lnameargsardef in let arityl = List.rev arityl in let notations = @@ -155,10 +250,17 @@ let build_recursive (lnameargsardef:(fixpoint_expr * decl_notation) list) boxed List.iter (fun (df,c,scope) -> (* No scope for tmp notation *) Metasyntax.add_notation_interpretation df rec_impls c None) notations; List.map2 - (fun ((_,_,bl,_,def),_) (evm, arity) -> - let def = abstract_constr_expr def bl in - interp_casted_constr (ref (Evd.create_evar_defs evm)) rec_sign ~impls:([],rec_impls) - def arity) + (fun ((_,_,bl,_,def),_) (isevars, info, arity) -> + match info with + None -> + let def = abstract_constr_expr def bl in + isevars, info, interp_casted_constr isevars rec_sign ~impls:([],rec_impls) + def arity + | Some (n, artyp, wfrel, bl, intern_bl, intern_arity) -> + let rec_sign = push_rel_context bl rec_sign in + let cstr = interp_casted_constr isevars rec_sign ~impls:([],rec_impls) + def intern_arity + in isevars, info, cstr) lnameargsardef arityl with e -> States.unfreeze fs; raise e in @@ -173,7 +275,8 @@ let build_recursive (lnameargsardef:(fixpoint_expr * decl_notation) list) boxed Array.map (subst_vars (List.rev (Array.to_list namerec))) defrec in let recdecls = (Array.map (fun id -> Name id) namerec, arrec, recvec) in let rec declare i fi = - trace (str "Declaring: " ++ pr_id fi); + trace (str "Declaring: " ++ pr_id fi ++ spc () ++ + my_print_constr env0 (recvec.(i))); let ce = { const_entry_body = mkFix ((nvrec',i),recdecls); const_entry_type = Some arrec.(i); @@ -185,76 +288,131 @@ let build_recursive (lnameargsardef:(fixpoint_expr * decl_notation) list) boxed (* declare the recursive definitions *) let lrefrec = Array.mapi declare namerec in Options.if_verbose ppnl (recursive_message lrefrec); - (* (* The others are declared as normal definitions *) - let var_subst id = (id, Constrintern.global_reference id) in - let _ = - List.fold_left + + + (*(* The others are declared as normal definitions *) + let var_subst id = (id, Constrintern.global_reference id) in + let _ = + List.fold_left (fun subst (f,def,t) -> - let ce = { const_entry_body = replace_vars subst def; - const_entry_type = Some t; - const_entry_opaque = false; - const_entry_boxed = boxed } in - let _ = - Declare.declare_constant f (DefinitionEntry ce,IsDefinition Definition) - in - warning ((string_of_id f)^" is non-recursively defined"); - (var_subst f) :: subst) + let ce = { const_entry_body = replace_vars subst def; + const_entry_type = Some t; + const_entry_opaque = false; + const_entry_boxed = boxed } in + let _ = + Declare.declare_constant f (DefinitionEntry ce,IsDefinition Definition) + in + warning ((string_of_id f)^" is non-recursively defined"); + (var_subst f) :: subst) (List.map var_subst (Array.to_list namerec)) lnonrec - in*) + in*) List.iter (fun (df,c,scope) -> Metasyntax.add_notation_interpretation df [] c scope) notations in let declare l = let recvec = Array.of_list l - and arrec = Array.map snd arrec + and arrec = Array.map pi3 arrec in declare arrec recvec in let recdefs = Array.length defrec in trace (int recdefs ++ str " recursive definitions"); (* Solve remaining evars *) - let rec solve_evars i acc = + let rec collect_evars i acc = if i < recdefs then - let (evm, def) = defrec.(i) in - if Evd.dom evm = [] then - solve_evars (succ i) (def :: acc) - else - let _,typ = arrec.(i) in - let id = namerec.(i) in - (* Generalize by the recursive prototypes *) - let def = - Termops.it_mkNamedLambda_or_LetIn def (Environ.named_context rec_sign) - and typ = - Termops.it_mkNamedProd_or_LetIn typ (Environ.named_context rec_sign) in - let tac = Eterm.etermtac (evm, def) in - let _ = - trace (str "Starting proof of a fixpoint def:" ++ spc () ++ my_print_constr env0 def ++ - spc () ++ str " with goal: " ++ my_print_constr env0 typ ++ - spc () ++ str " with evar map = " ++ Evd.pr_evar_map evm) - in - begin - Command.start_proof (id_of_string (string_of_id id ^ "_evars")) goal_kind typ - (fun _ gr -> - let _ = trace (str "Got a proof of: " ++ pr_global gr) in - let constant = match gr with Libnames.ConstRef c -> c - | _ -> assert(false) - in - try - let def = Environ.constant_value (Global.env ()) constant in - let _ = trace (str "Got constant value") in - let _, c = decompose_lam_n recdefs def in - let _ = trace (str "Fixpoint body is: " ++ spc () ++ my_print_constr (Global.env ()) c) in - solve_evars (succ i) (c :: acc) - with Environ.NotEvaluableConst cer -> - match cer with - Environ.NoBody -> trace (str "Constant has no body") - | Environ.Opaque -> trace (str "Constant is opaque") - ); - trace (str "Started proof"); - Pfedit.by tac; - trace (str "Applied eterm tac"); - end - else declare (List.rev acc) - in solve_evars 0 [] + let (isevars, info, def) = defrec.(i) in + let _ = trace (str "In solve evars, isevars is: " ++ Evd.pr_evar_defs !isevars) in + let def = evar_nf isevars def in + let isevars = Evd.undefined_evars !isevars in + let _ = trace (str "In solve evars, undefined is: " ++ Evd.pr_evar_defs isevars) in + let evm = Evd.evars_of isevars in + let _, _, typ = arrec.(i) in + let id = namerec.(i) in + let def = + match info with + Some (n, artyp, wfrel, funbl, bl, arity) -> + def (* mkApp (def, *) - + | None -> def + in + (* Generalize by the recursive prototypes *) + let def = + Termops.it_mkNamedLambda_or_LetIn def (Environ.named_context rec_sign) + and typ = + Termops.it_mkNamedProd_or_LetIn typ (Environ.named_context rec_sign) in + let evars_def, evars_typ, evars = Eterm.eterm_term evm def (Some typ) in + (*let evars_typ = match evars_typ with Some t -> t | None -> assert(false) in*) + (*let fi = id_of_string (string_of_id id ^ "_evars") in*) + (*let ce = + { const_entry_body = evars_def; + const_entry_type = Some evars_typ; + const_entry_opaque = false; + const_entry_boxed = boxed} in + let kn = Declare.declare_constant fi (DefinitionEntry ce,IsDefinition Definition) in + definition_message fi; + trace (str (string_of_id fi) ++ str " is defined");*) + let evar_sum = + if evars = [] then None + else + let sum = Subtac_utils.build_dependent_sum evars in + trace (str "Evars sum: " ++ my_print_constr env0 (pi1 sum)); + Some sum + in + collect_evars (succ i) ((id, evars_def, evar_sum) :: acc) + else acc + in + let defs = collect_evars 0 [] in + + (* Solve evars then create the definitions *) + let real_evars = + filter_map (fun (id, kn, sum) -> + match sum with Some (sumg, sumtac, _) -> Some (id, kn, sumg, sumtac) | None -> None) + defs + in + Subtac_utils.and_tac real_evars + (fun f _ gr -> + let _ = trace (str "Got a proof of: " ++ pr_global gr) in + let constant = match gr with Libnames.ConstRef c -> c + | _ -> assert(false) + in + try + (*let value = Environ.constant_value (Global.env ()) constant in*) + let pis = f (mkConst constant) in + trace (str "Accessors: " ++ + List.fold_right (fun (_, _, _, c) acc -> my_print_constr env0 c ++ spc () ++ acc) + pis (mt())); + trace (str "Applied existentials: " ++ + (List.fold_right + (fun (id, kn, sumg, pi) acc -> + let args = Subtac_utils.destruct_ex pi sumg in + my_print_constr env0 (mkApp (kn, Array.of_list args))) + pis (mt ()))); + let rec aux pis acc = function + (id, kn, sum) :: tl -> + (match sum with + None -> aux pis (kn :: acc) tl + | Some (sumg, _, _) -> + let (id, kn, sumg, pi), pis = List.hd pis, List.tl pis in + let args = Subtac_utils.destruct_ex pi sumg in + let args = + List.map (fun c -> + try Reductionops.whd_betadeltaiota (Global.env ()) Evd.empty c + with Not_found -> + trace (str "Not_found while reducing " ++ + my_print_constr (Global.env ()) c); + c + ) args + in + let _, newdef = decompose_lam_n (recdefs + List.length args) kn in + let constr = Term.substl (mkRel 1 :: List.rev args) newdef in + aux pis (constr :: acc) tl) + | [] -> List.rev acc + in + declare (aux pis [] defs) + with Environ.NotEvaluableConst cer -> + match cer with + Environ.NoBody -> trace (str "Constant has no body") + | Environ.Opaque -> trace (str "Constant is opaque") + ) + + diff --git a/contrib/subtac/subtac_command.mli b/contrib/subtac/subtac_command.mli index 6c1b0103ff..e1bbbbb5b0 100644 --- a/contrib/subtac/subtac_command.mli +++ b/contrib/subtac/subtac_command.mli @@ -16,26 +16,26 @@ val interp_gen : ?impls:full_implicits_env -> ?allow_soapp:bool -> ?ltacvars:ltac_sign -> - constr_expr -> evar_map * constr + constr_expr -> constr val interp_constr : evar_defs ref -> - env -> constr_expr -> evar_map * constr + env -> constr_expr -> constr val interp_type : evar_defs ref -> env -> ?impls:full_implicits_env -> - constr_expr -> evar_map * constr + constr_expr -> constr val interp_casted_constr : evar_defs ref -> env -> ?impls:full_implicits_env -> - constr_expr -> types -> evar_map * constr + constr_expr -> types -> constr val interp_open_constr : evar_defs ref -> env -> constr_expr -> constr val interp_constr_judgment : evar_defs ref -> env -> - constr_expr -> evar_defs * unsafe_judgment + constr_expr -> unsafe_judgment val list_chop_hd : int -> 'a list -> 'a list * 'a * 'a list val recursive_message : global_reference array -> std_ppcmds val build_recursive : diff --git a/contrib/subtac/subtac_interp_fixpoint.ml b/contrib/subtac/subtac_interp_fixpoint.ml index 3ed5790e33..599dbe39e2 100644 --- a/contrib/subtac/subtac_interp_fixpoint.ml +++ b/contrib/subtac/subtac_interp_fixpoint.ml @@ -51,8 +51,8 @@ let list_of_local_binders l = | [] -> List.rev acc in aux [] l -let abstract_constr_expr_bl c bl = - List.fold_right (fun (n, t) c -> mkLambdaC ([n], t, c)) bl c +let abstract_constr_expr_bl abs c bl = + List.fold_right (fun (n, t) c -> abs ([n], t, c)) bl c let pr_binder_list b = List.fold_right (fun ((loc, name), t) acc -> Nameops.pr_name name ++ str " : " ++ @@ -73,14 +73,14 @@ let rewrite_fixpoint env l (f, decl) = Ppconstr.pr_constr_expr typ ++ str " := " ++ spc () ++ Ppconstr.pr_constr_expr body) in - let after, before = list_chop n bls in + let before, after = list_chop n bls in let _ = trace (str "Binders before the recursion arg: " ++ spc () ++ pr_binder_list before ++ str "; after the recursion arg: " ++ pr_binder_list after) in - let ((locn, name), ntyp), before = match before with + let ((locn, name) as lnid, ntyp), after = match after with hd :: tl -> hd, tl - | _ -> assert(false) (* Rec arg must be in before *) + | _ -> assert(false) (* Rec arg must be in after *) in let nid = match name with Name id -> id @@ -96,27 +96,36 @@ let rewrite_fixpoint env l (f, decl) = id_of_string (nid ^ "'"), id_of_string ("Acc_" ^ nid) in let lnid', laccproofid = (dummy_loc, Name nid'), (dummy_loc, Name accproofid) in - let coqP = abstract_constr_expr_bl typ after in + let wf_prop = (mkAppC (wfrel, [ mkIdentC nid'; mkIdentC nid ])) in + let lam_wf_prop = mkLambdaC ([lnid'], ntyp, wf_prop) in + let typnid' = mkSubset lnid' ntyp wf_prop in + let internal_type = + abstract_constr_expr_bl mkProdC + (mkProdC ([lnid'], typnid', + mkLetInC (lnid, mkProj1 ntyp lam_wf_prop (mkIdentC nid'), + abstract_constr_expr_bl mkProdC typ after))) + before + in let body' = - let prop = (mkAppC (wfrel, [ mkIdentC nid'; mkIdentC nid ])) in - let lamprop = mkLambdaC ([lnid'], ntyp, prop) in - let typnid' = mkSubset lnid' ntyp prop in + let body = + (* cast or we will loose some info at pretyping time as body + is a function *) + CCast (dummy_loc, body, DEFAULTcast, typ) + in let body' = (* body abstracted by rec call *) - mkLambdaC ([(dummy_loc, Name id)], - mkProdC ([lnid'], typnid', coqP), - body) + mkLambdaC ([(dummy_loc, Name id)], internal_type, body) in mkAppC (body', [mkLambdaC ([lnid'], typnid', mkAppC (mkIdentC id, - [mkProj1 ntyp lamprop (mkIdentC nid'); + [mkProj1 ntyp lam_wf_prop (mkIdentC nid'); (mkAppExplC (acc_inv_ref, [ ntyp; wfrel; mkIdentC nid; mkIdentC accproofid; - mkProj1 ntyp lamprop (mkIdentC nid'); - mkProj2 ntyp lamprop (mkIdentC nid') ])) ]))]) + mkProj1 ntyp lam_wf_prop (mkIdentC nid'); + mkProj2 ntyp lam_wf_prop (mkIdentC nid') ])) ]))]) in let acctyp = mkAppExplC (acc_ref, [ ntyp; wfrel; mkIdentC nid ]) in let bl' = @@ -127,12 +136,12 @@ let rewrite_fixpoint env l (f, decl) = let rec aux' bl' = function ((loc, name') as x) :: tl -> if name' = name then - LocalRawAssum (List.rev (x :: bl'), typ) :: - LocalRawAssum ([(dummy_loc, Name accproofid)], acctyp) :: - if tl = [] then [] else [LocalRawAssum (tl, typ)] + (if tl = [] then [] else [LocalRawAssum (tl, typ)]) @ + LocalRawAssum ([(dummy_loc, Name accproofid)], acctyp) :: + [LocalRawAssum (List.rev (x :: bl'), typ)] else aux' (x :: bl') tl | [] -> [assum] - in aux (acc @ List.rev (aux' [] bl)) tl + in aux (aux' [] bl @ acc) tl | [] -> List.rev acc in aux [] bl in @@ -173,7 +182,7 @@ let rewrite_cases_aux (loc, po, tml, eqns) = let po = List.fold_right (fun (n,t) acc -> - RProd (dummy_loc, n, t, acc)) + RProd (dummy_loc, Anonymous, t, acc)) eqs_types (match po with Some e -> e | None -> mkHole) diff --git a/contrib/subtac/subtac_interp_fixpoint.mli b/contrib/subtac/subtac_interp_fixpoint.mli index ef551aa8d2..b0de064167 100644 --- a/contrib/subtac/subtac_interp_fixpoint.mli +++ b/contrib/subtac/subtac_interp_fixpoint.mli @@ -12,10 +12,6 @@ val mkProj2 : val list_of_local_binders : Topconstr.local_binder list -> (Names.name Util.located * Topconstr.constr_expr) list -val abstract_constr_expr_bl : - Topconstr.constr_expr -> - (Names.name Util.located * Topconstr.constr_expr) list -> - Topconstr.constr_expr val pr_binder_list : (('a * Names.name) * Topconstr.constr_expr) list -> Pp.std_ppcmds val rewrite_rec_calls : 'a -> 'b -> 'b diff --git a/contrib/subtac/subtac_pretyping.ml b/contrib/subtac/subtac_pretyping.ml index 5cc7351855..f73a6e393d 100644 --- a/contrib/subtac/subtac_pretyping.ml +++ b/contrib/subtac/subtac_pretyping.ml @@ -71,7 +71,8 @@ let merge_evms x y = let interp env isevars c tycon = let j = pretype tycon env isevars ([],[]) c in - j.uj_val, j.uj_type + let evm = evars_of !isevars in + nf_evar evm j.uj_val, nf_evar evm j.uj_type let find_with_index x l = let rec aux i = function diff --git a/contrib/subtac/subtac_utils.ml b/contrib/subtac/subtac_utils.ml index eb9888c161..d620c8e9fa 100644 --- a/contrib/subtac/subtac_utils.ml +++ b/contrib/subtac/subtac_utils.ml @@ -12,11 +12,15 @@ let contrib_name = "subtac" let subtac_dir = [contrib_name] let fix_sub_module = "FixSub" +let utils_module = "Utils" let fixsub_module = subtac_dir @ [fix_sub_module] +let utils_module = subtac_dir @ [utils_module] let init_constant dir s = gen_constant contrib_name dir s let init_reference dir s = gen_reference contrib_name dir s let fixsub = lazy (init_constant fixsub_module "Fix_sub") +let ex_pi1 = lazy (init_constant utils_module "ex_pi1") +let ex_pi2 = lazy (init_constant utils_module "ex_pi2") let make_ref s = Qualid (dummy_loc, (qualid_of_string s)) let well_founded_ref = make_ref "Init.Wf.Well_founded" @@ -41,6 +45,12 @@ let eqind = lazy (init_constant ["Init"; "Logic"] "eq") let eqind_ref = lazy (init_reference ["Init"; "Logic"] "eq") let refl_equal_ref = lazy (init_reference ["Init"; "Logic"] "refl_equal") +let ex_ind = lazy (init_constant ["Init"; "Logic"] "ex") +let ex_intro = lazy (init_reference ["Init"; "Logic"] "ex_intro") + +let proj1 = lazy (init_constant ["Init"; "Logic"] "proj1") +let proj2 = lazy (init_constant ["Init"; "Logic"] "proj2") + let boolind = lazy (init_constant ["Init"; "Datatypes"] "bool") let sumboolind = lazy (init_constant ["Init"; "Specif"] "sumbool") let natind = lazy (init_constant ["Init"; "Datatypes"] "nat") @@ -49,9 +59,14 @@ let existSind = lazy (init_constant ["Init"; "Specif"] "sigS") let existS = lazy (build_sigma_set ()) +let prod = lazy (build_prod ()) + + (* orders *) let well_founded = lazy (init_constant ["Init"; "Wf"] "well_founded") let fix = lazy (init_constant ["Init"; "Wf"] "Fix") +let acc = lazy (init_constant ["Init"; "Wf"] "Acc") +let acc_inv = lazy (init_constant ["Init"; "Wf"] "Acc_inv") let extconstr = Constrextern.extern_constr true (Global.env ()) let extsort s = Constrextern.extern_constr true (Global.env ()) (mkSort s) @@ -62,21 +77,22 @@ let my_print_constr = Termops.print_constr_env let my_print_context = Termops.print_rel_context let my_print_env = Termops.print_env let my_print_rawconstr = Printer.pr_rawconstr_env +let my_print_evardefs = Evd.pr_evar_defs + +let my_print_tycon_type = Evarutil.pr_tycon_type -let debug_level = ref 0 let debug n s = - if n >= !debug_level then ( - msgnl s; - msg_warning s; - ) else () + if !Options.debug then + msgnl s + else () let debug_msg n s = - if n >= !debug_level then s + if !Options.debug then s else mt () let trace s = - if !debug_level < 2 then (msgnl s) + if !Options.debug then msgnl s else () let wf_relations = Hashtbl.create 10 @@ -138,3 +154,92 @@ let goal_kind = Decl_kinds.Global, Decl_kinds.DefinitionBody Decl_kinds.Definiti let global_fix_kind = Decl_kinds.IsDefinition Decl_kinds.Fixpoint let goal_fix_kind = Decl_kinds.Global, Decl_kinds.DefinitionBody Decl_kinds.Fixpoint + +open Tactics +open Tacticals + +let build_dependent_sum l = + let rec aux (acc, tac, typ) = function + (n, t) :: tl -> + let t' = mkLambda (Name n, t, typ) in + trace (str ("treating " ^ string_of_id n) ++ + str "assert: " ++ my_print_constr (Global.env ()) t); + let tac' = + tclTHEN (assert_tac true (Name n) t) + (tclTHENLIST + [intros; + (tclTHENSEQ + [tclTRY (constructor_tac (Some 1) 1 + (Rawterm.ImplicitBindings [mkVar n])); + tac]); + ]) + in + aux (mkApp (Lazy.force ex_ind, [| t; t'; |]), tac', t') tl + | [] -> acc, tac, typ + in + match l with + (_, hd) :: tl -> aux (hd, intros, hd) tl + | [] -> raise (Invalid_argument "build_dependent_sum") + +open Proof_type +open Tacexpr + +let mkProj1 a b c = + mkApp (Lazy.force proj1, [| a; b; c |]) + +let mkProj2 a b c = + mkApp (Lazy.force proj2, [| a; b; c |]) + +let mk_ex_pi1 a b c = + mkApp (Lazy.force ex_pi1, [| a; b; c |]) + +let mk_ex_pi2 a b c = + mkApp (Lazy.force ex_pi2, [| a; b; c |]) + + +let mkSubset name typ prop = + mkApp ((Lazy.force sig_).typ, + [| typ; mkLambda (name, typ, prop) |]) + +let and_tac l hook = + let andc = Coqlib.build_coq_and () in + let rec aux ((accid, goal, tac, extract) as acc) = function + | [] -> (* Singleton *) acc + + | (id, x, elgoal, eltac) :: tl -> + let tac' = tclTHEN simplest_split (tclTHENLIST [tac; eltac]) in + let proj = fun c -> mkProj2 goal elgoal c in + let extract = List.map (fun (id, x, y, f) -> (id, x, y, (fun c -> f (mkProj1 goal elgoal c)))) extract in + aux ((string_of_id id) ^ "_" ^ accid, mkApp (andc, [| goal; elgoal |]), tac', + (id, x, elgoal, proj) :: extract) tl + + in + let and_proof_id, and_goal, and_tac, and_extract = + match l with + | [] -> raise (Invalid_argument "and_tac: empty list of goals") + | (hdid, x, hdg, hdt) :: tl -> aux (string_of_id hdid, hdg, hdt, [hdid, x, hdg, (fun c -> c)]) tl + in + let and_proofid = id_of_string (and_proof_id ^ "_and_proof") in + Command.start_proof and_proofid goal_kind and_goal + (hook (fun c -> List.map (fun (id, x, t, f) -> (id, x, t, f c)) and_extract)); + trace (str "Started and proof"); + Pfedit.by and_tac; + trace (str "Applied and tac") + + +let destruct_ex ext ex = + let rec aux c acc = + match kind_of_term c with + App (f, args) -> + (match kind_of_term f with + Ind i when i = Term.destInd (Lazy.force ex_ind) && Array.length args = 2 -> + let (dom, rng) = + try (args.(0), args.(1)) + with _ -> assert(false) + in + (mk_ex_pi1 dom rng acc) :: aux rng (mk_ex_pi2 dom rng acc) + | _ -> [acc]) + | _ -> [acc] + in aux ex ext + + diff --git a/contrib/subtac/subtac_utils.mli b/contrib/subtac/subtac_utils.mli index 84001c3740..a53016484b 100644 --- a/contrib/subtac/subtac_utils.mli +++ b/contrib/subtac/subtac_utils.mli @@ -8,6 +8,8 @@ open Decl_kinds open Topconstr open Rawterm open Util +open Evarutil +open Names val contrib_name : string val subtac_dir : string list @@ -36,15 +38,22 @@ val natind : constr lazy_t val intind : constr lazy_t val existSind : constr lazy_t val existS : coq_sigma_data lazy_t +val prod : coq_sigma_data lazy_t + val well_founded : constr lazy_t val fix : constr lazy_t +val acc : constr lazy_t +val acc_inv : constr lazy_t val extconstr : constr -> constr_expr val extsort : sorts -> constr_expr val my_print_constr : env -> constr -> std_ppcmds +val my_print_evardefs : evar_defs -> std_ppcmds val my_print_context : env -> std_ppcmds val my_print_env : env -> std_ppcmds val my_print_rawconstr : env -> rawconstr -> std_ppcmds -val debug_level : int ref +val my_print_tycon_type : env -> type_constraint_type -> std_ppcmds + + val debug : int -> std_ppcmds -> unit val debug_msg : int -> std_ppcmds -> std_ppcmds val trace : std_ppcmds -> unit @@ -61,3 +70,15 @@ val global_kind : logical_kind val goal_kind : locality_flag * goal_object_kind val global_fix_kind : logical_kind val goal_fix_kind : locality_flag * goal_object_kind + +val mkSubset : name -> constr -> constr -> constr +val mkProj1 : constr -> constr -> constr -> constr +val mkProj1 : constr -> constr -> constr -> constr +val mk_ex_pi1 : constr -> constr -> constr -> constr +val mk_ex_pi1 : constr -> constr -> constr -> constr + +val build_dependent_sum : (identifier * types) list -> constr * Proof_type.tactic * types +val and_tac : (identifier * 'a * constr * Proof_type.tactic) list -> + ((constr -> (identifier * 'a * constr * constr) list) -> Tacexpr.declaration_hook) -> unit + +val destruct_ex : constr -> constr -> constr list diff --git a/contrib/xml/xmlcommand.ml b/contrib/xml/xmlcommand.ml index 39a12a7ea7..871a7f15cb 100644 --- a/contrib/xml/xmlcommand.ml +++ b/contrib/xml/xmlcommand.ml @@ -214,7 +214,6 @@ let theory_filename xml_library_root = None -> None (* stdout *) | Some xml_library_root' -> let toks = List.map N.string_of_id (N.repr_dirpath (Lib.library_dp ())) in - let hd = List.hd toks in (* theory from A/B/C/F.v goes into A/B/C/F.theory *) let alltoks = List.rev toks in Some (join_dirs xml_library_root' alltoks ^ ".theory") diff --git a/doc/Makefile b/doc/Makefile index e0fd8ac7bf..07b130394c 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -112,7 +112,7 @@ REFMANCOQTEXFILES=\ refman/RefMan-tacex.v.tex refman/RefMan-syn.v.tex \ refman/RefMan-oth.v.tex \ refman/Cases.v.tex refman/Coercion.v.tex refman/Extraction.v.tex \ - refman/Omega.v.tex refman/Polynom.v.tex \ + refman/Program.v.tex refman/Omega.v.tex refman/Polynom.v.tex \ refman/Setoid.v.tex refman/Helm.tex # refman/Natural.v.tex REFMANTEXFILES=\ diff --git a/doc/refman/Program.tex b/doc/refman/Program.tex new file mode 100644 index 0000000000..4f3fadcbd5 --- /dev/null +++ b/doc/refman/Program.tex @@ -0,0 +1,491 @@ +\def\Program{\textsc{Program}} +\def\Russel{\textsc{Russel}} + +\achapter{The \Program{} tactic} +\label{Program} +\aauthor{Matthieu Sozeau} +\index{Program} + +\begin{flushleft} + \em The status of \Program is experimental. +\end{flushleft} + +We present here the \Coq\ \Program tactic commands, used to build certified +\Coq programs, elaborating them from their algorithmic skeleton and a +rich specification. It can be sought of as a dual of extraction \ref{Extraction}. +The languages available as input are currently restricted to \Coq's term +language, but may be extended to \ocaml{}, \textsc{Haskell} and others +in the future. Input terms and types are typed in an extended system (\Russel) and +interpreted into \Coq\ terms. The interpretation process may produce +some proof obligations which need to be resolved to create the final term. + +\asection{Elaborating programs} +\comindex{Program Fixpoint} + +The next two commands are similar to they standard counterparts +\ref{Simpl-definitions} and \ref{Fixpoint} in that +they define constants. However, they may require the user to prove some +goals to construct the final definitions. + +\section{\tt Program Definition {\ident} := {\term}. + \comindex{Program Definition}\label{ProgramDefinition}} + +This command types the value {\term} in \Russel and generate subgoals +corresponding to proof obligations. Once solved, it binds the final +\Coq\ term to the name {\ident} in the environment. + +\begin{ErrMsgs} +\item \errindex{{\ident} already exists} +\end{ErrMsgs} + +\begin{Variants} +\item {\tt Program Definition {\ident} {\tt :}{\term$_1$} := + {\term$_2$}.}\\ + It interprets the type {\term$_1$}, potentially generating proof + obligations to be resolved. Once done with them, we have a \Coq\ type + {\term$_1'$}. It then checks that the type of the interpretation of + {\term$_2$} is coercible to {\term$_1'$}, and registers {\ident} as + being of type {\term$_1'$} once the set of obligations generated + during the interpretation of {\term$_2$} and the aforementioned + coercion derivation are solved. +\item {\tt Program Definition {\ident} {\binder$_1$}\ldots{\binder$_n$} + {\tt :}\term$_1$ {\tt :=} {\term$_2$}.}\\ + This is equivalent to \\ + {\tt Program Definition\,{\ident}\,{\tt :\,forall}\,% + {\binder$_1$}\ldots{\binder$_n$}{\tt ,}\,\term$_1$\,{\tt :=}}\,% + {\tt fun}\,{\binder$_1$}\ldots{\binder$_n$}\,{\tt =>}\,{\term$_2$}\,% + {\tt .} +\end{Variants} + +\begin{ErrMsgs} +\item \errindex{In environment {\dots} the term: {\term$_2$} does not have type + {\term$_1$}}.\\ + \texttt{Actually, it has type {\term$_3$}}. +\end{ErrMsgs} + +\SeeAlso Sections \ref{Opaque}, \ref{Transparent}, \ref{unfold} + +\section{\tt Program Fixpoint {\ident} {\params} {\tt \{order\}} : type$_0$ := \term$_0$ + \comindex{Program Fixpoint} + \label{ProgramFixpoint}} + +This command allows to define objects using a fixed point +construction. The meaning of this declaration is to define {\it ident} +a recursive function with arguments specified by +{\binder$_1$}\ldots{\binder$_n$} such that {\it ident} applied to +arguments corresponding to these binders has type \type$_0$, and is +equivalent to the expression \term$_0$. The type of the {\ident} is +consequently {\tt forall {\params} {\tt,} \type$_0$} +and the value is equivalent to {\tt fun {\params} {\tt =>} \term$_0$}. + +There are two ways to define fixpoints with \Program{}, structural and +well-founded recursion. + +\subsection{\tt Program Fixpoint {\ident} {\params} {\tt \{struct} + \ident$_0$ {\tt \}} : type$_0$ := \term$_0$ + \comindex{Program Fixpoint Struct} + \label{ProgramFixpointStruct}} + +To be accepted, a structural {\tt Fixpoint} definition has to satisfy some +syntactical constraints on a special argument called the decreasing +argument. They are needed to ensure that the {\tt Fixpoint} definition +always terminates. The point of the {\tt \{struct \ident {\tt \}}} +annotation is to let the user tell the system which argument decreases +along the recursive calls. This annotation may be left implicit for +fixpoints with one argument. For instance, one can define the identity +function on naturals as : + +\begin{coq_example} +Program Fixpoint id (n : nat) : { x : nat | x = n } := + match n with + | O => O + | S p => S (id p) + end. +\end{coq_example} + +The {\tt match} operator matches a value (here \verb:n:) with the +various constructors of its (inductive) type. The remaining arguments +give the respective values to be returned, as functions of the +parameters of the corresponding constructor. Thus here when \verb:n: +equals \verb:O: we return \verb:0:, and when \verb:n: equals +\verb:(S p): we return \verb:(S (id p)):. + +The {\tt match} operator is formally described +in detail in Section~\ref{Caseexpr}. The system recognizes that in +the inductive call {\tt (id p)} the argument actually +decreases because it is a {\em pattern variable} coming from {\tt match + n with}. + +Here again, proof obligations may be generated. In our example, we would +have one for each branch: +\begin{coq_example} +Show. +\end{coq_example} + +% \subsection{\tt Program Fixpoint {\ident} {(\ident_$_0$ : \type_$_0$) +% \cdots (\ident_$_n$ : \type_$_n$)} {\tt \{wf} +% \ident$_i$ \term_{wf} {\tt \}} : type$_t$ := \term$_0$ +% \comindex{Program Fixpoint Wf} +% \label{ProgramFixpointWf}} + +% To be accepted, a well-founded {\tt Fixpoint} definition has to satisfy some +% logical constraints on the decreasing argument. +% They are needed to ensure that the {\tt Fixpoint} definition +% always terminates. The point of the {\tt \{wf \ident \term {\tt \}}} +% annotation is to let the user tell the system which argument decreases +% in which well-founded relation along the recursive calls. +% The term \term$_0$ will be typed in a different context than usual, +% The typing problem will in fact be reduced to: + +% % \begin{center} +% % {\tt forall} {\params} {\ident : (\ident$_0$ : type$_0$) \cdots +% % \{ \ident$_i'$ : \type$_i$ | \term_{wf} \ident$_i'$ \ident$_i$ \} +% % \cdots (\ident$_n$ : type$_n$), type$_t$} : type$_t$ := \term$_0$ +% % \end{center} + +% \begin{coq_example} +% Program Fixpoint id (n : nat) : { x : nat | x = n } := +% match n with +% | O => O +% | S p => S (id p) +% end +% \end{coq_example} + +% The {\tt match} operator matches a value (here \verb:n:) with the +% various constructors of its (inductive) type. The remaining arguments +% give the respective values to be returned, as functions of the +% parameters of the corresponding constructor. Thus here when \verb:n: +% equals \verb:O: we return \verb:0:, and when \verb:n: equals +% \verb:(S p): we return \verb:(S (id p)):. + +% The {\tt match} operator is formally described +% in detail in Section~\ref{Caseexpr}. The system recognizes that in +% the inductive call {\tt (id p)} the argument actually +% decreases because it is a {\em pattern variable} coming from {\tt match +% n with}. + +% Here again, proof obligations may be generated. In our example, we would +% have one for each branch: +% \begin{coq_example} +% Show. +% \end{coq_example} +% \begin{coq_eval} +% Abort. +% \end{coq_eval} + + + + +% \asubsection{A detailed example: Euclidean division} + +% The file {\tt Euclid} contains the proof of Euclidean division +% (theorem {\tt eucl\_dev}). The natural numbers defined in the example +% files are unary integers defined by two constructors $O$ and $S$: +% \begin{coq_example*} +% Inductive nat : Set := +% | O : nat +% | S : nat -> nat. +% \end{coq_example*} + +% This module contains a theorem {\tt eucl\_dev}, and its extracted term +% is of type +% \begin{verbatim} +% forall b:nat, b > 0 -> forall a:nat, diveucl a b +% \end{verbatim} +% where {\tt diveucl} is a type for the pair of the quotient and the modulo. +% We can now extract this program to \ocaml: + +% \begin{coq_eval} +% Reset Initial. +% \end{coq_eval} +% \begin{coq_example} +% Require Import Euclid. +% Extraction Inline Wf_nat.gt_wf_rec Wf_nat.lt_wf_rec. +% Recursive Extraction eucl_dev. +% \end{coq_example} + +% The inlining of {\tt gt\_wf\_rec} and {\tt lt\_wf\_rec} is not +% mandatory. It only enhances readability of extracted code. +% You can then copy-paste the output to a file {\tt euclid.ml} or let +% \Coq\ do it for you with the following command: + +% \begin{coq_example} +% Extraction "euclid" eucl_dev. +% \end{coq_example} + +% Let us play the resulting program: + +% \begin{verbatim} +% # #use "euclid.ml";; +% type sumbool = Left | Right +% type nat = O | S of nat +% type diveucl = Divex of nat * nat +% val minus : nat -> nat -> nat = <fun> +% val le_lt_dec : nat -> nat -> sumbool = <fun> +% val le_gt_dec : nat -> nat -> sumbool = <fun> +% val eucl_dev : nat -> nat -> diveucl = <fun> +% # eucl_dev (S (S O)) (S (S (S (S (S O)))));; +% - : diveucl = Divex (S (S O), S O) +% \end{verbatim} +% It is easier to test on \ocaml\ integers: +% \begin{verbatim} +% # let rec i2n = function 0 -> O | n -> S (i2n (n-1));; +% val i2n : int -> nat = <fun> +% # let rec n2i = function O -> 0 | S p -> 1+(n2i p);; +% val n2i : nat -> int = <fun> +% # let div a b = +% let Divex (q,r) = eucl_dev (i2n b) (i2n a) in (n2i q, n2i r);; +% div : int -> int -> int * int = <fun> +% # div 173 15;; +% - : int * int = 11, 8 +% \end{verbatim} + +% \asubsection{Another detailed example: Heapsort} + +% The file {\tt Heap.v} +% contains the proof of an efficient list sorting algorithm described by +% Bjerner. Is is an adaptation of the well-known {\em heapsort} +% algorithm to functional languages. The main function is {\tt +% treesort}, whose type is shown below: + + +% \begin{coq_eval} +% Reset Initial. +% Require Import Relation_Definitions. +% Require Import List. +% Require Import Sorting. +% Require Import Permutation. +% \end{coq_eval} +% \begin{coq_example} +% Require Import Heap. +% Check treesort. +% \end{coq_example} + +% Let's now extract this function: + +% \begin{coq_example} +% Extraction Inline sort_rec is_heap_rec. +% Extraction NoInline list_to_heap. +% Extraction "heapsort" treesort. +% \end{coq_example} + +% One more time, the {\tt Extraction Inline} and {\tt NoInline} +% directives are cosmetic. Without it, everything goes right, +% but the output is less readable. +% Here is the produced file {\tt heapsort.ml}: + +% \begin{verbatim} +% type nat = +% | O +% | S of nat + +% type 'a sig2 = +% 'a +% (* singleton inductive, whose constructor was exist2 *) + +% type sumbool = +% | Left +% | Right + +% type 'a list = +% | Nil +% | Cons of 'a * 'a list + +% type 'a multiset = +% 'a -> nat +% (* singleton inductive, whose constructor was Bag *) + +% type 'a merge_lem = +% 'a list +% (* singleton inductive, whose constructor was merge_exist *) + +% (** val merge : ('a1 -> 'a1 -> sumbool) -> ('a1 -> 'a1 -> sumbool) -> +% 'a1 list -> 'a1 list -> 'a1 merge_lem **) + +% let rec merge leA_dec eqA_dec l1 l2 = +% match l1 with +% | Nil -> l2 +% | Cons (a, l) -> +% let rec f = function +% | Nil -> Cons (a, l) +% | Cons (a0, l3) -> +% (match leA_dec a a0 with +% | Left -> Cons (a, +% (merge leA_dec eqA_dec l (Cons (a0, l3)))) +% | Right -> Cons (a0, (f l3))) +% in f l2 + +% type 'a tree = +% | Tree_Leaf +% | Tree_Node of 'a * 'a tree * 'a tree + +% type 'a insert_spec = +% 'a tree +% (* singleton inductive, whose constructor was insert_exist *) + +% (** val insert : ('a1 -> 'a1 -> sumbool) -> ('a1 -> 'a1 -> sumbool) -> +% 'a1 tree -> 'a1 -> 'a1 insert_spec **) + +% let rec insert leA_dec eqA_dec t a = +% match t with +% | Tree_Leaf -> Tree_Node (a, Tree_Leaf, Tree_Leaf) +% | Tree_Node (a0, t0, t1) -> +% let h3 = fun x -> insert leA_dec eqA_dec t0 x in +% (match leA_dec a0 a with +% | Left -> Tree_Node (a0, t1, (h3 a)) +% | Right -> Tree_Node (a, t1, (h3 a0))) + +% type 'a build_heap = +% 'a tree +% (* singleton inductive, whose constructor was heap_exist *) + +% (** val list_to_heap : ('a1 -> 'a1 -> sumbool) -> ('a1 -> 'a1 -> +% sumbool) -> 'a1 list -> 'a1 build_heap **) + +% let rec list_to_heap leA_dec eqA_dec = function +% | Nil -> Tree_Leaf +% | Cons (a, l0) -> +% insert leA_dec eqA_dec (list_to_heap leA_dec eqA_dec l0) a + +% type 'a flat_spec = +% 'a list +% (* singleton inductive, whose constructor was flat_exist *) + +% (** val heap_to_list : ('a1 -> 'a1 -> sumbool) -> ('a1 -> 'a1 -> +% sumbool) -> 'a1 tree -> 'a1 flat_spec **) + +% let rec heap_to_list leA_dec eqA_dec = function +% | Tree_Leaf -> Nil +% | Tree_Node (a, t0, t1) -> Cons (a, +% (merge leA_dec eqA_dec (heap_to_list leA_dec eqA_dec t0) +% (heap_to_list leA_dec eqA_dec t1))) + +% (** val treesort : ('a1 -> 'a1 -> sumbool) -> ('a1 -> 'a1 -> sumbool) +% -> 'a1 list -> 'a1 list sig2 **) + +% let treesort leA_dec eqA_dec l = +% heap_to_list leA_dec eqA_dec (list_to_heap leA_dec eqA_dec l) + +% \end{verbatim} + +% Let's test it: +% % Format.set_margin 72;; +% \begin{verbatim} +% # #use "heapsort.ml";; +% type sumbool = Left | Right +% type nat = O | S of nat +% type 'a tree = Tree_Leaf | Tree_Node of 'a * 'a tree * 'a tree +% type 'a list = Nil | Cons of 'a * 'a list +% val merge : +% ('a -> 'a -> sumbool) -> 'b -> 'a list -> 'a list -> 'a list = <fun> +% val heap_to_list : +% ('a -> 'a -> sumbool) -> 'b -> 'a tree -> 'a list = <fun> +% val insert : +% ('a -> 'a -> sumbool) -> 'b -> 'a tree -> 'a -> 'a tree = <fun> +% val list_to_heap : +% ('a -> 'a -> sumbool) -> 'b -> 'a list -> 'a tree = <fun> +% val treesort : +% ('a -> 'a -> sumbool) -> 'b -> 'a list -> 'a list = <fun> +% \end{verbatim} + +% One can remark that the argument of {\tt treesort} corresponding to +% {\tt eqAdec} is never used in the informative part of the terms, +% only in the logical parts. So the extracted {\tt treesort} never use +% it, hence this {\tt 'b} argument. We will use {\tt ()} for this +% argument. Only remains the {\tt leAdec} +% argument (of type {\tt 'a -> 'a -> sumbool}) to really provide. + +% \begin{verbatim} +% # let leAdec x y = if x <= y then Left else Right;; +% val leAdec : 'a -> 'a -> sumbool = <fun> +% # let rec listn = function 0 -> Nil +% | n -> Cons(Random.int 10000,listn (n-1));; +% val listn : int -> int list = <fun> +% # treesort leAdec () (listn 9);; +% - : int list = Cons (160, Cons (883, Cons (1874, Cons (3275, Cons +% (5392, Cons (7320, Cons (8512, Cons (9632, Cons (9876, Nil))))))))) +% \end{verbatim} + +% Some tests on longer lists (10000 elements) show that the program is +% quite efficient for Caml code. + + +% \asubsection{The Standard Library} + +% As a test, we propose an automatic extraction of the +% Standard Library of \Coq. In particular, we will find back the +% two previous examples, {\tt Euclid} and {\tt Heapsort}. +% Go to directory {\tt contrib/extraction/test} of the sources of \Coq, +% and run commands: +% \begin{verbatim} +% make tree; make +% \end{verbatim} +% This will extract all Standard Library files and compile them. +% It is done via many {\tt Extraction Module}, with some customization +% (see subdirectory {\tt custom}). + +% %The result of this extraction of the Standard Library can be browsed +% %at URL +% %\begin{flushleft} +% %\url{http://www.lri.fr/~letouzey/extraction}. +% %\end{flushleft} + +% %Reals theory is normally not extracted, since it is an axiomatic +% %development. We propose nonetheless a dummy realization of those +% %axioms, to test, run: \\ +% % +% %\mbox{\tt make reals}\\ + +% This test works also with Haskell. In the same directory, run: +% \begin{verbatim} +% make tree; make -f Makefile.haskell +% \end{verbatim} +% The haskell compiler currently used is {\tt hbc}. Any other should +% also work, just adapt the {\tt Makefile.haskell}. In particular {\tt +% ghc} is known to work. + +% \asubsection{Extraction's horror museum} + +% Some pathological examples of extraction are grouped in the file +% \begin{verbatim} +% contrib/extraction/test_extraction.v +% \end{verbatim} +% of the sources of \Coq. + +% \asubsection{Users' Contributions} + +% Several of the \Coq\ Users' Contributions use extraction to produce +% certified programs. In particular the following ones have an automatic +% extraction test (just run {\tt make} in those directories): + +% \begin{itemize} +% \item Bordeaux/Additions +% \item Bordeaux/EXCEPTIONS +% \item Bordeaux/SearchTrees +% \item Dyade/BDDS +% \item Lannion +% \item Lyon/CIRCUITS +% \item Lyon/FIRING-SQUAD +% \item Marseille/CIRCUITS +% \item Muenchen/Higman +% \item Nancy/FOUnify +% \item Rocq/ARITH/Chinese +% \item Rocq/COC +% \item Rocq/GRAPHS +% \item Rocq/HIGMAN +% \item Sophia-Antipolis/Stalmarck +% \item Suresnes/BDD +% \end{itemize} + +% Lannion, Rocq/HIGMAN and Lyon/CIRCUITS are a bit particular. They are +% the only examples of developments where {\tt Obj.magic} are needed. +% This is probably due to an heavy use of impredicativity. +% After compilation those two examples run nonetheless, +% thanks to the correction of the extraction~\cite{Let02}. + +% $Id$ + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "Reference-Manual" +%%% End: diff --git a/doc/refman/Reference-Manual.tex b/doc/refman/Reference-Manual.tex index b125720d34..8cebfa4d9b 100644 --- a/doc/refman/Reference-Manual.tex +++ b/doc/refman/Reference-Manual.tex @@ -89,9 +89,9 @@ \include{Coercion.v}% %%SUPPRIME \include{Natural.v}% \include{Omega.v}% -%%SUPPRIME \include{Program.v}% %%SUPPRIME \include{Correctness.v}% = preuve de pgms imperatifs \include{Extraction.v}% +\include{Program.v}% \include{Polynom.v}% = Ring \include{Setoid.v}% Tactique pour les setoides %BEGIN LATEX diff --git a/interp/coqlib.ml b/interp/coqlib.ml index 8a55bfc2c8..f9a1c6466f 100644 --- a/interp/coqlib.ml +++ b/interp/coqlib.ml @@ -166,6 +166,13 @@ let build_sigma_type () = intro = init_constant ["Specif"] "existT"; typ = init_constant ["Specif"] "sigT" } +let build_prod () = + { proj1 = init_constant ["Datatypes"] "fst"; + proj2 = init_constant ["Datatypes"] "snd"; + elim = init_constant ["Datatypes"] "prod_rec"; + intro = init_constant ["Datatypes"] "pair"; + typ = init_constant ["Datatypes"] "prod" } + (* Equalities *) type coq_leibniz_eq_data = { eq : constr; diff --git a/interp/coqlib.mli b/interp/coqlib.mli index ed5e46c1a0..098dad1d5f 100644 --- a/interp/coqlib.mli +++ b/interp/coqlib.mli @@ -94,6 +94,8 @@ type coq_sigma_data = { val build_sigma_set : coq_sigma_data delayed val build_sigma_type : coq_sigma_data delayed +(* Non-dependent pairs in Set from Datatypes *) +val build_prod : coq_sigma_data delayed type coq_leibniz_eq_data = { eq : constr; diff --git a/pretyping/cases.ml b/pretyping/cases.ml index b2ef8060d6..ec1246b745 100644 --- a/pretyping/cases.ml +++ b/pretyping/cases.ml @@ -352,7 +352,7 @@ let find_tomatch_tycon isevars env loc = function | Some (_,ind,_),_ (* Otherwise try to get constraints from (the 1st) constructor in clauses *) | None, Some (_,(ind,_)) -> - Some (inductive_template isevars env loc ind) + Some (0, inductive_template isevars env loc ind) | None, None -> empty_tycon @@ -412,7 +412,7 @@ let adjust_tomatch_to_pattern pb ((current,typ),deps) = current else (evd_comb2 (Coercion.inh_conv_coerce_to dummy_loc pb.env) - pb.isevars (make_judge current typ) indt).uj_val in + pb.isevars (make_judge current typ) (0, indt)).uj_val in let sigma = Evd.evars_of !(pb.isevars) in let typ = IsInd (indt,find_rectype pb.env sigma indt) in ((current,typ),deps)) @@ -1571,8 +1571,9 @@ let prepare_predicate_from_tycon loc dep env isevars tomatchs c = e_new_evar isevars env ~src:(loc, Evd.CasesType) (Retyping.get_type_of env (Evd.evars_of !isevars) c) else - map_constr_with_full_binders push_rel build_skeleton env c in - names, build_skeleton env (lift n c) + map_constr_with_full_binders push_rel build_skeleton env c + in + names, build_skeleton env (lift n c) (* Here, [pred] is assumed to be in the context built from all *) (* realargs and terms to match *) @@ -1639,6 +1640,15 @@ let extract_arity_signature env0 tomatchl tmsign = | _ -> assert false in List.rev (buildrec 0 (tomatchl,tmsign)) +let inh_conv_coerce_to_tycon loc env isevars j tycon = + match tycon with + | Some p -> + let (evd',j) = Coercion.inh_conv_coerce_to loc env !isevars j p in + isevars := evd'; + j + | None -> j + + (* Builds the predicate. If the predicate is dependent, its context is * made of 1+nrealargs assumptions for each matched term in an inductive * type and 1 assumption for each term not _syntactically_ in an @@ -1653,11 +1663,11 @@ let prepare_predicate loc typing_fun isevars env tomatchs sign tycon = function (* No type annotation *) | None -> (match tycon with - | None -> None - | Some t -> - let names,pred = - prepare_predicate_from_tycon loc false env isevars tomatchs t in - Some (build_initial_predicate false names pred)) + | Some (0, t) -> + let names,pred = + prepare_predicate_from_tycon loc false env isevars tomatchs t + in Some (build_initial_predicate false names pred) + | _ -> None) (* Some type annotation *) | Some rtntyp -> @@ -1665,52 +1675,52 @@ let prepare_predicate loc typing_fun isevars env tomatchs sign tycon = function let arsign = extract_arity_signature env tomatchs sign in let env = List.fold_right push_rels arsign env in let allnames = List.rev (List.map (List.map pi1) arsign) in - let predccl = (typing_fun (mk_tycon (new_Type ())) env rtntyp).uj_val in - Some (build_initial_predicate true allnames predccl) + let predcclj = typing_fun (mk_tycon (new_Type ())) env rtntyp in + let _ = + option_app (fun tycon -> + isevars := Coercion.inh_conv_coerces_to loc env !isevars predcclj.uj_val tycon) + tycon + in + let predccl = (j_nf_isevar !isevars predcclj).uj_val in + Some (build_initial_predicate true allnames predccl) (**************************************************************************) (* Main entry of the matching compilation *) - let compile_cases loc (typing_fun, isevars) tycon env (predopt, tomatchl, eqns)= +let compile_cases loc (typing_fun, isevars) (tycon : Evarutil.type_constraint) env (predopt, tomatchl, eqns)= + + (* We build the matrix of patterns and right-hand-side *) + let matx = matx_of_eqns env tomatchl eqns in + + (* We build the vector of terms to match consistently with the *) + (* constructors found in patterns *) + let tomatchs = coerce_to_indtype typing_fun isevars env matx tomatchl in + + (* We build the elimination predicate if any and check its consistency *) + (* with the type of arguments to match *) + let tmsign = List.map snd tomatchl in + let pred = prepare_predicate loc typing_fun isevars env tomatchs tmsign tycon predopt in - (* We build the matrix of patterns and right-hand-side *) - let matx = matx_of_eqns env tomatchl eqns in - - (* We build the vector of terms to match consistently with the *) - (* constructors found in patterns *) - let tomatchs = coerce_to_indtype typing_fun isevars env matx tomatchl in - - (* We build the elimination predicate if any and check its consistency *) - (* with the type of arguments to match *) - let tmsign = List.map snd tomatchl in - let pred = prepare_predicate loc typing_fun isevars env tomatchs tmsign tycon predopt in - - (* We deal with initial aliases *) - let matx = prepare_initial_aliases (known_dependent pred) tomatchs matx in - - (* We push the initial terms to match and push their alias to rhs' envs *) - (* names of aliases will be recovered from patterns (hence Anonymous here) *) - let initial_pushed = List.map (fun tm -> Pushed (tm,[])) tomatchs in - - let pb = - { env = env; - isevars = isevars; - pred = pred; - tomatch = initial_pushed; - history = start_history (List.length initial_pushed); - mat = matx; - caseloc = loc; - typing_function = typing_fun } in - - let _, j = compile pb in - - (* We check for unused patterns *) - List.iter (check_unused_pattern env) matx; - - match tycon with - | Some p -> - let (evd',j) = Coercion.inh_conv_coerce_to loc env !isevars j p in - isevars := evd'; - j - | None -> j + (* We deal with initial aliases *) + let matx = prepare_initial_aliases (known_dependent pred) tomatchs matx in + + (* We push the initial terms to match and push their alias to rhs' envs *) + (* names of aliases will be recovered from patterns (hence Anonymous here) *) + let initial_pushed = List.map (fun tm -> Pushed (tm,[])) tomatchs in + + let pb = + { env = env; + isevars = isevars; + pred = pred; + tomatch = initial_pushed; + history = start_history (List.length initial_pushed); + mat = matx; + caseloc = loc; + typing_function = typing_fun } in + + let _, j = compile pb in + (* We check for unused patterns *) + List.iter (check_unused_pattern env) matx; + inh_conv_coerce_to_tycon loc env isevars j tycon end + diff --git a/pretyping/clenv.ml b/pretyping/clenv.ml index cb8c2cf2b5..d7c8a6dd0c 100644 --- a/pretyping/clenv.ml +++ b/pretyping/clenv.ml @@ -31,7 +31,7 @@ open Mod_subst (* *) let w_coerce env c ctyp target evd = let j = make_judge c ctyp in - let (evd',j') = Coercion.Default.inh_conv_coerce_to dummy_loc env evd j target in + let (evd',j') = Coercion.Default.inh_conv_coerce_to dummy_loc env evd j (mk_tycon_type target) in (evd',j'.uj_val) let pf_env gls = Global.env_of_context gls.it.evar_hyps diff --git a/pretyping/coercion.ml b/pretyping/coercion.ml index 3b2d85b1ff..841e6b0347 100644 --- a/pretyping/coercion.ml +++ b/pretyping/coercion.ml @@ -40,8 +40,15 @@ module type S = sig [t]; i.e. it inserts a coercion into [j], if needed, in such a way [t] and [j.uj_type] are convertible; it fails if no coercion is applicable *) val inh_conv_coerce_to : loc -> - env -> evar_defs -> unsafe_judgment -> types -> evar_defs * unsafe_judgment + env -> evar_defs -> unsafe_judgment -> type_constraint_type -> evar_defs * unsafe_judgment + + (* [inh_conv_coerces_to loc env isevars t t'] checks if an object of type [t] + is coercible to an object of type [t'] adding evar constraints if needed; + it fails if no coercion exists *) + val inh_conv_coerces_to : loc -> + env -> evar_defs -> types -> type_constraint_type -> evar_defs + (* [inh_pattern_coerce_to loc env isevars pat ind1 ind2] coerces the Cases pattern [pat] typed in [ind1] into a pattern typed in [ind2]; raises [Not_found] if no coercion found *) @@ -136,73 +143,129 @@ module Default = struct | _ -> inh_tosort_force loc env isevars j - let inh_coerce_to_fail env isevars c1 hj = - let hj' = + let inh_coerce_to_fail env isevars c1 v t = + let v', t' = try let t1,i1 = class_of1 env (evars_of isevars) c1 in - let t2,i2 = class_of1 env (evars_of isevars) hj.uj_type in + let t2,i2 = class_of1 env (evars_of isevars) t in let p = lookup_path_between (i2,i1) in - apply_coercion env p hj t2 + match v with + Some v -> + let j = apply_coercion env p {uj_val = v; uj_type = t} t2 in + Some j.uj_val, j.uj_type + | None -> None, t with Not_found -> raise NoCoercion in - try (the_conv_x_leq env hj'.uj_type c1 isevars, hj') + try (the_conv_x_leq env t' c1 isevars, v', t') with Reduction.NotConvertible -> raise NoCoercion - - let rec inh_conv_coerce_to_fail env isevars hj c1 = - let {uj_val = v; uj_type = t} = hj in - try (the_conv_x_leq env t c1 isevars, hj) - with Reduction.NotConvertible -> - (try - inh_coerce_to_fail env isevars c1 hj + open Pp + let rec inh_conv_coerce_to_fail loc env isevars v t c1 = +(* (try *) +(* msgnl (str "inh_conv_coerces_to_fail called for " ++ *) +(* Termops.print_constr_env env t ++ str " and "++ spc () ++ *) +(* Termops.print_constr_env env c1 ++ str " with evars: " ++ spc () ++ *) +(* Evd.pr_evar_defs isevars ++ str " in env: " ++ spc () ++ *) +(* Termops.print_env env); *) +(* with _ -> ()); *) + try (the_conv_x_leq env t c1 isevars, v, t) + with Reduction.NotConvertible -> + (try + inh_coerce_to_fail env isevars c1 v t with NoCoercion -> (match kind_of_term (whd_betadeltaiota env (evars_of isevars) t), kind_of_term (whd_betadeltaiota env (evars_of isevars) c1) with | Prod (_,t1,t2), Prod (name,u1,u2) -> - let v' = whd_betadeltaiota env (evars_of isevars) v in + let v' = option_app (whd_betadeltaiota env (evars_of isevars)) v in let (evd',b) = - match kind_of_term v' with - | Lambda (_,v1,v2) -> - (try the_conv_x env v1 u1 isevars, true (* leq v1 u1? *) - with Reduction.NotConvertible -> (isevars, false)) - | _ -> (isevars,false) in - if b - then - let (x,v1,v2) = destLambda v' in - let env1 = push_rel (x,None,v1) env in - let (evd'',h2) = inh_conv_coerce_to_fail env1 evd' - {uj_val = v2; uj_type = t2 } u2 in - (evd'',{ uj_val = mkLambda (x, v1, h2.uj_val); - uj_type = mkProd (x, v1, h2.uj_type) }) - else - (* Mismatch on t1 and u1 or not a lambda: we eta-expand *) - (* we look for a coercion c:u1->t1 s.t. [name:u1](v' (c x)) *) - (* has type (name:u1)u2 (with v' recursively obtained) *) - let name = (match name with - | Anonymous -> Name (id_of_string "x") - | _ -> name) in - let env1 = push_rel (name,None,u1) env in - let (evd',h1) = - inh_conv_coerce_to_fail env1 isevars - {uj_val = mkRel 1; uj_type = (lift 1 u1) } - (lift 1 t1) in - let (evd'',h2) = inh_conv_coerce_to_fail env1 evd' - { uj_val = mkApp (lift 1 v, [|h1.uj_val|]); - uj_type = subst1 h1.uj_val t2 } - u2 - in - (evd'', - { uj_val = mkLambda (name, u1, h2.uj_val); - uj_type = mkProd (name, u1, h2.uj_type) }) + match v' with + Some v' -> + (match kind_of_term v' with + | Lambda (x,v1,v2) -> + (try the_conv_x env v1 u1 isevars, Some (x, v1, v2) (* leq v1 u1? *) + with Reduction.NotConvertible -> (isevars, None)) + | _ -> (isevars, None)) + | None -> (isevars, None) + in + (match b with + Some (x, v1, v2) -> + let env1 = push_rel (x,None,v1) env in + let (evd'', v2', t2') = inh_conv_coerce_to_fail loc env1 evd' + (Some v2) t2 u2 in + (evd'', option_app (fun v2' -> mkLambda (x, v1, v2')) v2', + mkProd (x, v1, t2')) + | None -> + (* Mismatch on t1 and u1 or not a lambda: we eta-expand *) + (* we look for a coercion c:u1->t1 s.t. [name:u1](v' (c x)) *) + (* has type (name:u1)u2 (with v' recursively obtained) *) + let name = (match name with + | Anonymous -> Name (id_of_string "x") + | _ -> name) in + let env1 = push_rel (name,None,u1) env in + let (evd', v1', t1') = + inh_conv_coerce_to_fail loc env1 isevars + (Some (mkRel 1)) (lift 1 u1) (lift 1 t1) + in + let (evd'', v2', t2') = + let v2 = + match v with + Some v -> option_app (fun v1' -> mkApp (lift 1 v, [|v1'|])) v1' + | None -> None + and evd', t2 = + match v1' with + Some v1' -> evd', subst1 v1' t2 + | None -> + let evd', ev = new_evar evd' env ~src:(loc, InternalHole) t1' in + evd', subst1 ev t2 + in + inh_conv_coerce_to_fail loc env1 evd' v2 t2 u2 + in + (evd'', option_app (fun v2' -> mkLambda (name, u1, v2')) v2', + mkProd (name, u1, t2'))) | _ -> raise NoCoercion)) - + (* Look for cj' obtained from cj by inserting coercions, s.t. cj'.typ = t *) - let inh_conv_coerce_to loc env isevars cj t = - let (evd',cj') = - try - inh_conv_coerce_to_fail env isevars cj t - with NoCoercion -> - let sigma = evars_of isevars in - error_actual_type_loc loc env sigma cj t + let inh_conv_coerce_to loc env isevars cj (n, t) = + if n = 0 then + let (evd', val', type') = + try + inh_conv_coerce_to_fail loc env isevars (Some cj.uj_val) cj.uj_type t + with NoCoercion -> + let sigma = evars_of isevars in + error_actual_type_loc loc env sigma cj t + in + let val' = match val' with Some v -> v | None -> assert(false) in + (evd',{ uj_val = val'; uj_type = t }) + else + (isevars, cj) + + open Pp + let inh_conv_coerces_to loc env isevars t (abs, t') = isevars + (* Still bugguy +(* (try *) +(* msgnl (str "inh_conv_coerces_to called for " ++ *) +(* Termops.print_constr_env env t ++ str " and "++ spc () ++ *) +(* Evarutil.pr_tycon_type env tycon ++ str " with evars: " ++ spc () ++ *) +(* Evd.pr_evar_defs isevars ++ str " in env: " ++ spc () ++ *) +(* Termops.print_env env); *) +(* with _ -> ()); *) + try let (rels, rng) = + (* a little more effort to get products is needed *) + try decompose_prod_n abs t + with _ -> + raise (Invalid_argument "Coercion.inh_conv_coerces_to") in - (evd',{ uj_val = cj'.uj_val; uj_type = t }) + if noccur_between 0 (succ abs) rng then ( + (* msgnl (str "No occur between 0 and " ++ int (succ abs)); *) + let env', t, t' = + let env' = List.fold_right (fun (n, t) env -> push_rel (n, None, t) env) rels env in + env', rng, lift abs t' + in + try + pi1 (inh_conv_coerce_to_fail loc env' isevars None t t') + with NoCoercion -> + isevars) (* Maybe not enough information to unify *) + (*let sigma = evars_of isevars in + error_cannot_coerce env' sigma (t, t'))*) + else isevars + with Invalid_argument _ -> isevars *) end diff --git a/pretyping/coercion.mli b/pretyping/coercion.mli index cc2211f5f8..667f207307 100644 --- a/pretyping/coercion.mli +++ b/pretyping/coercion.mli @@ -38,8 +38,14 @@ module type S = sig [t]; i.e. it inserts a coercion into [j], if needed, in such a way [t] and [j.uj_type] are convertible; it fails if no coercion is applicable *) val inh_conv_coerce_to : loc -> - env -> evar_defs -> unsafe_judgment -> types -> evar_defs * unsafe_judgment - + env -> evar_defs -> unsafe_judgment -> type_constraint_type -> evar_defs * unsafe_judgment + + (* [inh_conv_coerces_to loc env isevars t t'] checks if an object of type [t] + is coercible to an object of type [t'] adding evar constraints if needed; + it fails if no coercion exists *) + val inh_conv_coerces_to : loc -> + env -> evar_defs -> types -> type_constraint_type -> evar_defs + (* [inh_pattern_coerce_to loc env isevars pat ind1 ind2] coerces the Cases pattern [pat] typed in [ind1] into a pattern typed in [ind2]; raises [Not_found] if no coercion found *) diff --git a/pretyping/evarutil.ml b/pretyping/evarutil.ml index ec8fcd2a8c..e9f605ecb5 100644 --- a/pretyping/evarutil.ml +++ b/pretyping/evarutil.ml @@ -67,6 +67,17 @@ let nf_evar_info evc info = evar_hyps = map_named_val (Reductionops.nf_evar evc) info.evar_hyps; evar_body = info.evar_body} +let nf_evars evm = Evd.fold (fun ev evi evm' -> Evd.add evm' ev (nf_evar_info evm evi)) + evm Evd.empty + +let nf_evar_defs isevars = Evd.evars_reset_evd (nf_evars (Evd.evars_of isevars)) isevars + +let nf_isevar isevars = nf_evar (Evd.evars_of isevars) +let j_nf_isevar isevars = j_nf_evar (Evd.evars_of isevars) +let jl_nf_isevar isevars = jl_nf_evar (Evd.evars_of isevars) +let jv_nf_isevar isevars = jv_nf_evar (Evd.evars_of isevars) +let tj_nf_isevar isevars = tj_nf_evar (Evd.evars_of isevars) + (**********************) (* Creating new metas *) (**********************) @@ -545,7 +556,9 @@ let solve_simple_eqn conv_algo env isevars (pbty,(n1,args1 as ev1),t2) = (* Operations on value/type constraints *) -type type_constraint = constr option +type type_constraint_type = int * constr +type type_constraint = type_constraint_type option + type val_constraint = constr option (* Old comment... @@ -565,8 +578,13 @@ type val_constraint = constr option (* The empty type constraint *) let empty_tycon = None +let mk_tycon_type c = (0, c) +let mk_abstr_tycon_type n c = (n, c) + (* Builds a type constraint *) -let mk_tycon ty = Some ty +let mk_tycon ty = Some (mk_tycon_type ty) + +let mk_abstr_tycon n ty = Some (mk_abstr_tycon_type n ty) (* Constrains the value of a type *) let empty_valcon = None @@ -579,7 +597,7 @@ let mk_valcon c = Some c (* Declaring any type to be in the sort Type shouldn't be harmful since cumulativity now includes Prop and Set in Type... It is, but that's not too bad *) -let define_evar_as_arrow evd (ev,args) = +let define_evar_as_abstraction abs evd (ev,args) = let evi = Evd.map (evars_of evd) ev in let evenv = evar_env evi in let (evd1,dom) = new_evar evd evenv (new_Type()) in @@ -589,14 +607,20 @@ let define_evar_as_arrow evd (ev,args) = let newenv = push_named (nvar, None, dom) evenv in let (evd2,rng) = new_evar evd1 newenv ~src:(evar_source ev evd1) (new_Type()) in - let prod = mkProd (Name nvar, dom, subst_var nvar rng) in + let prod = abs (Name nvar, dom, subst_var nvar rng) in let evd3 = Evd.evar_define ev prod evd2 in let evdom = fst (destEvar dom), args in let evrng = fst (destEvar rng), array_cons (mkRel 1) (Array.map (lift 1) args) in - let prod' = mkProd (Name nvar, mkEvar evdom, mkEvar evrng) in + let prod' = abs (Name nvar, mkEvar evdom, mkEvar evrng) in (evd3,prod') +let define_evar_as_arrow evd (ev,args) = + define_evar_as_abstraction (fun t -> mkProd t) evd (ev,args) + +let define_evar_as_lambda evd (ev,args) = + define_evar_as_abstraction (fun t -> mkLambda t) evd (ev,args) + let define_evar_as_sort isevars (ev,args) = let s = new_Type () in Evd.evar_define ev s isevars, destSort s @@ -612,20 +636,44 @@ let judge_of_new_Type () = Typeops.judge_of_type (new_univ ()) constraint on its domain and codomain. If the input constraint is an evar instantiate it with the product of 2 new evars. *) -let split_tycon loc env isevars = function - | None -> isevars,(Anonymous,None,None) - | Some c -> - let sigma = evars_of isevars in - let t = whd_betadeltaiota env sigma c in +let split_tycon loc env isevars tycon = + let rec real_split c = + let sigma = evars_of isevars in + let t = whd_betadeltaiota env sigma c in match kind_of_term t with - | Prod (na,dom,rng) -> isevars, (na, Some dom, Some rng) + | Prod (na,dom,rng) -> isevars, (na, mk_tycon dom, mk_tycon rng) | Evar ev when not (Evd.is_defined_evar isevars ev) -> let (isevars',prod) = define_evar_as_arrow isevars ev in - let (_,dom,rng) = destProd prod in - isevars',(Anonymous, Some dom, Some rng) + let (_,dom,rng) = destProd prod in + isevars',(Anonymous, mk_tycon dom, mk_tycon rng) | _ -> error_not_product_loc loc env sigma c - -let valcon_of_tycon x = x - -let lift_tycon = option_app (lift 1) + in + match tycon with + | None -> isevars,(Anonymous,None,None) + | Some (abs, c) -> + if abs = 0 then real_split c + else if abs = 1 then + isevars, (Anonymous, None, mk_tycon c) + else + isevars, (Anonymous, None, Some (pred abs, c)) + +let valcon_of_tycon x = + match x with + | Some (0, t) -> Some t + | _ -> None + +let lift_tycon_type n (abs, c) = + let abs' = abs + n in + if abs' < 0 then raise (Invalid_argument "lift_tycon_type") + else (abs', c) + +let lift_tycon n = option_app (lift_tycon_type n) + +let pr_tycon_type env (abs, t) = + if abs = 0 then Termops.print_constr_env env t + else str "Abstract " ++ int abs ++ str " " ++ Termops.print_constr_env env t + +let pr_tycon env = function + None -> str "None" + | Some t -> pr_tycon_type env t diff --git a/pretyping/evarutil.mli b/pretyping/evarutil.mli index 8550258c45..7260b5ad35 100644 --- a/pretyping/evarutil.mli +++ b/pretyping/evarutil.mli @@ -79,6 +79,7 @@ val solve_simple_eqn : evar_defs * bool val define_evar_as_arrow : evar_defs -> existential -> evar_defs * types +val define_evar_as_lambda : evar_defs -> existential -> evar_defs * types val define_evar_as_sort : evar_defs -> existential -> evar_defs * sorts (***********************************************************) @@ -86,11 +87,16 @@ val define_evar_as_sort : evar_defs -> existential -> evar_defs * sorts val judge_of_new_Type : unit -> unsafe_judgment -type type_constraint = constr option +type type_constraint_type = int * constr +type type_constraint = type_constraint_type option + type val_constraint = constr option val empty_tycon : type_constraint +val mk_tycon_type : constr -> type_constraint_type +val mk_abstr_tycon_type : int -> constr -> type_constraint_type val mk_tycon : constr -> type_constraint +val mk_abstr_tycon : int -> constr -> type_constraint val empty_valcon : val_constraint val mk_valcon : constr -> val_constraint @@ -100,7 +106,8 @@ val split_tycon : val valcon_of_tycon : type_constraint -> val_constraint -val lift_tycon : type_constraint -> type_constraint +val lift_tycon_type : int -> type_constraint_type -> type_constraint_type +val lift_tycon : int -> type_constraint -> type_constraint (***********************************************************) @@ -117,8 +124,27 @@ val tj_nf_evar : evar_map -> unsafe_type_judgment -> unsafe_type_judgment val nf_evar_info : evar_map -> evar_info -> evar_info +val nf_evars : evar_map -> evar_map + +(* Same for evar defs *) +val nf_isevar : evar_defs -> constr -> constr +val j_nf_isevar : evar_defs -> unsafe_judgment -> unsafe_judgment +val jl_nf_isevar : + evar_defs -> unsafe_judgment list -> unsafe_judgment list +val jv_nf_isevar : + evar_defs -> unsafe_judgment array -> unsafe_judgment array +val tj_nf_isevar : + evar_defs -> unsafe_type_judgment -> unsafe_type_judgment + +val nf_evar_defs : evar_defs -> evar_defs (* Replacing all evars *) exception Uninstantiated_evar of existential_key val whd_ise : evar_map -> constr -> constr val whd_castappevar : evar_map -> constr -> constr + +(*********************************************************************) +(* debug pretty-printer: *) + +val pr_tycon_type : env -> type_constraint_type -> Pp.std_ppcmds +val pr_tycon : env -> type_constraint -> Pp.std_ppcmds diff --git a/pretyping/evd.ml b/pretyping/evd.ml index 26df8f7938..5da0a1a999 100644 --- a/pretyping/evd.ml +++ b/pretyping/evd.ml @@ -408,6 +408,13 @@ let is_undefined_evar isevars c = match kind_of_term c with | Evar ev -> not (is_defined_evar isevars ev) | _ -> false +let undefined_evars isevars = + let evd = + fold (fun ev evi sigma -> if evi.evar_body = Evar_empty then + add sigma ev evi else sigma) + isevars.evars empty + in + { isevars with evars = evd } (* extracts conversion problems that satisfy predicate p *) (* Note: conv_pbs not satisying p are stored back in reverse order *) diff --git a/pretyping/evd.mli b/pretyping/evd.mli index 2f51ebf2d3..35bcb67c70 100644 --- a/pretyping/evd.mli +++ b/pretyping/evd.mli @@ -126,6 +126,7 @@ type hole_kind = | TomatchTypeParameter of inductive * int val is_defined_evar : evar_defs -> existential -> bool val is_undefined_evar : evar_defs -> constr -> bool +val undefined_evars : evar_defs -> evar_defs val evar_declare : Environ.named_context_val -> evar -> types -> ?src:loc * hole_kind -> evar_defs -> evar_defs diff --git a/pretyping/pretype_errors.ml b/pretyping/pretype_errors.ml index d87a26c223..a0f69e701b 100644 --- a/pretyping/pretype_errors.ml +++ b/pretyping/pretype_errors.ml @@ -157,6 +157,9 @@ let error_unsolvable_implicit loc env sigma e = let error_cannot_unify env sigma (m,n) = raise (PretypeError (env_ise sigma env,CannotUnify (m,n))) +let error_cannot_coerce env sigma (m,n) = + raise (PretypeError (env_ise sigma env,CannotUnify (m,n))) + (*s Ml Case errors *) let error_cant_find_case_type_loc loc env sigma expr = diff --git a/pretyping/pretype_errors.mli b/pretyping/pretype_errors.mli index dc8fdd03d9..ad8293a3c7 100644 --- a/pretyping/pretype_errors.mli +++ b/pretyping/pretype_errors.mli @@ -82,6 +82,8 @@ val error_ill_typed_rec_body_loc : val error_not_a_type_loc : loc -> env -> Evd.evar_map -> unsafe_judgment -> 'b +val error_cannot_coerce : env -> Evd.evar_map -> constr * constr -> 'b + (*s Implicit arguments synthesis errors *) val error_occur_check : env -> Evd.evar_map -> existential_key -> constr -> 'b diff --git a/pretyping/pretyping.ml b/pretyping/pretyping.ml index b99ada7693..217a9714be 100644 --- a/pretyping/pretyping.ml +++ b/pretyping/pretyping.ml @@ -72,6 +72,9 @@ sig val understand_tcc : evar_map -> env -> ?expected_type:types -> rawconstr -> open_constr + + val understand_tcc_evars : + evar_defs ref -> env -> typing_constraint -> rawconstr -> constr (* More general entry point with evars from ltac *) @@ -109,7 +112,7 @@ sig (* Idem but do not fail on unresolved evars *) - val understand_judgment_tcc : evar_map -> env -> rawconstr -> evar_map * unsafe_judgment + val understand_judgment_tcc : evar_defs ref -> env -> rawconstr -> unsafe_judgment (*i*) (* Internal of Pretyping... @@ -189,7 +192,7 @@ module Pretyping_F (Coercion : Coercion.S) = struct (* coerce to tycon if any *) let inh_conv_coerce_to_tycon loc env isevars j = function | None -> j - | Some typ -> evd_comb2 (Coercion.inh_conv_coerce_to loc env) isevars j typ + | Some t -> evd_comb2 (Coercion.inh_conv_coerce_to loc env) isevars j t let push_rels vars env = List.fold_right push_rel vars env @@ -256,8 +259,14 @@ module Pretyping_F (Coercion : Coercion.S) = struct (* [pretype tycon env isevars lvar lmeta cstr] attempts to type [cstr] *) (* in environment [env], with existential variables [(evars_of isevars)] and *) (* the type constraint tycon *) - let rec pretype tycon env isevars lvar = function - + let rec pretype (tycon : type_constraint) env isevars lvar c = +(* (try + msgnl (str "pretype with tycon: " ++ + Evarutil.pr_tycon env tycon ++ str " with evars: " ++ spc () ++ + Evd.pr_evar_defs !isevars ++ str " in env: " ++ spc () ++ + Termops.print_env env); + with _ -> ());*) + match c with | RRef (loc,ref) -> inh_conv_coerce_to_tycon loc env isevars (pretype_ref isevars env ref) @@ -285,8 +294,8 @@ module Pretyping_F (Coercion : Coercion.S) = struct | RHole (loc,k) -> let ty = match tycon with - | Some ty -> ty - | None -> + | Some (n, ty) when n = 0 -> ty + | None | Some _ -> e_new_evar isevars env ~src:(loc,InternalHole) (new_Type ()) in { uj_val = e_new_evar isevars env ~src:(loc,k) ty; uj_type = ty } @@ -344,31 +353,48 @@ module Pretyping_F (Coercion : Coercion.S) = struct inh_conv_coerce_to_tycon loc env isevars (pretype_sort s) tycon | RApp (loc,f,args) -> - let fj = pretype empty_tycon env isevars lvar f in - let floc = loc_of_rawconstr f in - let rec apply_rec env n resj = function + let length = List.length args in + let ftycon = + match tycon with + None -> None + | Some (n, ty) -> + if n = 0 then mk_abstr_tycon length ty + else Some (n + length, ty) + in + let fj = pretype ftycon env isevars lvar f in + let floc = loc_of_rawconstr f in + let rec apply_rec env n resj tycon = function | [] -> resj | c::rest -> let argloc = loc_of_rawconstr c in let resj = evd_comb1 (Coercion.inh_app_fun env) isevars resj in - let resty = - whd_betadeltaiota env (evars_of !isevars) resj.uj_type in + let resty = whd_betadeltaiota env (evars_of !isevars) resj.uj_type in match kind_of_term resty with | Prod (na,c1,c2) -> let hj = pretype (mk_tycon c1) env isevars lvar c in - let newresj = - { uj_val = applist (j_val resj, [j_val hj]); - uj_type = subst1 hj.uj_val c2 } in - apply_rec env (n+1) newresj rest + let value, typ = applist (j_val resj, [j_val hj]), subst1 hj.uj_val c2 in + let typ' = nf_isevar !isevars typ in + let tycon = + match tycon with + Some (abs, ty) -> + isevars := Coercion.inh_conv_coerces_to loc env !isevars typ' + (abs, ty); + Some (pred abs, nf_isevar !isevars ty) + | None -> None + in + apply_rec env (n+1) + { uj_val = nf_isevar !isevars value; + uj_type = nf_isevar !isevars typ' } + tycon rest | _ -> let hj = pretype empty_tycon env isevars lvar c in error_cant_apply_not_functional_loc (join_loc floc argloc) env (evars_of !isevars) resj [hj] - - in - let resj = j_nf_evar (evars_of !isevars) (apply_rec env 1 fj args) in + in + let ftycon = lift_tycon (-1) ftycon in + let resj = j_nf_evar (evars_of !isevars) (apply_rec env 1 fj ftycon args) in let resj = match kind_of_term resj.uj_val with | App (f,args) when isInd f -> @@ -402,7 +428,7 @@ module Pretyping_F (Coercion : Coercion.S) = struct let j = pretype empty_tycon env isevars lvar c1 in let t = refresh_universes j.uj_type in let var = (name,Some j.uj_val,t) in - let tycon = option_app (lift 1) tycon in + let tycon = lift_tycon 1 tycon in let j' = pretype tycon (push_rel var env) isevars lvar c2 in { uj_val = mkLetIn (name, j.uj_val, t, j'.uj_val) ; uj_type = subst1 j.uj_val j'.uj_type } @@ -454,7 +480,7 @@ module Pretyping_F (Coercion : Coercion.S) = struct { uj_val = v; uj_type = substl (realargs@[cj.uj_val]) ccl } | None -> - let tycon = option_app (lift cs.cs_nargs) tycon in + let tycon = lift_tycon cs.cs_nargs tycon in let fj = pretype tycon env_f isevars lvar d in let f = it_mkLambda_or_LetIn fj.uj_val fsign in let ccl = nf_evar (evars_of !isevars) fj.uj_type in @@ -499,17 +525,24 @@ module Pretyping_F (Coercion : Coercion.S) = struct let pj = pretype_type empty_valcon env_p isevars lvar p in let ccl = nf_evar (evars_of !isevars) pj.utj_val in let pred = it_mkLambda_or_LetIn ccl psign in - pred, lift (- nar) (beta_applist (pred,[cj.uj_val])) + let typ = lift (- nar) (beta_applist (pred,[cj.uj_val])) in + let jtyp = inh_conv_coerce_to_tycon loc env isevars {uj_val = pred; + uj_type = typ} tycon + in + jtyp.uj_val, jtyp.uj_type | None -> let p = match tycon with - | Some ty -> ty - | None -> + | Some (n, ty) when n = 0 -> ty + | None | Some _ -> e_new_evar isevars env ~src:(loc,InternalHole) (new_Type ()) in it_mkLambda_or_LetIn (lift (nar+1) p) psign, p in + let pred = nf_evar (evars_of !isevars) pred in + let p = nf_evar (evars_of !isevars) p in + (* msgnl (str "Pred is: " ++ Termops.print_constr_env env pred);*) let f cs b = let n = rel_context_length cs.cs_args in - let pi = liftn n 2 pred in + let pi = lift n pred in (* liftn n 2 pred ? *) let pi = beta_applist (pi, [build_dependent_constructor cs]) in let csgn = if not !allow_anonymous_refs then @@ -523,12 +556,11 @@ module Pretyping_F (Coercion : Coercion.S) = struct cs.cs_args in let env_c = push_rels csgn env in - let bj = pretype (Some pi) env_c isevars lvar b in +(* msgnl (str "Pi is: " ++ Termops.print_constr_env env_c pi); *) + let bj = pretype (mk_tycon pi) env_c isevars lvar b in it_mkLambda_or_LetIn bj.uj_val cs.cs_args in let b1 = f cstrs.(0) b1 in let b2 = f cstrs.(1) b2 in - let pred = nf_evar (evars_of !isevars) pred in - let p = nf_evar (evars_of !isevars) p in let v = let mis,_ = dest_ind_family indf in let ci = make_default_case_info env IfStyle mis in @@ -542,7 +574,7 @@ module Pretyping_F (Coercion : Coercion.S) = struct tycon env (* loc *) (po,tml,eqns) | RCast(loc,c,k,t) -> - let tj = pretype_type empty_tycon env isevars lvar t in + let tj = pretype_type empty_valcon env isevars lvar t in let cj = pretype (mk_tycon tj.utj_val) env isevars lvar c in (* User Casts are for helping pretyping, experimentally not to be kept*) (* ... except for Correctness *) @@ -641,22 +673,21 @@ module Pretyping_F (Coercion : Coercion.S) = struct check_evars env sigma isevars (mkCast(j.uj_val,DEFAULTcast, j.uj_type)); j - let understand_judgment_tcc sigma env c = - let isevars = ref (create_evar_defs sigma) in + let understand_judgment_tcc isevars env c = let j = pretype empty_tycon env isevars ([],[]) c in let sigma = evars_of !isevars in let j = j_nf_evar sigma j in - sigma, j + j (* Raw calls to the unsafe inference machine: boolean says if we must fail on unresolved evars; the unsafe_judgment list allows us to extend env with some bindings *) let ise_pretype_gen fail_evar sigma env lvar kind c = - let isevars = ref (create_evar_defs sigma) in + let isevars = ref (Evd.create_evar_defs sigma) in let c = pretype_gen isevars env lvar kind c in if fail_evar then check_evars env sigma isevars c; - (!isevars, c) + !isevars, c (** Entry points of the high-level type synthesis algorithm *) @@ -671,10 +702,13 @@ module Pretyping_F (Coercion : Coercion.S) = struct let understand_ltac sigma env lvar kind c = ise_pretype_gen false sigma env lvar kind c + + let understand_tcc_evars isevars env kind c = + pretype_gen isevars env ([],[]) kind c let understand_tcc sigma env ?expected_type:exptyp c = - let evars,c = ise_pretype_gen false sigma env ([],[]) (OfType exptyp) c in - evars_of evars,c + let ev, t = ise_pretype_gen false sigma env ([],[]) (OfType exptyp) c in + Evd.evars_of ev, t end module Default : S = Pretyping_F(Coercion.Default) diff --git a/pretyping/pretyping.mli b/pretyping/pretyping.mli index ac8e6fec78..ca1a1ded4c 100644 --- a/pretyping/pretyping.mli +++ b/pretyping/pretyping.mli @@ -38,6 +38,9 @@ sig val understand_tcc : evar_map -> env -> ?expected_type:types -> rawconstr -> open_constr + val understand_tcc_evars : + evar_defs ref -> env -> typing_constraint -> rawconstr -> constr + (* More general entry point with evars from ltac *) (* Generic call to the interpreter from rawconstr to constr, failing @@ -73,7 +76,7 @@ sig val understand_judgment : evar_map -> env -> rawconstr -> unsafe_judgment (* Idem but do not fail on unresolved evars *) - val understand_judgment_tcc : evar_map -> env -> rawconstr -> evar_map * unsafe_judgment + val understand_judgment_tcc : evar_defs ref -> env -> rawconstr -> unsafe_judgment (*i*) |
