Skip to content

Commit 5f7f494

Browse files
Merge PR #22451: Correctly implement δ-resolver lift for modules.
Reviewed-by: SkySkimmer Co-authored-by: SkySkimmer <SkySkimmer@users.noreply.github.com>
2 parents a9bbe52 + 61d0a8c commit 5f7f494

5 files changed

Lines changed: 154 additions & 46 deletions

File tree

checker/values.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,11 @@ let v_univ_abstracted v = v_tuple "univ_abstracted" [|v;v_abs_context|]
300300
let v_delta_hint =
301301
v_sum "delta_hint" 0 [|[|v_int; v_opt (v_univ_abstracted v_constr)|];[|v_kn|]|]
302302

303+
let v_mp_hint = v_sum "mp_hint" 1 [|[|v_mp|]|]
304+
303305
let v_resolver =
304306
v_tuple "delta_resolver"
305-
[|v_mp; v_map v_mp v_mp;
307+
[|v_mp; v_map v_mp v_mp_hint;
306308
v_hmap v_kn v_delta_hint|]
307309

308310
let v_subst =

kernel/mod_subst.ml

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@ open Util
2020
open Names
2121
open Constr
2222

23+
(* The modpath part of a resolver holds two kinds of statements.
24+
- [p ↦ MPequiv q] states that [q] is the canonical name of [p].
25+
- [p ↦ MPlift] states that [p] is a bound name that should be left untouched
26+
by substitution. *)
27+
type mp_hint =
28+
| MPequiv of ModPath.t (** the canonical form of the key *)
29+
| MPlift (** the prefix rule stops here *)
30+
2331
(* For Inline, the int is an inlining level, and the constr (if present)
2432
is the term into which we should inline.
2533
Equiv gives the canonical name in the given context. *)
26-
2734
type delta_hint =
2835
| Inline of int * constr UVars.univ_abstracted option
2936
| Equiv of KerName.t
@@ -35,7 +42,7 @@ module Deltamap = struct
3542
type t = {
3643
root : ModPath.t;
3744
(** Common root of all keys in the deltamap *)
38-
mmap : ModPath.t ModPath.Map.t;
45+
mmap : mp_hint ModPath.Map.t;
3946
(** All bindings [mp ↦ _] must satisfy [mp ⊆ root] *)
4047
kmap : delta_hint KerName.Map.t;
4148
(** All bindings [kn ↦ _] must satisfy [modpath(kn) ⊆ root] *)
@@ -53,16 +60,19 @@ module Deltamap = struct
5360
let () = assert (ModPath.subpath reso.root (KerName.modpath kn)) in
5461
{ reso with kmap = KerName.Map.add kn hint reso.kmap }
5562

56-
let add_mp mp mp' reso =
63+
let add_mp_hint mp hint reso =
5764
let () = assert (ModPath.subpath reso.root mp) in
58-
{ reso with mmap = ModPath.Map.add mp mp' reso.mmap }
65+
{ reso with mmap = ModPath.Map.add mp hint reso.mmap }
5966

60-
let find_mp mp reso = ModPath.Map.find mp reso.mmap
67+
let add_mp mp mp' reso = add_mp_hint mp (MPequiv mp') reso
68+
let lift_mp mp reso = add_mp_hint mp MPlift reso
69+
70+
let find_mp_opt mp reso = ModPath.Map.find_opt mp reso.mmap
6171
let find_kn kn reso = KerName.Map.find kn reso.kmap
6272
let fold_kn f reso i = KerName.Map.fold f reso.kmap i
6373
let fold fmp fkn reso accu =
6474
ModPath.Map.fold fmp reso.mmap (KerName.Map.fold fkn reso.kmap accu)
65-
let join map1 map2 = fold add_mp add_kn map1 map2
75+
let join map1 map2 = fold add_mp_hint add_kn map1 map2
6676

6777
(** if mp0 ⊆ root, we can see a resolver on root as a resolver on mp *)
6878
let upcast mp0 reso =
@@ -87,8 +97,8 @@ module Deltamap = struct
8797
path in mm above root, as find_prefix will always return this one
8898
without considering the less precise ones. *)
8999
let glb = match glb with
90-
| None -> Some mp
91-
| Some glb -> if ModPath.subpath glb mp then Some mp else Some glb
100+
| None -> Some (mp, data)
101+
| Some (g, _) as old -> if ModPath.subpath g mp then Some (mp, data) else old
92102
in
93103
glb, accu
94104
else
@@ -98,21 +108,23 @@ module Deltamap = struct
98108
let glb, mm' = ModPath.Map.fold fold_mp mm (None, ModPath.Map.empty) in
99109
let mm' = match glb with
100110
| None -> mm'
101-
| Some glb ->
111+
| Some (glb, data) ->
102112
if ModPath.Map.mem root mm then mm'
103113
else
104114
(* Add root to the resolver and map it to what find_prefix would have
105115
returned on root *)
106-
let rec diff accu mp =
107-
if ModPath.equal mp glb then accu
108-
else match mp with
109-
| MPdot (mp, l) -> diff (l :: accu) mp
110-
| MPbound _ | MPfile _ -> assert false
111-
in
112-
let diff = diff [] root in
113-
let data = ModPath.Map.get glb mm in
114-
let data' = List.fold_left (fun accu l -> MPdot (accu, l)) data diff in
115-
ModPath.Map.add root data' mm'
116+
match data with
117+
| MPlift -> ModPath.Map.add root MPlift mm'
118+
| MPequiv data ->
119+
let rec diff accu mp =
120+
if ModPath.equal mp glb then accu
121+
else match mp with
122+
| MPdot (mp, l) -> diff (l :: accu) mp
123+
| MPbound _ | MPfile _ -> assert false
124+
in
125+
let diff = diff [] root in
126+
let data' = List.fold_left (fun accu l -> MPdot (accu, l)) data diff in
127+
ModPath.Map.add root (MPequiv data') mm'
116128
in
117129
(* filter the kernames *)
118130
let filter_kn kn _ = ModPath.subpath root (KerName.modpath kn) in
@@ -166,12 +178,16 @@ let string_of_hint pr = function
166178
| Inline (lvl, None) -> str "inline[" ++ int lvl ++ str "]"
167179
| Equiv kn -> str "equiv(" ++ KerName.print kn ++ str ")"
168180

181+
let debug_pr_mp_hint = function
182+
| MPequiv mp -> ModPath.print mp
183+
| MPlift -> str "<lift>"
184+
169185
let debug_pr_delta pr resolve =
170186
let kn_to_string kn hint l =
171187
hov 2 (KerName.print kn ++ str " =>" ++ spc() ++ string_of_hint pr hint) :: l
172188
in
173-
let mp_to_string mp mp' l =
174-
hov 2 (ModPath.print mp ++ str " =>" ++ spc() ++ ModPath.print mp') :: l
189+
let mp_to_string mp hint l =
190+
hov 2 (ModPath.print mp ++ str " =>" ++ spc() ++ debug_pr_mp_hint hint) :: l
175191
in
176192
let l = Deltamap.fold mp_to_string kn_to_string resolve [] in
177193
v 0 @@ prlist_with_sep pr_comma (fun p -> p) (List.rev l)
@@ -207,7 +223,11 @@ let add_kn_delta_resolver kn kn' =
207223
assert (Id.equal (KerName.label kn) (KerName.label kn'));
208224
Deltamap.add_kn kn (Equiv kn')
209225

210-
let add_mp_delta_resolver mp1 mp2 = Deltamap.add_mp mp1 mp2
226+
let add_mp_delta_resolver mp1 mp2 =
227+
let () = assert (not (ModPath.equal mp1 mp2)) in
228+
Deltamap.add_mp mp1 mp2
229+
230+
let lift_mp_delta_resolver mp = Deltamap.lift_mp mp
211231

212232
(** Extending a [substitution] without sequential composition *)
213233

@@ -223,13 +243,19 @@ let map_mbid mbid mp resolve =
223243
let map_mp mp1 mp2 resolve = add_mp mp1 mp2 resolve empty_subst
224244

225245
let find_prefix resolve mp =
226-
let rec sub_mp = function
227-
| MPdot(mp,l) as mp_sup ->
228-
(try Deltamap.find_mp mp_sup resolve
229-
with Not_found -> MPdot(sub_mp mp,l))
230-
| p -> Deltamap.find_mp p resolve
246+
let rec sub_mp mp = match Deltamap.find_mp_opt mp resolve with
247+
| Some (MPequiv mp') -> mp'
248+
| Some MPlift -> mp
249+
| None ->
250+
match mp with
251+
| MPdot (mp1, l) ->
252+
(* Preserving sharing is not an optimisation: [progress] in [subst_con0]
253+
and [subst_mind] tests with [!=]. This should be fixed at some point. *)
254+
let mp1' = sub_mp mp1 in
255+
if mp1' == mp1 then mp else MPdot (mp1', l)
256+
| MPbound _ | MPfile _ -> mp
231257
in
232-
try sub_mp mp with Not_found -> mp
258+
sub_mp mp
233259

234260
(* TODO: remove the indirection at some point *)
235261
let mp_of_delta = find_prefix
@@ -543,8 +569,8 @@ let replace_mp_in_kn mpfrom mpto kn =
543569
let mp_in_mp = ModPath.subpath
544570

545571
let subset_prefixed_by mp resolver =
546-
let mp_prefix mkey mequ rslv =
547-
if mp_in_mp mp mkey then Deltamap.add_mp mkey mequ rslv else rslv
572+
let mp_prefix mkey hint rslv =
573+
if mp_in_mp mp mkey then Deltamap.add_mp_hint mkey hint rslv else rslv
548574
in
549575
let kn_prefix kn hint rslv =
550576
match hint with
@@ -557,8 +583,8 @@ let subset_prefixed_by mp resolver =
557583
let subst_dom_delta_resolver mp_from mp_to resolver =
558584
let () = assert (ModPath.equal mp_from resolver.Deltamap.root) in
559585
let subst = map_mp mp_from mp_to (empty_delta_resolver mp_to) in
560-
let mp_apply_subst mkey mequ rslv =
561-
Deltamap.add_mp (subst_mp subst mkey) mequ rslv
586+
let mp_apply_subst mkey hint rslv =
587+
Deltamap.add_mp_hint (subst_mp subst mkey) hint rslv
562588
in
563589
let kn_apply_subst kkey hint rslv =
564590
Deltamap.add_kn (subst_kn subst kkey) hint rslv
@@ -594,10 +620,13 @@ let subst_mp_delta subst mp mkey =
594620
reso, mp1
595621

596622
let gen_subst_delta_resolver dom subst resolver =
597-
let mp_apply_subst mkey mequ rslv =
623+
let mp_apply_subst mkey hint rslv =
598624
let mkey' = if dom then subst_mp subst mkey else mkey in
599-
let rslv',mequ' = subst_mp_delta subst mequ mkey' in
600-
Deltamap.join rslv' (Deltamap.add_mp mkey' mequ' rslv)
625+
match hint with
626+
| MPlift -> Deltamap.add_mp_hint mkey' MPlift rslv
627+
| MPequiv mequ ->
628+
let rslv', mequ' = subst_mp_delta subst mequ mkey' in
629+
Deltamap.join rslv' (Deltamap.add_mp_hint mkey' (MPequiv mequ') rslv)
601630
in
602631
let kn_apply_subst kkey hint rslv =
603632
let kkey' = if dom then subst_kn subst kkey else kkey in
@@ -615,8 +644,10 @@ let subst_codom_delta_resolver = gen_subst_delta_resolver false
615644
let subst_dom_codom_delta_resolver = gen_subst_delta_resolver true
616645

617646
let update_delta_resolver resolver1 resolver2 =
618-
let mp_apply_rslv mkey mequ rslv =
619-
Deltamap.add_mp mkey (find_prefix resolver2 mequ) rslv
647+
let mp_apply_rslv mkey hint rslv = match hint with
648+
| MPlift -> Deltamap.add_mp_hint mkey MPlift rslv
649+
| MPequiv mequ ->
650+
Deltamap.add_mp_hint mkey (MPequiv (find_prefix resolver2 mequ)) rslv
620651
in
621652
let kn_apply_rslv kkey hint1 rslv =
622653
let hint = match hint1 with

kernel/mod_subst.mli

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@ val empty_delta_resolver : ModPath.t -> delta_resolver
2525

2626
val has_root_delta_resolver : ModPath.t -> delta_resolver -> bool
2727

28-
(** [add_mp_delta_resolver mp v reso] assumes that root(reso) ⊆ mp. *)
28+
(** [add_mp_delta_resolver mp v reso] assumes that root(reso) ⊆ mp and mp ≠ v. *)
2929
val add_mp_delta_resolver :
3030
ModPath.t -> ModPath.t -> delta_resolver -> delta_resolver
3131

32+
(** [lift_mp_delta_resolver mp reso] marks [mp] as being a bound name that must
33+
be left untouched by substitution. This is the semantics of delayed
34+
resolvers for functors and module types. Assumes that root(reso) ⊆ mp. *)
35+
val lift_mp_delta_resolver :
36+
ModPath.t -> delta_resolver -> delta_resolver
37+
3238
(** [add_kn_delta_resolver kn v reso] assumes that root(reso) ⊆ modpath(kn). *)
3339
val add_kn_delta_resolver :
3440
KerName.t -> KerName.t -> delta_resolver -> delta_resolver

kernel/modops.ml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ let rec strengthen_module mp mb = match mod_type mb with
293293
if mp_is_alias delta_mb mp then mb (* already strengthened *)
294294
else
295295
let reso, struc' = strengthen_signature mp struc delta_mb in
296-
let reso = add_mp_delta_resolver mp mp (add_delta_resolver delta_mb reso) in
296+
let reso = lift_mp_delta_resolver mp (add_delta_resolver delta_mb reso) in
297297
strengthen_module_body ~src:mp (NoFunctor struc') reso mb
298298
| MoreFunctor _ -> mb
299299

@@ -307,7 +307,7 @@ and strengthen_signature mp struc reso0 =
307307
let reso = match mod_global_delta mb with
308308
| None ->
309309
(* See {!strengthen_and_subst_module} *)
310-
add_mp_delta_resolver mp' mp' reso
310+
lift_mp_delta_resolver mp' reso
311311
| Some delta ->
312312
add_delta_resolver delta reso
313313
in
@@ -324,7 +324,7 @@ let strengthen mtb mp = match mod_type mtb with
324324
if mp_is_alias delta_mtb mp then mtb
325325
else
326326
let reso', struc' = strengthen_signature mp struc delta_mtb in
327-
let reso' = add_delta_resolver delta_mtb (add_mp_delta_resolver mp mp reso') in
327+
let reso' = add_delta_resolver delta_mtb (lift_mp_delta_resolver mp reso') in
328328
strengthen_module_type struc' reso' mtb
329329
| MoreFunctor _ -> mtb
330330

@@ -408,7 +408,7 @@ and strengthen_and_subst_struct struc subst mp_from mp_to alias incl reso =
408408
semantic for functor this should be changed.*)
409409
begin match mod_global_delta mb' with
410410
| None -> (* functor case *)
411-
add_mp_delta_resolver mp_to' mp_to' reso', item'
411+
lift_mp_delta_resolver mp_to' reso', item'
412412
| Some delta ->
413413
add_delta_resolver delta reso', item'
414414
end
@@ -418,7 +418,7 @@ and strengthen_and_subst_struct struc subst mp_from mp_to alias incl reso =
418418
let subst' = add_mp mp_from' mp_to' (empty_delta_resolver mp_to') subst in
419419
let mty' = subst_modtype subst_dom_codom subst' mp_from' mty in
420420
let item' = if mty' == mty then item else (l, SFBmodtype mty') in
421-
add_mp_delta_resolver mp_to' mp_to' reso', item'
421+
lift_mp_delta_resolver mp_to' reso', item'
422422
in
423423
List.Smart.fold_left_map strengthen_and_subst_field (empty_delta_resolver mp_to) struc
424424

@@ -449,7 +449,7 @@ let expand_self_delta mp sign reso =
449449
let self = mp_of_delta reso mp in
450450
(* [mp] is only equivalent to itself, it stops the prefix rule from reaching
451451
the fields the includer will get later. *)
452-
let reso0 = add_mp_delta_resolver mp mp reso in
452+
let reso0 = lift_mp_delta_resolver mp reso in
453453
let expand accu (l, item) = match item with
454454
| SFBconst _ | SFBmind _ | SFBrules _ ->
455455
let kn = KerName.make mp l in
@@ -465,7 +465,7 @@ let expand_self_delta mp sign reso =
465465
| SFBmodtype _ ->
466466
(* as in [strengthen_and_subst_struct], module types are only equivalent
467467
to themselves *)
468-
add_mp_delta_resolver (MPdot (mp, l)) (MPdot (mp, l)) accu
468+
lift_mp_delta_resolver (MPdot (mp, l)) accu
469469
in
470470
List.fold_left expand reso0 (struct_of_signature sign)
471471

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
(* A delta-resolver binding on a modpath records either the canonical form of
2+
that path, or a marker saying that the prefix rule must not be extrapolated
3+
into it ([Mod_subst.lift_mp_delta_resolver], recorded by
4+
[Modops.strengthen_and_subst_struct] for every functor field and every module
5+
type field of the module it builds).
6+
7+
The two transport differently -- a canonical name is left alone, a marker
8+
travels with its key -- and while both were spelled [p -> p] they were
9+
indistinguishable, so α-renaming a resolver turned a marker into a genuine
10+
alias to the source. The two directions of the equivalence check that
11+
[Subtyping.check_signatures] runs on a module type field then disagreed on
12+
the canonical name of the inductive declared inside it, and the sealing below
13+
was rejected with an error that could not even be printed. *)
14+
15+
Module Type HasS. Module Type S. Inductive I := c. End S. End HasS.
16+
Module Type T. Declare Module M : HasS. End T.
17+
18+
Module B.
19+
Module Type S. Inductive I := c. End S.
20+
End B.
21+
22+
Module C := B. (* first hop: marks C.S *)
23+
24+
Module D : T.
25+
Module M := C. (* second hop: used to lose the mark *)
26+
End D.
27+
28+
(* The same shape with a functor field rather than a module type field, the
29+
other kind of field [strengthen_and_subst_struct] marks. It takes a
30+
[with Module] to make the parameter type's [M] be [B'] itself, so that the
31+
two sides of the check compare the resolver of [B'] with the ambient one;
32+
sealing against a module type that merely declares a functor field of the
33+
same shape gives them nothing in common to disagree about. The parameter type
34+
of [Fn] must have content, for the same reason.
35+
36+
On its own this no longer trips anything -- the [subst_mp_delta] fix of
37+
#22445 is enough for it -- so it is kept as a guard on the marker of a
38+
functor field, not as a reproducer. *)
39+
40+
Module Type Any. End Any.
41+
Module Type TAny. Declare Module M : Any. End TAny.
42+
43+
Module Type WithI. Inductive I := c. End WithI.
44+
45+
Module B'.
46+
Module Fn (X : WithI) := X.
47+
End B'.
48+
49+
Module D' : TAny with Module M := B'.
50+
Module M := B'.
51+
End D'.
52+
53+
(* Another funky test. *)
54+
55+
Module Other.
56+
57+
Module A.
58+
Module E. Definition n := false. End E.
59+
Module B.
60+
Module P. Include A.E. End P.
61+
End B.
62+
Include B.
63+
End A.
64+
65+
Include A.
66+
67+
Definition p := P.n.
68+
69+
End Other.

0 commit comments

Comments
 (0)