Skip to content

Commit fa6d92d

Browse files
Merge PR #19611: Evarconv: save keys
Reviewed-by: gares Co-authored-by: gares <[email protected]>
2 parents fffbf85 + e2fd033 commit fa6d92d

File tree

7 files changed

+168
-59
lines changed

7 files changed

+168
-59
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- **Changed:**
2+
The unification algorithm does not solve unification problems of the
3+
form `proj _ ~ _` using canonical structures when the LHS reduces or
4+
is ground
5+
(`#19611 <https://github.com/coq/coq/pull/19611>`_,
6+
by ).

engine/evarutil.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ let has_undefined_evars evd t =
100100
try let _ = f h t in false
101101
with (Not_found | NotInstantiatedEvar) -> true
102102

103+
let has_undefined_evars_or_metas evd t =
104+
let rec has_ev t =
105+
match EConstr.kind evd t with
106+
| Evar _ | Meta _ -> raise NotInstantiatedEvar
107+
| _ -> EConstr.iter evd has_ev t in
108+
try let _ = has_ev t in false
109+
with (Not_found | NotInstantiatedEvar) -> true
110+
103111
let is_ground_term evd t =
104112
not (has_undefined_evars evd t)
105113

engine/evarutil.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ val whd_head_evar : evar_map -> constr -> constr
7575

7676
(* An over-approximation of [has_undefined (nf_evars evd c)] *)
7777
val has_undefined_evars : evar_map -> constr -> bool
78+
val has_undefined_evars_or_metas : evar_map -> constr -> bool
7879

7980
val is_ground_term : evar_map -> constr -> bool
8081
val is_ground_env : evar_map -> env -> bool

pretyping/evarconv.ml

Lines changed: 138 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -272,35 +272,17 @@ let apply_hooks env sigma proj pat =
272272
try CString.Map.get name !all_hooks env sigma proj pat
273273
with e when CErrors.noncritical e -> anomaly Pp.(str "CS hook " ++ str name ++ str " exploded")) !active_hooks
274274

275-
(* [check_conv_record env sigma (t1,stack1) (t2,stack2)] tries to decompose
276-
the problem (t1 stack1) = (t2 stack2) into a problem
277-
278-
stack1 = params1@[c1]@extra_args1
279-
stack2 = us2@extra_args2
280-
t1 params1 c1 = proji params (c xs)
281-
t2 us2 = head us
282-
extra_args1 = extra_args2
283-
284-
by finding a record R and an object c := [xs:bs](Build_R params v1..vn)
285-
with vi = (head us), for which we know that the i-th projection proji
286-
satisfies
287-
288-
proji params (c xs) = head us
289-
290-
Rem: such objects, usable for conversion, are defined in the objdef
291-
table; practically, it amounts to "canonically" equip t2 into a
292-
object c in structure R (since, if c1 were not an evar, the
293-
projection would have been reduced) *)
294-
295-
let check_conv_record ?metas env sigma (t1,sk1) (t2,sk2) =
275+
let decompose_proj ?metas env sigma (t1, sk1) =
296276
(* I only recognize ConstRef projections since these are the only ones for which
297277
I know how to obtain the number of parameters. *)
298278
let (proji, u), arg =
299279
match Termops.global_app_of_constr sigma t1 with
300280
| (Names.GlobRef.ConstRef proji, u), arg -> (proji, u), arg
301-
| _ -> raise Not_found in
281+
| _ -> raise Not_found
282+
| exception _ -> raise Not_found in
302283
(* Given a ConstRef projection, I obtain the structure it is a projection from. *)
303-
let structure = Structures.Structure.find_from_projection proji in
284+
let structure = try Structures.Structure.find_from_projection proji
285+
with _ -> raise Not_found in
304286
(* Knowing the structure and hence its number of arguments, I can cut sk1 into pieces. *)
305287
let params1, c1, extra_args1 =
306288
match arg with
@@ -323,6 +305,29 @@ let check_conv_record ?metas env sigma (t1,sk1) (t2,sk2) =
323305
match Reductionops.Stack.strip_n_app structure.nparams sk1 with
324306
| Some (params1, c1, extra_args1) -> (Reductionops.Stack.list_of_app_stack params1), c1, extra_args1
325307
| _ -> raise Not_found in
308+
((proji, u), (params1, c1, extra_args1))
309+
310+
(* [check_conv_record env sigma (t1,stack1) (t2,stack2)] tries to decompose
311+
the problem (t1 stack1) = (t2 stack2) into a problem
312+
313+
stack1 = params1@[c1]@extra_args1
314+
stack2 = us2@extra_args2
315+
t1 params1 c1 = proji params (c xs)
316+
t2 us2 = head us
317+
extra_args1 = extra_args2
318+
319+
by finding a record R and an object c := [xs:bs](Build_R params v1..vn)
320+
with vi = (head us), for which we know that the i-th projection proji
321+
satisfies
322+
323+
proji params (c xs) = head us
324+
325+
Rem: such objects, usable for conversion, are defined in the objdef
326+
table; practically, it amounts to "canonically" equip t2 into a
327+
object c in structure R (since, if c1 were not an evar, the
328+
projection would have been reduced) *)
329+
330+
let check_conv_record env sigma ((proji, u), (params1, c1, extra_args1)) (t2,sk2) =
326331
let h2, sk2' = decompose_app sigma (shrink_eta sigma t2) in
327332
let sk2 = Stack.append_app sk2' sk2 in
328333
let k = Reductionops.Stack.args_size sk2 - Reductionops.Stack.args_size extra_args1 in
@@ -642,6 +647,32 @@ let infer_conv_noticing_evars ~pb ~ts env sigma t1 t2 =
642647
if !has_evar then None
643648
else Some (UnifFailure (sigma, UnifUnivInconsistency e))
644649

