aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar herbelin <herbelin@85f007b7-540e-0410-9357-904b9bb8a0f7>2011-10-10 22:00:26 +0000
committerGravatar herbelin <herbelin@85f007b7-540e-0410-9357-904b9bb8a0f7>2011-10-10 22:00:26 +0000
commitbfc6434ee257e9e9830f54546ffbd6f4b66e96d4 (patch)
tree0521253f4b55c3d5cc6a75b8b6099cd2c02dc9fa
parent698c79dc28a13baf864c863800a2b48690207e34 (diff)
More robust and uniform treatment of aliases in evarutil.ml
- Compute chain of aliases once for all so as to simplify code. - In is_unification_pattern, expand all vars/rels of the unification problem until they are no longer vars/rels so that the list of vars/rels used in the rhs is correct, and, the list of arguments of the evars eventually become irreducible vars/rels (in particular, this solves bug #2615). - Some points remain unclear, e.g. whether solve_evar_evar should reason with all let-in expanded or with let-in expanded only up to the last expansion which is still a var or rel. git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/coq/trunk@14539 85f007b7-540e-0410-9357-904b9bb8a0f7
-rw-r--r--pretyping/evarconv.ml50
-rw-r--r--pretyping/evarutil.ml225
-rw-r--r--pretyping/evarutil.mli6
-rw-r--r--pretyping/unification.ml33
4 files changed, 178 insertions, 136 deletions
diff --git a/pretyping/evarconv.ml b/pretyping/evarconv.ml
index 121de57bf..ac6a574fc 100644
--- a/pretyping/evarconv.ml
+++ b/pretyping/evarconv.ml
@@ -227,17 +227,17 @@ and evar_eqappr_x ts 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 env term1 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
+ 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 *)
@@ -250,27 +250,27 @@ and evar_eqappr_x ts env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) =
(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 f2 i =
+ and f3 i =
match eval_flexible_term ts env flex2 with
| Some v2 ->
evar_eqappr_x ts env i pbty appr1 (evar_apprec ts env i l2 v2)
| None -> (i,false)
in
- ise_try evd [f1; f2]
+ 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 env term2 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 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 *)
@@ -282,13 +282,13 @@ and evar_eqappr_x ts env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) =
(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 f2 i =
+ and f3 i =
match eval_flexible_term ts env flex1 with
| Some v1 ->
evar_eqappr_x ts env i pbty (evar_apprec ts env i l1 v1) appr2
| None -> (i,false)
in
- ise_try evd [f1; f2]
+ ise_try evd [f1; f2; f3]
| MaybeFlexible flex1, MaybeFlexible flex2 -> begin
match kind_of_term flex1, kind_of_term flex2 with
@@ -358,36 +358,32 @@ and evar_eqappr_x ts env evd pbty (term1,l1 as appr1) (term2,l2 as appr2) =
evar_conv_x ts (push_rel (na,None,c) env) i CONV c'1 c'2)]
| Flexible ev1, (Rigid _ | PseudoRigid _) ->
- if
- is_unification_pattern_evar env ev1 l1 (applist appr2) &
- not (occur_evar (fst ev1) (applist appr2))
- then
+ (match is_unification_pattern env term1 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 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
+ true)
| (Rigid _ | PseudoRigid _), Flexible ev2 ->
- if
- is_unification_pattern_evar env ev2 l2 (applist appr1) &
- not (occur_evar (fst ev2) (applist appr1))
- then
+ (match is_unification_pattern env term2 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 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 _ | PseudoRigid _) ->
let f3 i =
diff --git a/pretyping/evarutil.ml b/pretyping/evarutil.ml
index 860a2c830..e9f8db47b 100644
--- a/pretyping/evarutil.ml
+++ b/pretyping/evarutil.ml
@@ -166,74 +166,110 @@ let new_evar_instance sign evd typ ?(src=(dummy_loc,InternalHole)) ?filter insta
dependencies in variables are canonically associated to the most ancient
variable in its family of aliased variables *)
-let compute_aliases sign =
+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 id'' = try Idmap.find id' aliases with Not_found -> id' in
- Idmap.add id id'' aliases
- | _ -> aliases)
- | None -> aliases) sign Idmap.empty
-
-let alias_of_var id aliases = try Idmap.find id aliases with Not_found -> id
+ let aliases_of_id = match kind_of_term t with
+ | Var id' -> (try Idmap.find id' aliases with Not_found -> [])
+ | _ -> [] in
+ Idmap.add id (aliases_of_id@[t]) aliases
+ | None -> aliases)
+ sign Idmap.empty
+
+let compute_rel_aliases var_aliases rels =
+ snd (List.fold_right (fun (_,b,t) (n,aliases) ->
+ (n-1,
+ match b with
+ | Some t ->
+ let aliases_of_n = match kind_of_term t with
+ | Var id' -> (try Idmap.find id' var_aliases with Not_found -> [])
+ | Rel p -> (try Intmap.find (p+n) aliases with Not_found -> [])
+ | _ -> [] in
+ Intmap.add n (aliases_of_n@[lift n t]) aliases
+ | None -> aliases))
+ rels (List.length rels,Intmap.empty))
let make_alias_map env =
- let var_aliases = compute_aliases (named_context env) in
- let rels = rel_context env in
- let rel_aliases =
- snd (List.fold_right (fun (_,b,t) (n,aliases) ->
- (n-1,
- match b with
- | Some t when isRel t or isVar t -> Intmap.add n (lift n t) aliases
- | _ -> aliases)) rels (List.length rels,Intmap.empty)) in
+ (* 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 expand_var_once aliases x = match kind_of_term x with
- | Rel n -> Intmap.find n (snd aliases)
- | Var id -> mkVar (Idmap.find id (fst aliases))
- | _ -> raise Not_found
+let get_alias_chain_of aliases x = match kind_of_term x with
+ | Rel n -> (try Intmap.find n (snd aliases) with Not_found -> [])
+ | Var id -> (try Idmap.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 || isVar a -> Some a (* Oldest alias is var/rel *)
+ | [a] -> None (* Only one alias but not to var/rel *)
+ | _::a::_ -> Some a (* Last alias not to var/rel, but penultimate must be ok*)
-let rec expand_var_at_least_once aliases x =
- let t = expand_var_once aliases x in
- try expand_var_at_least_once aliases t
- with Not_found -> t
+let normalize_alias aliases x =
+ match normalize_alias_opt aliases x with
+ | Some x -> x
+ | None -> x
-let expand_var aliases x =
- try expand_var_at_least_once aliases x with Not_found -> x
+let expand_alias aliases x =
+ match get_alias_chain_of aliases x with
+ | [] -> x
+ | a::_ -> x
-let expand_var_opt aliases x =
- try Some (expand_var_at_least_once aliases x) with Not_found -> None
+let normalize_alias_var var_aliases id =
+ destVar (normalize_alias (var_aliases,Intmap.empty) (mkVar id))
let extend_alias (_,b,_) (var_aliases,rel_aliases) =
let rel_aliases =
- Intmap.fold (fun n c -> Intmap.add (n+1) (lift 1 c))
+ Intmap.fold (fun n l -> Intmap.add (n+1) (List.map (lift 1) l))
rel_aliases Intmap.empty in
let rel_aliases =
match b with
- | Some t when isRel t or isVar t -> Intmap.add 1 (lift 1 t) rel_aliases
- | _ -> rel_aliases in
+ | Some t ->
+ let aliases = match kind_of_term t with
+ | Var id' -> (try Idmap.find id' var_aliases with Not_found -> [])
+ | Rel p -> (try Intmap.find (p+1) rel_aliases with Not_found -> [])
+ | _ -> [] in
+ Intmap.add 1 (aliases@[lift 1 t]) rel_aliases
+ | None -> rel_aliases in
(var_aliases, rel_aliases)
+let rec expansions_of_var aliases x =
+ match get_alias_chain_of aliases x with
+ | a::l when not (isVar a or isRel a) -> x :: List.rev l
+ | l -> x :: List.rev l
+
let rec expand_vars_in_term_using aliases t = match kind_of_term t with
| Rel _ | Var _ ->
- expand_var aliases t
+ 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 rec expansions_of_var aliases x =
- try
- let t = expand_var_once aliases x in
- t :: expansions_of_var aliases t
- with Not_found ->
- [x]
-
-let expand_full_opt aliases y =
- try expand_var_opt aliases y with Not_found -> None
+let free_vars_and_rels_up_alias_expansion aliases c =
+ let acc1 = ref Intset.empty and acc2 = ref Idset.empty in
+ let rec frec (aliases,depth) c = match kind_of_term c with
+ | Rel _ | Var _ ->
+ let c = expand_alias aliases c in
+ (match kind_of_term c with
+ | Var id -> acc2 := Idset.add id !acc2
+ | Rel n -> if n >= depth+1 then acc1 := Intset.add (n-depth) !acc1
+ | _ ->
+ (* not optimal: would need sharing if alias occurs more than once *)
+ frec (aliases,depth) c)
+ | Const _ | Ind _ | Construct _ ->
+ acc2 := List.fold_right Idset.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)
(* Knowing that [Gamma |- ev : T] and that [ev] is applied to [args],
* [make_projectable_subst ev args] builds the substitution [Gamma:=args].
@@ -247,7 +283,7 @@ let expand_full_opt aliases y =
let make_projectable_subst aliases sigma evi args =
let sign = evar_filtered_context evi in
- let evar_aliases = compute_aliases sign in
+ let evar_aliases = compute_var_aliases sign in
let (_,full_subst,cstr_subst) =
List.fold_right
(fun (id,b,c) (args,all,cstrs) ->
@@ -261,21 +297,21 @@ let make_projectable_subst aliases sigma evi args =
let l = try Constrmap.find cstr cstrs with Not_found -> [] in
Constrmap.add cstr ((args,id)::l) cstrs
| _ -> cstrs in
- (rest,Idmap.add id [a,expand_full_opt aliases a,id] all,cstrs)
+ (rest,Idmap.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 = alias_of_var id' evar_aliases in
+ let idc = normalize_alias_var evar_aliases id' in
let sub = try Idmap.find idc all with Not_found -> [] in
if List.exists (fun (c,_,_) -> eq_constr a c) sub then
(rest,all,cstrs)
else
(rest,
- Idmap.add idc ((a,expand_full_opt aliases a,id)::sub) all,
+ Idmap.add idc ((a,normalize_alias_opt aliases a,id)::sub) all,
cstrs)
| _ ->
- (rest,Idmap.add id [a,expand_full_opt aliases a,id] all,cstrs))
+ (rest,Idmap.add id [a,normalize_alias_opt aliases a,id] all,cstrs))
| _ -> anomaly "Instance does not match its signature")
sign (array_rev_to_list args,Idmap.empty,Constrmap.empty) in
(full_subst,cstr_subst)
@@ -666,12 +702,12 @@ let rec assoc_up_to_alias sigma aliases y yc = function
if l <> [] then assoc_up_to_alias sigma aliases y yc l
else
(* Last chance, we reason up to alias conversion *)
- match (if c == c' then cc else expand_full_opt aliases c') with
+ 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 = expand_var aliases y in
+ 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
@@ -1249,18 +1285,14 @@ let whd_head_evar_stack sigma c =
let whd_head_evar sigma c = applist (whd_head_evar_stack sigma c)
-(* Check if an applied evar "?X[args] l" is a Miller's pattern; note
- that we don't care whether args itself contains Rel's or even Rel's
- distinct from the ones in l *)
-
-let rec expand_and_check_vars env = function
+let rec expand_and_check_vars aliases = function
| [] -> []
| a::l ->
if isRel a or isVar a then
- let l = expand_and_check_vars env l in
- match expand_var_opt env a with
- | None -> a :: l
- | Some a' when isRel a' or isVar a' -> list_add_set a' l
+ let l = expand_and_check_vars aliases l in
+ match get_alias_chain_of aliases a with
+ | [] -> a :: l
+ | a'::_ when isRel a' or isVar a' -> a' :: l
| _ -> raise Exit
else
raise Exit
@@ -1280,49 +1312,54 @@ let rec constr_list_distinct l =
| [] -> true
in loop l
-let is_unification_pattern_evar env (_,args) l t =
- List.for_all (fun x -> isRel x || isVar x) l (* common failure case *)
- &&
+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 -> Idset.mem id fv_ids
+ | Rel n -> Intset.mem n fv_rels
+ | _ -> assert false) l
+
+(* Check if an applied evar "?X[args] l" is a Miller's pattern *)
+
+let find_unification_pattern_args env args l t =
+ if List.for_all (fun x -> isRel x || isVar x) l (* common failure case *) then
let aliases = make_alias_map env in
let l' = Array.to_list args @ l in
- let l'' = try Some (expand_and_check_vars aliases l') with Exit -> None in
- match l'' with
- | Some l ->
- let deps =
- 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 t = expand_vars_in_term_using aliases t in
- let fv_rels = free_rels t in
- let fv_ids = global_vars env t in
- List.filter (fun c ->
- match kind_of_term c with
- | Var id -> List.mem id fv_ids
- | Rel n -> Intset.mem n fv_rels
- | _ -> assert false) l in
- constr_list_distinct deps
- | None -> false
-
-let is_unification_pattern (env,nb) f l t =
+ match (try Some (expand_and_check_vars aliases l') with Exit -> None) with
+ | Some l when constr_list_distinct (get_actual_deps aliases l t) ->
+ Some (list_skipn (Array.length args) l)
+ | _ ->
+ None
+ else
+ None
+
+let is_unification_pattern env f l t =
match kind_of_term f with
- | Meta _ ->
- let dummy_ev = (f,[||]) in
- is_unification_pattern_evar env dummy_ev (Array.to_list l) t
- | Evar ev ->
- is_unification_pattern_evar env ev (Array.to_list l) t
+ | Meta m ->
+ (match find_unification_pattern_args env [||] l t with
+ | Some _ as x when not (dependent f t) -> x
+ | _ -> None)
+ | Evar (evk,args) ->
+ (match find_unification_pattern_args env args l t with
+ | Some _ as x when not (occur_evar evk t) -> x
+ | _ -> None)
| _ ->
- false
+ None
-(* From a unification problem "?X l1 = term1 l2" such that l1 is made
- of distinct rel's, build "\x1...xn.(term1 l2)" (patterns unification) *)
-(* NB: does not work when (term1 l2) contains metas because metas
+(* 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 l1 c =
- let l1 = List.map (expand_var (make_alias_map env)) l1 in
+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
@@ -1330,7 +1367,7 @@ let solve_pattern_eqn env l1 c =
| Rel n -> let (na,_,t) = lookup_rel n env in mkLambda (na,lift n t,c')
| Var id -> let (id,_,t) = lookup_named id env in mkNamedLambda id t c'
| _ -> assert false)
- l1 c in
+ l c in
(* Warning: we may miss some opportunity to eta-reduce more since c'
is not in normal form *)
whd_eta c'
diff --git a/pretyping/evarutil.mli b/pretyping/evarutil.mli
index d5f70b08d..365276853 100644
--- a/pretyping/evarutil.mli
+++ b/pretyping/evarutil.mli
@@ -96,10 +96,8 @@ 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 -> existential -> constr list ->
- constr -> bool
-val is_unification_pattern : env * int -> constr -> constr array ->
- constr -> bool
+val is_unification_pattern : env -> constr -> constr list ->
+ constr -> constr list option
val evar_absorb_arguments : env -> evar_map -> existential -> constr list ->
evar_map * existential
diff --git a/pretyping/unification.ml b/pretyping/unification.ml
index 4a873f1f9..f1fdde390 100644
--- a/pretyping/unification.ml
+++ b/pretyping/unification.ml
@@ -112,18 +112,30 @@ let pose_all_metas_as_evars env evd t =
let solve_pattern_eqn_array (env,nb) f l c (sigma,metasubst,evarsubst) =
match kind_of_term f with
| Meta k ->
- let c = solve_pattern_eqn env (Array.to_list l) c in
+ let sigma,c = pose_all_metas_as_evars env sigma c in
+ let c = solve_pattern_eqn env l c in
let pb = (Conv,TypeNotProcessed) in
if noccur_between 1 nb c then
sigma,(k,lift (-nb) c,pb)::metasubst,evarsubst
- else error_cannot_unify_local env sigma (mkApp (f, l),c,c)
+ else error_cannot_unify_local env sigma (applist (f, l),c,c)
| Evar ev ->
let sigma,c = pose_all_metas_as_evars env sigma c in
- sigma,metasubst,(env,ev,solve_pattern_eqn env (Array.to_list l) c)::evarsubst
+ sigma,metasubst,(env,ev,solve_pattern_eqn env l c)::evarsubst
| _ -> assert false
let push d (env,n) = (push_rel_assum d env,n+1)
+(* Hack to propagate data from an ocaml when clause to the actual branch code *)
+(* of a pattern-matching clause *)
+
+let whenmem = ref []
+
+let store mem = function
+ | Some l -> whenmem := l; true
+ | None -> false
+
+let restore mem = !mem
+
(*******************************)
(* Unification à l'ordre 0 de m et n: [unify_0 env sigma cv_pb m n]
@@ -415,18 +427,16 @@ let unify_0_with_initial_metas (sigma,ms,es as subst) conv_at_top env cv_pb flag
reduce curenvnb pb b substn cM cN)
| App (f1,l1), _ when
- (isMeta f1 && use_metas_pattern_unification flags (snd curenvnb) l1
+ (isMeta f1 && use_metas_pattern_unification flags nb l1
|| use_evars_pattern_unification flags && isAllowedEvar flags f1) &
- is_unification_pattern curenvnb f1 l1 cN &
- not (dependent f1 cN) ->
- solve_pattern_eqn_array curenvnb f1 l1 cN substn
+ store whenmem (is_unification_pattern curenv f1 (Array.to_list l1) cN) ->
+ solve_pattern_eqn_array curenvnb f1 (restore whenmem) cN substn
| _, App (f2,l2) when
- (isMeta f2 && use_metas_pattern_unification flags (snd curenvnb) l2
+ (isMeta f2 && use_metas_pattern_unification flags nb l2
|| use_evars_pattern_unification flags && isAllowedEvar flags f2) &
- is_unification_pattern curenvnb f2 l2 cM &
- not (dependent f2 cM) ->
- solve_pattern_eqn_array curenvnb f2 l2 cM substn
+ store whenmem (is_unification_pattern curenv f2 (Array.to_list l2) cM) ->
+ solve_pattern_eqn_array curenvnb f2 (restore whenmem) cM substn
| App (f1,l1), App (f2,l2) ->
let len1 = Array.length l1
@@ -461,6 +471,7 @@ let unify_0_with_initial_metas (sigma,ms,es as subst) conv_at_top env cv_pb flag
match kind_of_term cN with App (f,l) -> (f,l) | _ -> (cN,[||]) in
expand curenvnb pb b substn cM f1 l1 cN f2 l2
+
and reduce curenvnb pb b (sigma, metas, evars as substn) cM cN =
if use_full_betaiota flags && not (subterm_restriction b flags) then
let cM' = do_reduce flags.modulo_delta curenvnb sigma cM in