Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minimize the set of multiple inheritance (coercion) paths to check for conversion #13909

Merged
merged 2 commits into from Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,6 @@
- **Changed:**
Improve the :cmd:`Coercion` command to reduce the number of ambiguous paths to
report. A pair of multiple inheritance paths that can be reduced to smaller
adjoining pairs will not be reported as ambiguous paths anymore.
(`#13909 <https://github.com/coq/coq/pull/13909>`_,
by Kazuhiko Sakaguchi).
119 changes: 80 additions & 39 deletions pretyping/coercionops.ml
Expand Up @@ -30,10 +30,6 @@ type cl_typ =
| CL_IND of inductive
| CL_PROJ of Projection.Repr.t

type cl_info_typ = {
cl_param : int
}

let cl_typ_ord t1 t2 = match t1, t2 with
| CL_SECVAR v1, CL_SECVAR v2 -> Id.compare v1 v2
| CL_CONST c1, CL_CONST c2 -> Constant.CanOrd.compare c1 c2
Expand All @@ -56,9 +52,21 @@ struct
if Int.equal c 0 then cl_typ_ord j1 j2 else c
end

module ClTypSet = Set.Make(ClTyp)
module ClTypMap = Map.Make(ClTyp)
module ClPairMap = Map.Make(ClPairOrd)

type cl_info_typ = {
(* The number of parameters of the coercion class. *)
cl_param : int;
(* The sets of coercion classes respectively reachable from and to the
coercion class. *)
cl_reachable_from : ClTypSet.t;
cl_reachable_to : ClTypSet.t;
(* The representative class of the strongly connected component. *)
cl_repr : cl_typ;
}

type coe_typ = GlobRef.t

module CoeTypMap = GlobRef.Map_env
Expand All @@ -73,18 +81,18 @@ type coe_info_typ = {
coe_param : int;
}

let coe_info_typ_equal c1 c2 =
GlobRef.equal c1.coe_value c2.coe_value &&
c1.coe_local == c2.coe_local &&
c1.coe_is_identity == c2.coe_is_identity &&
c1.coe_is_projection == c2.coe_is_projection &&
Int.equal c1.coe_param c2.coe_param

type inheritance_path = coe_info_typ list

let init_class_tab =
let open ClTypMap in
add CL_FUN { cl_param = 0 } (add CL_SORT { cl_param = 0 } empty)
let cl_info params cl =
let cl_singleton = ClTypSet.singleton cl in
{ cl_param = params;
cl_reachable_from = cl_singleton;
cl_reachable_to = cl_singleton;
cl_repr = cl }
in
add CL_FUN (cl_info 0 CL_FUN) (add CL_SORT (cl_info 0 CL_SORT) empty)

let class_tab =
Summary.ref ~name:"class_tab" (init_class_tab : cl_info_typ ClTypMap.t)
Expand All @@ -98,8 +106,7 @@ let inheritance_graph =
(* ajout de nouveaux "objets" *)

let add_new_class cl s =
if not (ClTypMap.mem cl !class_tab) then
class_tab := ClTypMap.add cl s !class_tab
class_tab := ClTypMap.add cl s !class_tab

let add_new_coercion coe s =
coercion_tab := CoeTypMap.add coe s !coercion_tab
Expand All @@ -111,6 +118,8 @@ let add_new_path x y =

let class_info cl = ClTypMap.find cl !class_tab

let class_nparams cl = (class_info cl).cl_param

let class_exists cl = ClTypMap.mem cl !class_tab

let coercion_info coe = CoeTypMap.find coe !coercion_tab
Expand Down Expand Up @@ -283,54 +292,80 @@ let different_class_params env ci =
| _ -> false

