Skip to content

Commit a784056

Browse files
authored
Merge pull request #12650 from Octachron/fix_printtyp_bugs_5.1
Fix cyclic type error messages in presence of `-short-paths`: 5.1.1 version
2 parents d0992b7 + 2644a57 commit a784056

File tree

3 files changed

+94
-15
lines changed

3 files changed

+94
-15
lines changed

Changes

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ OCaml 5.1.1
66
- #12623, fix the computation of variance composition
77
(Florian Angeletti, report by Vesa Karvonen, review by Gabriel Scherer)
88

9+
- #12645, #12649 fix error messages for cyclic type definitions in presence of
10+
the `-short-paths` flag.
11+
(Florian Angeletti, report by Vesa Karvonen, review by Gabriel Scherer)
12+
913
OCaml 5.1.0 (14 September 2023)
1014
-------------------------------
1115

testsuite/tests/typing-short-paths/errors.ml

+58
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,61 @@ Line 7, characters 9-18:
3434
Error: This expression has type c
3535
It has no method bar
3636
|}]
37+
38+
(** Cycle type definitions *)
39+
40+
type 'a t = 'a t
41+
[%%expect {|
42+
Line 3, characters 0-16:
43+
3 | type 'a t = 'a t
44+
^^^^^^^^^^^^^^^^
45+
Error: The type abbreviation t is cyclic:
46+
'a t = 'a t
47+
|}]
48+
49+
type 'a t = 'a u
50+
and 'a u = 'a v * 'a
51+
and 'a v = 'a w list
52+
and 'a w = 'a option z
53+
and 'a z = 'a t
54+
[%%expect {|
55+
Line 1, characters 0-16:
56+
1 | type 'a t = 'a u
57+
^^^^^^^^^^^^^^^^
58+
Error: The type abbreviation t is cyclic:
59+
'a t = 'a u,
60+
'a u = 'a v * 'a,
61+
'a v * 'a contains 'a v,
62+
'a v = 'a w list,
63+
'a w list contains 'a w,
64+
'a w = 'a option z,
65+
'a option z = 'a option t
66+
|}]
67+
68+
69+
type 'a u = < x : 'a>
70+
and 'a t = 'a t u;;
71+
[%%expect{|
72+
Line 2, characters 0-17:
73+
2 | and 'a t = 'a t u;;
74+
^^^^^^^^^^^^^^^^^
75+
Error: The type abbreviation t is cyclic:
76+
'a t u contains 'a t,
77+
'a t = 'a t u,
78+
'a t u contains 'a t
79+
|}];; (* fails since 4.04 *)
80+
81+
82+
module rec A : sig type t = B.t -> int end = struct type t = B.t -> int end
83+
and B : sig type t = A.t end = struct type t = A.t end;;
84+
[%%expect {|
85+
Line 1, characters 0-75:
86+
1 | module rec A : sig type t = B.t -> int end = struct type t = B.t -> int end
87+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88+
Error: The definition of A.t contains a cycle:
89+
B.t -> int contains B.t,
90+
B.t = B.t,
91+
B.t = B.t -> int,
92+
B.t -> int contains B.t,
93+
B.t = B.t
94+
|}]

typing/typedecl.ml

+32-15
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,12 @@ let add_type ~check id decl env =
9595
Builtin_attributes.warning_scope ~ppwarning:false decl.type_attributes
9696
(fun () -> Env.add_type ~check id decl env)
9797

98-
let enter_type rec_flag env sdecl (id, uid) =
98+
(* Add a dummy type declaration to the environment, with the given arity.
99+
The [type_kind] is [Type_abstract], but there is a generic [type_manifest]
100+
for abbreviations, to allow polymorphic expansion, except if
101+
[abstract_abbrevs] is [true].
102+
This function is only used in [transl_type_decl]. *)
103+
let enter_type ~abstract_abbrevs rec_flag env sdecl (id, uid) =
99104
let needed =
100105
match rec_flag with
101106
| Asttypes.Nonrecursive ->
@@ -111,15 +116,17 @@ let enter_type rec_flag env sdecl (id, uid) =
111116
in
112117
let arity = List.length sdecl.ptype_params in
113118
if not needed then env else
119+
let type_manifest = match sdecl.ptype_manifest, abstract_abbrevs with
120+
| None, _ | Some _, true -> None
121+
| Some _, false -> Some(Ctype.newvar ())
122+
in
114123
let decl =
115124
{ type_params =
116125
List.map (fun _ -> Btype.newgenvar ()) sdecl.ptype_params;
117126
type_arity = arity;
118127
type_kind = Type_abstract;
119128
type_private = sdecl.ptype_private;
120-
type_manifest =
121-
begin match sdecl.ptype_manifest with None -> None
122-
| Some _ -> Some(Ctype.newvar ()) end;
129+
type_manifest;
123130
type_variance = Variance.unknown_signature ~injective:false ~arity;
124131
type_separability = Types.Separability.default_signature ~arity;
125132
type_is_newtype = false;
@@ -782,7 +789,7 @@ let check_abbrev env sdecl (id, decl) =
782789
- if -rectypes is not used, we only allow cycles in the type graph
783790
if they go through an object or polymorphic variant type *)
784791

