diff options
Diffstat (limited to 'pretyping/find_subterm.ml')
-rw-r--r-- | pretyping/find_subterm.ml | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/pretyping/find_subterm.ml b/pretyping/find_subterm.ml new file mode 100644 index 00000000..7f7f4d76 --- /dev/null +++ b/pretyping/find_subterm.ml @@ -0,0 +1,179 @@ +(************************************************************************) +(* v * The Coq Proof Assistant / The Coq Development Team *) +(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2015 *) +(* \VV/ **************************************************************) +(* // * This file is distributed under the terms of the *) +(* * GNU Lesser General Public License Version 2.1 *) +(************************************************************************) + +open Pp +open Util +open Errors +open Names +open Locus +open Context +open Term +open Nameops +open Termops +open Pretype_errors + +(** Processing occurrences *) + +type occurrence_error = + | InvalidOccurrence of int list + | IncorrectInValueOccurrence of Id.t + +let explain_invalid_occurrence l = + let l = List.sort_uniquize Int.compare l in + str ("Invalid occurrence " ^ String.plural (List.length l) "number" ^": ") + ++ prlist_with_sep spc int l ++ str "." + +let explain_incorrect_in_value_occurrence id = + pr_id id ++ str " has no value." + +let explain_occurrence_error = function + | InvalidOccurrence l -> explain_invalid_occurrence l + | IncorrectInValueOccurrence id -> explain_incorrect_in_value_occurrence id + +let error_occurrences_error e = + errorlabstrm "" (explain_occurrence_error e) + +let error_invalid_occurrence occ = + error_occurrences_error (InvalidOccurrence occ) + +let check_used_occurrences nbocc (nowhere_except_in,locs) = + let rest = List.filter (fun o -> o >= nbocc) locs in + match rest with + | [] -> () + | _ -> error_occurrences_error (InvalidOccurrence rest) + +let proceed_with_occurrences f occs x = + match occs with + | NoOccurrences -> x + | occs -> + let plocs = Locusops.convert_occs occs in + assert (List.for_all (fun x -> x >= 0) (snd plocs)); + let (nbocc,x) = f 1 x in + check_used_occurrences nbocc plocs; + x + +(** Applying a function over a named_declaration with an hypothesis + location request *) + +let map_named_declaration_with_hyploc f hyploc acc (id,bodyopt,typ) = + let f = f (Some (id,hyploc)) in + match bodyopt,hyploc with + | None, InHypValueOnly -> + error_occurrences_error (IncorrectInValueOccurrence id) + | None, _ | Some _, InHypTypeOnly -> + let acc,typ = f acc typ in acc,(id,bodyopt,typ) + | Some body, InHypValueOnly -> + let acc,body = f acc body in acc,(id,Some body,typ) + | Some body, InHyp -> + let acc,body = f acc body in + let acc,typ = f acc typ in + acc,(id,Some body,typ) + +(** Finding a subterm up to some testing function *) + +exception SubtermUnificationError of subterm_unification_error + +exception NotUnifiable of (constr * constr * unification_error) option + +type 'a testing_function = { + match_fun : 'a -> constr -> 'a; + merge_fun : 'a -> 'a -> 'a; + mutable testing_state : 'a; + mutable last_found : position_reporting option +} + +(* Find subterms using a testing function, but only at a list of + locations or excluding a list of locations; in the occurrences list + (b,l), b=true means no occurrence except the ones in l and b=false, + means all occurrences except the ones in l *) + +let replace_term_occ_gen_modulo occs like_first test bywhat cl occ t = + let (nowhere_except_in,locs) = Locusops.convert_occs occs in + let maxocc = List.fold_right max locs 0 in + let pos = ref occ in + let nested = ref false in + let add_subst t subst = + try + test.testing_state <- test.merge_fun subst test.testing_state; + test.last_found <- Some ((cl,!pos),t) + with NotUnifiable e when not like_first -> + let lastpos = Option.get test.last_found in + raise (SubtermUnificationError (!nested,((cl,!pos),t),lastpos,e)) in + let rec substrec k t = + if nowhere_except_in && !pos > maxocc then t else + if not (Vars.closed0 t) then subst_below k t else + try + let subst = test.match_fun test.testing_state t in + if Locusops.is_selected !pos occs then + (if !nested then begin + (* in case it is nested but not later detected as unconvertible, + as when matching "id _" in "id (id 0)" *) + let lastpos = Option.get test.last_found in + raise (SubtermUnificationError (!nested,((cl,!pos),t),lastpos,None)) + end; + add_subst t subst; incr pos; + (* Check nested matching subterms *) + if occs != Locus.AllOccurrences && occs != Locus.NoOccurrences then + begin nested := true; ignore (subst_below k t); nested := false end; + (* Do the effective substitution *) + Vars.lift k (bywhat ())) + else + (incr pos; subst_below k t) + with NotUnifiable _ -> + subst_below k t + and subst_below k t = + map_constr_with_binders_left_to_right (fun d k -> k+1) substrec k t + in + let t' = substrec 0 t in + (!pos, t') + +let replace_term_occ_modulo occs test bywhat t = + let occs',like_first = + match occs with AtOccs occs -> occs,false | LikeFirst -> AllOccurrences,true in + proceed_with_occurrences + (replace_term_occ_gen_modulo occs' like_first test bywhat None) occs' t + +let replace_term_occ_decl_modulo occs test bywhat d = + let (plocs,hyploc),like_first = + match occs with AtOccs occs -> occs,false | LikeFirst -> (AllOccurrences,InHyp),true in + proceed_with_occurrences + (map_named_declaration_with_hyploc + (replace_term_occ_gen_modulo plocs like_first test bywhat) + hyploc) + plocs d + +(** Finding an exact subterm *) + +let make_eq_univs_test env evd c = + { match_fun = (fun evd c' -> + let b, cst = Universes.eq_constr_universes_proj env c c' in + if b then + try Evd.add_universe_constraints evd cst + with Evd.UniversesDiffer -> raise (NotUnifiable None) + else raise (NotUnifiable None)); + merge_fun = (fun evd _ -> evd); + testing_state = evd; + last_found = None +} + +let subst_closed_term_occ env evd occs c t = + let test = make_eq_univs_test env evd c in + let bywhat () = mkRel 1 in + let t' = replace_term_occ_modulo occs test bywhat t in + t', test.testing_state + +let subst_closed_term_occ_decl env evd occs c d = + let test = make_eq_univs_test env evd c in + let (plocs,hyploc),like_first = + match occs with AtOccs occs -> occs,false | LikeFirst -> (AllOccurrences,InHyp),true in + let bywhat () = mkRel 1 in + proceed_with_occurrences + (map_named_declaration_with_hyploc + (fun _ -> replace_term_occ_gen_modulo plocs like_first test bywhat None) + hyploc) plocs d, + test.testing_state |