let add_coercion_in_graph env sigma ic =
let source = ic.coe_source in
let target = ic.coe_target in
let source_info = class_info source in
let target_info = class_info target in
let old_inheritance_graph = !inheritance_graph in
let ambig_paths :
((cl_typ * cl_typ) * inheritance_path * inheritance_path) list ref =
ref [] in
let check_coherence (i, j as ij) p q =
let i_info = class_info i in
let j_info = class_info j in
let between_ij = ClTypSet.inter i_info.cl_reachable_from j_info.cl_reachable_to in
if cl_typ_eq i_info.cl_repr i &&
cl_typ_eq j_info.cl_repr j &&
ClTypSet.is_empty
(ClTypSet.diff (ClTypSet.inter between_ij source_info.cl_reachable_to)
i_info.cl_reachable_to) &&
ClTypSet.is_empty
(ClTypSet.diff
(ClTypSet.inter between_ij target_info.cl_reachable_from)
j_info.cl_reachable_from) &&
not (compare_path env sigma i p q)
then
ambig_paths := (ij, p, q) :: !ambig_paths
in
let try_add_new_path (i,j as ij) p =
(* If p is a cycle, we check whether p is definitionally an identity
function or not. If it is not, we report p as an ambiguous inheritance
path. *)
if cl_typ_eq i j && not (compare_path env sigma i p []) then
ambig_paths := (ij,p,[])::!ambig_paths;
if cl_typ_eq i j then check_coherence ij p [];
if not (cl_typ_eq i j) || different_class_params env i then
match lookup_path_between_class ij with
| q ->
(* p has the same source and target classes as an existing path q. We
report them as ambiguous inheritance paths if
1. p and q have no common element, and
2. p and q are not convertible.
If 1 does not hold, say p = p1 @ [c] @ p2 and q = q1 @ [c] @ q2,
convertibility of p1 and q1, also, p2 and q2 should be checked; thus,
checking the ambiguity of p and q is redundant with them. *)
if not (List.exists (fun c -> List.exists (coe_info_typ_equal c) q) p ||
compare_path env sigma i p q) then
ambig_paths := (ij,p,q)::!ambig_paths;
false
| exception Not_found -> (add_new_path ij p; true)
| q -> (if not (cl_typ_eq i j) then check_coherence ij p q); false
| exception Not_found -> add_new_path ij p; true
else
false
in
let try_add_new_path1 ij p =
let _ = try_add_new_path ij p in ()
in
if try_add_new_path (ic.coe_source, ic.coe_target) [ic] then begin
if try_add_new_path (source, target) [ic] then begin
ClPairMap.iter
(fun (s,t) p ->
if not (cl_typ_eq s t) then begin
if cl_typ_eq t ic.coe_source then begin
try_add_new_path1 (s, ic.coe_target) (p@[ic]);
if cl_typ_eq t source then begin
try_add_new_path1 (s, target) (p@[ic]);
ClPairMap.iter
(fun (u,v) q ->
if not (cl_typ_eq u v) && cl_typ_eq u ic.coe_target then
if not (cl_typ_eq u v) && cl_typ_eq u target then
try_add_new_path1 (s,v) (p@[ic]@q))
old_inheritance_graph
end;
if cl_typ_eq s ic.coe_target then
try_add_new_path1 (ic.coe_source, t) (ic::p)
if cl_typ_eq s target then try_add_new_path1 (source, t) (ic::p)
end)
old_inheritance_graph
end;
class_tab := ClTypMap.mapi (fun k k_info ->
let reachable_k_source = ClTypSet.mem k source_info.cl_reachable_to in
let reachable_target_k = ClTypSet.mem k target_info.cl_reachable_from in
{ k_info with
cl_reachable_from =
if reachable_k_source then
ClTypSet.union
k_info.cl_reachable_from target_info.cl_reachable_from
else
k_info.cl_reachable_from;
cl_reachable_to =
if reachable_target_k then
ClTypSet.union k_info.cl_reachable_to source_info.cl_reachable_to
else
k_info.cl_reachable_to;
cl_repr =
if reachable_k_source && reachable_target_k then
target_info.cl_repr
else
k_info.cl_repr
}) !class_tab;
match !ambig_paths with [] -> () | _ -> warn_ambiguous_path !ambig_paths

let subst_coercion subst c =
Expand Down Expand Up @@ -365,7 +400,13 @@ let class_params env sigma = function
(* add_class : cl_typ -> locality_flag option -> bool -> unit *)

let add_class env sigma cl =
add_new_class cl { cl_param = class_params env sigma cl }
if not (class_exists cl) then
let cl_singleton = ClTypSet.singleton cl in
add_new_class cl {
cl_param = class_params env sigma cl;
cl_reachable_from = cl_singleton;
cl_reachable_to = cl_singleton;
cl_repr = cl }

let declare_coercion env sigma c =
let () = add_class env sigma c.coe_source in
Expand Down
6 changes: 1 addition & 5 deletions pretyping/coercionops.mli
Expand Up @@ -31,10 +31,6 @@ val subst_cl_typ : env -> substitution -> cl_typ -> cl_typ
(** Comparison of [cl_typ] *)
val cl_typ_ord : cl_typ -> cl_typ -> int

(** This is the type of infos for declared classes *)
type cl_info_typ = {
cl_param : int }

(** This is the type of coercion kinds *)
type coe_typ = GlobRef.t

Expand All @@ -57,7 +53,7 @@ type inheritance_path = coe_info_typ list
val class_exists : cl_typ -> bool