785-
let check_well_founded env loc path to_check visited ty0 =
792+
let check_well_founded ~abs_env env loc path to_check visited ty0 =
786793
let rec check parents trace ty =
787794
if TypeSet.mem ty parents then begin
788795
(*Format.eprintf "@[%a@]@." Printtyp.raw_type_expr ty;*)
@@ -798,8 +805,8 @@ let check_well_founded env loc path to_check visited ty0 =
798805
| trace -> List.rev trace, false
799806
in
800807
if rec_abbrev
801-
then Recursive_abbrev (Path.name path, env, reaching_path)
802-
else Cycle_in_def (Path.name path, env, reaching_path)
808+
then Recursive_abbrev (Path.name path, abs_env, reaching_path)
809+
else Cycle_in_def (Path.name path, abs_env, reaching_path)
803810
in raise (Error (loc, err))
804811
end;
805812
let (fini, parents) =
@@ -844,11 +851,11 @@ let check_well_founded env loc path to_check visited ty0 =
844851
(* Will be detected by check_regularity *)
845852
Btype.backtrack snap
846853

847-
let check_well_founded_manifest env loc path decl =
854+
let check_well_founded_manifest ~abs_env env loc path decl =
848855
if decl.type_manifest = None then () else
849856
let args = List.map (fun _ -> Ctype.newvar()) decl.type_params in
850857
let visited = ref TypeMap.empty in
851-
check_well_founded env loc path (Path.same path) visited
858+
check_well_founded ~abs_env env loc path (Path.same path) visited
852859
(Ctype.newconstr path args)
853860

854861
(* Given a new type declaration [type t = ...] (potentially mutually-recursive),
@@ -866,7 +873,7 @@ let check_well_founded_manifest env loc path decl =
866873
(we don't have an example at hand where it is necessary), but we
867874
are doing it anyway out of caution.
868875
*)
869-
let check_well_founded_decl env loc path decl to_check =
876+
let check_well_founded_decl ~abs_env env loc path decl to_check =
870877
let open Btype in
871878
(* We iterate on all subexpressions of the declaration to check
872879
"in depth" that no ill-founded type exists. *)
@@ -885,7 +892,7 @@ let check_well_founded_decl env loc path decl to_check =
885892
{type_iterators with it_type_expr =
886893
(fun self ty ->
887894
if TypeSet.mem ty !checked then () else begin
888-
check_well_founded env loc path to_check visited ty;
895+
check_well_founded ~abs_env env loc path to_check visited ty;
889896
checked := TypeSet.add ty !checked;
890897
self.it_do_type_expr self ty
891898
end)} in
@@ -1073,7 +1080,8 @@ let transl_type_decl env rec_flag sdecl_list =
10731080
Ctype.with_local_level_iter ~post:generalize_decl begin fun () ->
10741081
(* Enter types. *)
10751082
let temp_env =
1076-
List.fold_left2 (enter_type rec_flag) env sdecl_list ids_list in
1083+
List.fold_left2 (enter_type ~abstract_abbrevs:false rec_flag)
1084+
env sdecl_list ids_list in
10771085
(* Translate each declaration. *)
10781086
let current_slot = ref None in
10791087
let warn_unused =
@@ -1130,14 +1138,23 @@ let transl_type_decl env rec_flag sdecl_list =
11301138
List.map2 (fun (id, _) sdecl -> (id, sdecl.ptype_loc))
11311139
ids_list sdecl_list
11321140
in
1141+
(* Error messages cannot use the new environment, as this might result in
1142+
non-termination. Instead we use a completely abstract version of the
1143+
temporary environment, giving a reason for why abbreviations cannot be
1144+
expanded (#12645, #12649) *)
1145+
let abs_env =
1146+
List.fold_left2
1147+
(enter_type ~abstract_abbrevs:true rec_flag)
1148+
env sdecl_list ids_list in
11331149
List.iter (fun (id, decl) ->
1134-
check_well_founded_manifest new_env (List.assoc id id_loc_list)
1150+
check_well_founded_manifest ~abs_env new_env (List.assoc id id_loc_list)
11351151
(Path.Pident id) decl)
11361152
decls;
11371153
let to_check =
11381154
function Path.Pident id -> List.mem_assoc id id_loc_list | _ -> false in
11391155
List.iter (fun (id, decl) ->
1140-
check_well_founded_decl new_env (List.assoc id id_loc_list) (Path.Pident id)
1156+
check_well_founded_decl ~abs_env new_env (List.assoc id id_loc_list)
1157+
(Path.Pident id)
11411158
decl to_check)
11421159
decls;
11431160
List.iter
@@ -1818,7 +1835,7 @@ let check_recmod_typedecl env loc recmod_ids path decl =
18181835
(* recmod_ids is the list of recursively-defined module idents.
18191836
(path, decl) is the type declaration to be checked. *)
18201837
let to_check path = Path.exists_free recmod_ids path in
1821-
check_well_founded_decl env loc path decl to_check;
1838+
check_well_founded_decl ~abs_env:env env loc path decl to_check;
18221839
check_regularity ~orig_env:env env loc path decl to_check;
18231840
(* additionally check coherece, as one might build an incoherent signature,
18241841
and use it to build an incoherent module, cf. #7851 *)

0 commit comments

Comments
 (0)