650+
module Cs_keys_cache = struct
651+
type t = (Names.GlobRef.t Queue.t * state Names.GlobRef.Map.t) * (Names.GlobRef.t Queue.t * state Names.GlobRef.Map.t)
652+
653+
let empty () : t = ((Queue.create (), Names.GlobRef.Map.empty), (Queue.create (), Names.GlobRef.Map.empty))
654+
655+
let flip (c1, c2) = (c2, c1)
656+
657+
let add_left sigma appr (((c1, m1), c2) as c) =
658+
match fst @@ EConstr.destRef sigma (fst appr) with
659+
| k when not (Names.GlobRef.Map.mem k m1) ->
660+
let () = Queue.push k c1 in
661+
((c1, Names.GlobRef.Map.add k appr m1), c2)
662+
| _ | exception DestKO -> c
663+
664+
let add sigma l2r appr c =
665+
if l2r then add_left sigma appr c else flip (add_left sigma appr (flip c))
666+
667+
let fold_left f acc ((c, m), _) = Queue.fold (fun acc k -> f acc (Names.GlobRef.Map.find k m)) acc c
668+
let fold l2r f acc c = fold_left f acc (if l2r then c else flip c)
669+
670+
let clear_left ((c, _), _) = Queue.clear c
671+
672+
let clear l2r c =
673+
if l2r then clear_left c else clear_left (flip c)
674+
end
675+
645676
let rec evar_conv_x flags env evd pbty term1 term2 =
646677
let term1 = whd_head_evar evd term1 in
647678
let term2 = whd_head_evar evd term2 in
@@ -662,7 +693,7 @@ let rec evar_conv_x flags env evd pbty term1 term2 =
662693
let term2 = apprec_nohdbeta flags env evd term2 in
663694
let default () =
664695
match
665-
evar_eqappr_x flags env evd pbty
696+
evar_eqappr_x flags env evd pbty (Cs_keys_cache.empty ()) None
666697
(whd_nored_state env evd (term1,Stack.empty))
667698
(whd_nored_state env evd (term2,Stack.empty))
668699
with
@@ -699,6 +730,8 @@ let rec evar_conv_x flags env evd pbty term1 term2 =
699730
end
700731

