diff options
Diffstat (limited to 'pretyping')
| -rw-r--r-- | pretyping/matching.ml | 110 | ||||
| -rw-r--r-- | pretyping/matching.mli | 13 | ||||
| -rw-r--r-- | pretyping/pattern.ml | 42 | ||||
| -rw-r--r-- | pretyping/pattern.mli | 48 | ||||
| -rw-r--r-- | pretyping/pretyping.ml | 33 | ||||
| -rw-r--r-- | pretyping/pretyping.mli | 21 |
6 files changed, 197 insertions, 70 deletions
diff --git a/pretyping/matching.ml b/pretyping/matching.ml index 2c0285ed24..ddfe5dd076 100644 --- a/pretyping/matching.ml +++ b/pretyping/matching.ml @@ -46,9 +46,10 @@ type bound_ident_map = (identifier * identifier) list exception PatternMatchingFailure -let constrain (n,m) (names,terms as subst) = +let constrain (n,(ids,m as x)) (names,terms as subst) = try - if eq_constr m (List.assoc n terms) then subst + let (ids',m') = List.assoc n terms in + if ids = ids' && eq_constr m m' then subst else raise PatternMatchingFailure with Not_found -> @@ -56,7 +57,7 @@ let constrain (n,m) (names,terms as subst) = Flags.if_verbose Pp.warning ("Collision between bound variable "^string_of_id n^ " and a metavariable of same name."); - (names,(n,m)::terms) + (names,(n,x)::terms) let add_binders na1 na2 (names,terms as subst) = match na1, na2 with @@ -76,7 +77,7 @@ let add_binders na1 na2 (names,terms as subst) = let build_lambda toabstract stk (m : constr) = let rec buildrec m p_0 p_1 = match p_0,p_1 with | (_, []) -> m - | (n, (na,t)::tl) -> + | (n, (_,na,t)::tl) -> if List.mem n toabstract then buildrec (mkLambda (na,t,m)) (n+1) tl else @@ -96,7 +97,58 @@ let same_case_structure (_,cs1,ind,_) ci2 br1 br2 = | Some ind -> ind = ci2.ci_ind | None -> cs1 = ci2.ci_cstr_ndecls -let matches_core convert allow_partial_app pat c = +let rec list_insert f a = function + | [] -> [a] + | b::l when f a b -> a::b::l + | b::l when a = b -> raise PatternMatchingFailure + | b::l -> b :: list_insert f a l + +let extract_bound_vars = + let rec aux k = function + | ([],_) -> [] + | (n::l,(na1,na2,_)::stk) when k = n -> + begin match na1,na2 with + | Name id1,Name _ -> list_insert (<) id1 (aux (k+1) (l,stk)) + | Name _,Anonymous -> anomaly "Unnamed bound variable" + | Anonymous,_ -> raise PatternMatchingFailure + end + | (l,_::stk) -> aux (k+1) (l,stk) + | (_,[]) -> assert false + in aux 1 + +let dummy_constr = mkProp + +let rec make_renaming ids = function + | (Name id,Name _,_)::stk -> + let renaming = make_renaming ids stk in + (try mkRel (list_index id ids) :: renaming + with Not_found -> dummy_constr :: renaming) + | (_,_,_)::stk -> + dummy_constr :: make_renaming ids stk + | [] -> + [] + +let merge_binding allow_bound_rels stk n cT subst = + let depth = List.length stk in + let c = + if depth = 0 then + (* Optimization *) + ([],cT) + else + let frels = Intset.elements (free_rels cT) in + let frels = List.filter (fun i -> i <= depth) frels in + if allow_bound_rels then + let frels = Sort.list (<) frels in + let canonically_ordered_vars = extract_bound_vars (frels,stk) in + let renaming = make_renaming canonically_ordered_vars stk in + (canonically_ordered_vars, substl renaming cT) + else if frels = [] then + ([],lift (-depth) cT) + else + raise PatternMatchingFailure in + constrain (n,c) subst + +let matches_core convert allow_partial_app allow_bound_rels pat c = let conv = match convert with | None -> eq_constr | Some (env,sigma) -> is_conv env sigma in @@ -112,21 +164,11 @@ let matches_core convert allow_partial_app pat c = args in let frels = Intset.elements (free_rels cT) in if list_subset frels relargs then - constrain (n,build_lambda relargs stk cT) subst + constrain (n,([],build_lambda relargs stk cT)) subst else raise PatternMatchingFailure - | PMeta (Some n), m -> - let depth = List.length stk in - if depth = 0 then - (* Optimisation *) - constrain (n,cT) subst - else - let frels = Intset.elements (free_rels cT) in - if List.for_all (fun i -> i > depth) frels then - constrain (n,lift (-depth) cT) subst - else - raise PatternMatchingFailure + | PMeta (Some n), m -> merge_binding allow_bound_rels stk n cT subst | PMeta None, m -> subst @@ -151,14 +193,8 @@ let matches_core convert allow_partial_app pat c = let p = Array.length args2 - Array.length args1 in if p>=0 then let args21, args22 = array_chop p args2 in - let subst = - let depth = List.length stk in - let c = mkApp(c2,args21) in - let frels = Intset.elements (free_rels c) in - if List.for_all (fun i -> i > depth) frels then - constrain (n,lift (-depth) c) subst - else - raise PatternMatchingFailure in + let c = mkApp(c2,args21) in + let subst = merge_binding allow_bound_rels stk n c subst in array_fold_left2 (sorec stk) subst args1 args22 else raise PatternMatchingFailure @@ -167,15 +203,15 @@ let matches_core convert allow_partial_app pat c = with Invalid_argument _ -> raise PatternMatchingFailure) | PProd (na1,c1,d1), Prod(na2,c2,d2) -> - sorec ((na2,c2)::stk) + sorec ((na1,na2,c2)::stk) (add_binders na1 na2 (sorec stk subst c1 c2)) d1 d2 | PLambda (na1,c1,d1), Lambda(na2,c2,d2) -> - sorec ((na2,c2)::stk) + sorec ((na1,na2,c2)::stk) (add_binders na1 na2 (sorec stk subst c1 c2)) d1 d2 | PLetIn (na1,c1,d1), LetIn(na2,c2,t2,d2) -> - sorec ((na2,t2)::stk) + sorec ((na1,na2,t2)::stk) (add_binders na1 na2 (sorec stk subst c1 c2)) d1 d2 | PIf (a1,b1,b1'), Case (ci,_,a2,[|b2;b2'|]) -> @@ -184,8 +220,10 @@ let matches_core convert allow_partial_app pat c = let n = rel_context_length ctx in let n' = rel_context_length ctx' in if noccur_between 1 n b2 & noccur_between 1 n' b2' then - let s = List.fold_left (fun l (na,_,t) -> (na,t)::l) stk ctx in - let s' = List.fold_left (fun l (na,_,t) -> (na,t)::l) stk ctx' in + let s = + List.fold_left (fun l (na,_,t) -> (Anonymous,na,t)::l) stk ctx in + let s' = + List.fold_left (fun l (na,_,t) -> (Anonymous,na,t)::l) stk ctx' in let b1 = lift_pattern n b1 and b1' = lift_pattern n' b1' in sorec s' (sorec s (sorec stk subst a1 a2) b1 b2) b1' b2' else @@ -206,16 +244,20 @@ let matches_core convert allow_partial_app pat c = let names,terms = sorec [] ([],[]) pat c in (names,Sort.list (fun (a,_) (b,_) -> a<b) terms) -let extended_matches = matches_core None true +let matches_core_closed convert allow_partial_app pat c = + let names,subst = matches_core convert allow_partial_app false pat c in + (names, List.map (fun (a,(_,b)) -> (a,b)) subst) + +let extended_matches = matches_core None true true -let matches c p = snd (matches_core None true c p) +let matches c p = snd (matches_core_closed None true c p) let special_meta = (-1) (* Tells if it is an authorized occurrence and if the instance is closed *) let authorized_occ partial_app closed pat c mk_ctx next = try - let sigma = matches_core None partial_app pat c in + let sigma = matches_core_closed None partial_app pat c in if closed && not (List.for_all (fun (_,c) -> closed0 c) (snd sigma)) then next () else sigma, mk_ctx (mkMeta special_meta), next @@ -308,7 +350,7 @@ let is_matching_appsubterm ?(closed=true) pat c = with PatternMatchingFailure -> false let matches_conv env sigma c p = - snd (matches_core (Some (env,sigma)) false c p) + snd (matches_core_closed (Some (env,sigma)) false c p) let is_matching_conv env sigma pat n = try let _ = matches_conv env sigma pat n in true diff --git a/pretyping/matching.mli b/pretyping/matching.mli index 95314054e5..eb9c8fc8c6 100644 --- a/pretyping/matching.mli +++ b/pretyping/matching.mli @@ -6,18 +6,25 @@ * GNU Lesser General Public License Version 2.1 ***********************************************************************) +(** This module implements pattern-matching on terms *) + open Names open Term open Environ open Pattern open Termops -(** {6 This modules implements pattern-matching on terms } *) - +(** [PatternMatchingFailure] is the exception raised when pattern + matching fails *) exception PatternMatchingFailure +(** [special_meta] is the default name of the meta holding the + surrounding context in subterm matching *) val special_meta : metavariable +(** [bound_ident_map] represents the result of matching binding + identifiers of the pattern with the binding identifiers of the term + matched *) type bound_ident_map = (identifier * identifier) list (** [matches pat c] matches [c] against [pat] and returns the resulting @@ -31,7 +38,7 @@ val matches : constr_pattern -> constr -> patvar_map variables or metavariables have the same name, the metavariable, or else the rightmost bound variable, takes precedence *) val extended_matches : - constr_pattern -> constr -> bound_ident_map * patvar_map + constr_pattern -> constr -> bound_ident_map * extended_patvar_map (** [is_matching pat c] just tells if [c] matches against [pat] *) val is_matching : constr_pattern -> constr -> bool diff --git a/pretyping/pattern.ml b/pretyping/pattern.ml index 48218f47f2..59f3cde88a 100644 --- a/pretyping/pattern.ml +++ b/pretyping/pattern.ml @@ -19,8 +19,10 @@ open Mod_subst (* Metavariables *) +type constr_under_binders = identifier list * constr + type patvar_map = (patvar * constr) list -let pr_patvar = pr_id +type extended_patvar_map = (patvar * constr_under_binders) list (* Patterns *) @@ -143,9 +145,9 @@ let pattern_of_constr sigma t = let map_pattern_with_binders g f l = function | PApp (p,pl) -> PApp (f l p, Array.map (f l) pl) | PSoApp (n,pl) -> PSoApp (n, List.map (f l) pl) - | PLambda (n,a,b) -> PLambda (n,f l a,f (g l) b) - | PProd (n,a,b) -> PProd (n,f l a,f (g l) b) - | PLetIn (n,a,b) -> PLetIn (n,f l a,f (g l) b) + | PLambda (n,a,b) -> PLambda (n,f l a,f (g n l) b) + | PProd (n,a,b) -> PProd (n,f l a,f (g n l) b) + | PLetIn (n,a,b) -> PLetIn (n,f l a,f (g n l) b) | PIf (c,b1,b2) -> PIf (f l c,f l b1,f l b2) | PCase (ci,po,p,pl) -> PCase (ci,f l po,f l p,Array.map (f l) pl) (* Non recursive *) @@ -153,18 +155,40 @@ let map_pattern_with_binders g f l = function (* Bound to terms *) | PFix _ | PCoFix _ as x) -> x -let map_pattern f = map_pattern_with_binders (fun () -> ()) (fun () -> f) () +let map_pattern f = map_pattern_with_binders (fun _ () -> ()) (fun () -> f) () + +let error_instantiate_pattern id l = + let is = if List.length l = 1 then "is" else "are" in + errorlabstrm "" (str "Cannot substitute the term bound to " ++ pr_id id + ++ strbrk " in pattern because the term refers to " ++ pr_enum pr_id l + ++ strbrk " which " ++ str is ++ strbrk " not bound in the pattern.") -let rec instantiate_pattern lvar = function - | PVar id as x -> (try Lazy.force(List.assoc id lvar) with Not_found -> x) +let instantiate_pattern lvar c = + let rec aux vars = function + | PVar id as x -> + (try + let ctx,c = List.assoc id lvar in + try + let inst = + List.map (fun id -> mkRel (list_index (Name id) vars)) ctx in + let c = substl inst c in + snd (pattern_of_constr Evd.empty c) + with Not_found (* list_index failed *) -> + let vars = + list_map_filter (function Name id -> Some id | _ -> None) vars in + error_instantiate_pattern id (list_subtract ctx vars) + with Not_found (* List.assoc failed *) -> + x) | (PFix _ | PCoFix _) -> error ("Non instantiable pattern.") - | c -> map_pattern (instantiate_pattern lvar) c + | c -> + map_pattern_with_binders (fun id vars -> id::vars) aux vars c in + aux [] c let rec liftn_pattern k n = function | PRel i as x -> if i >= n then PRel (i+k) else x | PFix x -> PFix (destFix (liftn k n (mkFix x))) | PCoFix x -> PCoFix (destCoFix (liftn k n (mkCoFix x))) - | c -> map_pattern_with_binders succ (liftn_pattern k) n c + | c -> map_pattern_with_binders (fun _ -> succ) (liftn_pattern k) n c let lift_pattern k = liftn_pattern k 1 diff --git a/pretyping/pattern.mli b/pretyping/pattern.mli index d805730e97..3b61c1e437 100644 --- a/pretyping/pattern.mli +++ b/pretyping/pattern.mli @@ -6,6 +6,9 @@ * GNU Lesser General Public License Version 2.1 ***********************************************************************) +(** This module defines the type of pattern used for pattern-matching + terms in tatics *) + open Pp open Names open Sign @@ -16,12 +19,46 @@ open Nametab open Rawterm open Mod_subst -(** Pattern variables *) +(** {5 Maps of pattern variables} *) + +(** Type [constr_under_binders] is for representing the term resulting + of a matching. Matching can return terms defined in a some context + of named binders; in the context, variable names are ordered by + (<) and referred to by index in the term Thanks to the canonical + ordering, a matching problem like + + [match ... with [(fun x y => ?p,fun y x => ?p)] => [forall x y => p]] + + will be accepted. Thanks to the reference by index, a matching + problem like + + [match ... with [(fun x => ?p)] => [forall x => p]] + + will work even if [x] is also the name of an existing goal + variable. + + Note: we do not keep types in the signature. Besides simplicity, + the main reason is that it would force to close the signature over + binders that occur only in the types of effective binders but not + in the term itself (e.g. for a term [f x] with [f:A -> True] and + [x:A]). + + On the opposite side, by not keeping the types, we loose + opportunity to propagate type informations which otherwise would + not be inferable, as e.g. when matching [forall x, x = 0] with + pattern [forall x, ?h = 0] and using the solution "x|-h:=x" in + expression [forall x, h = x] where nothing tells how the type of x + could be inferred. We also loose the ability of typing ltac + variables before calling the right-hand-side of ltac matching clauses. *) + +type constr_under_binders = identifier list * constr + +(** Types of substitutions with or w/o bound variables *) type patvar_map = (patvar * constr) list -val pr_patvar : patvar -> std_ppcmds +type extended_patvar_map = (patvar * constr_under_binders) list -(** Patterns *) +(** {5 Patterns} *) type constr_pattern = | PRef of global_reference @@ -41,6 +78,8 @@ type constr_pattern = | PFix of fixpoint | PCoFix of cofixpoint +(** {5 Functions on patterns} *) + val occur_meta_pattern : constr_pattern -> bool val subst_pattern : substitution -> constr_pattern -> constr_pattern @@ -72,6 +111,7 @@ val pattern_of_rawconstr : rawconstr -> patvar list * constr_pattern val instantiate_pattern : - (identifier * constr_pattern Lazy.t) list -> constr_pattern -> constr_pattern + (identifier * (identifier list * constr)) list -> + constr_pattern -> constr_pattern val lift_pattern : int -> constr_pattern -> constr_pattern diff --git a/pretyping/pretyping.ml b/pretyping/pretyping.ml index 71a21f6c88..e63b9a234f 100644 --- a/pretyping/pretyping.ml +++ b/pretyping/pretyping.ml @@ -45,8 +45,10 @@ open Evarconv open Pattern type typing_constraint = OfType of types option | IsType -type var_map = (identifier * unsafe_judgment) list +type var_map = (identifier * constr_under_binders) list type unbound_ltac_var_map = (identifier * identifier option) list +type ltac_var_map = var_map * unbound_ltac_var_map +type rawconstr_ltac_closure = ltac_var_map * rawconstr (************************************************************************) (* This concerns Cases *) @@ -131,7 +133,7 @@ sig *) val understand_ltac : - bool -> evar_map -> env -> var_map * unbound_ltac_var_map -> + bool -> evar_map -> env -> ltac_var_map -> typing_constraint -> rawconstr -> evar_map * constr (* Standard call to get a constr from a rawconstr, resolving implicit args *) @@ -162,18 +164,15 @@ sig *) val pretype : type_constraint -> env -> evar_map ref -> - var_map * (identifier * identifier option) list -> - rawconstr -> unsafe_judgment + ltac_var_map -> rawconstr -> unsafe_judgment val pretype_type : val_constraint -> env -> evar_map ref -> - var_map * (identifier * identifier option) list -> - rawconstr -> unsafe_type_judgment + ltac_var_map -> rawconstr -> unsafe_type_judgment val pretype_gen : bool -> bool -> bool -> evar_map ref -> env -> - var_map * (identifier * identifier option) list -> - typing_constraint -> rawconstr -> constr + ltac_var_map -> typing_constraint -> rawconstr -> constr (*i*) end @@ -225,7 +224,7 @@ module Pretyping_F (Coercion : Coercion.S) = struct let check_branches_message loc env evdref c (explft,lft) = for i = 0 to Array.length explft - 1 do if not (e_cumul env evdref lft.(i) explft.(i)) then - let sigma = !evdref in + let sigma = !evdref in error_ill_formed_branch_loc loc env sigma c i lft.(i) explft.(i) done @@ -243,7 +242,14 @@ module Pretyping_F (Coercion : Coercion.S) = struct | Anonymous -> name' | _ -> name - let pretype_id loc env (lvar,unbndltacvars) id = + let invert_ltac_bound_name env id0 id = + try mkRel (pi1 (lookup_rel_id id (rel_context env))) + with Not_found -> + errorlabstrm "" (str "Ltac variable " ++ pr_id id0 ++ + str " depends on pattern variable name " ++ pr_id id ++ + str " which is not bound in current context.") + + let pretype_id loc env sigma (lvar,unbndltacvars) id = (* Look for the binder of [id] *) try let (n,_,typ) = lookup_rel_id id (rel_context env) in @@ -251,7 +257,10 @@ module Pretyping_F (Coercion : Coercion.S) = struct with Not_found -> (* Check if [id] is an ltac variable *) try - List.assoc id lvar + let (ids,c) = List.assoc id lvar in + let subst = List.map (invert_ltac_bound_name env id) ids in + let c = substl subst c in + { uj_val = c; uj_type = Retyping.get_type_of env sigma c } with Not_found -> (* Check if [id] is a section or goal variable *) try @@ -295,7 +304,7 @@ module Pretyping_F (Coercion : Coercion.S) = struct | RVar (loc, id) -> inh_conv_coerce_to_tycon loc env evdref - (pretype_id loc env lvar id) + (pretype_id loc env !evdref lvar id) tycon | REvar (loc, evk, instopt) -> diff --git a/pretyping/pretyping.mli b/pretyping/pretyping.mli index 18a6b03a71..00eb613abc 100644 --- a/pretyping/pretyping.mli +++ b/pretyping/pretyping.mli @@ -6,6 +6,12 @@ * GNU Lesser General Public License Version 2.1 ***********************************************************************) +(** This file implements type inference. It maps [rawconstr] + (i.e. untyped terms whose names are located) to [constr]. In + particular, it drives complex pattern-matching problems ("match") + into elementary ones, insertion of coercions and resolution of + implicit arguments. *) + open Names open Sign open Term @@ -21,8 +27,10 @@ val search_guard : type typing_constraint = OfType of types option | IsType -type var_map = (identifier * unsafe_judgment) list +type var_map = (identifier * Pattern.constr_under_binders) list type unbound_ltac_var_map = (identifier * identifier option) list +type ltac_var_map = var_map * unbound_ltac_var_map +type rawconstr_ltac_closure = ltac_var_map * rawconstr module type S = sig @@ -56,7 +64,7 @@ sig *) val understand_ltac : - bool -> evar_map -> env -> var_map * unbound_ltac_var_map -> + bool -> evar_map -> env -> ltac_var_map -> typing_constraint -> rawconstr -> evar_map * constr (** Standard call to get a constr from a rawconstr, resolving implicit args *) @@ -84,18 +92,15 @@ sig (** Internal of Pretyping... *) val pretype : type_constraint -> env -> evar_map ref -> - var_map * (identifier * identifier option) list -> - rawconstr -> unsafe_judgment + ltac_var_map -> rawconstr -> unsafe_judgment val pretype_type : val_constraint -> env -> evar_map ref -> - var_map * (identifier * identifier option) list -> - rawconstr -> unsafe_type_judgment + ltac_var_map -> rawconstr -> unsafe_type_judgment val pretype_gen : bool -> bool -> bool -> evar_map ref -> env -> - var_map * (identifier * identifier option) list -> - typing_constraint -> rawconstr -> constr + ltac_var_map -> typing_constraint -> rawconstr -> constr (**/**) |
