diff options
-rw-r--r-- | dev/printers.mllib | 1 | ||||
-rw-r--r-- | pretyping/cases.ml | 1 | ||||
-rw-r--r-- | pretyping/evarconv.ml | 1 | ||||
-rw-r--r-- | pretyping/evarsolve.ml | 1363 | ||||
-rw-r--r-- | pretyping/evarsolve.mli | 48 | ||||
-rw-r--r-- | pretyping/evarutil.ml | 1345 | ||||
-rw-r--r-- | pretyping/evarutil.mli | 40 | ||||
-rw-r--r-- | pretyping/pretyping.mllib | 1 | ||||
-rw-r--r-- | pretyping/unification.ml | 1 |
9 files changed, 1428 insertions, 1373 deletions
diff --git a/dev/printers.mllib b/dev/printers.mllib index 7caf268b9..fa6d9324c 100644 --- a/dev/printers.mllib +++ b/dev/printers.mllib @@ -95,6 +95,7 @@ Retyping Cbv Pretype_errors Evarutil +Evarsolve Term_dnet Recordops Evarconv diff --git a/pretyping/cases.ml b/pretyping/cases.ml index ce8e296ba..60e3025b4 100644 --- a/pretyping/cases.ml +++ b/pretyping/cases.ml @@ -24,6 +24,7 @@ open Glob_ops open Retyping open Pretype_errors open Evarutil +open Evarsolve open Evarconv open Evd diff --git a/pretyping/evarconv.ml b/pretyping/evarconv.ml index ca5ed190f..cb0bed51e 100644 --- a/pretyping/evarconv.ml +++ b/pretyping/evarconv.ml @@ -17,6 +17,7 @@ open Termops open Environ open Recordops open Evarutil +open Evarsolve open Globnames open Evd diff --git a/pretyping/evarsolve.ml b/pretyping/evarsolve.ml new file mode 100644 index 000000000..1b8518098 --- /dev/null +++ b/pretyping/evarsolve.ml @@ -0,0 +1,1363 @@ +(************************************************************************) +(* v * The Coq Proof Assistant / The Coq Development Team *) +(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2012 *) +(* \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 Term +open Environ +open Termops +open Evd +open Namegen +open Retyping +open Reductionops +open Evarutil +open Pretype_errors + +let normalize_evar evd ev = + match kind_of_term (whd_evar evd (mkEvar ev)) with + | Evar (evk,args) -> (evk,args) + | _ -> assert false + +(************************) +(* Manipulating filters *) +(************************) + +let extract_subfilter initial_filter refined_filter = + snd (List.filter2 (fun b1 b2 -> b1) initial_filter refined_filter) + +let apply_subfilter filter subfilter = + fst (List.fold_right (fun oldb (l,filter) -> + if oldb then List.hd filter::l,List.tl filter else (false::l,filter)) + filter ([], List.rev subfilter)) + +(*------------------------------------* + * Restricting existing evars * + *------------------------------------*) + +let rec eq_filter l1 l2 = match l1, l2 with +| [], [] -> true +| h1 :: l1, h2 :: l2 -> + (if h1 then h2 else not h2) && eq_filter l1 l2 +| _ -> false + +let restrict_evar_key evd evk filter candidates = + match filter, candidates with + | None, None -> evd, evk + | _ -> + let evi = Evd.find_undefined evd evk in + let oldfilter = evar_filter evi in + begin match filter, candidates with + | Some filter, None when eq_filter oldfilter filter -> + evd, evk + | _ -> + let filter = match filter with + | None -> evar_filter evi + | Some filter -> filter in + let candidates = match candidates with + | None -> evi.evar_candidates + | Some _ -> candidates in + let ccl = evi.evar_concl in + let sign = evar_hyps evi in + let src = evi.evar_source in + let evd,newevk = new_pure_evar evd sign ccl ~src ~filter ?candidates in + let _, ctxt = List.filter2 (fun b c -> b) filter (evar_context evi) in + let id_inst = Array.of_list (List.map (fun (id,_,_) -> mkVar id) ctxt) in + Evd.define evk (mkEvar(newevk,id_inst)) evd,newevk + end + +(* Restrict an applied evar and returns its restriction in the same context *) +let restrict_applied_evar evd (evk,argsv) filter candidates = + let evd,newevk = restrict_evar_key evd evk filter candidates in + let newargsv = match filter with + | None -> (* optim *) argsv + | Some filter -> + let evi = Evd.find evd evk in + let subfilter = extract_subfilter (evar_filter evi) filter in + Array.filter_with subfilter argsv in + evd,(newevk,newargsv) + +(* Restrict an evar in the current evar_map *) +let restrict_evar evd evk filter candidates = + fst (restrict_evar_key evd evk filter candidates) + +(* Restrict an evar in the current evar_map *) +let restrict_instance evd evk filter argsv = + match filter with None -> argsv | Some filter -> + let evi = Evd.find evd evk in + Array.filter_with (extract_subfilter (evar_filter evi) filter) argsv + +let noccur_evar env evd evk c = + let rec occur_rec k c = match kind_of_term c with + | Evar (evk',args' as ev') -> + (match safe_evar_value evd ev' with + | Some c -> occur_rec k c + | None -> + if Int.equal evk evk' then raise Occur + else Array.iter (occur_rec k) args') + | Rel i when i > k -> + (match pi2 (Environ.lookup_rel (i-k) env) with + | None -> () + | Some b -> occur_rec k (lift i b)) + | _ -> iter_constr_with_binders succ occur_rec k c + in + try occur_rec 0 c; true with Occur -> false + +(***************************************) +(* Managing chains of local definitons *) +(***************************************) + +(* Expand rels and vars that are bound to other rels or vars so that + dependencies in variables are canonically associated to the most ancient + variable in its family of aliased variables *) + +let compute_var_aliases sign = + List.fold_right (fun (id,b,c) aliases -> + match b with + | Some t -> + (match kind_of_term t with + | Var id' -> + let aliases_of_id = + try Id.Map.find id' aliases with Not_found -> [] in + Id.Map.add id (aliases_of_id@[t]) aliases + | _ -> + Id.Map.add id [t] aliases) + | None -> aliases) + sign Id.Map.empty + +let compute_rel_aliases var_aliases rels = + snd (List.fold_right (fun (_,b,t) (n,aliases) -> + (n-1, + match b with + | Some t -> + (match kind_of_term t with + | Var id' -> + let aliases_of_n = + try Id.Map.find id' var_aliases with Not_found -> [] in + Int.Map.add n (aliases_of_n@[t]) aliases + | Rel p -> + let aliases_of_n = + try Int.Map.find (p+n) aliases with Not_found -> [] in + Int.Map.add n (aliases_of_n@[mkRel (p+n)]) aliases + | _ -> + Int.Map.add n [lift n t] aliases) + | None -> aliases)) + rels (List.length rels,Int.Map.empty)) + +let make_alias_map env = + (* We compute the chain of aliases for each var and rel *) + let var_aliases = compute_var_aliases (named_context env) in + let rel_aliases = compute_rel_aliases var_aliases (rel_context env) in + (var_aliases,rel_aliases) + +let lift_aliases n (var_aliases,rel_aliases as aliases) = + if Int.equal n 0 then aliases else + (var_aliases, + Int.Map.fold (fun p l -> Int.Map.add (p+n) (List.map (lift n) l)) + rel_aliases Int.Map.empty) + +let get_alias_chain_of aliases x = match kind_of_term x with + | Rel n -> (try Int.Map.find n (snd aliases) with Not_found -> []) + | Var id -> (try Id.Map.find id (fst aliases) with Not_found -> []) + | _ -> [] + +let normalize_alias_opt aliases x = + match get_alias_chain_of aliases x with + | [] -> None + | a::_ when isRel a or isVar a -> Some a + | [_] -> None + | _::a::_ -> Some a + +let normalize_alias aliases x = + match normalize_alias_opt aliases x with + | Some a -> a + | None -> x + +let normalize_alias_var var_aliases id = + destVar (normalize_alias (var_aliases,Int.Map.empty) (mkVar id)) + +let extend_alias (_,b,_) (var_aliases,rel_aliases) = + let rel_aliases = + Int.Map.fold (fun n l -> Int.Map.add (n+1) (List.map (lift 1) l)) + rel_aliases Int.Map.empty in + let rel_aliases = + match b with + | Some t -> + (match kind_of_term t with + | Var id' -> + let aliases_of_binder = + try Id.Map.find id' var_aliases with Not_found -> [] in + Int.Map.add 1 (aliases_of_binder@[t]) rel_aliases + | Rel p -> + let aliases_of_binder = + try Int.Map.find (p+1) rel_aliases with Not_found -> [] in + Int.Map.add 1 (aliases_of_binder@[mkRel (p+1)]) rel_aliases + | _ -> + Int.Map.add 1 [lift 1 t] rel_aliases) + | None -> rel_aliases in + (var_aliases, rel_aliases) + +let expand_alias_once aliases x = + match get_alias_chain_of aliases x with + | [] -> None + | l -> Some (List.last l) + +let expansions_of_var aliases x = + match get_alias_chain_of aliases x with + | [] -> [x] + | a::_ as l when isRel a || isVar a -> x :: List.rev l + | _::l -> x :: List.rev l + +let expansion_of_var aliases x = + match get_alias_chain_of aliases x with + | [] -> x + | a::_ -> a + +let rec expand_vars_in_term_using aliases t = match kind_of_term t with + | Rel _ | Var _ -> + normalize_alias aliases t + | _ -> + map_constr_with_full_binders + extend_alias expand_vars_in_term_using aliases t + +let expand_vars_in_term env = expand_vars_in_term_using (make_alias_map env) + +let free_vars_and_rels_up_alias_expansion aliases c = + let acc1 = ref Int.Set.empty and acc2 = ref Id.Set.empty in + let cache_rel = ref Int.Set.empty and cache_var = ref Id.Set.empty in + let is_in_cache depth = function + | Rel n -> Int.Set.mem (n-depth) !cache_rel + | Var s -> Id.Set.mem s !cache_var + | _ -> false in + let put_in_cache depth = function + | Rel n -> cache_rel := Int.Set.add (n-depth) !cache_rel + | Var s -> cache_var := Id.Set.add s !cache_var + | _ -> () in + let rec frec (aliases,depth) c = + match kind_of_term c with + | Rel _ | Var _ as ck -> + if is_in_cache depth ck then () else begin + put_in_cache depth ck; + let c = expansion_of_var aliases c in + match kind_of_term c with + | Var id -> acc2 := Id.Set.add id !acc2 + | Rel n -> if n >= depth+1 then acc1 := Int.Set.add (n-depth) !acc1 + | _ -> frec (aliases,depth) c end + | Const _ | Ind _ | Construct _ -> + acc2 := List.fold_right Id.Set.add (vars_of_global (Global.env()) c) !acc2 + | _ -> + iter_constr_with_full_binders + (fun d (aliases,depth) -> (extend_alias d aliases,depth+1)) + frec (aliases,depth) c + in + frec (aliases,0) c; + (!acc1,!acc2) + +(********************************) +(* Managing pattern-unification *) +(********************************) + +let rec expand_and_check_vars aliases = function + | [] -> [] + | a::l when isRel a or isVar a -> + let a = expansion_of_var aliases a in + if isRel a or isVar a then a :: expand_and_check_vars aliases l + else raise Exit + | _ -> + raise Exit + +module Constrhash = Hashtbl.Make + (struct type t = constr + let equal = eq_constr + let hash = hash_constr + end) + +let constr_list_distinct l = + let visited = Constrhash.create 23 in + let rec loop = function + | h::t -> + if Constrhash.mem visited h then false + else (Constrhash.add visited h h; loop t) + | [] -> true + in loop l + +let get_actual_deps aliases l t = + if occur_meta_or_existential t then + (* Probably no restrictions on allowed vars in presence of evars *) + l + else + (* Probably strong restrictions coming from t being evar-closed *) + let (fv_rels,fv_ids) = free_vars_and_rels_up_alias_expansion aliases t in + List.filter (fun c -> + match kind_of_term c with + | Var id -> Id.Set.mem id fv_ids + | Rel n -> Int.Set.mem n fv_rels + | _ -> assert false) l + +let remove_instance_local_defs evd evk args = + let evi = Evd.find evd evk in + let rec aux = function + | (_,Some _,_)::sign, a::args -> aux (sign,args) + | (_,None,_)::sign, a::args -> a::aux (sign,args) + | [], [] -> [] + | _ -> assert false in + aux (evar_filtered_context evi, args) + +(* Check if an applied evar "?X[args] l" is a Miller's pattern *) + +let find_unification_pattern_args env l t = + if List.for_all (fun x -> isRel x || isVar x) l (* common failure case *) then + let aliases = make_alias_map env in + match (try Some (expand_and_check_vars aliases l) with Exit -> None) with + | Some l as x when constr_list_distinct (get_actual_deps aliases l t) -> x + | _ -> None + else + None + +let is_unification_pattern_meta env nb m l t = + (* Variables from context and rels > nb are implicitly all there *) + (* so we need to be a rel <= nb *) + if List.for_all (fun x -> isRel x && destRel x <= nb) l then + match find_unification_pattern_args env l t with + | Some _ as x when not (dependent (mkMeta m) t) -> x + | _ -> None + else + None + +let is_unification_pattern_evar env evd (evk,args) l t = + if List.for_all (fun x -> isRel x || isVar x) l & noccur_evar env evd evk t + then + let args = remove_instance_local_defs evd evk (Array.to_list args) in + let n = List.length args in + match find_unification_pattern_args env (args @ l) t with + | Some l -> Some (List.skipn n l) + | _ -> None + else + None + +let is_unification_pattern_pure_evar env evd (evk,args) t = + let is_ev = is_unification_pattern_evar env evd (evk,args) [] t in + match is_ev with + | None -> false + | Some _ -> true + +let is_unification_pattern (env,nb) evd f l t = + match kind_of_term f with + | Meta m -> is_unification_pattern_meta env nb m l t + | Evar ev -> is_unification_pattern_evar env evd ev l t + | _ -> None + +(* From a unification problem "?X l = c", build "\x1...xn.(term1 l2)" + (pattern unification). It is assumed that l is made of rel's that + are distinct and not bound to aliases. *) +(* It is also assumed that c does not contain metas because metas + *implicitly* depend on Vars but lambda abstraction will not reflect this + dependency: ?X x = ?1 (?1 is a meta) will return \_.?1 while it should + return \y. ?1{x\y} (non constant function if ?1 depends on x) (BB) *) +let solve_pattern_eqn env l c = + let c' = List.fold_right (fun a c -> + let c' = subst_term (lift 1 a) (lift 1 c) in + match kind_of_term a with + (* Rem: if [a] links to a let-in, do as if it were an assumption *) + | Rel n -> + let d = map_rel_declaration (lift n) (lookup_rel n env) in + mkLambda_or_LetIn d c' + | Var id -> + let d = lookup_named id env in mkNamedLambda_or_LetIn d c' + | _ -> assert false) + l c in + (* Warning: we may miss some opportunity to eta-reduce more since c' + is not in normal form *) + whd_eta c' + +(*****************************************) +(* Refining/solving unification problems *) +(*****************************************) + +(* Knowing that [Gamma |- ev : T] and that [ev] is applied to [args], + * [make_projectable_subst ev args] builds the substitution [Gamma:=args]. + * If a variable and an alias of it are bound to the same instance, we skip + * the alias (we just use eq_constr -- instead of conv --, since anyway, + * only instances that are variables -- or evars -- are later considered; + * morever, we can bet that similar instances came at some time from + * the very same substitution. The removal of aliased duplicates is + * useful to ensure the uniqueness of a projection. +*) + +let make_projectable_subst aliases sigma evi args = + let sign = evar_filtered_context evi in + let evar_aliases = compute_var_aliases sign in + let (_,full_subst,cstr_subst) = + List.fold_right + (fun (id,b,c) (args,all,cstrs) -> + match b,args with + | None, a::rest -> + let a = whd_evar sigma a in + let cstrs = + let a',args = decompose_app_vect a in + match kind_of_term a' with + | Construct cstr -> + let l = try Constrmap.find cstr cstrs with Not_found -> [] in + Constrmap.add cstr ((args,id)::l) cstrs + | _ -> cstrs in + (rest,Id.Map.add id [a,normalize_alias_opt aliases a,id] all,cstrs) + | Some c, a::rest -> + let a = whd_evar sigma a in + (match kind_of_term c with + | Var id' -> + let idc = normalize_alias_var evar_aliases id' in + let sub = try Id.Map.find idc all with Not_found -> [] in + if List.exists (fun (c,_,_) -> eq_constr a c) sub then + (rest,all,cstrs) + else + (rest, + Id.Map.add idc ((a,normalize_alias_opt aliases a,id)::sub) all, + cstrs) + | _ -> + (rest,Id.Map.add id [a,normalize_alias_opt aliases a,id] all,cstrs)) + | _ -> anomaly (Pp.str "Instance does not match its signature")) + sign (Array.rev_to_list args,Id.Map.empty,Constrmap.empty) in + (full_subst,cstr_subst) + +(*------------------------------------* + * operations on the evar constraints * + *------------------------------------*) + +(* We have a unification problem Σ; Γ |- ?e[u1..uq] = t : s where ?e is not yet + * declared in Σ but yet known to be declarable in some context x1:T1..xq:Tq. + * [define_evar_from_virtual_equation ... Γ Σ t (x1:T1..xq:Tq) .. (u1..uq) (x1..xq)] + * declares x1:T1..xq:Tq |- ?e : s such that ?e[u1..uq] = t holds. + *) + +let define_evar_from_virtual_equation define_fun env evd t_in_env sign filter inst_in_env = + let ty_t_in_env = Retyping.get_type_of env evd t_in_env in + let evd,evar_in_env = new_evar_instance sign evd ty_t_in_env ~filter inst_in_env in + let t_in_env = whd_evar evd t_in_env in + let evd = define_fun env evd (destEvar evar_in_env) t_in_env in + let ids = List.map pi1 (named_context_of_val sign) in + let inst_in_sign = List.map mkVar (List.filter_with filter ids) in + let evar_in_sign = mkEvar (fst (destEvar evar_in_env), Array.of_list inst_in_sign) in + (evd,whd_evar evd evar_in_sign) + +(* We have x1..xq |- ?e1 : τ and had to solve something like + * Σ; Γ |- ?e1[u1..uq] = (...\y1 ... \yk ... c), where c is typically some + * ?e2[v1..vn], hence flexible. We had to go through k binders and now + * virtually have x1..xq, y1'..yk' | ?e1' : τ' and the equation + * Γ, y1..yk |- ?e1'[u1..uq y1..yk] = c. + * [materialize_evar Γ evd k (?e1[u1..uq]) τ'] extends Σ with the declaration + * of ?e1' and returns both its instance ?e1'[x1..xq y1..yk] in an extension + * of the context of e1 so that e1 can be instantiated by + * (...\y1' ... \yk' ... ?e1'[x1..xq y1'..yk']), + * and the instance ?e1'[u1..uq y1..yk] so that the remaining equation + * ?e1'[u1..uq y1..yk] = c can be registered + * + * Note that, because invert_definition does not check types, we need to + * guess the types of y1'..yn' by inverting the types of y1..yn along the + * substitution u1..uq. + *) + +let materialize_evar define_fun env evd k (evk1,args1) ty_in_env = + let evi1 = Evd.find_undefined evd evk1 in + let env1,rel_sign = env_rel_context_chop k env in + let sign1 = evar_hyps evi1 in + let filter1 = evar_filter evi1 in + let ids1 = List.map pi1 (named_context_of_val sign1) in + let inst_in_sign = List.map mkVar (List.filter_with filter1 ids1) in + let (sign2,filter2,inst2_in_env,inst2_in_sign,_,evd,_) = + List.fold_right (fun (na,b,t_in_env as d) (sign,filter,inst_in_env,inst_in_sign,env,evd,avoid) -> + let id = next_name_away na avoid in + let evd,t_in_sign = + define_evar_from_virtual_equation define_fun env evd t_in_env + sign filter inst_in_env in + let evd,b_in_sign = match b with + | None -> evd,None + | Some b -> + let evd,b = define_evar_from_virtual_equation define_fun env evd b + sign filter inst_in_env in + evd,Some b in + (push_named_context_val (id,b_in_sign,t_in_sign) sign,true::filter, + (mkRel 1)::(List.map (lift 1) inst_in_env), + (mkRel 1)::(List.map (lift 1) inst_in_sign), + push_rel d env,evd,id::avoid)) + rel_sign + (sign1,filter1,Array.to_list args1,inst_in_sign,env1,evd,ids1) + in + let evd,ev2ty_in_sign = + define_evar_from_virtual_equation define_fun env evd ty_in_env + sign2 filter2 inst2_in_env in + let evd,ev2_in_sign = + new_evar_instance sign2 evd ev2ty_in_sign ~filter:filter2 inst2_in_sign in + let ev2_in_env = (fst (destEvar ev2_in_sign), Array.of_list inst2_in_env) in + (evd, ev2_in_sign, ev2_in_env) + +let restrict_upon_filter evd evk p args = + let newfilter = List.map p args in + if List.for_all (fun id -> id) newfilter then + None + else + let oldfullfilter = evar_filter (Evd.find_undefined evd evk) in + Some (apply_subfilter oldfullfilter newfilter) + +(* Inverting constructors in instances (common when inferring type of match) *) + +let find_projectable_constructor env evd cstr k args cstr_subst = + try + let l = Constrmap.find cstr cstr_subst in + let args = Array.map (lift (-k)) args in + let l = + List.filter (fun (args',id) -> + (* is_conv is maybe too strong (and source of useless computation) *) + (* (at least expansion of aliases is needed) *) + Array.for_all2 (is_conv env evd) args args') l in + List.map snd l + with Not_found -> + [] + +(* [find_projectable_vars env sigma y subst] finds all vars of [subst] + * that project on [y]. It is able to find solutions to the following + * two kinds of problems: + * + * - ?n[...;x:=y;...] = y + * - ?n[...;x:=?m[args];...] = y with ?m[args] = y recursively solvable + * + * (see test-suite/success/Fixpoint.v for an example of application of + * the second kind of problem). + * + * The seek for [y] is up to variable aliasing. In case of solutions that + * differ only up to aliasing, the binding that requires the less + * steps of alias reduction is kept. At the end, only one solution up + * to aliasing is kept. + * + * [find_projectable_vars] also unifies against evars that themselves mention + * [y] and recursively. + * + * In short, the following situations give the following solutions: + * + * problem evar ctxt soluce remark + * z1; z2:=z1 |- ?ev[z1;z2] = z1 y1:A; y2:=y1 y1 \ thanks to defs kept in + * z1; z2:=z1 |- ?ev[z1;z2] = z2 y1:A; y2:=y1 y2 / subst and preferring = + * z1; z2:=z1 |- ?ev[z1] = z2 y1:A y1 thanks to expand_var + * z1; z2:=z1 |- ?ev[z2] = z1 y1:A y1 thanks to expand_var + * z3 |- ?ev[z3;z3] = z3 y1:A; y2:=y1 y2 see make_projectable_subst + * + * Remark: [find_projectable_vars] assumes that identical instances of + * variables in the same set of aliased variables are already removed (see + * [make_projectable_subst]) + *) + +type evar_projection = +| ProjectVar +| ProjectEvar of existential * evar_info * Id.t * evar_projection + +exception NotUnique +exception NotUniqueInType of (Id.t * evar_projection) list + +let rec assoc_up_to_alias sigma aliases y yc = function + | [] -> raise Not_found + | (c,cc,id)::l -> + let c' = whd_evar sigma c in + if eq_constr y c' then id + else + match l with + | _ :: _ -> assoc_up_to_alias sigma aliases y yc l + | [] -> + (* Last chance, we reason up to alias conversion *) + match (if c == c' then cc else normalize_alias_opt aliases c') with + | Some cc when eq_constr yc cc -> id + | _ -> if eq_constr yc c then id else raise Not_found + +let rec find_projectable_vars with_evars aliases sigma y subst = + let yc = normalize_alias aliases y in + let is_projectable idc idcl subst' = + (* First test if some [id] aliased to [idc] is bound to [y] in [subst] *) + try + let id = assoc_up_to_alias sigma aliases y yc idcl in + (id,ProjectVar)::subst' + with Not_found -> + (* Then test if [idc] is (indirectly) bound in [subst] to some evar *) + (* projectable on [y] *) + if with_evars then + let idcl' = List.filter (fun (c,_,id) -> isEvar c) idcl in + match idcl' with + | [c,_,id] -> + begin + let (evk,argsv as t) = destEvar c in + let evi = Evd.find sigma evk in + let subst,_ = make_projectable_subst aliases sigma evi argsv in + let l = find_projectable_vars with_evars aliases sigma y subst in + match l with + | [id',p] -> (id,ProjectEvar (t,evi,id',p))::subst' + | _ -> subst' + end + | [] -> subst' + | _ -> anomaly (Pp.str "More than one non var in aliases class of evar instance") + else + subst' in + Id.Map.fold is_projectable subst [] + +(* [filter_solution] checks if one and only one possible projection exists + * among a set of solutions to a projection problem *) + +let filter_solution = function + | [] -> raise Not_found + | (id,p)::_::_ -> raise NotUnique + | [id,p] -> (mkVar id, p) + +let project_with_effects aliases sigma effects t subst = + let c, p = + filter_solution (find_projectable_vars false aliases sigma t subst) in + effects := p :: !effects; + c + +let rec find_solution_type evarenv = function + | (id,ProjectVar)::l -> pi3 (lookup_named id evarenv) + | [id,ProjectEvar _] -> (* bugged *) pi3 (lookup_named id evarenv) + | (id,ProjectEvar _)::l -> find_solution_type evarenv l + | [] -> assert false + +(* In case the solution to a projection problem requires the instantiation of + * subsidiary evars, [do_projection_effects] performs them; it + * also try to instantiate the type of those subsidiary evars if their + * type is an evar too. + * + * Note: typing creates new evar problems, which induces a recursive dependency + * with [define]. To avoid a too large set of recursive functions, we + * pass [define] to [do_projection_effects] as a parameter. + *) + +let rec do_projection_effects define_fun env ty evd = function + | ProjectVar -> evd + | ProjectEvar ((evk,argsv),evi,id,p) -> + let evd = Evd.define evk (mkVar id) evd in + (* TODO: simplify constraints involving evk *) + let evd = do_projection_effects define_fun env ty evd p in + let ty = whd_betadeltaiota env evd (Lazy.force ty) in + if not (isSort ty) then + (* Don't try to instantiate if a sort because if evar_concl is an + evar it may commit to a univ level which is not the right + one (however, regarding coercions, because t is obtained by + unif, we know that no coercion can be inserted) *) + let subst = make_pure_subst evi argsv in + let ty' = replace_vars subst evi.evar_concl in + let ty' = whd_evar evd ty' in + if isEvar ty' then define_fun env evd (destEvar ty') ty else evd + else + evd + +(* Assuming Σ; Γ, y1..yk |- c, [invert_arg_from_subst Γ k Σ [x1:=u1..xn:=un] c] + * tries to return φ(x1..xn) such that equation φ(u1..un) = c is valid. + * The strategy is to imitate the structure of c and then to invert + * the variables of c (i.e. rels or vars of Γ) using the algorithm + * implemented by project_with_effects/find_projectable_vars. + * It returns either a unique solution or says whether 0 or more than + * 1 solutions is found. + * + * Precondition: Σ; Γ, y1..yk |- c /\ Σ; Γ |- u1..un + * Postcondition: if φ(x1..xn) is returned then + * Σ; Γ, y1..yk |- φ(u1..un) = c /\ x1..xn |- φ(x1..xn) + * + * The effects correspond to evars instantiated while trying to project. + * + * [invert_arg_from_subst] is used on instances of evars. Since the + * evars are flexible, these instances are potentially erasable. This + * is why we don't investigate whether evars in the instances of evars + * are unifiable, to the contrary of [invert_definition]. + *) + +type projectibility_kind = + | NoUniqueProjection + | UniqueProjection of constr * evar_projection list + +type projectibility_status = + | CannotInvert + | Invertible of projectibility_kind + +let invert_arg_from_subst evd aliases k0 subst_in_env_extended_with_k_binders c_in_env_extended_with_k_binders = + let effects = ref [] in + let rec aux k t = + let t = whd_evar evd t in + match kind_of_term t with + | Rel i when i>k0+k -> aux' k (mkRel (i-k)) + | Var id -> aux' k t + | _ -> map_constr_with_binders succ aux k t + and aux' k t = + try project_with_effects aliases evd effects t subst_in_env_extended_with_k_binders + with Not_found -> + match expand_alias_once aliases t with + | None -> raise Not_found + | Some c -> aux k c in + try + let c = aux 0 c_in_env_extended_with_k_binders in + Invertible (UniqueProjection (c,!effects)) + with + | Not_found -> CannotInvert + | NotUnique -> Invertible NoUniqueProjection + +let invert_arg fullenv evd aliases k evk subst_in_env_extended_with_k_binders c_in_env_extended_with_k_binders = + let res = invert_arg_from_subst evd aliases k subst_in_env_extended_with_k_binders c_in_env_extended_with_k_binders in + match res with + | Invertible (UniqueProjection (c,_)) when not (noccur_evar fullenv evd evk c) + -> + CannotInvert + | _ -> + res + +exception NotEnoughInformationToInvert + +let extract_unique_projections projs = + List.map (function + | Invertible (UniqueProjection (c,_)) -> c + | _ -> + (* For instance, there are evars with non-invertible arguments and *) + (* we cannot arbitrarily restrict these evars before knowing if there *) + (* will really be used; it can also be due to some argument *) + (* (typically a rel) that is not inversible and that cannot be *) + (* inverted either because it is needed for typing the conclusion *) + (* of the evar to project *) + raise NotEnoughInformationToInvert) projs + +let extract_candidates sols = + try + Some + (List.map (function (id,ProjectVar) -> mkVar id | _ -> raise Exit) sols) + with Exit -> + None + +let invert_invertible_arg fullenv evd aliases k (evk,argsv) args' = + let evi = Evd.find_undefined evd evk in + let subst,_ = make_projectable_subst aliases evd evi argsv in + let projs = + Array.map_to_list (invert_arg fullenv evd aliases k evk subst) args' in + Array.of_list (extract_unique_projections projs) + +(* Redefines an evar with a smaller context (i.e. it may depend on less + * variables) such that c becomes closed. + * Example: in "fun (x:?1) (y:list ?2[x]) => x = y :> ?3[x,y] /\ x = nil bool" + * ?3 <-- ?1 no pb: env of ?3 is larger than ?1's + * ?1 <-- list ?2 pb: ?2 may depend on x, but not ?1. + * What we do is that ?2 is defined by a new evar ?4 whose context will be + * a prefix of ?2's env, included in ?1's env. + * + * If "hyps |- ?e : T" and "filter" selects a subset hyps' of hyps then + * [do_restrict_hyps evd ?e filter] sets ?e:=?e'[hyps'] and returns ?e' + * such that "hyps' |- ?e : T" + *) + +let filter_candidates evd evk filter candidates = + let evi = Evd.find_undefined evd evk in + let candidates = match candidates with + | None -> evi.evar_candidates + | Some _ -> candidates in + match candidates,filter with + | None,_ | _, None -> candidates + | Some l, Some filter -> + let ids = List.map pi1 (List.filter_with filter (evar_context evi)) in + Some (List.filter (fun a -> + List.subset (Id.Set.elements (collect_vars a)) ids) l) + +let eq_filter f1 f2 = + let eq_bool b1 b2 = if b1 then b2 else not b2 in + List.equal eq_bool f1 f2 + +let closure_of_filter evd evk filter = + let evi = Evd.find_undefined evd evk in + let vars = collect_vars (evar_concl evi) in + let ids = List.map pi1 (evar_context evi) in + let test id b = b || Id.Set.mem id vars in + let newfilter = List.map2 test ids filter in + if eq_filter newfilter (evar_filter evi) then None else Some newfilter + +let restrict_hyps evd evk filter candidates = + (* What to do with dependencies? + Assume we have x:A, y:B(x), z:C(x,y) |- ?e:T(x,y,z) and restrict on y. + - If y is in a non-erasable position in C(x,y) (i.e. it is not below an + occurrence of x in the hnf of C), then z should be removed too. + - If y is in a non-erasable position in T(x,y,z) then the problem is + unsolvable. + Computing whether y is erasable or not may be costly and the + interest for this early detection in practice is not obvious. We let + it for future work. In any case, thanks to the use of filters, the whole + (unrestricted) context remains consistent. *) + let candidates = filter_candidates evd evk (Some filter) candidates in + let typablefilter = closure_of_filter evd evk filter in + (typablefilter,candidates) + +exception EvarSolvedWhileRestricting of evar_map * constr + +let do_restrict_hyps evd (evk,args as ev) filter candidates = + let filter,candidates = match filter with + | None -> None,candidates + | Some filter -> restrict_hyps evd evk filter candidates in + match candidates,filter with + | Some [], _ -> error "Not solvable." + | Some [nc],_ -> + let evd = Evd.define evk nc evd in + raise (EvarSolvedWhileRestricting (evd,whd_evar evd (mkEvar ev))) + | None, None -> evd,ev + | _ -> restrict_applied_evar evd ev filter candidates + +(* [postpone_non_unique_projection] postpones equation of the form ?e[?] = c *) +(* ?e is assumed to have no candidates *) + +let postpone_non_unique_projection env evd (evk,argsv as ev) sols rhs = + let rhs = expand_vars_in_term env rhs in + let filter = + restrict_upon_filter evd evk + (* Keep only variables that occur in rhs *) + (* This is not safe: is the variable is a local def, its body *) + (* may contain references to variables that are removed, leading to *) + (* a ill-formed context. We would actually need a notion of filter *) + (* that says that the body is hidden. Note that expand_vars_in_term *) + (* expands only rels and vars aliases, not rels or vars bound to an *) + (* arbitrary complex term *) + (fun a -> not (isRel a || isVar a) + || dependent a rhs || List.exists (fun (id,_) -> isVarId id a) sols) + (Array.to_list argsv) in + let filter = match filter with + | None -> None + | Some filter -> closure_of_filter evd evk filter in + let candidates = extract_candidates sols in + match candidates with + | None -> + (* We made an approximation by not expanding a local definition *) + let evd,ev = restrict_applied_evar evd ev filter None in + let pb = (Reduction.CONV,env,mkEvar ev,rhs) in + Evd.add_conv_pb pb evd + | Some _ -> + restrict_evar evd evk filter candidates + +(* [postpone_evar_evar] postpones an equation of the form ?e1[?1] = ?e2[?2] *) + +let postpone_evar_evar f env evd filter1 ev1 filter2 ev2 = + (* Leave an equation between (restrictions of) ev1 andv ev2 *) + try + let evd,ev1' = do_restrict_hyps evd ev1 filter1 None in + try + let evd,ev2' = do_restrict_hyps evd ev2 filter2 None in + add_conv_pb (Reduction.CONV,env,mkEvar ev1',mkEvar ev2') evd + with EvarSolvedWhileRestricting (evd,ev2) -> + (* ev2 solved on the fly *) + f env evd ev1' ev2 + with EvarSolvedWhileRestricting (evd,ev1) -> + (* ev1 solved on the fly *) + f env evd ev2 ev1 + +(* [solve_evar_evar f Γ Σ ?e1[u1..un] ?e2[v1..vp]] applies an heuristic + * to solve the equation Σ; Γ ⊢ ?e1[u1..un] = ?e2[v1..vp]: + * - if there are at most one φj for each vj s.t. vj = φj(u1..un), + * we first restrict ?e2 to the subset v_k1..v_kq of the vj that are + * inversible and we set ?e1[x1..xn] := ?e2[φk1(x1..xn)..φkp(x1..xn)] + * (this is a case of pattern-unification) + * - symmetrically if there are at most one ψj for each uj s.t. + * uj = ψj(v1..vp), + * - otherwise, each position i s.t. ui does not occur in v1..vp has to + * be restricted and similarly for the vi, and we leave the equation + * as an open equation (performed by [postpone_evar]) + * + * Warning: the notion of unique φj is relative to some given class + * of unification problems + * + * Note: argument f is the function used to instantiate evars. + *) + +let are_canonical_instances args1 args2 env = + let n1 = Array.length args1 in + let n2 = Array.length args2 in + let rec aux n = function + | (id,_,c)::sign + when n < n1 && isVarId id args1.(n) && isVarId id args2.(n) -> + aux (n+1) sign + | [] -> + let rec aux2 n = + Int.equal n n1 || + (isRelN (n1-n) args1.(n) && isRelN (n1-n) args2.(n) && aux2 (n+1)) + in aux2 n + | _ -> false in + Int.equal n1 n2 && aux 0 (named_context env) + +let filter_compatible_candidates conv_algo env evd evi args rhs c = + let c' = instantiate_evar (evar_filtered_context evi) c args in + let evd, b = conv_algo env evd Reduction.CONV rhs c' in + if b then Some (c,evd) else None + +exception DoesNotPreserveCandidateRestriction + +let restrict_candidates conv_algo env evd filter1 (evk1,argsv1) (evk2,argsv2) = + let evi1 = Evd.find evd evk1 in + let evi2 = Evd.find evd evk2 in + let cand1 = filter_candidates evd evk1 filter1 None in + let cand2 = evi2.evar_candidates in + match cand1, cand2 with + | _, None -> cand1 + | None, Some _ -> raise DoesNotPreserveCandidateRestriction + | Some l1, Some l2 -> + let args1 = Array.to_list argsv1 in + let args2 = Array.to_list argsv2 in + let l1' = List.filter (fun c1 -> + let c1' = instantiate_evar (evar_filtered_context evi1) c1 args1 in + let filter c2 = + let compatibility = filter_compatible_candidates conv_algo env evd evi2 args2 c1' c2 in + match compatibility with + | None -> false + | Some _ -> true + in + let filtered = List.filter filter l2 in + match filtered with [] -> false | _ -> true) l1 in + if Int.equal (List.length l1) (List.length l1') then None else Some l1' + +exception CannotProject of bool list option + +(* Assume that FV(?n[x1:=t1..xn:=tn]) belongs to some set U. + Can ?n be instantiated by a term u depending essentially on xi such that the + FV(u[x1:=t1..xn:=tn]) are in the set U? + - If ti is a variable, it has to be in U. + - If ti is a constructor, its parameters cannot be erased even if u + matches on it, so we have to discard ti if the parameters + contain variables not in U. + - If ti is rigid, we have to discard it if it contains variables in U. + + Note: when restricting as part of an equation ?n[x1:=t1..xn:=tn] = ?m[...] + then, occurrences of ?m in the ti can be seen, like variables, as occurrences + of subterms to eventually discard so as to be allowed to keep ti. +*) + +let rec is_constrainable_in k (ev,(fv_rels,fv_ids) as g) t = + let f,args = decompose_app_vect t in + match kind_of_term f with + | Construct (ind,_) -> + let params,_ = Array.chop (Inductiveops.inductive_nparams ind) args in + Array.for_all (is_constrainable_in k g) params + | Ind _ -> Array.for_all (is_constrainable_in k g) args + | Prod (_,t1,t2) -> is_constrainable_in k g t1 && is_constrainable_in k g t2 + | Evar (ev',_) -> not (Int.equal ev' ev) (*If ev' needed, one may also try to restrict it*) + | Var id -> Id.Set.mem id fv_ids + | Rel n -> n <= k || Int.Set.mem n fv_rels + | Sort _ -> true + | _ -> (* We don't try to be more clever *) true + +let has_constrainable_free_vars evd aliases k ev (fv_rels,fv_ids as fvs) t = + let t = expansion_of_var aliases t in + match kind_of_term t with + | Var id -> Id.Set.mem id fv_ids + | Rel n -> n <= k || Int.Set.mem n fv_rels + | _ -> is_constrainable_in k (ev,fvs) t + +let ensure_evar_independent g env evd (evk1,argsv1 as ev1) (evk2,argsv2 as ev2)= + let filter1 = + restrict_upon_filter evd evk1 (noccur_evar env evd evk2) + (Array.to_list argsv1) + in + let candidates1 = restrict_candidates g env evd filter1 ev1 ev2 in + let evd,(evk1,_ as ev1) = do_restrict_hyps evd ev1 filter1 candidates1 in + let filter2 = + restrict_upon_filter evd evk2 (noccur_evar env evd evk1) + (Array.to_list argsv2) + in + let candidates2 = restrict_candidates g env evd filter2 ev2 ev1 in + let evd,ev2 = do_restrict_hyps evd ev2 filter2 candidates2 in + evd,ev1,ev2 + +exception EvarSolvedOnTheFly of evar_map * constr + +let project_evar_on_evar g env evd aliases k2 (evk1,argsv1 as ev1) (evk2,argsv2 as ev2) = + (* Apply filtering on ev1 so that fvs(ev1) are in fvs(ev2). *) + let fvs2 = free_vars_and_rels_up_alias_expansion aliases (mkEvar ev2) in + let filter1 = restrict_upon_filter evd evk1 + (has_constrainable_free_vars evd aliases k2 evk2 fvs2) + (Array.to_list argsv1) in + (* Only try pruning on variable substitutions, postpone otherwise. *) + (* Rules out non-linear instances. *) + if is_unification_pattern_pure_evar env evd ev2 (mkEvar ev1) then + try + let candidates1 = restrict_candidates g env evd filter1 ev1 ev2 in + let evd,(evk1',args1) = do_restrict_hyps evd ev1 filter1 candidates1 in + evd,mkEvar (evk1',invert_invertible_arg env evd aliases k2 ev2 args1) + with + | EvarSolvedWhileRestricting (evd,ev1) -> + raise (EvarSolvedOnTheFly (evd,ev1)) + | DoesNotPreserveCandidateRestriction | NotEnoughInformationToInvert -> + raise (CannotProject filter1) + else + raise (CannotProject filter1) + +let solve_evar_evar_l2r f g env evd aliases ev1 (evk2,_ as ev2) = + try + let evd,body = project_evar_on_evar g env evd aliases 0 ev1 ev2 in + Evd.define evk2 body evd + with EvarSolvedOnTheFly (evd,c) -> + f env evd ev2 c + +let solve_evar_evar ?(force=false) f g env evd (evk1,args1 as ev1) (evk2,args2 as ev2) = + if are_canonical_instances args1 args2 env then + (* If instances are canonical, we solve the problem in linear time *) + let sign = evar_filtered_context (Evd.find evd evk2) in + let id_inst = Array.map (fun (id,_,_) -> mkVar id) (Array.of_list sign) in + Evd.define evk2 (mkEvar(evk1,id_inst)) evd + else + let evd,ev1,ev2 = + (* If an evar occurs in the instance of the other evar and the + use of an heuristic is forced, we restrict *) + if force then ensure_evar_independent g env evd ev1 ev2 else (evd,ev1,ev2) in + let aliases = make_alias_map env in + try solve_evar_evar_l2r f g env evd aliases ev1 ev2 + with CannotProject filter1 -> + try solve_evar_evar_l2r f g env evd aliases ev2 ev1 + with CannotProject filter2 -> + postpone_evar_evar f env evd filter1 ev1 filter2 ev2 + +type conv_fun = + env -> evar_map -> conv_pb -> constr -> constr -> evar_map * bool + +let check_evar_instance evd evk1 body conv_algo = + let evi = Evd.find evd evk1 in + let evenv = evar_env evi in + (* FIXME: The body might be ill-typed when this is called from w_merge *) + let ty = + try Retyping.get_type_of evenv evd body + with _ -> error "Ill-typed evar instance" + in + let evd,b = conv_algo evenv evd Reduction.CUMUL ty evi.evar_concl in + if b then evd else + user_err_loc (fst (evar_source evk1 evd),"", + str "Unable to find a well-typed instantiation") + +(* Solve pbs ?e[t1..tn] = ?e[u1..un] which arise often in fixpoint + * definitions. We try to unify the ti with the ui pairwise. The pairs + * that don't unify are discarded (i.e. ?e is redefined so that it does not + * depend on these args). *) + +let solve_refl ?(can_drop=false) conv_algo env evd evk argsv1 argsv2 = + if Array.equal eq_constr argsv1 argsv2 then evd else + (* Filter and restrict if needed *) + let untypedfilter = + restrict_upon_filter evd evk + (fun (a1,a2) -> snd (conv_algo env evd Reduction.CONV a1 a2)) + (List.combine (Array.to_list argsv1) (Array.to_list argsv2)) in + let candidates = filter_candidates evd evk untypedfilter None in + let filter = match untypedfilter with + | None -> None + | Some filter -> closure_of_filter evd evk filter in + let evd,ev1 = restrict_applied_evar evd (evk,argsv1) filter candidates in + if Int.equal (fst ev1) evk && can_drop then (* No refinement *) evd else + (* either progress, or not allowed to drop, e.g. to preserve possibly *) + (* informative equations such as ?e[x:=?y]=?e[x:=?y'] where we don't know *) + (* if e can depend on x until ?y is not resolved, or, conversely, we *) + (* don't know if ?y has to be unified with ?y, until e is resolved *) + let argsv2 = restrict_instance evd evk filter argsv2 in + let ev2 = (fst ev1,argsv2) in + (* Leave a unification problem *) + Evd.add_conv_pb (Reduction.CONV,env,mkEvar ev1,mkEvar ev2) evd + +(* If the evar can be instantiated by a finite set of candidates known + in advance, we check which of them apply *) + +exception NoCandidates + +let solve_candidates conv_algo env evd (evk,argsv as ev) rhs = + let evi = Evd.find evd evk in + let args = Array.to_list argsv in + match evi.evar_candidates with + | None -> raise NoCandidates + | Some l -> + let l' = + List.map_filter + (filter_compatible_candidates conv_algo env evd evi args rhs) l in + match l' with + | [] -> error_cannot_unify env evd (mkEvar ev, rhs) + | [c,evd] -> + (* solve_candidates might have been called recursively in the mean *) + (* time and the evar been solved by the filtering process *) + if Evd.is_undefined evd evk then Evd.define evk c evd else evd + | l when List.length l < List.length l' -> + let candidates = List.map fst l in + restrict_evar evd evk None (Some candidates) + | l -> evd + +(* We try to instantiate the evar assuming the body won't depend + * on arguments that are not Rels or Vars, or appearing several times + * (i.e. we tackle a generalization of Miller-Pfenning patterns unification) + * + * 1) Let "env |- ?ev[hyps:=args] = rhs" be the unification problem + * 2) We limit it to a patterns unification problem "env |- ev[subst] = rhs" + * where only Rel's and Var's are relevant in subst + * 3) We recur on rhs, "imitating" the term, and failing if some Rel/Var is + * not in the scope of ?ev. For instance, the problem + * "y:nat |- ?x[] = y" where "|- ?1:nat" is not satisfiable because + * ?1 would be instantiated by y which is not in the scope of ?1. + * 4) We try to "project" the term if the process of imitation fails + * and that only one projection is possible + * + * Note: we don't assume rhs in normal form, it may fail while it would + * have succeeded after some reductions. + * + * This is the work of [invert_definition Γ Σ ?ev[hyps:=args] c] + * Precondition: Σ; Γ, y1..yk |- c /\ Σ; Γ |- u1..un + * Postcondition: if φ(x1..xn) is returned then + * Σ; Γ, y1..yk |- φ(u1..un) = c /\ x1..xn |- φ(x1..xn) + *) + +exception NotInvertibleUsingOurAlgorithm of constr +exception NotEnoughInformationToProgress of (Id.t * evar_projection) list +exception OccurCheckIn of evar_map * constr + +let rec invert_definition conv_algo choose env evd (evk,argsv as ev) rhs = + let aliases = make_alias_map env in + let evdref = ref evd in + let progress = ref false in + let evi = Evd.find evd evk in + let subst,cstr_subst = make_projectable_subst aliases evd evi argsv in + + (* Projection *) + let project_variable t = + (* Evar/Var problem: unifiable iff variable projectable from ev subst *) + try + let sols = find_projectable_vars true aliases !evdref t subst in + let c, p = match sols with + | [] -> raise Not_found + | [id,p] -> (mkVar id, p) + | (id,p)::_::_ -> + if choose then (mkVar id, p) else raise (NotUniqueInType sols) + in + let ty = lazy (Retyping.get_type_of env !evdref t) in + let evd = do_projection_effects (evar_define conv_algo) env ty !evdref p in + evdref := evd; + c + with + | Not_found -> raise (NotInvertibleUsingOurAlgorithm t) + | NotUniqueInType sols -> + if not !progress then + raise (NotEnoughInformationToProgress sols); + (* No unique projection but still restrict to where it is possible *) + (* materializing is necessary, but is restricting useful? *) + let ty = find_solution_type (evar_filtered_env evi) sols in + let sign = evar_filtered_context evi in + let ty' = instantiate_evar sign ty (Array.to_list argsv) in + let (evd,evar,(evk',argsv' as ev')) = + materialize_evar (evar_define conv_algo) env !evdref 0 ev ty' in + let ts = expansions_of_var aliases t in + let test c = isEvar c or List.mem c ts in + let filter = Array.map_to_list test argsv' in + let filter = apply_subfilter (evar_filter (Evd.find_undefined evd evk)) filter in + + let filter = closure_of_filter evd evk' filter in + let candidates = extract_candidates sols in + let evd = match candidates with + | None -> + let evd, ev'' = restrict_applied_evar evd ev' filter None in + Evd.add_conv_pb (Reduction.CONV,env,mkEvar ev'',t) evd + | Some _ -> + restrict_evar evd evk' filter candidates + in + evdref := evd; + evar in + + let rec imitate (env',k as envk) t = + let t = whd_evar !evdref t in + match kind_of_term t with + | Rel i when i>k -> + (match pi2 (Environ.lookup_rel (i-k) env') with + | None -> project_variable (mkRel (i-k)) + | Some b -> + try project_variable (mkRel (i-k)) + with NotInvertibleUsingOurAlgorithm _ -> imitate envk (lift i b)) + | Var id -> + (match pi2 (Environ.lookup_named id env') with + | None -> project_variable t + | Some b -> + try project_variable t + with NotInvertibleUsingOurAlgorithm _ -> imitate envk b) + | Evar (evk',args' as ev') -> + if Int.equal evk evk' then raise (OccurCheckIn (evd,rhs)); + (* Evar/Evar problem (but left evar is virtual) *) + let aliases = lift_aliases k aliases in + (try + let ev = (evk,Array.map (lift k) argsv) in + let evd,body = project_evar_on_evar conv_algo env' !evdref aliases k ev' ev in + evdref := evd; + body + with + | EvarSolvedOnTheFly (evd,t) -> evdref:=evd; imitate envk t + | CannotProject filter' -> + assert !progress; + (* Make the virtual left evar real *) + let ty = get_type_of env' !evdref t in + let (evd,evar'',ev'') = + materialize_evar (evar_define conv_algo) env' !evdref k ev ty in + (* materialize_evar may instantiate ev' by another evar; adjust it *) + let (evk',args' as ev') = normalize_evar evd ev' in + let evd = + (* Try to project (a restriction of) the left evar ... *) + try + let evd,body = project_evar_on_evar conv_algo env' evd aliases 0 ev'' ev' in + Evd.define evk' body evd + with + | EvarSolvedOnTheFly _ -> assert false (* ev has no candidates *) + | CannotProject filter'' -> + (* ... or postpone the problem *) + postpone_evar_evar (evar_define conv_algo) env' evd filter'' ev'' filter' ev' in + evdref := evd; + evar'') + | _ -> + progress := true; + match + let c,args = decompose_app_vect t in + match kind_of_term c with + | Construct cstr when noccur_between 1 k t -> + (* This is common case when inferring the return clause of match *) + (* (currently rudimentary: we do not treat the case of multiple *) + (* possible inversions; we do not treat overlap with a possible *) + (* alternative inversion of the subterms of the constructor, etc)*) + (match find_projectable_constructor env evd cstr k args cstr_subst with + | _::_ as l -> Some (List.map mkVar l) + | _ -> None) + | _ -> None + with + | Some l -> + let ty = get_type_of env' !evdref t in + let candidates = + try + let t = + map_constr_with_full_binders (fun d (env,k) -> push_rel d env, k+1) + imitate envk t in + t::l + with _ -> l in + (match candidates with + | [x] -> x + | _ -> + let (evd,evar'',ev'') = + materialize_evar (evar_define conv_algo) env' !evdref k ev ty in + evdref := restrict_evar evd (fst ev'') None (Some candidates); + evar'') + | None -> + (* Evar/Rigid problem (or assimilated if not normal): we "imitate" *) + map_constr_with_full_binders (fun d (env,k) -> push_rel d env, k+1) + imitate envk t in + + let rhs = whd_beta evd rhs (* heuristic *) in + let body = imitate (env,0) rhs in + (!evdref,body) + +(* [define] tries to solve the problem "?ev[args] = rhs" when "?ev" is + * an (uninstantiated) evar such that "hyps |- ?ev : typ". Otherwise said, + * [define] tries to find an instance lhs such that + * "lhs [hyps:=args]" unifies to rhs. The term "lhs" must be closed in + * context "hyps" and not referring to itself. + *) + +and evar_define conv_algo ?(choose=false) env evd (evk,argsv as ev) rhs = + match kind_of_term rhs with + | Evar (evk2,argsv2 as ev2) -> + if Int.equal evk evk2 then + solve_refl ~can_drop:choose conv_algo env evd evk argsv argsv2 + else + solve_evar_evar ~force:choose + (evar_define conv_algo) conv_algo env evd ev ev2 + | _ -> + try solve_candidates conv_algo env evd ev rhs + with NoCandidates -> + try + let (evd',body) = invert_definition conv_algo choose env evd ev rhs in + if occur_meta body then error "Meta cannot occur in evar body."; + (* invert_definition may have instantiate some evars of rhs with evk *) + (* so we recheck acyclicity *) + if occur_evar evk body then raise (OccurCheckIn (evd',body)); + (* needed only if an inferred type *) + let body = refresh_universes body in +(* Cannot strictly type instantiations since the unification algorithm + * does not unify applications from left to right. + * e.g problem f x == g y yields x==y and f==g (in that order) + * Another problem is that type variables are evars of type Type + let _ = + try + let env = evar_filtered_env evi in + let ty = evi.evar_concl in + Typing.check env evd' body ty + with e -> + msg_info + (str "Ill-typed evar instantiation: " ++ fnl() ++ + pr_evar_map evd' ++ fnl() ++ + str "----> " ++ int ev ++ str " := " ++ + print_constr body); + raise e in*) + let evd' = Evd.define evk body evd' in + check_evar_instance evd' evk body conv_algo + with + | NotEnoughInformationToProgress sols -> + postpone_non_unique_projection env evd ev sols rhs + | NotInvertibleUsingOurAlgorithm t -> + error_not_clean env evd evk t (evar_source evk evd) + | OccurCheckIn (evd,rhs) -> + (* last chance: rhs actually reduces to ev *) + let c = whd_betadeltaiota env evd rhs in + match kind_of_term c with + | Evar (evk',argsv2) when Int.equal evk evk' -> + solve_refl + (fun env sigma pb c c' -> (evd,is_fconv pb env sigma c c')) + env evd evk argsv argsv2 + | _ -> + error_occur_check env evd evk rhs + +(* This code (i.e. solve_pb, etc.) takes a unification + * problem, and tries to solve it. If it solves it, then it removes + * all the conversion problems, and re-runs conversion on each one, in + * the hopes that the new solution will aid in solving them. + * + * The kinds of problems it knows how to solve are those in which + * the usable arguments of an existential var are all themselves + * universal variables. + * The solution to this problem is to do renaming for the Var's, + * to make them match up with the Var's which are found in the + * hyps of the existential, to do a "pop" for each Rel which is + * not an argument of the existential, and a subst1 for each which + * is, again, with the corresponding variable. This is done by + * define + * + * Thus, we take the arguments of the existential which we are about + * to assign, and zip them with the identifiers in the hypotheses. + * Then, we process all the Var's in the arguments, and sort the + * Rel's into ascending order. Then, we just march up, doing + * subst1's and pop's. + * + * NOTE: We can do this more efficiently for the relative arguments, + * by building a long substituend by hand, but this is a pain in the + * ass. + *) + +let status_changed lev (pbty,_,t1,t2) = + (try ExistentialSet.mem (head_evar t1) lev with NoHeadEvar -> false) or + (try ExistentialSet.mem (head_evar t2) lev with NoHeadEvar -> false) + +let reconsider_conv_pbs conv_algo evd = + let (evd,pbs) = extract_changed_conv_pbs evd status_changed in + List.fold_left + (fun (evd,b as p) (pbty,env,t1,t2) -> + if b then conv_algo env evd pbty t1 t2 else p) (evd,true) + pbs + +(* Tries to solve problem t1 = t2. + * Precondition: t1 is an uninstantiated evar + * Returns an optional list of evars that were instantiated, or None + * if the problem couldn't be solved. *) + +(* Rq: uncomplete algorithm if pbty = CONV_X_LEQ ! *) +let solve_simple_eqn conv_algo ?(choose=false) env evd (pbty,(evk1,args1 as ev1),t2) = + try + let t2 = whd_betaiota evd t2 in (* includes whd_evar *) + let evd = + match pbty with + | Some true when isEvar t2 -> + add_conv_pb (Reduction.CUMUL,env,mkEvar ev1,t2) evd + | Some false when isEvar t2 -> + add_conv_pb (Reduction.CUMUL,env,t2,mkEvar ev1) evd + | _ -> + evar_define conv_algo ~choose env evd ev1 t2 in + reconsider_conv_pbs conv_algo evd + with e when precatchable_exception e -> + (evd,false) + diff --git a/pretyping/evarsolve.mli b/pretyping/evarsolve.mli new file mode 100644 index 000000000..6c7635449 --- /dev/null +++ b/pretyping/evarsolve.mli @@ -0,0 +1,48 @@ +(************************************************************************) +(* v * The Coq Proof Assistant / The Coq Development Team *) +(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2012 *) +(* \VV/ **************************************************************) +(* // * This file is distributed under the terms of the *) +(* * GNU Lesser General Public License Version 2.1 *) +(************************************************************************) + +open Term +open Evd +open Environ + +(** Replace the vars and rels that are aliases to other vars and rels by + their representative that is most ancient in the context *) +val expand_vars_in_term : env -> constr -> constr + +type conv_fun = + env -> evar_map -> conv_pb -> constr -> constr -> evar_map * bool + +(** [evar_define choose env ev c] try to instantiate [ev] with [c] (typed in [env]), + possibly solving related unification problems, possibly leaving open + some problems that cannot be solved in a unique way (except if choose is + true); fails if the instance is not valid for the given [ev] *) +val evar_define : conv_fun -> ?choose:bool -> env -> evar_map -> + existential -> constr -> evar_map + +val solve_refl : ?can_drop:bool -> conv_fun -> env -> evar_map -> + existential_key -> constr array -> constr array -> evar_map + +val solve_evar_evar : ?force:bool -> + (env -> evar_map -> existential -> constr -> evar_map) -> conv_fun -> + env -> evar_map -> existential -> existential -> evar_map + +val solve_simple_eqn : conv_fun -> ?choose:bool -> env -> evar_map -> + bool option * existential * constr -> evar_map * bool + +val reconsider_conv_pbs : conv_fun -> evar_map -> evar_map * bool + +val is_unification_pattern_evar : env -> evar_map -> existential -> constr list -> + constr -> constr list option + +val is_unification_pattern : env * int -> evar_map -> constr -> constr list -> + constr -> constr list option + +val solve_pattern_eqn : env -> constr list -> constr -> constr + +val check_evar_instance : evar_map -> existential_key -> constr -> conv_fun -> + evar_map diff --git a/pretyping/evarutil.ml b/pretyping/evarutil.ml index 0c339df4e..39d6a59db 100644 --- a/pretyping/evarutil.ml +++ b/pretyping/evarutil.ml @@ -158,27 +158,6 @@ let whd_head_evar_stack sigma c = let whd_head_evar sigma c = applist (whd_head_evar_stack sigma c) -let noccur_evar env evd evk c = - let rec occur_rec k c = match kind_of_term c with - | Evar (evk',args' as ev') -> - (match safe_evar_value evd ev' with - | Some c -> occur_rec k c - | None -> - if Int.equal evk evk' then raise Occur - else Array.iter (occur_rec k) args') - | Rel i when i > k -> - (match pi2 (Environ.lookup_rel (i-k) env) with - | None -> () - | Some b -> occur_rec k (lift i b)) - | _ -> iter_constr_with_binders succ occur_rec k c - in - try occur_rec 0 c; true with Occur -> false - -let normalize_evar evd ev = - match kind_of_term (whd_evar evd (mkEvar ev)) with - | Evar (evk,args) -> (evk,args) - | _ -> assert false - (**********************) (* Creating new metas *) (**********************) @@ -266,14 +245,17 @@ let non_instantiated sigma = (* Manipulating filters *) (************************) -let apply_subfilter filter subfilter = - fst (List.fold_right (fun oldb (l,filter) -> - if oldb then List.hd filter::l,List.tl filter else (false::l,filter)) - filter ([], List.rev subfilter)) - let extract_subfilter initial_filter refined_filter = snd (List.filter2 (fun b1 b2 -> b1) initial_filter refined_filter) +let make_pure_subst evi args = + snd (List.fold_right + (fun (id,b,c) (args,l) -> + match args with + | a::rest -> (rest, (id,a)::l) + | _ -> anomaly (Pp.str "Instance does not match its signature")) + (evar_filtered_context evi) (Array.rev_to_list args,[])) + (**********************) (* Creating new evars *) (**********************) @@ -376,62 +358,6 @@ let e_new_evar evdref env ?(src=(Loc.ghost,Evar_kinds.InternalHole)) ?filter ?ca evdref := evd'; ev -(*------------------------------------* - * Restricting existing evars * - *------------------------------------*) - -let rec eq_filter l1 l2 = match l1, l2 with -| [], [] -> true -| h1 :: l1, h2 :: l2 -> - (if h1 then h2 else not h2) && eq_filter l1 l2 -| _ -> false - -let restrict_evar_key evd evk filter candidates = - match filter, candidates with - | None, None -> evd, evk - | _ -> - let evi = Evd.find_undefined evd evk in - let oldfilter = evar_filter evi in - begin match filter, candidates with - | Some filter, None when eq_filter oldfilter filter -> - evd, evk - | _ -> - let filter = match filter with - | None -> evar_filter evi - | Some filter -> filter in - let candidates = match candidates with - | None -> evi.evar_candidates - | Some _ -> candidates in - let ccl = evi.evar_concl in - let sign = evar_hyps evi in - let src = evi.evar_source in - let evd,newevk = new_pure_evar evd sign ccl ~src ~filter ?candidates in - let _, ctxt = List.filter2 (fun b c -> b) filter (evar_context evi) in - let id_inst = Array.of_list (List.map (fun (id,_,_) -> mkVar id) ctxt) in - Evd.define evk (mkEvar(newevk,id_inst)) evd,newevk - end - -(* Restrict an applied evar and returns its restriction in the same context *) -let restrict_applied_evar evd (evk,argsv) filter candidates = - let evd,newevk = restrict_evar_key evd evk filter candidates in - let newargsv = match filter with - | None -> (* optim *) argsv - | Some filter -> - let evi = Evd.find evd evk in - let subfilter = extract_subfilter (evar_filter evi) filter in - Array.filter_with subfilter argsv in - evd,(newevk,newargsv) - -(* Restrict an evar in the current evar_map *) -let restrict_evar evd evk filter candidates = - fst (restrict_evar_key evd evk filter candidates) - -(* Restrict an evar in the current evar_map *) -let restrict_instance evd evk filter argsv = - match filter with None -> argsv | Some filter -> - let evi = Evd.find evd evk in - Array.filter_with (extract_subfilter (evar_filter evi) filter) argsv - (* This assumes an evar with identity instance and generalizes it over only the De Bruijn part of the context *) let generalize_evar_over_rels sigma (ev,args) = @@ -442,156 +368,6 @@ let generalize_evar_over_rels sigma (ev,args) = if isRel a then (mkNamedProd_or_LetIn d c,a::inst) else x) (evi.evar_concl,[]) (Array.to_list args) sign -(***************************************) -(* Managing chains of local definitons *) -(***************************************) - -(* Expand rels and vars that are bound to other rels or vars so that - dependencies in variables are canonically associated to the most ancient - variable in its family of aliased variables *) - -let compute_var_aliases sign = - List.fold_right (fun (id,b,c) aliases -> - match b with - | Some t -> - (match kind_of_term t with - | Var id' -> - let aliases_of_id = - try Id.Map.find id' aliases with Not_found -> [] in - Id.Map.add id (aliases_of_id@[t]) aliases - | _ -> - Id.Map.add id [t] aliases) - | None -> aliases) - sign Id.Map.empty - -let compute_rel_aliases var_aliases rels = - snd (List.fold_right (fun (_,b,t) (n,aliases) -> - (n-1, - match b with - | Some t -> - (match kind_of_term t with - | Var id' -> - let aliases_of_n = - try Id.Map.find id' var_aliases with Not_found -> [] in - Int.Map.add n (aliases_of_n@[t]) aliases - | Rel p -> - let aliases_of_n = - try Int.Map.find (p+n) aliases with Not_found -> [] in - Int.Map.add n (aliases_of_n@[mkRel (p+n)]) aliases - | _ -> - Int.Map.add n [lift n t] aliases) - | None -> aliases)) - rels (List.length rels,Int.Map.empty)) - -let make_alias_map env = - (* We compute the chain of aliases for each var and rel *) - let var_aliases = compute_var_aliases (named_context env) in - let rel_aliases = compute_rel_aliases var_aliases (rel_context env) in - (var_aliases,rel_aliases) - -let lift_aliases n (var_aliases,rel_aliases as aliases) = - if Int.equal n 0 then aliases else - (var_aliases, - Int.Map.fold (fun p l -> Int.Map.add (p+n) (List.map (lift n) l)) - rel_aliases Int.Map.empty) - -let get_alias_chain_of aliases x = match kind_of_term x with - | Rel n -> (try Int.Map.find n (snd aliases) with Not_found -> []) - | Var id -> (try Id.Map.find id (fst aliases) with Not_found -> []) - | _ -> [] - -let normalize_alias_opt aliases x = - match get_alias_chain_of aliases x with - | [] -> None - | a::_ when isRel a or isVar a -> Some a - | [_] -> None - | _::a::_ -> Some a - -let normalize_alias aliases x = - match normalize_alias_opt aliases x with - | Some a -> a - | None -> x - -let normalize_alias_var var_aliases id = - destVar (normalize_alias (var_aliases,Int.Map.empty) (mkVar id)) - -let extend_alias (_,b,_) (var_aliases,rel_aliases) = - let rel_aliases = - Int.Map.fold (fun n l -> Int.Map.add (n+1) (List.map (lift 1) l)) - rel_aliases Int.Map.empty in - let rel_aliases = - match b with - | Some t -> - (match kind_of_term t with - | Var id' -> - let aliases_of_binder = - try Id.Map.find id' var_aliases with Not_found -> [] in - Int.Map.add 1 (aliases_of_binder@[t]) rel_aliases - | Rel p -> - let aliases_of_binder = - try Int.Map.find (p+1) rel_aliases with Not_found -> [] in - Int.Map.add 1 (aliases_of_binder@[mkRel (p+1)]) rel_aliases - | _ -> - Int.Map.add 1 [lift 1 t] rel_aliases) - | None -> rel_aliases in - (var_aliases, rel_aliases) - -let expand_alias_once aliases x = - match get_alias_chain_of aliases x with - | [] -> None - | l -> Some (List.last l) - -let expansions_of_var aliases x = - match get_alias_chain_of aliases x with - | [] -> [x] - | a::_ as l when isRel a || isVar a -> x :: List.rev l - | _::l -> x :: List.rev l - -let expansion_of_var aliases x = - match get_alias_chain_of aliases x with - | [] -> x - | a::_ -> a - -let rec expand_vars_in_term_using aliases t = match kind_of_term t with - | Rel _ | Var _ -> - normalize_alias aliases t - | _ -> - map_constr_with_full_binders - extend_alias expand_vars_in_term_using aliases t - -let expand_vars_in_term env = expand_vars_in_term_using (make_alias_map env) - -let free_vars_and_rels_up_alias_expansion aliases c = - let acc1 = ref Int.Set.empty and acc2 = ref Id.Set.empty in - let cache_rel = ref Int.Set.empty and cache_var = ref Id.Set.empty in - let is_in_cache depth = function - | Rel n -> Int.Set.mem (n-depth) !cache_rel - | Var s -> Id.Set.mem s !cache_var - | _ -> false in - let put_in_cache depth = function - | Rel n -> cache_rel := Int.Set.add (n-depth) !cache_rel - | Var s -> cache_var := Id.Set.add s !cache_var - | _ -> () in - let rec frec (aliases,depth) c = - match kind_of_term c with - | Rel _ | Var _ as ck -> - if is_in_cache depth ck then () else begin - put_in_cache depth ck; - let c = expansion_of_var aliases c in - match kind_of_term c with - | Var id -> acc2 := Id.Set.add id !acc2 - | Rel n -> if n >= depth+1 then acc1 := Int.Set.add (n-depth) !acc1 - | _ -> frec (aliases,depth) c end - | Const _ | Ind _ | Construct _ -> - acc2 := List.fold_right Id.Set.add (vars_of_global (Global.env()) c) !acc2 - | _ -> - iter_constr_with_full_binders - (fun d (aliases,depth) -> (extend_alias d aliases,depth+1)) - frec (aliases,depth) c - in - frec (aliases,0) c; - (!acc1,!acc2) - (************************************) (* Removing a dependency in an evar *) (************************************) @@ -708,1111 +484,6 @@ let clear_hyps_in_evi evdref hyps concl ids = in (nhyps,nconcl) -(********************************) -(* Managing pattern-unification *) -(********************************) - -let rec expand_and_check_vars aliases = function - | [] -> [] - | a::l when isRel a or isVar a -> - let a = expansion_of_var aliases a in - if isRel a or isVar a then a :: expand_and_check_vars aliases l - else raise Exit - | _ -> - raise Exit - -module Constrhash = Hashtbl.Make - (struct type t = constr - let equal = eq_constr - let hash = hash_constr - end) - -let constr_list_distinct l = - let visited = Constrhash.create 23 in - let rec loop = function - | h::t -> - if Constrhash.mem visited h then false - else (Constrhash.add visited h h; loop t) - | [] -> true - in loop l - -let get_actual_deps aliases l t = - if occur_meta_or_existential t then - (* Probably no restrictions on allowed vars in presence of evars *) - l - else - (* Probably strong restrictions coming from t being evar-closed *) - let (fv_rels,fv_ids) = free_vars_and_rels_up_alias_expansion aliases t in - List.filter (fun c -> - match kind_of_term c with - | Var id -> Id.Set.mem id fv_ids - | Rel n -> Int.Set.mem n fv_rels - | _ -> assert false) l - -let remove_instance_local_defs evd evk args = - let evi = Evd.find evd evk in - let rec aux = function - | (_,Some _,_)::sign, a::args -> aux (sign,args) - | (_,None,_)::sign, a::args -> a::aux (sign,args) - | [], [] -> [] - | _ -> assert false in - aux (evar_filtered_context evi, args) - -(* Check if an applied evar "?X[args] l" is a Miller's pattern *) - -let find_unification_pattern_args env l t = - if List.for_all (fun x -> isRel x || isVar x) l (* common failure case *) then - let aliases = make_alias_map env in - match (try Some (expand_and_check_vars aliases l) with Exit -> None) with - | Some l as x when constr_list_distinct (get_actual_deps aliases l t) -> x - | _ -> None - else - None - -let is_unification_pattern_meta env nb m l t = - (* Variables from context and rels > nb are implicitly all there *) - (* so we need to be a rel <= nb *) - if List.for_all (fun x -> isRel x && destRel x <= nb) l then - match find_unification_pattern_args env l t with - | Some _ as x when not (dependent (mkMeta m) t) -> x - | _ -> None - else - None - -let is_unification_pattern_evar env evd (evk,args) l t = - if List.for_all (fun x -> isRel x || isVar x) l & noccur_evar env evd evk t - then - let args = remove_instance_local_defs evd evk (Array.to_list args) in - let n = List.length args in - match find_unification_pattern_args env (args @ l) t with - | Some l -> Some (List.skipn n l) - | _ -> None - else - None - -let is_unification_pattern_pure_evar env evd (evk,args) t = - let is_ev = is_unification_pattern_evar env evd (evk,args) [] t in - match is_ev with - | None -> false - | Some _ -> true - -let is_unification_pattern (env,nb) evd f l t = - match kind_of_term f with - | Meta m -> is_unification_pattern_meta env nb m l t - | Evar ev -> is_unification_pattern_evar env evd ev l t - | _ -> None - -(* From a unification problem "?X l = c", build "\x1...xn.(term1 l2)" - (pattern unification). It is assumed that l is made of rel's that - are distinct and not bound to aliases. *) -(* It is also assumed that c does not contain metas because metas - *implicitly* depend on Vars but lambda abstraction will not reflect this - dependency: ?X x = ?1 (?1 is a meta) will return \_.?1 while it should - return \y. ?1{x\y} (non constant function if ?1 depends on x) (BB) *) -let solve_pattern_eqn env l c = - let c' = List.fold_right (fun a c -> - let c' = subst_term (lift 1 a) (lift 1 c) in - match kind_of_term a with - (* Rem: if [a] links to a let-in, do as if it were an assumption *) - | Rel n -> - let d = map_rel_declaration (lift n) (lookup_rel n env) in - mkLambda_or_LetIn d c' - | Var id -> - let d = lookup_named id env in mkNamedLambda_or_LetIn d c' - | _ -> assert false) - l c in - (* Warning: we may miss some opportunity to eta-reduce more since c' - is not in normal form *) - whd_eta c' - -(*****************************************) -(* Refining/solving unification problems *) -(*****************************************) - -(* Knowing that [Gamma |- ev : T] and that [ev] is applied to [args], - * [make_projectable_subst ev args] builds the substitution [Gamma:=args]. - * If a variable and an alias of it are bound to the same instance, we skip - * the alias (we just use eq_constr -- instead of conv --, since anyway, - * only instances that are variables -- or evars -- are later considered; - * morever, we can bet that similar instances came at some time from - * the very same substitution. The removal of aliased duplicates is - * useful to ensure the uniqueness of a projection. -*) - -let make_projectable_subst aliases sigma evi args = - let sign = evar_filtered_context evi in - let evar_aliases = compute_var_aliases sign in - let (_,full_subst,cstr_subst) = - List.fold_right - (fun (id,b,c) (args,all,cstrs) -> - match b,args with - | None, a::rest -> - let a = whd_evar sigma a in - let cstrs = - let a',args = decompose_app_vect a in - match kind_of_term a' with - | Construct cstr -> - let l = try Constrmap.find cstr cstrs with Not_found -> [] in - Constrmap.add cstr ((args,id)::l) cstrs - | _ -> cstrs in - (rest,Id.Map.add id [a,normalize_alias_opt aliases a,id] all,cstrs) - | Some c, a::rest -> - let a = whd_evar sigma a in - (match kind_of_term c with - | Var id' -> - let idc = normalize_alias_var evar_aliases id' in - let sub = try Id.Map.find idc all with Not_found -> [] in - if List.exists (fun (c,_,_) -> eq_constr a c) sub then - (rest,all,cstrs) - else - (rest, - Id.Map.add idc ((a,normalize_alias_opt aliases a,id)::sub) all, - cstrs) - | _ -> - (rest,Id.Map.add id [a,normalize_alias_opt aliases a,id] all,cstrs)) - | _ -> anomaly (Pp.str "Instance does not match its signature")) - sign (Array.rev_to_list args,Id.Map.empty,Constrmap.empty) in - (full_subst,cstr_subst) - -let make_pure_subst evi args = - snd (List.fold_right - (fun (id,b,c) (args,l) -> - match args with - | a::rest -> (rest, (id,a)::l) - | _ -> anomaly (Pp.str "Instance does not match its signature")) - (evar_filtered_context evi) (Array.rev_to_list args,[])) - -(*------------------------------------* - * operations on the evar constraints * - *------------------------------------*) - -(* We have a unification problem Σ; Γ |- ?e[u1..uq] = t : s where ?e is not yet - * declared in Σ but yet known to be declarable in some context x1:T1..xq:Tq. - * [define_evar_from_virtual_equation ... Γ Σ t (x1:T1..xq:Tq) .. (u1..uq) (x1..xq)] - * declares x1:T1..xq:Tq |- ?e : s such that ?e[u1..uq] = t holds. - *) - -let define_evar_from_virtual_equation define_fun env evd t_in_env sign filter inst_in_env = - let ty_t_in_env = Retyping.get_type_of env evd t_in_env in - let evd,evar_in_env = new_evar_instance sign evd ty_t_in_env ~filter inst_in_env in - let t_in_env = whd_evar evd t_in_env in - let evd = define_fun env evd (destEvar evar_in_env) t_in_env in - let ids = List.map pi1 (named_context_of_val sign) in - let inst_in_sign = List.map mkVar (List.filter_with filter ids) in - let evar_in_sign = mkEvar (fst (destEvar evar_in_env), Array.of_list inst_in_sign) in - (evd,whd_evar evd evar_in_sign) - -(* We have x1..xq |- ?e1 : τ and had to solve something like - * Σ; Γ |- ?e1[u1..uq] = (...\y1 ... \yk ... c), where c is typically some - * ?e2[v1..vn], hence flexible. We had to go through k binders and now - * virtually have x1..xq, y1'..yk' | ?e1' : τ' and the equation - * Γ, y1..yk |- ?e1'[u1..uq y1..yk] = c. - * [materialize_evar Γ evd k (?e1[u1..uq]) τ'] extends Σ with the declaration - * of ?e1' and returns both its instance ?e1'[x1..xq y1..yk] in an extension - * of the context of e1 so that e1 can be instantiated by - * (...\y1' ... \yk' ... ?e1'[x1..xq y1'..yk']), - * and the instance ?e1'[u1..uq y1..yk] so that the remaining equation - * ?e1'[u1..uq y1..yk] = c can be registered - * - * Note that, because invert_definition does not check types, we need to - * guess the types of y1'..yn' by inverting the types of y1..yn along the - * substitution u1..uq. - *) - -let materialize_evar define_fun env evd k (evk1,args1) ty_in_env = - let evi1 = Evd.find_undefined evd evk1 in - let env1,rel_sign = env_rel_context_chop k env in - let sign1 = evar_hyps evi1 in - let filter1 = evar_filter evi1 in - let ids1 = List.map pi1 (named_context_of_val sign1) in - let inst_in_sign = List.map mkVar (List.filter_with filter1 ids1) in - let (sign2,filter2,inst2_in_env,inst2_in_sign,_,evd,_) = - List.fold_right (fun (na,b,t_in_env as d) (sign,filter,inst_in_env,inst_in_sign,env,evd,avoid) -> - let id = next_name_away na avoid in - let evd,t_in_sign = - define_evar_from_virtual_equation define_fun env evd t_in_env - sign filter inst_in_env in - let evd,b_in_sign = match b with - | None -> evd,None - | Some b -> - let evd,b = define_evar_from_virtual_equation define_fun env evd b - sign filter inst_in_env in - evd,Some b in - (push_named_context_val (id,b_in_sign,t_in_sign) sign,true::filter, - (mkRel 1)::(List.map (lift 1) inst_in_env), - (mkRel 1)::(List.map (lift 1) inst_in_sign), - push_rel d env,evd,id::avoid)) - rel_sign - (sign1,filter1,Array.to_list args1,inst_in_sign,env1,evd,ids1) - in - let evd,ev2ty_in_sign = - define_evar_from_virtual_equation define_fun env evd ty_in_env - sign2 filter2 inst2_in_env in - let evd,ev2_in_sign = - new_evar_instance sign2 evd ev2ty_in_sign ~filter:filter2 inst2_in_sign in - let ev2_in_env = (fst (destEvar ev2_in_sign), Array.of_list inst2_in_env) in - (evd, ev2_in_sign, ev2_in_env) - -let restrict_upon_filter evd evk p args = - let newfilter = List.map p args in - if List.for_all (fun id -> id) newfilter then - None - else - let oldfullfilter = evar_filter (Evd.find_undefined evd evk) in - Some (apply_subfilter oldfullfilter newfilter) - -(* Inverting constructors in instances (common when inferring type of match) *) - -let find_projectable_constructor env evd cstr k args cstr_subst = - try - let l = Constrmap.find cstr cstr_subst in - let args = Array.map (lift (-k)) args in - let l = - List.filter (fun (args',id) -> - (* is_conv is maybe too strong (and source of useless computation) *) - (* (at least expansion of aliases is needed) *) - Array.for_all2 (is_conv env evd) args args') l in - List.map snd l - with Not_found -> - [] - -(* [find_projectable_vars env sigma y subst] finds all vars of [subst] - * that project on [y]. It is able to find solutions to the following - * two kinds of problems: - * - * - ?n[...;x:=y;...] = y - * - ?n[...;x:=?m[args];...] = y with ?m[args] = y recursively solvable - * - * (see test-suite/success/Fixpoint.v for an example of application of - * the second kind of problem). - * - * The seek for [y] is up to variable aliasing. In case of solutions that - * differ only up to aliasing, the binding that requires the less - * steps of alias reduction is kept. At the end, only one solution up - * to aliasing is kept. - * - * [find_projectable_vars] also unifies against evars that themselves mention - * [y] and recursively. - * - * In short, the following situations give the following solutions: - * - * problem evar ctxt soluce remark - * z1; z2:=z1 |- ?ev[z1;z2] = z1 y1:A; y2:=y1 y1 \ thanks to defs kept in - * z1; z2:=z1 |- ?ev[z1;z2] = z2 y1:A; y2:=y1 y2 / subst and preferring = - * z1; z2:=z1 |- ?ev[z1] = z2 y1:A y1 thanks to expand_var - * z1; z2:=z1 |- ?ev[z2] = z1 y1:A y1 thanks to expand_var - * z3 |- ?ev[z3;z3] = z3 y1:A; y2:=y1 y2 see make_projectable_subst - * - * Remark: [find_projectable_vars] assumes that identical instances of - * variables in the same set of aliased variables are already removed (see - * [make_projectable_subst]) - *) - -type evar_projection = -| ProjectVar -| ProjectEvar of existential * evar_info * Id.t * evar_projection - -exception NotUnique -exception NotUniqueInType of (Id.t * evar_projection) list - -let rec assoc_up_to_alias sigma aliases y yc = function - | [] -> raise Not_found - | (c,cc,id)::l -> - let c' = whd_evar sigma c in - if eq_constr y c' then id - else - match l with - | _ :: _ -> assoc_up_to_alias sigma aliases y yc l - | [] -> - (* Last chance, we reason up to alias conversion *) - match (if c == c' then cc else normalize_alias_opt aliases c') with - | Some cc when eq_constr yc cc -> id - | _ -> if eq_constr yc c then id else raise Not_found - -let rec find_projectable_vars with_evars aliases sigma y subst = - let yc = normalize_alias aliases y in - let is_projectable idc idcl subst' = - (* First test if some [id] aliased to [idc] is bound to [y] in [subst] *) - try - let id = assoc_up_to_alias sigma aliases y yc idcl in - (id,ProjectVar)::subst' - with Not_found -> - (* Then test if [idc] is (indirectly) bound in [subst] to some evar *) - (* projectable on [y] *) - if with_evars then - let idcl' = List.filter (fun (c,_,id) -> isEvar c) idcl in - match idcl' with - | [c,_,id] -> - begin - let (evk,argsv as t) = destEvar c in - let evi = Evd.find sigma evk in - let subst,_ = make_projectable_subst aliases sigma evi argsv in - let l = find_projectable_vars with_evars aliases sigma y subst in - match l with - | [id',p] -> (id,ProjectEvar (t,evi,id',p))::subst' - | _ -> subst' - end - | [] -> subst' - | _ -> anomaly (Pp.str "More than one non var in aliases class of evar instance") - else - subst' in - Id.Map.fold is_projectable subst [] - -(* [filter_solution] checks if one and only one possible projection exists - * among a set of solutions to a projection problem *) - -let filter_solution = function - | [] -> raise Not_found - | (id,p)::_::_ -> raise NotUnique - | [id,p] -> (mkVar id, p) - -let project_with_effects aliases sigma effects t subst = - let c, p = - filter_solution (find_projectable_vars false aliases sigma t subst) in - effects := p :: !effects; - c - -let rec find_solution_type evarenv = function - | (id,ProjectVar)::l -> pi3 (lookup_named id evarenv) - | [id,ProjectEvar _] -> (* bugged *) pi3 (lookup_named id evarenv) - | (id,ProjectEvar _)::l -> find_solution_type evarenv l - | [] -> assert false - -(* In case the solution to a projection problem requires the instantiation of - * subsidiary evars, [do_projection_effects] performs them; it - * also try to instantiate the type of those subsidiary evars if their - * type is an evar too. - * - * Note: typing creates new evar problems, which induces a recursive dependency - * with [define]. To avoid a too large set of recursive functions, we - * pass [define] to [do_projection_effects] as a parameter. - *) - -let rec do_projection_effects define_fun env ty evd = function - | ProjectVar -> evd - | ProjectEvar ((evk,argsv),evi,id,p) -> - let evd = Evd.define evk (mkVar id) evd in - (* TODO: simplify constraints involving evk *) - let evd = do_projection_effects define_fun env ty evd p in - let ty = whd_betadeltaiota env evd (Lazy.force ty) in - if not (isSort ty) then - (* Don't try to instantiate if a sort because if evar_concl is an - evar it may commit to a univ level which is not the right - one (however, regarding coercions, because t is obtained by - unif, we know that no coercion can be inserted) *) - let subst = make_pure_subst evi argsv in - let ty' = replace_vars subst evi.evar_concl in - let ty' = whd_evar evd ty' in - if isEvar ty' then define_fun env evd (destEvar ty') ty else evd - else - evd - -(* Assuming Σ; Γ, y1..yk |- c, [invert_arg_from_subst Γ k Σ [x1:=u1..xn:=un] c] - * tries to return φ(x1..xn) such that equation φ(u1..un) = c is valid. - * The strategy is to imitate the structure of c and then to invert - * the variables of c (i.e. rels or vars of Γ) using the algorithm - * implemented by project_with_effects/find_projectable_vars. - * It returns either a unique solution or says whether 0 or more than - * 1 solutions is found. - * - * Precondition: Σ; Γ, y1..yk |- c /\ Σ; Γ |- u1..un - * Postcondition: if φ(x1..xn) is returned then - * Σ; Γ, y1..yk |- φ(u1..un) = c /\ x1..xn |- φ(x1..xn) - * - * The effects correspond to evars instantiated while trying to project. - * - * [invert_arg_from_subst] is used on instances of evars. Since the - * evars are flexible, these instances are potentially erasable. This - * is why we don't investigate whether evars in the instances of evars - * are unifiable, to the contrary of [invert_definition]. - *) - -type projectibility_kind = - | NoUniqueProjection - | UniqueProjection of constr * evar_projection list - -type projectibility_status = - | CannotInvert - | Invertible of projectibility_kind - -let invert_arg_from_subst evd aliases k0 subst_in_env_extended_with_k_binders c_in_env_extended_with_k_binders = - let effects = ref [] in - let rec aux k t = - let t = whd_evar evd t in - match kind_of_term t with - | Rel i when i>k0+k -> aux' k (mkRel (i-k)) - | Var id -> aux' k t - | _ -> map_constr_with_binders succ aux k t - and aux' k t = - try project_with_effects aliases evd effects t subst_in_env_extended_with_k_binders - with Not_found -> - match expand_alias_once aliases t with - | None -> raise Not_found - | Some c -> aux k c in - try - let c = aux 0 c_in_env_extended_with_k_binders in - Invertible (UniqueProjection (c,!effects)) - with - | Not_found -> CannotInvert - | NotUnique -> Invertible NoUniqueProjection - -let invert_arg fullenv evd aliases k evk subst_in_env_extended_with_k_binders c_in_env_extended_with_k_binders = - let res = invert_arg_from_subst evd aliases k subst_in_env_extended_with_k_binders c_in_env_extended_with_k_binders in - match res with - | Invertible (UniqueProjection (c,_)) when not (noccur_evar fullenv evd evk c) - -> - CannotInvert - | _ -> - res - -exception NotEnoughInformationToInvert - -let extract_unique_projections projs = - List.map (function - | Invertible (UniqueProjection (c,_)) -> c - | _ -> - (* For instance, there are evars with non-invertible arguments and *) - (* we cannot arbitrarily restrict these evars before knowing if there *) - (* will really be used; it can also be due to some argument *) - (* (typically a rel) that is not inversible and that cannot be *) - (* inverted either because it is needed for typing the conclusion *) - (* of the evar to project *) - raise NotEnoughInformationToInvert) projs - -let extract_candidates sols = - try - Some - (List.map (function (id,ProjectVar) -> mkVar id | _ -> raise Exit) sols) - with Exit -> - None - -let invert_invertible_arg fullenv evd aliases k (evk,argsv) args' = - let evi = Evd.find_undefined evd evk in - let subst,_ = make_projectable_subst aliases evd evi argsv in - let projs = - Array.map_to_list (invert_arg fullenv evd aliases k evk subst) args' in - Array.of_list (extract_unique_projections projs) - -(* Redefines an evar with a smaller context (i.e. it may depend on less - * variables) such that c becomes closed. - * Example: in "fun (x:?1) (y:list ?2[x]) => x = y :> ?3[x,y] /\ x = nil bool" - * ?3 <-- ?1 no pb: env of ?3 is larger than ?1's - * ?1 <-- list ?2 pb: ?2 may depend on x, but not ?1. - * What we do is that ?2 is defined by a new evar ?4 whose context will be - * a prefix of ?2's env, included in ?1's env. - * - * If "hyps |- ?e : T" and "filter" selects a subset hyps' of hyps then - * [do_restrict_hyps evd ?e filter] sets ?e:=?e'[hyps'] and returns ?e' - * such that "hyps' |- ?e : T" - *) - -let filter_candidates evd evk filter candidates = - let evi = Evd.find_undefined evd evk in - let candidates = match candidates with - | None -> evi.evar_candidates - | Some _ -> candidates in - match candidates,filter with - | None,_ | _, None -> candidates - | Some l, Some filter -> - let ids = List.map pi1 (List.filter_with filter (evar_context evi)) in - Some (List.filter (fun a -> - List.subset (Id.Set.elements (collect_vars a)) ids) l) - -let closure_of_filter evd evk filter = - let evi = Evd.find_undefined evd evk in - let vars = collect_vars (evar_concl evi) in - let ids = List.map pi1 (evar_context evi) in - let test id b = b || Id.Set.mem id vars in - let newfilter = List.map2 test ids filter in - if eq_filter newfilter (evar_filter evi) then None else Some newfilter - -let restrict_hyps evd evk filter candidates = - (* What to do with dependencies? - Assume we have x:A, y:B(x), z:C(x,y) |- ?e:T(x,y,z) and restrict on y. - - If y is in a non-erasable position in C(x,y) (i.e. it is not below an - occurrence of x in the hnf of C), then z should be removed too. - - If y is in a non-erasable position in T(x,y,z) then the problem is - unsolvable. - Computing whether y is erasable or not may be costly and the - interest for this early detection in practice is not obvious. We let - it for future work. In any case, thanks to the use of filters, the whole - (unrestricted) context remains consistent. *) - let candidates = filter_candidates evd evk (Some filter) candidates in - let typablefilter = closure_of_filter evd evk filter in - (typablefilter,candidates) - -exception EvarSolvedWhileRestricting of evar_map * constr - -let do_restrict_hyps evd (evk,args as ev) filter candidates = - let filter,candidates = match filter with - | None -> None,candidates - | Some filter -> restrict_hyps evd evk filter candidates in - match candidates,filter with - | Some [], _ -> error "Not solvable." - | Some [nc],_ -> - let evd = Evd.define evk nc evd in - raise (EvarSolvedWhileRestricting (evd,whd_evar evd (mkEvar ev))) - | None, None -> evd,ev - | _ -> restrict_applied_evar evd ev filter candidates - -(* [postpone_non_unique_projection] postpones equation of the form ?e[?] = c *) -(* ?e is assumed to have no candidates *) - -let postpone_non_unique_projection env evd (evk,argsv as ev) sols rhs = - let rhs = expand_vars_in_term env rhs in - let filter = - restrict_upon_filter evd evk - (* Keep only variables that occur in rhs *) - (* This is not safe: is the variable is a local def, its body *) - (* may contain references to variables that are removed, leading to *) - (* a ill-formed context. We would actually need a notion of filter *) - (* that says that the body is hidden. Note that expand_vars_in_term *) - (* expands only rels and vars aliases, not rels or vars bound to an *) - (* arbitrary complex term *) - (fun a -> not (isRel a || isVar a) - || dependent a rhs || List.exists (fun (id,_) -> isVarId id a) sols) - (Array.to_list argsv) in - let filter = match filter with - | None -> None - | Some filter -> closure_of_filter evd evk filter in - let candidates = extract_candidates sols in - match candidates with - | None -> - (* We made an approximation by not expanding a local definition *) - let evd,ev = restrict_applied_evar evd ev filter None in - let pb = (Reduction.CONV,env,mkEvar ev,rhs) in - Evd.add_conv_pb pb evd - | Some _ -> - restrict_evar evd evk filter candidates - -(* [postpone_evar_evar] postpones an equation of the form ?e1[?1] = ?e2[?2] *) - -let postpone_evar_evar f env evd filter1 ev1 filter2 ev2 = - (* Leave an equation between (restrictions of) ev1 andv ev2 *) - try - let evd,ev1' = do_restrict_hyps evd ev1 filter1 None in - try - let evd,ev2' = do_restrict_hyps evd ev2 filter2 None in - add_conv_pb (Reduction.CONV,env,mkEvar ev1',mkEvar ev2') evd - with EvarSolvedWhileRestricting (evd,ev2) -> - (* ev2 solved on the fly *) - f env evd ev1' ev2 - with EvarSolvedWhileRestricting (evd,ev1) -> - (* ev1 solved on the fly *) - f env evd ev2 ev1 - -(* [solve_evar_evar f Γ Σ ?e1[u1..un] ?e2[v1..vp]] applies an heuristic - * to solve the equation Σ; Γ ⊢ ?e1[u1..un] = ?e2[v1..vp]: - * - if there are at most one φj for each vj s.t. vj = φj(u1..un), - * we first restrict ?e2 to the subset v_k1..v_kq of the vj that are - * inversible and we set ?e1[x1..xn] := ?e2[φk1(x1..xn)..φkp(x1..xn)] - * (this is a case of pattern-unification) - * - symmetrically if there are at most one ψj for each uj s.t. - * uj = ψj(v1..vp), - * - otherwise, each position i s.t. ui does not occur in v1..vp has to - * be restricted and similarly for the vi, and we leave the equation - * as an open equation (performed by [postpone_evar]) - * - * Warning: the notion of unique φj is relative to some given class - * of unification problems - * - * Note: argument f is the function used to instantiate evars. - *) - -let are_canonical_instances args1 args2 env = - let n1 = Array.length args1 in - let n2 = Array.length args2 in - let rec aux n = function - | (id,_,c)::sign - when n < n1 && isVarId id args1.(n) && isVarId id args2.(n) -> - aux (n+1) sign - | [] -> - let rec aux2 n = - Int.equal n n1 || - (isRelN (n1-n) args1.(n) && isRelN (n1-n) args2.(n) && aux2 (n+1)) - in aux2 n - | _ -> false in - Int.equal n1 n2 && aux 0 (named_context env) - -let filter_compatible_candidates conv_algo env evd evi args rhs c = - let c' = instantiate_evar (evar_filtered_context evi) c args in - let evd, b = conv_algo env evd Reduction.CONV rhs c' in - if b then Some (c,evd) else None - -exception DoesNotPreserveCandidateRestriction - -let restrict_candidates conv_algo env evd filter1 (evk1,argsv1) (evk2,argsv2) = - let evi1 = Evd.find evd evk1 in - let evi2 = Evd.find evd evk2 in - let cand1 = filter_candidates evd evk1 filter1 None in - let cand2 = evi2.evar_candidates in - match cand1, cand2 with - | _, None -> cand1 - | None, Some _ -> raise DoesNotPreserveCandidateRestriction - | Some l1, Some l2 -> - let args1 = Array.to_list argsv1 in - let args2 = Array.to_list argsv2 in - let l1' = List.filter (fun c1 -> - let c1' = instantiate_evar (evar_filtered_context evi1) c1 args1 in - let filter c2 = - let compatibility = filter_compatible_candidates conv_algo env evd evi2 args2 c1' c2 in - match compatibility with - | None -> false - | Some _ -> true - in - let filtered = List.filter filter l2 in - match filtered with [] -> false | _ -> true) l1 in - if Int.equal (List.length l1) (List.length l1') then None else Some l1' - -exception CannotProject of bool list option - -(* Assume that FV(?n[x1:=t1..xn:=tn]) belongs to some set U. - Can ?n be instantiated by a term u depending essentially on xi such that the - FV(u[x1:=t1..xn:=tn]) are in the set U? - - If ti is a variable, it has to be in U. - - If ti is a constructor, its parameters cannot be erased even if u - matches on it, so we have to discard ti if the parameters - contain variables not in U. - - If ti is rigid, we have to discard it if it contains variables in U. - - Note: when restricting as part of an equation ?n[x1:=t1..xn:=tn] = ?m[...] - then, occurrences of ?m in the ti can be seen, like variables, as occurrences - of subterms to eventually discard so as to be allowed to keep ti. -*) - -let rec is_constrainable_in k (ev,(fv_rels,fv_ids) as g) t = - let f,args = decompose_app_vect t in - match kind_of_term f with - | Construct (ind,_) -> - let params,_ = Array.chop (Inductiveops.inductive_nparams ind) args in - Array.for_all (is_constrainable_in k g) params - | Ind _ -> Array.for_all (is_constrainable_in k g) args - | Prod (_,t1,t2) -> is_constrainable_in k g t1 && is_constrainable_in k g t2 - | Evar (ev',_) -> not (Int.equal ev' ev) (*If ev' needed, one may also try to restrict it*) - | Var id -> Id.Set.mem id fv_ids - | Rel n -> n <= k || Int.Set.mem n fv_rels - | Sort _ -> true - | _ -> (* We don't try to be more clever *) true - -let has_constrainable_free_vars evd aliases k ev (fv_rels,fv_ids as fvs) t = - let t = expansion_of_var aliases t in - match kind_of_term t with - | Var id -> Id.Set.mem id fv_ids - | Rel n -> n <= k || Int.Set.mem n fv_rels - | _ -> is_constrainable_in k (ev,fvs) t - -let ensure_evar_independent g env evd (evk1,argsv1 as ev1) (evk2,argsv2 as ev2)= - let filter1 = - restrict_upon_filter evd evk1 (noccur_evar env evd evk2) - (Array.to_list argsv1) - in - let candidates1 = restrict_candidates g env evd filter1 ev1 ev2 in - let evd,(evk1,_ as ev1) = do_restrict_hyps evd ev1 filter1 candidates1 in - let filter2 = - restrict_upon_filter evd evk2 (noccur_evar env evd evk1) - (Array.to_list argsv2) - in - let candidates2 = restrict_candidates g env evd filter2 ev2 ev1 in - let evd,ev2 = do_restrict_hyps evd ev2 filter2 candidates2 in - evd,ev1,ev2 - -exception EvarSolvedOnTheFly of evar_map * constr - -let project_evar_on_evar g env evd aliases k2 (evk1,argsv1 as ev1) (evk2,argsv2 as ev2) = - (* Apply filtering on ev1 so that fvs(ev1) are in fvs(ev2). *) - let fvs2 = free_vars_and_rels_up_alias_expansion aliases (mkEvar ev2) in - let filter1 = restrict_upon_filter evd evk1 - (has_constrainable_free_vars evd aliases k2 evk2 fvs2) - (Array.to_list argsv1) in - (* Only try pruning on variable substitutions, postpone otherwise. *) - (* Rules out non-linear instances. *) - if is_unification_pattern_pure_evar env evd ev2 (mkEvar ev1) then - try - let candidates1 = restrict_candidates g env evd filter1 ev1 ev2 in - let evd,(evk1',args1) = do_restrict_hyps evd ev1 filter1 candidates1 in - evd,mkEvar (evk1',invert_invertible_arg env evd aliases k2 ev2 args1) - with - | EvarSolvedWhileRestricting (evd,ev1) -> - raise (EvarSolvedOnTheFly (evd,ev1)) - | DoesNotPreserveCandidateRestriction | NotEnoughInformationToInvert -> - raise (CannotProject filter1) - else - raise (CannotProject filter1) - -let solve_evar_evar_l2r f g env evd aliases ev1 (evk2,_ as ev2) = - try - let evd,body = project_evar_on_evar g env evd aliases 0 ev1 ev2 in - Evd.define evk2 body evd - with EvarSolvedOnTheFly (evd,c) -> - f env evd ev2 c - -let solve_evar_evar ?(force=false) f g env evd (evk1,args1 as ev1) (evk2,args2 as ev2) = - if are_canonical_instances args1 args2 env then - (* If instances are canonical, we solve the problem in linear time *) - let sign = evar_filtered_context (Evd.find evd evk2) in - let id_inst = Array.map (fun (id,_,_) -> mkVar id) (Array.of_list sign) in - Evd.define evk2 (mkEvar(evk1,id_inst)) evd - else - let evd,ev1,ev2 = - (* If an evar occurs in the instance of the other evar and the - use of an heuristic is forced, we restrict *) - if force then ensure_evar_independent g env evd ev1 ev2 else (evd,ev1,ev2) in - let aliases = make_alias_map env in - try solve_evar_evar_l2r f g env evd aliases ev1 ev2 - with CannotProject filter1 -> - try solve_evar_evar_l2r f g env evd aliases ev2 ev1 - with CannotProject filter2 -> - postpone_evar_evar f env evd filter1 ev1 filter2 ev2 - -type conv_fun = - env -> evar_map -> conv_pb -> constr -> constr -> evar_map * bool - -let check_evar_instance evd evk1 body conv_algo = - let evi = Evd.find evd evk1 in - let evenv = evar_env evi in - (* FIXME: The body might be ill-typed when this is called from w_merge *) - let ty = - try Retyping.get_type_of evenv evd body - with _ -> error "Ill-typed evar instance" - in - let evd,b = conv_algo evenv evd Reduction.CUMUL ty evi.evar_concl in - if b then evd else - user_err_loc (fst (evar_source evk1 evd),"", - str "Unable to find a well-typed instantiation") - -(* Solve pbs ?e[t1..tn] = ?e[u1..un] which arise often in fixpoint - * definitions. We try to unify the ti with the ui pairwise. The pairs - * that don't unify are discarded (i.e. ?e is redefined so that it does not - * depend on these args). *) - -let solve_refl ?(can_drop=false) conv_algo env evd evk argsv1 argsv2 = - if Array.equal eq_constr argsv1 argsv2 then evd else - (* Filter and restrict if needed *) - let untypedfilter = - restrict_upon_filter evd evk - (fun (a1,a2) -> snd (conv_algo env evd Reduction.CONV a1 a2)) - (List.combine (Array.to_list argsv1) (Array.to_list argsv2)) in - let candidates = filter_candidates evd evk untypedfilter None in - let filter = match untypedfilter with - | None -> None - | Some filter -> closure_of_filter evd evk filter in - let evd,ev1 = restrict_applied_evar evd (evk,argsv1) filter candidates in - if Int.equal (fst ev1) evk && can_drop then (* No refinement *) evd else - (* either progress, or not allowed to drop, e.g. to preserve possibly *) - (* informative equations such as ?e[x:=?y]=?e[x:=?y'] where we don't know *) - (* if e can depend on x until ?y is not resolved, or, conversely, we *) - (* don't know if ?y has to be unified with ?y, until e is resolved *) - let argsv2 = restrict_instance evd evk filter argsv2 in - let ev2 = (fst ev1,argsv2) in - (* Leave a unification problem *) - Evd.add_conv_pb (Reduction.CONV,env,mkEvar ev1,mkEvar ev2) evd - -(* If the evar can be instantiated by a finite set of candidates known - in advance, we check which of them apply *) - -exception NoCandidates - -let solve_candidates conv_algo env evd (evk,argsv as ev) rhs = - let evi = Evd.find evd evk in - let args = Array.to_list argsv in - match evi.evar_candidates with - | None -> raise NoCandidates - | Some l -> - let l' = - List.map_filter - (filter_compatible_candidates conv_algo env evd evi args rhs) l in - match l' with - | [] -> error_cannot_unify env evd (mkEvar ev, rhs) - | [c,evd] -> - (* solve_candidates might have been called recursively in the mean *) - (* time and the evar been solved by the filtering process *) - if Evd.is_undefined evd evk then Evd.define evk c evd else evd - | l when List.length l < List.length l' -> - let candidates = List.map fst l in - restrict_evar evd evk None (Some candidates) - | l -> evd - -(* We try to instantiate the evar assuming the body won't depend - * on arguments that are not Rels or Vars, or appearing several times - * (i.e. we tackle a generalization of Miller-Pfenning patterns unification) - * - * 1) Let "env |- ?ev[hyps:=args] = rhs" be the unification problem - * 2) We limit it to a patterns unification problem "env |- ev[subst] = rhs" - * where only Rel's and Var's are relevant in subst - * 3) We recur on rhs, "imitating" the term, and failing if some Rel/Var is - * not in the scope of ?ev. For instance, the problem - * "y:nat |- ?x[] = y" where "|- ?1:nat" is not satisfiable because - * ?1 would be instantiated by y which is not in the scope of ?1. - * 4) We try to "project" the term if the process of imitation fails - * and that only one projection is possible - * - * Note: we don't assume rhs in normal form, it may fail while it would - * have succeeded after some reductions. - * - * This is the work of [invert_definition Γ Σ ?ev[hyps:=args] c] - * Precondition: Σ; Γ, y1..yk |- c /\ Σ; Γ |- u1..un - * Postcondition: if φ(x1..xn) is returned then - * Σ; Γ, y1..yk |- φ(u1..un) = c /\ x1..xn |- φ(x1..xn) - *) - -exception NotInvertibleUsingOurAlgorithm of constr -exception NotEnoughInformationToProgress of (Id.t * evar_projection) list -exception OccurCheckIn of evar_map * constr - -let rec invert_definition conv_algo choose env evd (evk,argsv as ev) rhs = - let aliases = make_alias_map env in - let evdref = ref evd in - let progress = ref false in - let evi = Evd.find evd evk in - let subst,cstr_subst = make_projectable_subst aliases evd evi argsv in - - (* Projection *) - let project_variable t = - (* Evar/Var problem: unifiable iff variable projectable from ev subst *) - try - let sols = find_projectable_vars true aliases !evdref t subst in - let c, p = match sols with - | [] -> raise Not_found - | [id,p] -> (mkVar id, p) - | (id,p)::_::_ -> - if choose then (mkVar id, p) else raise (NotUniqueInType sols) - in - let ty = lazy (Retyping.get_type_of env !evdref t) in - let evd = do_projection_effects (evar_define conv_algo) env ty !evdref p in - evdref := evd; - c - with - | Not_found -> raise (NotInvertibleUsingOurAlgorithm t) - | NotUniqueInType sols -> - if not !progress then - raise (NotEnoughInformationToProgress sols); - (* No unique projection but still restrict to where it is possible *) - (* materializing is necessary, but is restricting useful? *) - let ty = find_solution_type (evar_filtered_env evi) sols in - let sign = evar_filtered_context evi in - let ty' = instantiate_evar sign ty (Array.to_list argsv) in - let (evd,evar,(evk',argsv' as ev')) = - materialize_evar (evar_define conv_algo) env !evdref 0 ev ty' in - let ts = expansions_of_var aliases t in - let test c = isEvar c or List.mem c ts in - let filter = Array.map_to_list test argsv' in - let filter = apply_subfilter (evar_filter (Evd.find_undefined evd evk)) filter in - - let filter = closure_of_filter evd evk' filter in - let candidates = extract_candidates sols in - let evd = match candidates with - | None -> - let evd, ev'' = restrict_applied_evar evd ev' filter None in - Evd.add_conv_pb (Reduction.CONV,env,mkEvar ev'',t) evd - | Some _ -> - restrict_evar evd evk' filter candidates - in - evdref := evd; - evar in - - let rec imitate (env',k as envk) t = - let t = whd_evar !evdref t in - match kind_of_term t with - | Rel i when i>k -> - (match pi2 (Environ.lookup_rel (i-k) env') with - | None -> project_variable (mkRel (i-k)) - | Some b -> - try project_variable (mkRel (i-k)) - with NotInvertibleUsingOurAlgorithm _ -> imitate envk (lift i b)) - | Var id -> - (match pi2 (Environ.lookup_named id env') with - | None -> project_variable t - | Some b -> - try project_variable t - with NotInvertibleUsingOurAlgorithm _ -> imitate envk b) - | Evar (evk',args' as ev') -> - if Int.equal evk evk' then raise (OccurCheckIn (evd,rhs)); - (* Evar/Evar problem (but left evar is virtual) *) - let aliases = lift_aliases k aliases in - (try - let ev = (evk,Array.map (lift k) argsv) in - let evd,body = project_evar_on_evar conv_algo env' !evdref aliases k ev' ev in - evdref := evd; - body - with - | EvarSolvedOnTheFly (evd,t) -> evdref:=evd; imitate envk t - | CannotProject filter' -> - assert !progress; - (* Make the virtual left evar real *) - let ty = get_type_of env' !evdref t in - let (evd,evar'',ev'') = - materialize_evar (evar_define conv_algo) env' !evdref k ev ty in - (* materialize_evar may instantiate ev' by another evar; adjust it *) - let (evk',args' as ev') = normalize_evar evd ev' in - let evd = - (* Try to project (a restriction of) the left evar ... *) - try - let evd,body = project_evar_on_evar conv_algo env' evd aliases 0 ev'' ev' in - Evd.define evk' body evd - with - | EvarSolvedOnTheFly _ -> assert false (* ev has no candidates *) - | CannotProject filter'' -> - (* ... or postpone the problem *) - postpone_evar_evar (evar_define conv_algo) env' evd filter'' ev'' filter' ev' in - evdref := evd; - evar'') - | _ -> - progress := true; - match - let c,args = decompose_app_vect t in - match kind_of_term c with - | Construct cstr when noccur_between 1 k t -> - (* This is common case when inferring the return clause of match *) - (* (currently rudimentary: we do not treat the case of multiple *) - (* possible inversions; we do not treat overlap with a possible *) - (* alternative inversion of the subterms of the constructor, etc)*) - (match find_projectable_constructor env evd cstr k args cstr_subst with - | _::_ as l -> Some (List.map mkVar l) - | _ -> None) - | _ -> None - with - | Some l -> - let ty = get_type_of env' !evdref t in - let candidates = - try - let t = - map_constr_with_full_binders (fun d (env,k) -> push_rel d env, k+1) - imitate envk t in - t::l - with _ -> l in - (match candidates with - | [x] -> x - | _ -> - let (evd,evar'',ev'') = - materialize_evar (evar_define conv_algo) env' !evdref k ev ty in - evdref := restrict_evar evd (fst ev'') None (Some candidates); - evar'') - | None -> - (* Evar/Rigid problem (or assimilated if not normal): we "imitate" *) - map_constr_with_full_binders (fun d (env,k) -> push_rel d env, k+1) - imitate envk t in - - let rhs = whd_beta evd rhs (* heuristic *) in - let body = imitate (env,0) rhs in - (!evdref,body) - -(* [define] tries to solve the problem "?ev[args] = rhs" when "?ev" is - * an (uninstantiated) evar such that "hyps |- ?ev : typ". Otherwise said, - * [define] tries to find an instance lhs such that - * "lhs [hyps:=args]" unifies to rhs. The term "lhs" must be closed in - * context "hyps" and not referring to itself. - *) - -and evar_define conv_algo ?(choose=false) env evd (evk,argsv as ev) rhs = - match kind_of_term rhs with - | Evar (evk2,argsv2 as ev2) -> - if Int.equal evk evk2 then - solve_refl ~can_drop:choose conv_algo env evd evk argsv argsv2 - else - solve_evar_evar ~force:choose - (evar_define conv_algo) conv_algo env evd ev ev2 - | _ -> - try solve_candidates conv_algo env evd ev rhs - with NoCandidates -> - try - let (evd',body) = invert_definition conv_algo choose env evd ev rhs in - if occur_meta body then error "Meta cannot occur in evar body."; - (* invert_definition may have instantiate some evars of rhs with evk *) - (* so we recheck acyclicity *) - if occur_evar evk body then raise (OccurCheckIn (evd',body)); - (* needed only if an inferred type *) - let body = refresh_universes body in -(* Cannot strictly type instantiations since the unification algorithm - * does not unify applications from left to right. - * e.g problem f x == g y yields x==y and f==g (in that order) - * Another problem is that type variables are evars of type Type - let _ = - try - let env = evar_filtered_env evi in - let ty = evi.evar_concl in - Typing.check env evd' body ty - with e -> - msg_info - (str "Ill-typed evar instantiation: " ++ fnl() ++ - pr_evar_map evd' ++ fnl() ++ - str "----> " ++ int ev ++ str " := " ++ - print_constr body); - raise e in*) - let evd' = Evd.define evk body evd' in - check_evar_instance evd' evk body conv_algo - with - | NotEnoughInformationToProgress sols -> - postpone_non_unique_projection env evd ev sols rhs - | NotInvertibleUsingOurAlgorithm t -> - error_not_clean env evd evk t (evar_source evk evd) - | OccurCheckIn (evd,rhs) -> - (* last chance: rhs actually reduces to ev *) - let c = whd_betadeltaiota env evd rhs in - match kind_of_term c with - | Evar (evk',argsv2) when Int.equal evk evk' -> - solve_refl - (fun env sigma pb c c' -> (evd,is_fconv pb env sigma c c')) - env evd evk argsv argsv2 - | _ -> - error_occur_check env evd evk rhs - -(* This code (i.e. solve_pb, etc.) takes a unification - * problem, and tries to solve it. If it solves it, then it removes - * all the conversion problems, and re-runs conversion on each one, in - * the hopes that the new solution will aid in solving them. - * - * The kinds of problems it knows how to solve are those in which - * the usable arguments of an existential var are all themselves - * universal variables. - * The solution to this problem is to do renaming for the Var's, - * to make them match up with the Var's which are found in the - * hyps of the existential, to do a "pop" for each Rel which is - * not an argument of the existential, and a subst1 for each which - * is, again, with the corresponding variable. This is done by - * define - * - * Thus, we take the arguments of the existential which we are about - * to assign, and zip them with the identifiers in the hypotheses. - * Then, we process all the Var's in the arguments, and sort the - * Rel's into ascending order. Then, we just march up, doing - * subst1's and pop's. - * - * NOTE: We can do this more efficiently for the relative arguments, - * by building a long substituend by hand, but this is a pain in the - * ass. - *) - -let status_changed lev (pbty,_,t1,t2) = - (try ExistentialSet.mem (head_evar t1) lev with NoHeadEvar -> false) or - (try ExistentialSet.mem (head_evar t2) lev with NoHeadEvar -> false) - -let reconsider_conv_pbs conv_algo evd = - let (evd,pbs) = extract_changed_conv_pbs evd status_changed in - List.fold_left - (fun (evd,b as p) (pbty,env,t1,t2) -> - if b then conv_algo env evd pbty t1 t2 else p) (evd,true) - pbs - -(* Tries to solve problem t1 = t2. - * Precondition: t1 is an uninstantiated evar - * Returns an optional list of evars that were instantiated, or None - * if the problem couldn't be solved. *) - -(* Rq: uncomplete algorithm if pbty = CONV_X_LEQ ! *) -let solve_simple_eqn conv_algo ?(choose=false) env evd (pbty,(evk1,args1 as ev1),t2) = - try - let t2 = whd_betaiota evd t2 in (* includes whd_evar *) - let evd = - match pbty with - | Some true when isEvar t2 -> - add_conv_pb (Reduction.CUMUL,env,mkEvar ev1,t2) evd - | Some false when isEvar t2 -> - add_conv_pb (Reduction.CUMUL,env,t2,mkEvar ev1) evd - | _ -> - evar_define conv_algo ~choose env evd ev1 t2 in - reconsider_conv_pbs conv_algo evd - with e when precatchable_exception e -> - (evd,false) (** The following functions return the set of evars immediately contained in the object, including defined evars *) diff --git a/pretyping/evarutil.mli b/pretyping/evarutil.mli index 5c32a9a5c..7f605ce3a 100644 --- a/pretyping/evarutil.mli +++ b/pretyping/evarutil.mli @@ -32,6 +32,10 @@ val new_evar : evar_map -> env -> ?src:Loc.t * Evar_kinds.t -> ?filter:bool list -> ?candidates:constr list -> types -> evar_map * constr +val new_pure_evar : + evar_map -> named_context_val -> ?src:Loc.t * Evar_kinds.t -> ?filter:bool list -> + ?candidates:constr list -> types -> evar_map * evar + (** the same with side-effects *) val e_new_evar : evar_map ref -> env -> ?src:Loc.t * Evar_kinds.t -> ?filter:bool list -> @@ -53,18 +57,6 @@ val new_evar_instance : val make_pure_subst : evar_info -> constr array -> (Id.t * constr) list -(** {6 Instantiate evars} *) - -type conv_fun = - env -> evar_map -> conv_pb -> constr -> constr -> evar_map * bool - -(** [evar_define choose env ev c] try to instantiate [ev] with [c] (typed in [env]), - possibly solving related unification problems, possibly leaving open - some problems that cannot be solved in a unique way (except if choose is - true); fails if the instance is not valid for the given [ev] *) -val evar_define : conv_fun -> ?choose:bool -> env -> evar_map -> - existential -> constr -> evar_map - (** {6 Evars/Metas switching...} *) (** [evars_to_metas] generates new metavariables for each non dependent @@ -85,16 +77,6 @@ val whd_head_evar : evar_map -> constr -> constr val is_ground_term : evar_map -> constr -> bool val is_ground_env : evar_map -> env -> bool -val solve_refl : ?can_drop:bool -> conv_fun -> env -> evar_map -> - existential_key -> constr array -> constr array -> evar_map -val solve_evar_evar : ?force:bool -> - (env -> evar_map -> existential -> constr -> evar_map) -> conv_fun -> - env -> evar_map -> existential -> existential -> evar_map - -val solve_simple_eqn : conv_fun -> ?choose:bool -> env -> evar_map -> - bool option * existential * constr -> evar_map * bool -val reconsider_conv_pbs : conv_fun -> evar_map -> evar_map * bool - (** [check_evars env initial_sigma extended_sigma c] fails if some new unresolved evar remains in [c] *) val check_evars : env -> evar_map -> evar_map -> constr -> unit @@ -103,17 +85,9 @@ val define_evar_as_product : evar_map -> existential -> evar_map * types val define_evar_as_lambda : env -> evar_map -> existential -> evar_map * types val define_evar_as_sort : evar_map -> existential -> evar_map * sorts -val is_unification_pattern_evar : env -> evar_map -> existential -> constr list -> - constr -> constr list option - -val is_unification_pattern : env * int -> evar_map -> constr -> constr list -> - constr -> constr list option - val evar_absorb_arguments : env -> evar_map -> existential -> constr list -> evar_map * existential -val solve_pattern_eqn : env -> constr list -> constr -> constr - (** The following functions return the set of evars immediately contained in the object, including defined evars *) @@ -196,10 +170,6 @@ val jv_nf_betaiotaevar : exception Uninstantiated_evar of existential_key val flush_and_check_evars : evar_map -> constr -> constr -(** Replace the vars and rels that are aliases to other vars and rels by - their representative that is most ancient in the context *) -val expand_vars_in_term : env -> constr -> constr - (** {6 debug pretty-printer:} *) val pr_tycon : env -> type_constraint -> Pp.std_ppcmds @@ -226,6 +196,4 @@ val push_rel_context_to_named_context : Environ.env -> types -> val generalize_evar_over_rels : evar_map -> existential -> types * constr list -val check_evar_instance : evar_map -> existential_key -> constr -> conv_fun -> - evar_map diff --git a/pretyping/pretyping.mllib b/pretyping/pretyping.mllib index 506c59ef3..d4892f2f8 100644 --- a/pretyping/pretyping.mllib +++ b/pretyping/pretyping.mllib @@ -10,6 +10,7 @@ Retyping Cbv Pretype_errors Evarutil +Evarsolve Term_dnet Recordops Evarconv diff --git a/pretyping/unification.ml b/pretyping/unification.ml index d5fee4595..d6f1fac88 100644 --- a/pretyping/unification.ml +++ b/pretyping/unification.ml @@ -17,6 +17,7 @@ open Evd open Reduction open Reductionops open Evarutil +open Evarsolve open Pretype_errors open Retyping open Coercion |