701732
and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
733+
keys (* canonical structure keys cache *)
734+
lastUnfolded (* tells which side was last unfolded, if any *)
702735
(term1, sk1 as appr1) (term2, sk2 as appr2) =
703736
let quick_fail i = (* not costly, loses info *)
704737
UnifFailure (i, NotSameHead)
@@ -736,8 +769,8 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
736769
flags.open_ts env' evd (c'1, Stack.empty) in
737770
let out2 = whd_nored_state env' evd
738771
(lift 1 (Stack.zip evd (term', sk')), Stack.append_app [|EConstr.mkRel 1|] Stack.empty) in
739-
if onleft then evar_eqappr_x flags env' evd CONV out1 out2
740-
else evar_eqappr_x flags env' evd CONV out2 out1
772+
if onleft then evar_eqappr_x flags env' evd CONV keys None out1 out2
773+
else evar_eqappr_x flags env' evd CONV (Cs_keys_cache.flip keys) None out2 out1
741774
in
742775
let rigids env evd sk term sk' term' =
743776
let nargs = Stack.args_size sk in
@@ -777,7 +810,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
777810
3. reduce the redex into M and recursively solve E[?n[inst]] =? E'[M] *)
778811
let switch f a b = if l2r then f a b else f b a in
779812
let delta i =
780-
switch (evar_eqappr_x flags env i pbty) apprF
813+
switch (evar_eqappr_x flags env i pbty keys None) apprF
781814
(whd_betaiota_deltazeta_for_iota_state flags.open_ts env i vskM)
782815
in
783816
let default i = ise_try i [miller l2r ev apprF apprM;
@@ -800,7 +833,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
800833
whd_betaiota_deltazeta_for_iota_state flags.open_ts env evd (termM',skM)
801834
in
802835
let delta' i =
803-
switch (evar_eqappr_x flags env i pbty) apprF apprM'
836+
switch (evar_eqappr_x flags env i pbty keys None) apprF apprM'
804837
in
805838
fun i -> ise_try i [miller l2r ev apprF apprM';
806839
consume l2r apprF apprM'; delta']
@@ -867,7 +900,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
867900
(position_problem true pbty,destEvar i' ev1',term2)
868901
else
869902
(* HH: Why not to drop sk1 and sk2 since they unified *)
870-
evar_eqappr_x flags env evd pbty
903+
evar_eqappr_x flags env evd pbty keys None
871904
(ev1', sk1) (term2, sk2)
872905
| Some (r,[]), Success i' ->
873906
(* We have sk1'[] = sk2[] for some sk1' s.t. sk1[]=sk1'[r[]] *)
@@ -877,7 +910,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
877910
solve_simple_eqn (conv_fun evar_conv_x) flags env i'
878911
(position_problem false pbty,destEvar i' ev2',Stack.zip i' (term1,r))
879912
else
880-
evar_eqappr_x flags env evd pbty
913+
evar_eqappr_x flags env evd pbty keys None
881914
(ev2', sk1) (term2, sk2)
882915
| Some ([],r), Success i' ->
883916
(* Symmetrically *)
@@ -889,7 +922,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
889922
(position_problem true pbty,destEvar i' ev1',Stack.zip i' (term2,r))
890923
else
891924
(* HH: Why not to drop sk1 and sk2 since they unified *)
892-
evar_eqappr_x flags env evd pbty
925+
evar_eqappr_x flags env evd pbty keys None
893926
(ev1', sk1) (term2, sk2)
894927
| None, (UnifFailure _ as x) ->
895928
(* sk1 and sk2 have no common outer part *)
@@ -923,6 +956,42 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
923956
in
924957
let app_empty = match sk1, sk2 with [], [] -> true | _ -> false in
925958
(* Evar must be undefined since we have flushed evars *)
959+
let keys = Cs_keys_cache.add evd true appr1 keys in
960+
let keys = Cs_keys_cache.add evd false appr2 keys in
961+
let get_cs env sigma l2r keys nokey appr1 appr2 =
962+
let appr1, appr2 = if l2r then appr1, appr2 else appr2, appr1 in
963+
try
964+
let (_, (_, c1, _)) as p1 = decompose_proj env sigma appr1 in
965+
let kill, reduce =
966+
(* TOTHINK: Should I keep c1 simplified? *)
967+
let c1 = whd_all env sigma c1 in
968+
(* [proj (ctor ...)]: don't use CS *)
969+
match kind sigma c1 with
970+
| App (h,_) when isConstruct sigma h -> true, true
971+
| Construct _ -> true, true
972+
| _ -> not (has_undefined_evars_or_metas sigma c1), false in
973+
let x =
974+
if nokey then
975+
(try Some (check_conv_record env sigma p1 appr2)
976+
with Not_found -> None)
977+
else
978+
let x = Cs_keys_cache.fold (not l2r) (fun r appr ->
979+
match r with
980+
| None ->
981+
(try Some (check_conv_record env sigma p1 appr)
982+
with | Not_found -> None)
983+
| _ -> r) None keys in
984+
(* If t is not a reference, it was not added to the keys cache, so we take care of it now. *)
985+
match x with
986+
| None when not (EConstr.isRef sigma (fst appr2)) ->
987+
(try Some (check_conv_record env sigma p1 appr2)
988+
with Not_found -> None)
989+
| _ -> x in
990+
if kill then Inr (reduce && (match x with | None -> false | _ -> true)) else
991+
(* The projection constant will not change, so there is no point in keeping the keys anymore. *)
992+
let () = Cs_keys_cache.clear (not l2r) keys in
993+
match x with | Some x -> Inl x | _ -> Inr false
994+
with _ -> Inr false in
926995
let () = debug_unification (fun () -> Pp.(v 0 (pr_state env evd appr1 ++ cut () ++ pr_state env evd appr2 ++ cut ()))) in
927996
match (flex_kind_of_term flags env evd term1 sk1,
928997
flex_kind_of_term flags env evd term2 sk2) with
@@ -991,7 +1060,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
9911060
and f2 i =
9921061
let out1 = whd_betaiota_deltazeta_for_iota_state flags.open_ts env i vsk1'
9931062
and out2 = whd_betaiota_deltazeta_for_iota_state flags.open_ts env i vsk2'
994-
in evar_eqappr_x flags env i pbty out1 out2
1063+
in evar_eqappr_x flags env i pbty keys None out1 out2
9951064
in
9961065
ise_try evd [f1; f2]
9971066

@@ -1003,7 +1072,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
10031072
and f2 i =
10041073
let out1 = whd_betaiota_deltazeta_for_iota_state flags.open_ts env i vsk1'
10051074
and out2 = whd_betaiota_deltazeta_for_iota_state flags.open_ts env i vsk2'
1006-
in evar_eqappr_x flags env i pbty out1 out2
1075+
in evar_eqappr_x flags env i pbty keys None out1 out2
10071076
in
10081077
ise_try evd [f1; f2]
10091078

@@ -1015,7 +1084,7 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
10151084
in
10161085
(match res with
10171086
| Some (f1,args1) ->
1018-
evar_eqappr_x flags env evd pbty (f1,Stack.append_app args1 sk1)
1087+
evar_eqappr_x flags env evd pbty keys None (f1,Stack.append_app args1 sk1)
10191088
appr2
10201089
| None -> UnifFailure (evd,NotSameHead))
10211090

@@ -1026,10 +1095,12 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
10261095
in
10271096
(match res with
10281097
| Some (f2,args2) ->
1029-
evar_eqappr_x flags env evd pbty appr1 (f2,Stack.append_app args2 sk2)
1098+
evar_eqappr_x flags env evd pbty keys None appr1 (f2,Stack.append_app args2 sk2)
10301099
| None -> UnifFailure (evd,NotSameHead))
10311100

10321101
| _, _ ->
1102+
(* We remember if the LHS is a reducible projection to decide if we unfold left first. *)
1103+
let no_cs1 = ref false in
10331104
let f1 i =
10341105
(* Gather the universe constraints that would make term1 and term2 equal.
10351106
If these only involve unifications of flexible universes to other universes,
@@ -1050,8 +1121,13 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
10501121
(try
10511122
if not flags.with_cs then raise Not_found
10521123
else conv_record flags env
1053-
(try check_conv_record env i appr1 appr2
1054-
with Not_found -> check_conv_record env i appr2 appr1)
1124+
(match get_cs env i true keys (lastUnfolded = Some true) appr1 appr2 with
1125+
| Inl x -> x
1126+
| Inr b ->
1127+
let () = no_cs1 := b in
1128+
(match get_cs env i false keys (lastUnfolded = Some false) appr1 appr2 with
1129+
| Inl x -> x
1130+
| Inr _ -> raise Not_found))
10551131
with Not_found -> UnifFailure (i,NoCanonicalStructure))
10561132
and f3 i =
10571133
(* heuristic: unfold second argument first, exception made
@@ -1079,16 +1155,21 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
10791155
let rhs_is_already_stuck =
10801156
rhs_is_already_stuck || rhs_is_stuck_and_unnamed () in
10811157

1082-
if (EConstr.isLambda i term1 || rhs_is_already_stuck)
1083-
&& (not (Stack.not_purely_applicative sk1')) then
1084-
evar_eqappr_x ~rhs_is_already_stuck flags env i pbty
1085-
(whd_betaiota_deltazeta_for_iota_state
1086-
flags.open_ts env i vsk1')
1087-
appr2
1088-
else
1089-
evar_eqappr_x flags env i pbty appr1
1090-
(whd_betaiota_deltazeta_for_iota_state
1091-
flags.open_ts env i vsk2')
1158+
let b = EConstr.isLambda i term1 || rhs_is_already_stuck
1159+
&& (not (Stack.not_purely_applicative sk1')) in
1160+
ise_try i [
1161+
(fun i ->
1162+
if b || !no_cs1 then
1163+
evar_eqappr_x flags env i pbty keys (Some false)
1164+
(whd_betaiota_deltazeta_for_iota_state
1165+
flags.open_ts env i vsk1')
1166+
appr2
1167+
else quick_fail i);
1168+
fun i ->
1169+
if b then quick_fail i else
1170+
evar_eqappr_x flags env i pbty keys (Some true) appr1
1171+
(whd_betaiota_deltazeta_for_iota_state
1172+
flags.open_ts env i vsk2')]
10921173
in
10931174
ise_try evd [f1; f2; f3]
10941175
end
@@ -1112,10 +1193,13 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
11121193
let f3 i =
11131194
(try
11141195
if not flags.with_cs then raise Not_found
1115-
else conv_record flags env (check_conv_record env i appr1 appr2)
1196+
else conv_record flags env (
1197+
match get_cs env i true keys false appr1 appr2 with
1198+
| Inl x -> x
1199+
| Inr _ -> raise Not_found)
11161200
with Not_found -> UnifFailure (i,NoCanonicalStructure))
11171201
and f4 i =
1118-
evar_eqappr_x flags env i pbty
1202+
evar_eqappr_x flags env i pbty keys (Some false)
11191203
(whd_betaiota_deltazeta_for_iota_state
11201204
flags.open_ts env i vsk1')
11211205
appr2
@@ -1126,10 +1210,13 @@ and evar_eqappr_x ?(rhs_is_already_stuck = false) flags env evd pbty
11261210
let f3 i =
11271211
(try
11281212
if not flags.with_cs then raise Not_found
1129-
else conv_record flags env (check_conv_record env i appr2 appr1)
1213+
else conv_record flags env (
1214+
match get_cs env i false keys false appr1 appr2 with
1215+
| Inl x -> x
1216+
| Inr _ -> raise Not_found)
11301217
with Not_found -> UnifFailure (i,NoCanonicalStructure))
11311218
and f4 i =
1132-
evar_eqappr_x flags env i pbty appr1
1219+
evar_eqappr_x flags env i pbty keys (Some true) appr1
11331220
(whd_betaiota_deltazeta_for_iota_state
11341221
flags.open_ts env i vsk2')
11351222
in

0 commit comments

Comments
 (0)