(** @raise Not_found if this type is not a class *)
val class_info : cl_typ -> cl_info_typ
val class_nparams : cl_typ -> int

(** [find_class_type env sigma c] returns the head reference of [c],
its universe instance and its arguments *)
Expand Down
2 changes: 1 addition & 1 deletion pretyping/pretyping.ml
Expand Up @@ -1423,7 +1423,7 @@ let path_convertible env sigma cl p q =
p'
| [] ->
(* identity function for the class [i]. *)
let params = (class_info cl).cl_param in
let params = class_nparams cl in
let clty =
match cl with
| CL_SORT -> mkGSort (Glob_term.UAnonymous {rigid=false})
Expand Down
50 changes: 37 additions & 13 deletions test-suite/output/relaxed_ambiguous_paths.out
@@ -1,13 +1,24 @@
File "stdin", line 10, characters 0-28:
File "stdin", line 13, characters 0-29:
Warning:
New coercion path [ac; cd] : A >-> D is ambiguous with existing
[ab; bd] : A >-> D. [ambiguous-paths,typechecker]
[ab] : A >-> B
[ac] : A >-> C
[ab; bd] : A >-> D
[bd] : B >-> D
[cd] : C >-> D
File "stdin", line 26, characters 0-28:
New coercion path [g1; f2] : A >-> B' is ambiguous with existing
[f1; g2] : A >-> B'. [ambiguous-paths,typechecker]
File "stdin", line 14, characters 0-29:
Warning:
New coercion path [h1; f3] : B >-> C' is ambiguous with existing
[f2; h2] : B >-> C'. [ambiguous-paths,typechecker]
[f1] : A >-> A'
[g1] : A >-> B
[f1; g2] : A >-> B'
[g1; h1] : A >-> C
[f1; g2; h2] : A >-> C'
[g2] : A' >-> B'
[g2; h2] : A' >-> C'
[f2] : B >-> B'
[h1] : B >-> C
[f2; h2] : B >-> C'
[h2] : B' >-> C'
[f3] : C >-> C'
File "stdin", line 33, characters 0-28:
Warning:
New coercion path [ab; bc] : A >-> C is ambiguous with existing
[ac] : A >-> C. [ambiguous-paths,typechecker]
Expand All @@ -17,6 +28,19 @@ New coercion path [ab; bc] : A >-> C is ambiguous with existing
[bc] : B >-> C
[bc; cd] : B >-> D
[cd] : C >-> D
File "stdin", line 50, characters 0-28:
Warning:
New coercion path [ab; bc] : A >-> C is ambiguous with existing
[ac] : A >-> C. [ambiguous-paths,typechecker]
File "stdin", line 51, characters 0-28:
Warning:
New coercion path [ba; ab] : B >-> B is not definitionally an identity function.
New coercion path [ab; ba] : A >-> A is not definitionally an identity function.
[ambiguous-paths,typechecker]
[ab] : A >-> B
[ac] : A >-> C
[ba] : B >-> A
[bc] : B >-> C
[B_A] : B >-> A
[C_A] : C >-> A
[D_A] : D >-> A
Expand All @@ -31,7 +55,7 @@ New coercion path [ab; bc] : A >-> C is ambiguous with existing
[D_B; B_A'] : D >-> A'
[D_B] : D >-> B
[D_C] : D >-> C
File "stdin", line 121, characters 0-86:
File "stdin", line 147, characters 0-86:
Warning:
New coercion path [D_C; C_A'] : D >-> A' is ambiguous with existing
[D_B; B_A'] : D >-> A'. [ambiguous-paths,typechecker]
Expand All @@ -44,15 +68,15 @@ New coercion path [D_C; C_A'] : D >-> A' is ambiguous with existing
[D_B; B_A'] : D >-> A'
[D_B] : D >-> B
[D_C] : D >-> C
File "stdin", line 130, characters 0-47:
File "stdin", line 156, characters 0-47:
Warning:
New coercion path [unwrap_nat; wrap_nat] : NAT >-> NAT is not definitionally an identity function.
[ambiguous-paths,typechecker]
File "stdin", line 131, characters 0-64:
File "stdin", line 157, characters 0-64:
Warning:
New coercion path [unwrap_list; wrap_list] : LIST >-> LIST is not definitionally an identity function.
[ambiguous-paths,typechecker]
File "stdin", line 132, characters 0-51:
File "stdin", line 158, characters 0-51:
Warning:
New coercion path [unwrap_Type; wrap_Type] : TYPE >-> TYPE is not definitionally an identity function.
[ambiguous-paths,typechecker]