diff options
Diffstat (limited to 'pretyping/evarconv.ml')
-rw-r--r-- | pretyping/evarconv.ml | 614 |
1 files changed, 414 insertions, 200 deletions
diff --git a/pretyping/evarconv.ml b/pretyping/evarconv.ml index 6d080016..04f86e70 100644 --- a/pretyping/evarconv.ml +++ b/pretyping/evarconv.ml @@ -1,13 +1,11 @@ (************************************************************************) (* v * The Coq Proof Assistant / The Coq Development Team *) -(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2011 *) +(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2010 *) (* \VV/ **************************************************************) (* // * This file is distributed under the terms of the *) (* * GNU Lesser General Public License Version 2.1 *) (************************************************************************) -(* $Id: evarconv.ml 14641 2011-11-06 11:59:10Z herbelin $ *) - open Pp open Util open Names @@ -24,44 +22,52 @@ open Evd type flex_kind_of_term = | Rigid of constr - | MaybeFlexible of constr + | PseudoRigid of constr (* approximated as rigid but not necessarily so *) + | MaybeFlexible of constr (* approx'ed as reducible but not necessarily so *) | Flexible of existential let flex_kind_of_term c l = match kind_of_term c with - | Const _ -> MaybeFlexible c - | Rel n -> MaybeFlexible c - | Var id -> MaybeFlexible c + | Rel _ | Const _ | Var _ -> MaybeFlexible c | Lambda _ when l<>[] -> MaybeFlexible c | LetIn _ -> MaybeFlexible c | Evar ev -> Flexible ev - | _ -> Rigid c + | Lambda _ | Prod _ | Sort _ | Ind _ | Construct _ | CoFix _ -> Rigid c + | Meta _ | Case _ | Fix _ -> PseudoRigid c + | Cast _ | App _ -> assert false -let eval_flexible_term env c = +let eval_flexible_term ts env c = match kind_of_term c with - | Const c -> constant_opt_value env c + | Const c -> + if is_transparent_constant ts c + then constant_opt_value env c + else None | Rel n -> (try let (_,v,_) = lookup_rel n env in Option.map (lift n) v with Not_found -> None) | Var id -> - (try let (_,v,_) = lookup_named id env in v with Not_found -> None) + (try + if is_transparent_variable ts id then + let (_,v,_) = lookup_named id env in v + else None + with Not_found -> None) | LetIn (_,b,_,c) -> Some (subst1 b c) | Lambda _ -> Some c | _ -> assert false -let evar_apprec env evd stack c = +let evar_apprec ts env evd stack c = let sigma = evd in let rec aux s = - let (t,stack) = whd_betaiota_deltazeta_for_iota_state env sigma s in + let (t,stack) = whd_betaiota_deltazeta_for_iota_state ts env sigma s in match kind_of_term t with | Evar (evk,_ as ev) when Evd.is_defined sigma evk -> aux (Evd.existential_value sigma ev, stack) | _ -> (t, list_of_stack stack) in aux (c, append_stack_list stack empty_stack) -let apprec_nohdbeta env evd c = +let apprec_nohdbeta ts env evd c = match kind_of_term (fst (Reductionops.whd_stack evd c)) with - | (Case _ | Fix _) -> applist (evar_apprec env evd [] c) + | (Case _ | Fix _) -> applist (evar_apprec ts env evd [] c) | _ -> c let position_problem l2r = function @@ -159,16 +165,15 @@ let ise_array2 evd f v1 v2 = if lv1 = Array.length v2 then allrec evd (pred lv1) else (evd,false) -let rec evar_conv_x env evd pbty term1 term2 = - let sigma = evd in - let term1 = whd_head_evar sigma term1 in - let term2 = whd_head_evar sigma term2 in +let rec evar_conv_x ts env evd pbty term1 term2 = + let term1 = whd_head_evar evd term1 in + let term2 = whd_head_evar evd term2 in (* Maybe convertible but since reducing can erase evars which [evar_apprec] could have found, we do it only if the terms are free of evar. Note: incomplete heuristic... *) let ground_test = if is_ground_term evd term1 && is_ground_term evd term2 then - if is_fconv pbty env evd term1 term2 then + if is_trans_fconv pbty ts env evd term1 term2 then Some true else if is_ground_env evd env then Some false else None @@ -176,19 +181,22 @@ let rec evar_conv_x env evd pbty term1 term2 = match ground_test with Some b -> (evd,b) | None -> - let term1 = apprec_nohdbeta env evd term1 in - let term2 = apprec_nohdbeta env evd term2 in + (* Until pattern-unification is used consistently, use nohdbeta to not + destroy beta-redexes that can be used for 1st-order unification *) + let term1 = apprec_nohdbeta ts env evd term1 in + let term2 = apprec_nohdbeta ts env evd term2 in if is_undefined_evar evd term1 then - solve_simple_eqn evar_conv_x env evd + solve_simple_eqn (evar_conv_x ts) env evd (position_problem true pbty,destEvar term1,term2) else if is_undefined_evar evd term2 then - solve_simple_eqn evar_conv_x env evd + solve_simple_eqn (evar_conv_x ts) env evd (position_problem false pbty,destEvar term2,term1) else - evar_eqappr_x env evd pbty + evar_eqappr_x ts env evd pbty (decompose_app term1) (decompose_app term2) -and evar_eqappr_x env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) = +and evar_eqappr_x ?(rhs_is_stuck_proj = false) + ts env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) = (* Evar must be undefined since we have flushed evars *) match (flex_kind_of_term term1 l1, flex_kind_of_term term2 l2) with | Flexible (sp1,al1 as ev1), Flexible (sp2,al2 as ev2) -> @@ -196,23 +204,23 @@ and evar_eqappr_x env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) = if List.length l1 > List.length l2 then let (deb1,rest1) = list_chop (List.length l1-List.length l2) l1 in ise_and i - [(fun i -> solve_simple_eqn evar_conv_x env i + [(fun i -> solve_simple_eqn (evar_conv_x ts) env i (position_problem false pbty,ev2,applist(term1,deb1))); (fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) rest1 l2)] + (fun i -> evar_conv_x ts env i CONV) rest1 l2)] else let (deb2,rest2) = list_chop (List.length l2-List.length l1) l2 in ise_and i - [(fun i -> solve_simple_eqn evar_conv_x env i + [(fun i -> solve_simple_eqn (evar_conv_x ts) env i (position_problem true pbty,ev1,applist(term2,deb2))); (fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) l1 rest2)] + (fun i -> evar_conv_x ts env i CONV) l1 rest2)] and f2 i = if sp1 = sp2 then ise_and i [(fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) l1 l2); - (fun i -> solve_refl evar_conv_x env i sp1 al1 al2, + (fun i -> evar_conv_x ts env i CONV) l1 l2); + (fun i -> solve_refl (evar_conv_x ts) env i sp1 al1 al2, true)] else (i,false) in @@ -220,17 +228,17 @@ and evar_eqappr_x env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) = | Flexible ev1, MaybeFlexible flex2 -> let f1 i = - if - is_unification_pattern_evar env ev1 l1 (applist appr2) & - not (occur_evar (fst ev1) (applist appr2)) - then + match is_unification_pattern_evar env evd ev1 l1 (applist appr2) with + | Some l1' -> (* Miller-Pfenning's patterns unification *) (* Preserve generality (except that CCI has no eta-conversion) *) let t2 = nf_evar evd (applist appr2) in - let t2 = solve_pattern_eqn env l1 t2 in - solve_simple_eqn evar_conv_x env evd + let t2 = solve_pattern_eqn env l1' t2 in + solve_simple_eqn (evar_conv_x ts) env evd (position_problem true pbty,ev1,t2) - else if + | None -> (i,false) + and f2 i = + if List.length l1 <= List.length l2 then (* Try first-order unification *) @@ -240,30 +248,30 @@ and evar_eqappr_x env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) = ise_and i (* First compare extra args for better failure message *) [(fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) l1 rest2); - (fun i -> evar_conv_x env i pbty term1 (applist(term2,deb2)))] + (fun i -> evar_conv_x ts env i CONV) l1 rest2); + (fun i -> evar_conv_x ts env i pbty term1 (applist(term2,deb2)))] else (i,false) - and f4 i = - match eval_flexible_term env flex2 with + and f3 i = + match eval_flexible_term ts env flex2 with | Some v2 -> - evar_eqappr_x env i pbty appr1 (evar_apprec env i l2 v2) + evar_eqappr_x ts env i pbty appr1 (evar_apprec ts env i l2 v2) | None -> (i,false) in - ise_try evd [f1; f4] + ise_try evd [f1; f2; f3] | MaybeFlexible flex1, Flexible ev2 -> let f1 i = - if - is_unification_pattern_evar env ev2 l2 (applist appr1) & - not (occur_evar (fst ev2) (applist appr1)) - then + match is_unification_pattern_evar env evd ev2 l2 (applist appr1) with + | Some l1' -> (* Miller-Pfenning's patterns unification *) (* Preserve generality (except that CCI has no eta-conversion) *) let t1 = nf_evar evd (applist appr1) in let t1 = solve_pattern_eqn env l2 t1 in - solve_simple_eqn evar_conv_x env evd + solve_simple_eqn (evar_conv_x ts) env evd (position_problem false pbty,ev2,t1) - else if + | None -> (i,false) + and f2 i = + if List.length l2 <= List.length l1 then (* Try first-order unification *) @@ -272,25 +280,43 @@ and evar_eqappr_x env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) = ise_and i (* First compare extra args for better failure message *) [(fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) rest1 l2); - (fun i -> evar_conv_x env i pbty (applist(term1,deb1)) term2)] + (fun i -> evar_conv_x ts env i CONV) rest1 l2); + (fun i -> evar_conv_x ts env i pbty (applist(term1,deb1)) term2)] else (i,false) - and f4 i = - match eval_flexible_term env flex1 with + and f3 i = + match eval_flexible_term ts env flex1 with | Some v1 -> - evar_eqappr_x env i pbty (evar_apprec env i l1 v1) appr2 + evar_eqappr_x ts env i pbty (evar_apprec ts env i l1 v1) appr2 | None -> (i,false) in - ise_try evd [f1; f4] + ise_try evd [f1; f2; f3] + + | MaybeFlexible flex1, MaybeFlexible flex2 -> begin + match kind_of_term flex1, kind_of_term flex2 with + | LetIn (na,b1,t1,c'1), LetIn (_,b2,_,c'2) -> + let f1 i = + ise_and i + [(fun i -> evar_conv_x ts env i CONV b1 b2); + (fun i -> + let b = nf_evar i b1 in + let t = nf_evar i t1 in + evar_conv_x ts (push_rel (na,Some b,t) env) i pbty c'1 c'2); + (fun i -> ise_list2 i (fun i -> evar_conv_x ts env i CONV) l1 l2)] + and f2 i = + let appr1 = evar_apprec ts env i l1 (subst1 b1 c'1) + and appr2 = evar_apprec ts env i l2 (subst1 b2 c'2) + in evar_eqappr_x ts env i pbty appr1 appr2 + in + ise_try evd [f1; f2] - | MaybeFlexible flex1, MaybeFlexible flex2 -> + | _, _ -> let f1 i = - if flex1 = flex2 then - ise_list2 i (fun i -> evar_conv_x env i CONV) l1 l2 + if eq_constr flex1 flex2 then + ise_list2 i (fun i -> evar_conv_x ts env i CONV) l1 l2 else (i,false) and f2 i = - (try conv_record env i + (try conv_record ts env i (try check_conv_record appr1 appr2 with Not_found -> check_conv_record appr2 appr1) with Not_found -> (i,false)) @@ -299,186 +325,204 @@ and evar_eqappr_x env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) = if the first argument is a beta-redex (expand a constant only if necessary) or the second argument is potentially usable as a canonical projection *) - if isLambda flex1 or is_open_canonical_projection i appr2 - then - match eval_flexible_term env flex1 with + let rhs_is_stuck_proj = + rhs_is_stuck_proj || is_open_canonical_projection env i appr2 in + if isLambda flex1 || rhs_is_stuck_proj then + match eval_flexible_term ts env flex1 with | Some v1 -> - evar_eqappr_x env i pbty (evar_apprec env i l1 v1) appr2 + evar_eqappr_x ~rhs_is_stuck_proj + ts env i pbty (evar_apprec ts env i l1 v1) appr2 | None -> - match eval_flexible_term env flex2 with + match eval_flexible_term ts env flex2 with | Some v2 -> - evar_eqappr_x env i pbty appr1 (evar_apprec env i l2 v2) + evar_eqappr_x ts env i pbty appr1 (evar_apprec ts env i l2 v2) | None -> (i,false) else - match eval_flexible_term env flex2 with + match eval_flexible_term ts env flex2 with | Some v2 -> - evar_eqappr_x env i pbty appr1 (evar_apprec env i l2 v2) + evar_eqappr_x ts env i pbty appr1 (evar_apprec ts env i l2 v2) | None -> - match eval_flexible_term env flex1 with + match eval_flexible_term ts env flex1 with | Some v1 -> - evar_eqappr_x env i pbty (evar_apprec env i l1 v1) appr2 + evar_eqappr_x ts env i pbty (evar_apprec ts env i l1 v1) appr2 | None -> (i,false) in ise_try evd [f1; f2; f3] - - | Flexible ev1, Rigid _ -> - if - is_unification_pattern_evar env ev1 l1 (applist appr2) & - not (occur_evar (fst ev1) (applist appr2)) - then - (* Miller-Pfenning's patterns unification *) - (* Preserve generality (except that CCI has no eta-conversion) *) + end + + | Rigid c1, Rigid c2 when isLambda c1 & isLambda c2 -> + let (na,c1,c'1) = destLambda c1 in + let (_,c2,c'2) = destLambda c2 in + assert (l1=[] & l2=[]); + ise_and evd + [(fun i -> evar_conv_x ts env i CONV c1 c2); + (fun i -> + let c = nf_evar i c1 in + evar_conv_x ts (push_rel (na,None,c) env) i CONV c'1 c'2)] + + | Flexible ev1, (Rigid _ | PseudoRigid _) -> + (match is_unification_pattern_evar env evd ev1 l1 (applist appr2) with + | Some l1 -> + (* Miller-Pfenning's pattern unification *) + (* Preserve generality thanks to eta-conversion) *) let t2 = nf_evar evd (applist appr2) in let t2 = solve_pattern_eqn env l1 t2 in - solve_simple_eqn evar_conv_x env evd + solve_simple_eqn (evar_conv_x ts) env evd (position_problem true pbty,ev1,t2) - else + | None -> (* Postpone the use of an heuristic *) add_conv_pb (pbty,env,applist appr1,applist appr2) evd, - true - - | Rigid _, Flexible ev2 -> - if - is_unification_pattern_evar env ev2 l2 (applist appr1) & - not (occur_evar (fst ev2) (applist appr1)) - then - (* Miller-Pfenning's patterns unification *) - (* Preserve generality (except that CCI has no eta-conversion) *) + true) + + | (Rigid _ | PseudoRigid _), Flexible ev2 -> + (match is_unification_pattern_evar env evd ev2 l2 (applist appr1) with + | Some l2 -> + (* Miller-Pfenning's pattern unification *) + (* Preserve generality thanks to eta-conversion) *) let t1 = nf_evar evd (applist appr1) in let t1 = solve_pattern_eqn env l2 t1 in - solve_simple_eqn evar_conv_x env evd + solve_simple_eqn (evar_conv_x ts) env evd (position_problem false pbty,ev2,t1) - else + | None -> (* Postpone the use of an heuristic *) add_conv_pb (pbty,env,applist appr1,applist appr2) evd, - true + true) - | MaybeFlexible flex1, Rigid _ -> + | MaybeFlexible flex1, (Rigid _ | PseudoRigid _) -> let f3 i = - (try conv_record env i (check_conv_record appr1 appr2) + (try conv_record ts env i (check_conv_record appr1 appr2) with Not_found -> (i,false)) and f4 i = - match eval_flexible_term env flex1 with + match eval_flexible_term ts env flex1 with | Some v1 -> - evar_eqappr_x env i pbty (evar_apprec env i l1 v1) appr2 + evar_eqappr_x ts env i pbty (evar_apprec ts env i l1 v1) appr2 | None -> (i,false) in ise_try evd [f3; f4] - | Rigid _ , MaybeFlexible flex2 -> + | (Rigid _ | PseudoRigid _), MaybeFlexible flex2 -> let f3 i = - (try conv_record env i (check_conv_record appr2 appr1) + (try conv_record ts env i (check_conv_record appr2 appr1) with Not_found -> (i,false)) and f4 i = - match eval_flexible_term env flex2 with + match eval_flexible_term ts env flex2 with | Some v2 -> - evar_eqappr_x env i pbty appr1 (evar_apprec env i l2 v2) + evar_eqappr_x ts env i pbty appr1 (evar_apprec ts env i l2 v2) | None -> (i,false) in ise_try evd [f3; f4] - | Rigid c1, Rigid c2 -> match kind_of_term c1, kind_of_term c2 with - - | Cast (c1,_,_), _ -> evar_eqappr_x env evd pbty (c1,l1) appr2 - - | _, Cast (c2,_,_) -> evar_eqappr_x env evd pbty appr1 (c2,l2) + (* Eta-expansion *) + | Rigid c1, _ when isLambda c1 -> + assert (l1 = []); + let (na,c1,c'1) = destLambda c1 in + let c = nf_evar evd c1 in + let env' = push_rel (na,None,c) env in + let appr1 = evar_apprec ts env' evd [] c'1 in + let appr2 = (lift 1 term2, List.map (lift 1) l2 @ [mkRel 1]) in + evar_eqappr_x ts env' evd CONV appr1 appr2 + + | _, Rigid c2 when isLambda c2 -> + assert (l2 = []); + let (na,c2,c'2) = destLambda c2 in + let c = nf_evar evd c2 in + let env' = push_rel (na,None,c) env in + let appr1 = (lift 1 term1, List.map (lift 1) l1 @ [mkRel 1]) in + let appr2 = evar_apprec ts env' evd [] c'2 in + evar_eqappr_x ts env' evd CONV appr1 appr2 + + | Rigid c1, Rigid c2 -> begin + match kind_of_term c1, kind_of_term c2 with | Sort s1, Sort s2 when l1=[] & l2=[] -> - (evd,base_sort_cmp pbty s1 s2) - - | Lambda (na,c1,c'1), Lambda (_,c2,c'2) when l1=[] & l2=[] -> - ise_and evd - [(fun i -> evar_conv_x env i CONV c1 c2); - (fun i -> - let c = nf_evar i c1 in - evar_conv_x (push_rel (na,None,c) env) i CONV c'1 c'2)] - - | LetIn (na,b1,t1,c'1), LetIn (_,b2,_,c'2) -> - let f1 i = - ise_and i - [(fun i -> evar_conv_x env i CONV b1 b2); - (fun i -> - let b = nf_evar i b1 in - let t = nf_evar i t1 in - evar_conv_x (push_rel (na,Some b,t) env) i pbty c'1 c'2); - (fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) l1 l2)] - and f2 i = - let appr1 = evar_apprec env i l1 (subst1 b1 c'1) - and appr2 = evar_apprec env i l2 (subst1 b2 c'2) - in evar_eqappr_x env i pbty appr1 appr2 - in - ise_try evd [f1; f2] - - | LetIn (_,b1,_,c'1), _ ->(* On fait commuter les args avec le Let *) - let appr1 = evar_apprec env evd l1 (subst1 b1 c'1) - in evar_eqappr_x env evd pbty appr1 appr2 - - | _, LetIn (_,b2,_,c'2) -> - let appr2 = evar_apprec env evd l2 (subst1 b2 c'2) - in evar_eqappr_x env evd pbty appr1 appr2 + (try + let evd' = + if pbty = CONV + then Evd.set_eq_sort evd s1 s2 + else Evd.set_leq_sort evd s1 s2 + in (evd', true) + with Univ.UniverseInconsistency _ -> (evd, false) + | _ -> (evd, false)) | Prod (n,c1,c'1), Prod (_,c2,c'2) when l1=[] & l2=[] -> ise_and evd - [(fun i -> evar_conv_x env i CONV c1 c2); + [(fun i -> evar_conv_x ts env i CONV c1 c2); (fun i -> let c = nf_evar i c1 in - evar_conv_x (push_rel (n,None,c) env) i pbty c'1 c'2)] + evar_conv_x ts (push_rel (n,None,c) env) i pbty c'1 c'2)] | Ind sp1, Ind sp2 -> if eq_ind sp1 sp2 then - ise_list2 evd (fun i -> evar_conv_x env i CONV) l1 l2 + ise_list2 evd (fun i -> evar_conv_x ts env i CONV) l1 l2 else (evd, false) | Construct sp1, Construct sp2 -> if eq_constructor sp1 sp2 then - ise_list2 evd (fun i -> evar_conv_x env i CONV) l1 l2 + ise_list2 evd (fun i -> evar_conv_x ts env i CONV) l1 l2 else (evd, false) + | CoFix (i1,(_,tys1,bds1 as recdef1)), CoFix (i2,(_,tys2,bds2)) -> + if i1=i2 then + ise_and evd + [(fun i -> ise_array2 i + (fun i -> evar_conv_x ts env i CONV) tys1 tys2); + (fun i -> ise_array2 i + (fun i -> evar_conv_x ts (push_rec_types recdef1 env) i CONV) + bds1 bds2); + (fun i -> ise_list2 i + (fun i -> evar_conv_x ts env i CONV) l1 l2)] + else (evd,false) + + | (Ind _ | Construct _ | Sort _ | Prod _ | CoFix _), _ -> (evd,false) + | _, (Ind _ | Construct _ | Sort _ | Prod _ | CoFix _) -> (evd,false) + + | (App _ | Meta _ | Cast _ | Case _ | Fix _), _ -> assert false + | (LetIn _ | Rel _ | Var _ | Const _ | Evar _), _ -> assert false + | (Lambda _), _ -> assert false + + end + + | PseudoRigid c1, PseudoRigid c2 -> begin + match kind_of_term c1, kind_of_term c2 with + | Case (_,p1,c1,cl1), Case (_,p2,c2,cl2) -> ise_and evd - [(fun i -> evar_conv_x env i CONV p1 p2); - (fun i -> evar_conv_x env i CONV c1 c2); + [(fun i -> evar_conv_x ts env i CONV p1 p2); + (fun i -> evar_conv_x ts env i CONV c1 c2); (fun i -> ise_array2 i - (fun i -> evar_conv_x env i CONV) cl1 cl2); - (fun i -> ise_list2 i (fun i -> evar_conv_x env i CONV) l1 l2)] + (fun i -> evar_conv_x ts env i CONV) cl1 cl2); + (fun i -> ise_list2 i (fun i -> evar_conv_x ts env i CONV) l1 l2)] | Fix (li1,(_,tys1,bds1 as recdef1)), Fix (li2,(_,tys2,bds2)) -> if li1=li2 then ise_and evd [(fun i -> ise_array2 i - (fun i -> evar_conv_x env i CONV) tys1 tys2); + (fun i -> evar_conv_x ts env i CONV) tys1 tys2); (fun i -> ise_array2 i - (fun i -> evar_conv_x (push_rec_types recdef1 env) i CONV) + (fun i -> evar_conv_x ts (push_rec_types recdef1 env) i CONV) bds1 bds2); (fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) l1 l2)] + (fun i -> evar_conv_x ts env i CONV) l1 l2)] else (evd,false) - | CoFix (i1,(_,tys1,bds1 as recdef1)), CoFix (i2,(_,tys2,bds2)) -> - if i1=i2 then - ise_and evd - [(fun i -> ise_array2 i - (fun i -> evar_conv_x env i CONV) tys1 tys2); - (fun i -> ise_array2 i - (fun i -> evar_conv_x (push_rec_types recdef1 env) i CONV) - bds1 bds2); - (fun i -> ise_list2 i - (fun i -> evar_conv_x env i CONV) l1 l2)] - else (evd,false) - | (Meta _ | Lambda _), _ -> (evd,false) - | _, (Meta _ | Lambda _) -> (evd,false) + | (Meta _ | Case _ | Fix _ | CoFix _), + (Meta _ | Case _ | Fix _ | CoFix _) -> (evd,false) + + | (App _ | Ind _ | Construct _ | Sort _ | Prod _), _ -> assert false + | _, (App _ | Ind _ | Construct _ | Sort _ | Prod _) -> assert false - | (Ind _ | Construct _ | Sort _ | Prod _), _ -> (evd,false) - | _, (Ind _ | Construct _ | Sort _ | Prod _) -> (evd,false) + | (LetIn _ | Cast _), _ -> assert false + | _, (LetIn _ | Cast _) -> assert false - | (App _ | Case _ | Fix _ | CoFix _), - (App _ | Case _ | Fix _ | CoFix _) -> (evd,false) + | (Lambda _ | Rel _ | Var _ | Const _ | Evar _), _ -> assert false + | _, (Lambda _ | Rel _ | Var _ | Const _ | Evar _) -> assert false + end - | (Rel _ | Var _ | Const _ | Evar _), _ -> assert false - | _, (Rel _ | Var _ | Const _ | Evar _) -> assert false + | PseudoRigid _, Rigid _ -> (evd,false) -and conv_record env evd (c,bs,(params,params1),(us,us2),(ts,ts1),c1,(n,t2)) = + | Rigid _, PseudoRigid _ -> (evd,false) + +and conv_record trs env evd (c,bs,(params,params1),(us,us2),(ts,ts1),c1,(n,t2)) = let (evd',ks,_) = List.fold_left (fun (i,ks,m) b -> @@ -491,89 +535,259 @@ and conv_record env evd (c,bs,(params,params1),(us,us2),(ts,ts1),c1,(n,t2)) = ise_and evd' [(fun i -> ise_list2 i - (fun i x1 x -> evar_conv_x env i CONV x1 (substl ks x)) + (fun i x1 x -> evar_conv_x trs env i CONV x1 (substl ks x)) params1 params); (fun i -> ise_list2 i - (fun i u1 u -> evar_conv_x env i CONV u1 (substl ks u)) + (fun i u1 u -> evar_conv_x trs env i CONV u1 (substl ks u)) us2 us); - (fun i -> evar_conv_x env i CONV c1 (applist (c,(List.rev ks)))); - (fun i -> ise_list2 i (fun i -> evar_conv_x env i CONV) ts ts1)] + (fun i -> evar_conv_x trs env i CONV c1 (applist (c,(List.rev ks)))); + (fun i -> ise_list2 i (fun i -> evar_conv_x trs env i CONV) ts ts1)] + +(* getting rid of the optional argument rhs_is_stuck_proj *) +let evar_eqappr_x ts env evd pbty appr1 appr2 = + evar_eqappr_x ts env evd pbty appr1 appr2 (* We assume here |l1| <= |l2| *) -let first_order_unification env evd (ev1,l1) (term2,l2) = +let first_order_unification ts env evd (ev1,l1) (term2,l2) = let (deb2,rest2) = list_chop (List.length l2-List.length l1) l2 in ise_and evd (* First compare extra args for better failure message *) - [(fun i -> ise_list2 i (fun i -> evar_conv_x env i CONV) rest2 l1); + [(fun i -> ise_list2 i (fun i -> evar_conv_x ts env i CONV) rest2 l1); (fun i -> (* Then instantiate evar unless already done by unifying args *) let t2 = applist(term2,deb2) in if is_defined_evar i ev1 then - evar_conv_x env i CONV t2 (mkEvar ev1) + evar_conv_x ts env i CONV t2 (mkEvar ev1) else - solve_simple_eqn ~choose:true evar_conv_x env i (None,ev1,t2))] + solve_simple_eqn ~choose:true (evar_conv_x ts) env i (None,ev1,t2))] let choose_less_dependent_instance evk evd term args = - let evi = Evd.find evd evk in + let evi = Evd.find_undefined evd evk in let subst = make_pure_subst evi args in - let subst' = List.filter (fun (id,c) -> c = term) subst in + let subst' = List.filter (fun (id,c) -> eq_constr c term) subst in if subst' = [] then error "Too complex unification problem." else Evd.define evk (mkVar (fst (List.hd subst'))) evd -let apply_conversion_problem_heuristic env evd pbty t1 t2 = - let t1 = apprec_nohdbeta env evd (whd_head_evar evd t1) in - let t2 = apprec_nohdbeta env evd (whd_head_evar evd t2) in +let apply_on_subterm f c t = + let rec applyrec (k,c as kc) t = + (* By using eq_constr, we make an approximation, for instance, we *) + (* could also be interested in finding a term u convertible to t *) + (* such that c occurs in u *) + if eq_constr c t then f k + else + map_constr_with_binders_left_to_right (fun d (k,c) -> (k+1,lift 1 c)) + applyrec kc t + in + applyrec (0,c) t + +let filter_possible_projections c args = + let fv1 = free_rels c in + let fv2 = collect_vars c in + List.map (fun a -> + a == c || + (* Here we make an approximation, for instance, we could also be *) + (* interested in finding a term u convertible to c such that a occurs *) + (* in u *) + isRel a && Intset.mem (destRel a) fv1 || + isVar a && Idset.mem (destVar a) fv2) + args + +let initial_evar_data evi = + let ids = List.map pi1 (evar_context evi) in + (evar_filter evi, List.map mkVar ids) + +let solve_evars = ref (fun _ -> failwith "solve_evars not installed") +let set_solve_evars f = solve_evars := f + +(* We solve the problem env_rhs |- ?e[u1..un] = rhs knowing + * x1:T1 .. xn:Tn |- ev : ty + * by looking for a maximal well-typed abtraction over u1..un in rhs + * + * We first build C[e11..e1p1,..,en1..enpn] obtained from rhs by replacing + * all occurrences of u1..un by evars eij of type Ti' where itself Ti' has + * been obtained from the type of ui by also replacing all occurrences of + * u1..ui-1 by evars. + * + * Then, we use typing to infer the relations between the different + * occurrences. If some occurrence is still unconstrained after typing, + * we instantiate successively the unresolved occurrences of un by xn, + * of un-1 by xn-1, etc [the idea comes from Chung-Kil Hur, that he + * used for his Heq plugin; extensions to several arguments based on a + * proposition from Dan Grayson] + *) + +let second_order_matching ts env_rhs evd (evk,args) argoccs rhs = + try + let args = Array.to_list args in + let evi = Evd.find_undefined evd evk in + let env_evar = evar_env evi in + let sign = named_context_val env_evar in + let ctxt = evar_filtered_context evi in + let filter = evar_filter evi in + let instance = List.map mkVar (List.map pi1 ctxt) in + + let rec make_subst = function + | (id,_,t)::ctxt, c::l, occs::occsl when isVarId id c -> + if occs<>None then + error "Cannot force abstraction on identity instance." + else + make_subst (ctxt,l,occsl) + | (id,_,t)::ctxt, c::l, occs::occsl -> + let evs = ref [] in + let filter = List.map2 (&&) filter (filter_possible_projections c args) in + let ty = Retyping.get_type_of env_rhs evd c in + (id,t,c,ty,evs,filter,occs) :: make_subst (ctxt,l,occsl) + | [], [], [] -> [] + | _ -> anomaly "Signature, instance and occurrences list do not match" in + + let rec set_holes evdref rhs = function + | (id,_,c,cty,evsref,filter,occs)::subst -> + let set_var k = + match occs with + | Some (false,[]) -> mkVar id + | Some _ -> error "Selection of specific occurrences not supported" + | None -> + let evty = set_holes evdref cty subst in + let instance = snd (list_filter2 (fun b c -> b) (filter,instance)) in + let evd,ev = new_evar_instance sign !evdref evty ~filter instance in + evdref := evd; + evsref := (fst (destEvar ev),evty)::!evsref; + ev in + set_holes evdref (apply_on_subterm set_var c rhs) subst + | [] -> rhs in + + let subst = make_subst (ctxt,args,argoccs) in + + let evdref = ref evd in + let rhs = set_holes evdref rhs subst in + let evd = !evdref in + + (* We instantiate the evars of which the value is forced by typing *) + let evd,rhs = + try !solve_evars env_evar evd rhs + with e when Pretype_errors.precatchable_exception e -> + (* Could not revert all subterms *) + raise Exit in + + let rec abstract_free_holes evd = function + | (id,idty,c,_,evsref,_,_)::l -> + let rec force_instantiation evd = function + | (evk,evty)::evs -> + let evd = + if is_undefined evd evk then + (* We force abstraction over this unconstrained occurrence *) + (* and we use typing to propagate this instantiation *) + (* This is an arbitrary choice *) + let evd = Evd.define evk (mkVar id) evd in + let evd,b = evar_conv_x ts env_evar evd CUMUL idty evty in + if not b then error "Cannot find an instance"; + let evd,b = reconsider_conv_pbs (evar_conv_x ts) evd in + if not b then error "Cannot find an instance"; + evd + else + evd + in + force_instantiation evd evs + | [] -> + abstract_free_holes evd l + in + force_instantiation evd !evsref + | [] -> + Evd.define evk rhs evd in + + abstract_free_holes evd subst, true + with Exit -> evd, false + +let second_order_matching_with_args ts env evd ev l t = +(* + let evd,ev = evar_absorb_arguments env evd ev l in + let argoccs = array_map_to_list (fun _ -> None) (snd ev) in + second_order_matching ts env evd ev argoccs t +*) + (evd,false) + +let apply_conversion_problem_heuristic ts env evd pbty t1 t2 = + let t1 = apprec_nohdbeta ts env evd (whd_head_evar evd t1) in + let t2 = apprec_nohdbeta ts env evd (whd_head_evar evd t2) in let (term1,l1 as appr1) = decompose_app t1 in let (term2,l2 as appr2) = decompose_app t2 in match kind_of_term term1, kind_of_term term2 with | Evar (evk1,args1), (Rel _|Var _) when l1 = [] & l2 = [] - & array_for_all (fun a -> a = term2 or isEvar a) args1 -> + & array_for_all (fun a -> eq_constr a term2 or isEvar a) args1 -> (* The typical kind of constraint coming from pattern-matching return type inference *) choose_less_dependent_instance evk1 evd term2 args1, true | (Rel _|Var _), Evar (evk2,args2) when l1 = [] & l2 = [] - & array_for_all (fun a -> a = term1 or isEvar a) args2 -> + & array_for_all (fun a -> eq_constr a term1 or isEvar a) args2 -> (* The typical kind of constraint coming from pattern-matching return type inference *) choose_less_dependent_instance evk2 evd term1 args2, true | Evar ev1,_ when List.length l1 <= List.length l2 -> (* On "?n t1 .. tn = u u1 .. u(n+p)", try first-order unification *) - first_order_unification env evd (ev1,l1) appr2 + (* and otherwise second-order matching *) + ise_try evd + [(fun evd -> first_order_unification ts env evd (ev1,l1) appr2); + (fun evd -> + second_order_matching_with_args ts env evd ev1 l1 (applist appr2))] | _,Evar ev2 when List.length l2 <= List.length l1 -> (* On "u u1 .. u(n+p) = ?n t1 .. tn", try first-order unification *) - first_order_unification env evd (ev2,l2) appr1 + (* and otherwise second-order matching *) + ise_try evd + [(fun evd -> first_order_unification ts env evd (ev2,l2) appr1); + (fun evd -> + second_order_matching_with_args ts env evd ev2 l2 (applist appr1))] + | Evar ev1,_ -> + (* Try second-order pattern-matching *) + second_order_matching_with_args ts env evd ev1 l1 (applist appr2) + | _,Evar ev2 -> + (* Try second-order pattern-matching *) + second_order_matching_with_args ts env evd ev2 l2 (applist appr1) | _ -> (* Some head evar have been instantiated, or unknown kind of problem *) - evar_conv_x env evd pbty t1 t2 + evar_conv_x ts env evd pbty t1 t2 + +let check_problems_are_solved env evd = + match snd (extract_all_conv_pbs evd) with + | (pbty,env,t1,t2)::_ -> Pretype_errors.error_cannot_unify env evd (t1, t2) + | _ -> () -let consider_remaining_unif_problems env evd = +let consider_remaining_unif_problems ?(ts=full_transparent_state) env evd = let (evd,pbs) = extract_all_conv_pbs evd in - List.fold_left + let heuristic_solved_evd = List.fold_left (fun evd (pbty,env,t1,t2) -> - let evd', b = apply_conversion_problem_heuristic env evd pbty t1 t2 in + let evd', b = apply_conversion_problem_heuristic ts env evd pbty t1 t2 in if b then evd' else Pretype_errors.error_cannot_unify env evd (t1, t2)) - evd pbs + evd pbs in + check_problems_are_solved env heuristic_solved_evd; + Evd.fold_undefined (fun ev ev_info evd' -> match ev_info.evar_source with + |_,ImpossibleCase -> + Evd.define ev (j_type (coq_unit_judge ())) evd' + |_ -> + match ev_info.evar_candidates with + | Some (a::l) -> Evd.define ev a evd' + | Some [] -> error "Unsolvable existential variables" + | None -> evd') heuristic_solved_evd heuristic_solved_evd (* Main entry points *) -let the_conv_x env t1 t2 evd = - match evar_conv_x env evd CONV t1 t2 with +let the_conv_x ?(ts=full_transparent_state) env t1 t2 evd = + match evar_conv_x ts env evd CONV t1 t2 with (evd',true) -> evd' | _ -> raise Reduction.NotConvertible -let the_conv_x_leq env t1 t2 evd = - match evar_conv_x env evd CUMUL t1 t2 with +let the_conv_x_leq ?(ts=full_transparent_state) env t1 t2 evd = + match evar_conv_x ts env evd CUMUL t1 t2 with (evd', true) -> evd' | _ -> raise Reduction.NotConvertible -let e_conv env evd t1 t2 = - match evar_conv_x env !evd CONV t1 t2 with +let e_conv ?(ts=full_transparent_state) env evd t1 t2 = + match evar_conv_x ts env !evd CONV t1 t2 with (evd',true) -> evd := evd'; true | _ -> false -let e_cumul env evd t1 t2 = - match evar_conv_x env !evd CUMUL t1 t2 with +let e_cumul ?(ts=full_transparent_state) env evd t1 t2 = + match evar_conv_x ts env !evd CUMUL t1 t2 with (evd',true) -> evd := evd'; true | _ -> false |