diff options
| -rw-r--r-- | tactics/setoid_replace.ml | 386 |
1 files changed, 241 insertions, 145 deletions
diff --git a/tactics/setoid_replace.ml b/tactics/setoid_replace.ml index 1c0798ee67..93a0e29458 100644 --- a/tactics/setoid_replace.ml +++ b/tactics/setoid_replace.ml @@ -763,10 +763,26 @@ type direction = Left2Right | Right2Left +let prdirection = + function + Left2Right -> str "->" + | Right2Left -> str "<-" + type constr_with_marks = | MApp of constr * morphism_class * constr_with_marks array * direction | ToReplace - | ToKeep of constr * direction + | ToKeep of constr * relation relation_class * direction + +let rec prconstr_with_marks = + function + MApp (c,m,a,d) -> + str "(" ++ prterm c ++ str " " ++ + prvect_with_sep (fun () -> str " ") prconstr_with_marks a ++ str ")" + | ToReplace -> str "*" + | ToKeep (c,Leibniz _,_) + | ToKeep (c,Relation {rel_refl=Some _},_) -> prterm c + | ToKeep (c,Relation {rel_aeq=aeq; rel_refl=None},_) -> + str "(?:" ++ prterm (mkApp (aeq, [| c ; c|])) ++ str ")" let is_to_replace = function | ToKeep _ -> false @@ -790,7 +806,7 @@ let direction_of_constr_with_marks hole_direction = function MApp (_,_,_,dir) -> cic_direction_of_direction dir | ToReplace -> hole_direction - | ToKeep (_,dir) -> cic_direction_of_direction dir + | ToKeep (_,_,dir) -> cic_direction_of_direction dir type argument = Toapply of constr (* apply the function to the argument *) @@ -828,8 +844,6 @@ let relation_of_hypothesis_and_term_to_rewrite gl (_,h) t = errorlabstrm "Setoid_rewrite" (prterm aeq ++ str " is not a setoid equality.") -exception BackTrack - (* rel1 is a subrelation of rel2 whenever forall x1 x2, rel1 x1 x2 -> rel2 x1 x2 The Coq part of the tactic, however, needs rel1 == rel2. @@ -850,16 +864,76 @@ let subrelation rel1 rel2 = *) | _,_ -> false -let mark_occur gl t in_c input_relation input_direction - output_relation output_direction -= +(* two marked_constr are equivalent if they produce the same set of new goals *) +(*CSC: Note: it would be better if the tactic got rid of the smaller multiset + of goals when the two multisets are equal sets. I.e.: the tactic that + open one new goal G should be preferred to the tactic that opens two + copies of the new goal G. Moreover, a smaller proof term should be + preferred to a bigger one when the size of the two multisets is equal. *) +let marked_constr_equiv to_marked_constr c1 c2 = + let rec collect_new_goals = + function + MApp (_,_,a,_) -> List.concat (List.map collect_new_goals (Array.to_list a)) + | ToReplace + | ToKeep (_,Leibniz _,_) + | ToKeep (_,Relation {rel_refl=Some _},_) -> [] + | ToKeep (c,Relation {rel_refl=None},_) -> [c] + in + let glc1 = collect_new_goals (to_marked_constr c1) in + let glc2 = collect_new_goals (to_marked_constr c2) in + let rec equal_sets = + function + [],[] -> true + | [],_ + | _,[] -> false + | he::tl,l -> + equal_sets + ((List.filter (fun e -> not (eq_constr e he)) tl), + (List.filter (fun e -> not (eq_constr e he)) l)) + in + equal_sets (glc1,glc2) +;; + +(* given a list of constr_with_marks, it returns the list without duplicates + up to marked_constr_equiv *) +let elim_duplicates to_marked_constr = + let rec aux = + function + [] -> [] + | he:: tl -> + if List.exists (marked_constr_equiv to_marked_constr he) tl then + (*CSC: here we choose to get rid of he. But this may not be the + best choice. See the comment about marked_constr_equiv.*) + aux tl + else he::aux tl + in + aux + +(* given the array of lists [| l1 ; ... ; ln |] it returns the list of arrays + [ c1 ; ... ; cn ] that is the cartesian product of the sets l1, ..., ln *) +let cartesian_product a = + let rec aux = + function + [] -> assert false + | [he] -> List.map (fun e -> [e]) he + | he::tl -> + let tl' = aux tl in + List.flatten + (List.map (function e -> List.map (function l -> e :: l) tl') he) + in + List.map Array.of_list + (aux (List.map (elim_duplicates identity) (Array.to_list a))) + +exception BackTrack + +let mark_occur gl t in_c input_relation input_direction = let rec aux output_relation output_direction in_c = if eq_constr t in_c then if input_direction = output_direction && subrelation (Relation input_relation) output_relation then - ToReplace + [ToReplace] else - raise BackTrack + [] (*CSC: nice error message, incompatible with backtracking errorlabstrm "Setoid_rewrite" (str "The term " ++ prterm in_c ++ str " that must be rewritten occurs " ++ @@ -869,29 +943,20 @@ let mark_occur gl t in_c input_relation input_direction else match kind_of_term in_c with | App (c,al) -> - let mor = - try - begin - match morphism_table_find c with - [] -> assert false - | morphism::tl -> - if tl <> [] then - msg_warning - (str "There are several morphisms declared for " ++ - prterm c ++ - str ". The morphism " ++ prmorphism c morphism ++ - str " is randomly chosen.") ; - let relation_morphism = - relation_morphism_of_constr_morphism morphism in - if subrelation relation_morphism.output output_relation then - Some relation_morphism - else - raise BackTrack - end - with Not_found -> None + let mors = + try + let morphisms = + List.map relation_morphism_of_constr_morphism + (morphism_table_find c) + in + List.filter + (fun mor -> subrelation mor.output output_relation) morphisms + with Not_found -> [] in - (match mor with - Some mor -> + (* First we look for well typed morphisms *) + let res_mors = + List.fold_left + (fun res mor -> let a = let arguments = Array.of_list mor.args in let apply_variance_to_direction default_dir = @@ -902,83 +967,97 @@ let mark_occur gl t in_c input_relation input_direction in Util.array_map2 (fun a (variance,relation) -> - try - aux relation - (apply_variance_to_direction Left2Right variance) a - with BackTrack -> -try - aux relation - (apply_variance_to_direction Right2Left variance) a -(*CSC: an horrible hack. This should be no longer useful once that every possibility is tried. *) -with BackTrack -> ToKeep (a,apply_variance_to_direction Left2Right variance) + (aux relation + (apply_variance_to_direction Left2Right variance) a) @ + (aux relation + (apply_variance_to_direction Right2Left variance) a) ) al arguments in - if not (get_mark a) then ToKeep (in_c,output_direction) - else - MApp (c,ACMorphism mor,a,output_direction) - | None -> - (* the tactic works only if the function type is - made of non-dependent products only. However, here we - can cheat a bit by partially istantiating c to match - the requirement when the arguments to be replaced are - bound by non-dependent products only. *) - let typeofc = Tacmach.pf_type_of gl c in - let typ = nf_betaiota typeofc in - let rec find_non_dependent_function env c c_args_rev typ - f_args_rev a_rev - = - function - [] -> - if not (get_mark (Array.of_list a_rev)) then - ToKeep (in_c,output_direction) - else - let mor = - ACFunction {f_args=List.rev f_args_rev ; f_output=typ} in - let func = beta_expand c c_args_rev in - MApp - (func,mor,Array.of_list (List.rev a_rev), - output_direction) - | (he::tl) as a-> - let typnf = Reduction.whd_betadeltaiota env typ in - match kind_of_term typnf with - Cast (typ,_) -> - find_non_dependent_function env c c_args_rev typ - f_args_rev a_rev a - | Prod (name,s,t) -> - let env' = push_rel (name,None,s) env in - let he = - try - aux (Leibniz s) Left2Right he - with BackTrack -> - aux (Leibniz s) Right2Left he in - begin - match noccurn 1 t, he with - _, ToKeep (arg,_) -> - (* generic product, to keep *) - find_non_dependent_function - env' c ((Toapply arg)::c_args_rev) - (subst1 arg t) f_args_rev a_rev tl - | true, _ -> - (* non-dependent product, to replace *) - find_non_dependent_function - env' c ((Toexpand (name,s))::c_args_rev) - (lift 1 t) (s::f_args_rev) (he::a_rev) tl - | false, _ -> - (* dependent product, to replace *) - (*CSC: this limitation is due to the reflexive - implementation and it is hard to lift *) - errorlabstrm "Setoid_replace" - (str "Cannot rewrite in the argument of a " ++ - str "dependent product. If you need this " ++ - str "feature, please report to the authors.") - end - | _ -> assert false - in - find_non_dependent_function (Tacmach.pf_env gl) c [] typ [] [] - (Array.to_list al)) + let a' = cartesian_product a in + (List.map + (function a -> + if not (get_mark a) then + ToKeep (in_c,output_relation,output_direction) + else + MApp (c,ACMorphism mor,a,output_direction)) a') @ res + ) [] mors in + (* Then we look for well typed functions *) + let res_functions = + (* the tactic works only if the function type is + made of non-dependent products only. However, here we + can cheat a bit by partially istantiating c to match + the requirement when the arguments to be replaced are + bound by non-dependent products only. *) + let typeofc = Tacmach.pf_type_of gl c in + let typ = nf_betaiota typeofc in + let rec find_non_dependent_function env c c_args_rev typ f_args_rev + a_rev + = + function + [] -> + if not (get_mark (Array.of_list a_rev)) then + [ToKeep (in_c,output_relation,output_direction)] + else + let err = + match output_relation with + Leibniz typ' when eq_constr typ typ' -> false + | _ when output_relation = (Lazy.force coq_prop_relation) -> + false + | _ -> true + in + if err then [] + else + let mor = + ACFunction {f_args=List.rev f_args_rev; f_output=typ} in + let func = beta_expand c c_args_rev in + [MApp + (func,mor,Array.of_list (List.rev a_rev), + output_direction)] + | (he::tl) as a-> + let typnf = Reduction.whd_betadeltaiota env typ in + match kind_of_term typnf with + Cast (typ,_) -> + find_non_dependent_function env c c_args_rev typ + f_args_rev a_rev a + | Prod (name,s,t) -> + let env' = push_rel (name,None,s) env in + let he = + (aux (Leibniz s) Left2Right he) @ + (aux (Leibniz s) Right2Left he) in +(*CSC: let's make it simple at first *) +let he = match he with [] -> raise BackTrack | he::_ -> (*ppnl (str "!!!!!! THE TACTIC IS STILL INCOMPLETE HERE !!!!!");*) he in + begin + match noccurn 1 t, he with + _, ToKeep (arg,_,_) -> + (* generic product, to keep *) + find_non_dependent_function + env' c ((Toapply arg)::c_args_rev) + (subst1 arg t) f_args_rev a_rev tl + | true, _ -> + (* non-dependent product, to replace *) + find_non_dependent_function + env' c ((Toexpand (name,s))::c_args_rev) + (lift 1 t) (s::f_args_rev) (he::a_rev) tl + | false, _ -> + (* dependent product, to replace *) + (*CSC: this limitation is due to the reflexive + implementation and it is hard to lift *) + errorlabstrm "Setoid_replace" + (str "Cannot rewrite in the argument of a " ++ + str "dependent product. If you need this " ++ + str "feature, please report to the author.") + end + | _ -> assert false + in +try + find_non_dependent_function (Tacmach.pf_env gl) c [] typ [] [] + (Array.to_list al) +with BackTrack -> [] + in + elim_duplicates identity (res_functions @ res_mors) | Prod (_, c1, c2) -> if (dependent (mkRel 1) c2) - then ToKeep (in_c,output_direction) + then [ToKeep (in_c,output_relation,output_direction)] else (*CSC: output_relation in the two lines below is a bug. I should use the two arguments of the relation associated to @@ -988,16 +1067,46 @@ with BackTrack -> ToKeep (a,apply_variance_to_direction Left2Right variance) let c2m = aux output_relation output_direction c2 in - if ((is_to_replace c1m)||(is_to_replace c2m)) - then - (* this can be optimized since c1 and c2 will be *) - (* processed again *) - aux output_relation output_direction - (mkApp ((Lazy.force coq_impl), [| c1 ; c2 |])) - else ToKeep (in_c,output_direction) - | _ -> ToKeep (in_c,output_direction) + let a = cartesian_product [| c1m ; c2m |] in + List.fold_left + (fun res a -> + if get_mark a + then + (* this can be optimized since c1 and c2 will be *) + (* processed again *) + (aux output_relation output_direction + (mkApp ((Lazy.force coq_impl), [| c1 ; c2 |]))) @ res + else (ToKeep (in_c,output_relation,output_direction))::res) [] a + | _ -> [ToKeep (in_c,output_relation,output_direction)] in - aux output_relation output_direction in_c + let aux2 output_relation output_direction = + List.map + (fun res -> output_relation,output_direction,res) + (aux output_relation output_direction in_c) in + let res = + (aux2 (Lazy.force coq_prop_relation) Right2Left) @ + (* This is the case of a proposition of signature A ++> iff or B --> iff *) + (aux2 (Lazy.force coq_prop_relation) Left2Right) @ + (aux2 (Lazy.force coq_prop_relation2) Right2Left) in + let res = elim_duplicates (function (_,_,t) -> t) res in + match res with + [] -> + errorlabstrm "Setoid_rewrite" + (str "Either the term " ++ prterm in_c ++ str " that must be " ++ + str "rewritten occurs in a covariant position or the goal is not " ++ + str "made of morphism applications only. You can replace only " ++ + str "occurrences that are in a contravariant position and such that " ++ + str "the context obtained by abstracting them is made of morphism " ++ + str "applications only.") + | [he] -> he + | he::_ -> + msg_warning + (str "There are several possible ways to perform this rewriting:" ++ + pr_fnl () ++ + prlist_with_sep pr_fnl + (function (_,_,mc) -> prconstr_with_marks mc) res ++ pr_fnl () ++ + str " The first one is randomly chosen.") ; + he let cic_morphism_context_list_of_list hole_relation hole_direction out_direction = @@ -1084,7 +1193,7 @@ let syntactic_but_representation_of_marked_but hole_relation hole_direction = morphism_theory ; argst|]) | ToReplace -> mkApp ((Lazy.force coq_ToReplace), [| hole_relation ; hole_direction |]) - | ToKeep (c,direction) -> + | ToKeep (c,_,direction) -> let direction = cic_direction_of_direction direction in if is_reflexive then mkApp ((Lazy.force coq_ToKeep), @@ -1134,39 +1243,24 @@ let apply_coq_setoid_rewrite hole_relation prop_relation c1 c2 (direction,h) let relation_rewrite c1 c2 (input_direction,cl) gl = let but = pf_concl gl in let (hyp,c1,c2) = +let oldc1 = c1 in let (env',c1) = - w_unify_to_subterm ~mod_delta:false (pf_env gl) (c1,but) cl.env in + try + w_unify_to_subterm ~mod_delta:false (pf_env gl) (c1,but) cl.env + with + Pretype_errors.PretypeError _ -> + (*CSC: not very nice; to make Ring work *) + w_unify_to_subterm ~mod_delta:true (pf_env gl) (c1,but) cl.env + in let cl' = {cl with env = env' } in let c2 = Clenv.clenv_nf_meta cl' c2 in (input_direction,Clenv.clenv_value cl'), c1, c2 in let input_relation = relation_of_hypothesis_and_term_to_rewrite gl hyp c1 in let output_relation,output_direction,marked_but = - (*CSC: this code must be moved into mark_occur, as well as the choice of - the output relation *) - try - Lazy.force coq_prop_relation, Right2Left, - mark_occur gl c1 but input_relation input_direction - (Lazy.force coq_prop_relation) Right2Left - with BackTrack -> try - (* This is the case of a proposition of signature A ++> iff or B --> iff *) - Lazy.force coq_prop_relation, Left2Right, - mark_occur gl c1 but input_relation input_direction - (Lazy.force coq_prop_relation) Left2Right - with BackTrack -> try - Lazy.force coq_prop_relation2, Right2Left, - mark_occur gl c1 but input_relation input_direction - (Lazy.force coq_prop_relation2) Right2Left - with BackTrack -> - (*CSC: the error is too detailed. The tactic can now fail also for other - CSC: more stupid reasons (i.e. randomly not choosing the right morphism *) - errorlabstrm "Setoid_rewrite" - (str "The term " ++ prterm c1 ++ str " that must be rewritten occurs " ++ - str "in a covariant position. You can replace only occurrences that " ++ - str "are in a contravariant position.") - in - let cic_output_direction = cic_direction_of_direction output_direction in -let temporary_solution gl = + mark_occur gl c1 but input_relation input_direction in + let cic_output_direction = cic_direction_of_direction output_direction in + let if_output_relation_is_iff gl = let th = apply_coq_setoid_rewrite input_relation output_relation c1 c2 hyp cic_output_direction marked_but @@ -1181,9 +1275,8 @@ let temporary_solution gl = let impl2 = mkProd (Anonymous, hyp1, lift 1 hyp2) in let th' = mkApp (proj, [|impl2; impl1; th|]) in Tactics.refine - (mkApp (th', [| mkCast (Evarutil.mk_new_meta(), new_but) |])) gl -in -let temporary_solution2 gl = + (mkApp (th', [| mkCast (Evarutil.mk_new_meta(), new_but) |])) gl in + let if_output_relation_is_if gl = let th = apply_coq_setoid_rewrite input_relation output_relation c1 c2 hyp cic_output_direction marked_but @@ -1191,8 +1284,11 @@ let temporary_solution2 gl = let new_but = Termops.replace_term c1 c2 but in Tactics.refine (mkApp (th, [| mkCast (Evarutil.mk_new_meta(), new_but) |])) gl -in - Tacticals.tclFIRST [temporary_solution ; temporary_solution2] gl + in + if output_relation = (Lazy.force coq_prop_relation) then + if_output_relation_is_iff gl + else + if_output_relation_is_if gl let general_s_rewrite lft2rgt c gl = let direction = if lft2rgt then Left2Right else Right2Left in |
