Skip to content

Commit 7c3bfe6

Browse files
committed
Track bound_vars in constrain_type_jkind
1 parent 7079753 commit 7c3bfe6

File tree

5 files changed

+117
-59
lines changed

5 files changed

+117
-59
lines changed

testsuite/tests/typing-jkind-bounds/with_basics.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,7 @@ Error: This value is "nonportable" but expected to be "portable".
11871187

11881188
(*********************************************)
11891189
(* Reduction of error seen in the tree *)
1190-
(* This requires the [is_open] technology in Ctype. *)
1190+
(* This requires the [bound_vars] technology in Ctype. *)
11911191

11921192
type 'k t1 = T of Obj.t [@@unboxed]
11931193

testsuite/tests/typing-layouts/basics.ml

+9-1
Original file line numberDiff line numberDiff line change
@@ -2913,5 +2913,13 @@ type ('a : immediate & immediate) t2
29132913
type t3 = t1 t2 (* it is important to reject this *)
29142914

29152915
[%%expect{|
2916-
type t3 = t1 t2
2916+
Line 1, characters 10-12:
2917+
1 | type t3 = t1 t2 (* it is important to reject this *)
2918+
^^
2919+
Error: This type "t1" should be an instance of type
2920+
"('a : immediate & immediate)"
2921+
The kind of t1 is immediate with 'a & immediate with 'a
2922+
because it is an unboxed tuple.
2923+
But the kind of t1 must be a subkind of immediate & immediate
2924+
because of the definition of t2 at line 2, characters 0-36.
29172925
|}]

typing/ctype.ml

+81-52
Original file line numberDiff line numberDiff line change
@@ -1466,6 +1466,14 @@ let instance_parameterized_type ?keep_names sch_args sch =
14661466
(ty_args, ty)
14671467
)
14681468

1469+
let instance_parameterized_type_ex ?keep_names sch_args sch ~existentials =
1470+
For_copy.with_scope (fun copy_scope ->
1471+
let ty_args = List.map (fun t -> copy ?keep_names copy_scope t) sch_args in
1472+
let ty = copy copy_scope sch in
1473+
let existentials = List.map (copy copy_scope) existentials in
1474+
(ty_args, ty, existentials)
1475+
)
1476+
14691477
let instance_parameterized_kind args jkind =
14701478
For_copy.with_scope (fun copy_scope ->
14711479
let ty_args = List.map (fun t -> copy copy_scope t) args in
@@ -1812,7 +1820,7 @@ let instance_prim (desc : Primitive.description) ty =
18121820
let unify_var' = (* Forward declaration *)
18131821
ref (fun _env _ty1 _ty2 -> assert false)
18141822

1815-
let subst env level priv abbrev oty params args body =
1823+
let subst env level priv abbrev oty params args body ~existentials =
18161824
if List.length params <> List.length args then raise Cannot_subst;
18171825
let old_level = !current_level in
18181826
current_level := level;
@@ -1829,14 +1837,16 @@ let subst env level priv abbrev oty params args body =
18291837
| _ -> assert false
18301838
in
18311839
abbreviations := abbrev;
1832-
let (params', body') = instance_parameterized_type params body in
1840+
let (params', body', existentials') =
1841+
instance_parameterized_type_ex params body ~existentials
1842+
in
18331843
abbreviations := ref Mnil;
18341844
let uenv = Expression {env; in_subst = true} in
18351845
try
18361846
!unify_var' uenv body0 body';
18371847
List.iter2 (!unify_var' uenv) params' args;
18381848
current_level := old_level;
1839-
body'
1849+
(body', existentials')
18401850
with Unify _ ->
18411851
current_level := old_level;
18421852
undo_abbrev ();
@@ -1892,11 +1902,11 @@ let jkind_subst env level params args jkind =
18921902
it ensures invariants on types are enforced (decreasing levels), and we don't
18931903
care about efficiency here.
18941904
*)
1895-
let apply ?(use_current_level = false) env params body args =
1905+
let apply ?(use_current_level = false) env params body args ~existentials =
18961906
simple_abbrevs := Mnil;
18971907
let level = if use_current_level then !current_level else generic_level in
18981908
try
1899-
subst env level Public (ref Mnil) None params args body
1909+
subst env level Public (ref Mnil) None params args body ~existentials
19001910
with
19011911
Cannot_subst -> raise Cannot_apply
19021912

@@ -1975,9 +1985,10 @@ let expand_abbrev_gen kind find_type_expansion env ty =
19751985
| (params, body, lv) ->
19761986
(* prerr_endline
19771987
("add a "^string_of_kind kind^" expansion for "^Path.name path);*)
1978-
let ty' =
1988+
let ty', _existentials =
19791989
try
19801990
subst env level kind abbrev (Some ty) params args body
1991+
~existentials:[]
19811992
with Cannot_subst -> raise_escape_exn Constraint
19821993
in
19831994
(* For gadts, remember type as non exportable *)
@@ -2118,11 +2129,14 @@ let is_principal ty =
21182129

21192130
type unwrapped_type_expr =
21202131
{ ty : type_expr
2121-
; is_open : bool
2132+
; bound_vars : TypeSet.t
21222133
; modality : Mode.Modality.Value.Const.t }
21232134

21242135
let mk_unwrapped_type_expr ty =
2125-
{ ty; is_open = false; modality = Mode.Modality.Value.Const.id }
2136+
{ ty; bound_vars = TypeSet.empty; modality = Mode.Modality.Value.Const.id }
2137+
2138+
let mk_bound_vars vars_list =
2139+
TypeSet.of_list (List.map Transient_expr.repr vars_list)
21262140

21272141
type unbox_result =
21282142
(* unboxing process made a step: either an unboxing or removal of a [Tpoly] *)
@@ -2140,7 +2154,9 @@ let unbox_once env ty =
21402154
begin match Env.find_type p env with
21412155
| exception Not_found -> Missing p
21422156
| decl ->
2143-
let apply ty2 = apply env decl.type_params ty2 args in
2157+
let apply ty2 existentials =
2158+
apply env decl.type_params ty2 args ~existentials
2159+
in
21442160
begin match find_unboxed_type decl with
21452161
| Some (ty2, modality) ->
21462162
let ty2 = match get_desc ty2 with Tpoly (t, _) -> t | _ -> ty2 in
@@ -2153,8 +2169,9 @@ let unbox_once env ty =
21532169
| Type_record_unboxed_product _ | Type_open -> []
21542170
| exception Not_found -> (* but we found it earlier! *) assert false
21552171
in
2156-
Stepped { ty = apply ty2;
2157-
is_open = not (Misc.Stdlib.List.is_empty existentials);
2172+
let substed_ty, substed_existentials = apply ty2 existentials in
2173+
Stepped { ty = substed_ty;
2174+
bound_vars = mk_bound_vars substed_existentials;
21582175
modality }
21592176
| None -> begin match decl.type_kind with
21602177
| Type_record_unboxed_product ([_], Record_unboxed_product, _) ->
@@ -2163,8 +2180,8 @@ let unbox_once env ty =
21632180
| Type_record_unboxed_product
21642181
((_::_::_ as lbls), Record_unboxed_product, _) ->
21652182
Stepped_record_unboxed_product
2166-
(List.map (fun ld -> { ty = apply ld.ld_type;
2167-
is_open = false;
2183+
(List.map (fun ld -> { ty = fst (apply ld.ld_type []);
2184+
bound_vars = TypeSet.empty;
21682185
modality = ld.ld_modalities }) lbls)
21692186
| Type_record_unboxed_product ([], _, _) ->
21702187
Misc.fatal_error "Ctype.unboxed_once: fieldless record"
@@ -2175,15 +2192,15 @@ let unbox_once env ty =
21752192
end
21762193
| Tpoly (ty, bound_vars) ->
21772194
Stepped { ty;
2178-
is_open = not (Misc.Stdlib.List.is_empty bound_vars);
2195+
bound_vars = mk_bound_vars bound_vars;
21792196
modality = Mode.Modality.Value.Const.id }
21802197
| _ -> Final_result
21812198

21822199
let contained_without_boxing env ty =
21832200
match get_desc ty with
21842201
| Tconstr _ ->
21852202
begin match unbox_once env ty with
2186-
| Stepped { ty; is_open = _; modality = _ } -> [ty]
2203+
| Stepped { ty; bound_vars = _; modality = _ } -> [ty]
21872204
| Stepped_record_unboxed_product tys ->
21882205
List.map (fun { ty; _ } -> ty) tys
21892206
| Final_result | Missing _ -> []
@@ -2198,28 +2215,30 @@ let contained_without_boxing env ty =
21982215
allowing us to return a type for which a definition was found even if
21992216
we eventually bottom out at a missing cmi file, or otherwise. *)
22002217
let rec get_unboxed_type_representation
2201-
~is_open ~modality env ty_prev ty fuel =
2202-
if fuel < 0 then Error { ty; is_open; modality }
2218+
~bound_vars ~modality env ty_prev ty fuel =
2219+
if fuel < 0 then Error { ty; bound_vars; modality }
22032220
else
22042221
(* We use expand_head_opt version of expand_head to get access
22052222
to the manifest type of private abbreviations. *)
22062223
let ty = expand_head_opt env ty in
22072224
match unbox_once env ty with
2208-
| Stepped { ty = ty2; is_open = is_open2; modality = modality2 } ->
2209-
let is_open = is_open || is_open2 in
2225+
| Stepped { ty = ty2; bound_vars = bound_vars2; modality = modality2 } ->
2226+
let bound_vars = TypeSet.union bound_vars bound_vars2 in
22102227
let modality =
22112228
Mode.Modality.Value.Const.concat modality ~then_:modality2
22122229
in
22132230
get_unboxed_type_representation
2214-
~is_open ~modality env ty ty2 (fuel - 1)
2231+
~bound_vars ~modality env ty ty2 (fuel - 1)
22152232
| Stepped_record_unboxed_product _ | Final_result ->
2216-
Ok { ty; is_open; modality }
2217-
| Missing _ -> Ok { ty = ty_prev; is_open; modality }
2233+
Ok { ty; bound_vars; modality }
2234+
| Missing _ -> Ok { ty = ty_prev; bound_vars; modality }
22182235

22192236
let get_unboxed_type_representation env ty =
22202237
(* Do not give too much fuel: PR#7424 *)
22212238
get_unboxed_type_representation
2222-
~is_open:false ~modality:Mode.Modality.Value.Const.id env ty ty 100
2239+
~bound_vars:TypeSet.empty
2240+
~modality:Mode.Modality.Value.Const.id
2241+
env ty ty 100
22232242

22242243
let get_unboxed_type_approximation env ty =
22252244
match get_unboxed_type_representation env ty with
@@ -2250,12 +2269,14 @@ let rec estimate_type_jkind ~expand_component env ty =
22502269
| Tarrow _ -> Jkind.for_arrow
22512270
| Ttuple elts -> Jkind.for_boxed_tuple elts
22522271
| Tunboxed_tuple ltys ->
2253-
let is_open, tys_modalities =
2272+
let bound_vars, tys_modalities =
22542273
List.fold_left_map
2255-
(fun is_open1 (_lbl, ty) ->
2256-
let { ty; is_open = is_open2; modality } = expand_component ty in
2257-
(is_open1 || is_open2), (ty, modality))
2258-
false ltys
2274+
(fun bound_vars1 (_lbl, ty) ->
2275+
let { ty; bound_vars = bound_vars2; modality } =
2276+
expand_component ty
2277+
in
2278+
TypeSet.union bound_vars1 bound_vars2, (ty, modality))
2279+
TypeSet.empty ltys
22592280
in
22602281
(* CR layouts v2.8: This pretty ridiculous use of [estimate_type_jkind]
22612282
just to throw most of it away will go away once we get [layout_of]. *)
@@ -2267,7 +2288,7 @@ let rec estimate_type_jkind ~expand_component env ty =
22672288
in
22682289
Jkind.Builtin.product
22692290
~why:Unboxed_tuple tys_modalities layouts |>
2270-
close_open_jkind ~expand_component ~is_open env
2291+
close_open_jkind ~expand_component ~bound_vars env
22712292
| Tconstr (p, args, _) -> begin try
22722293
let type_decl = Env.find_type p env in
22732294
let jkind = type_decl.type_jkind in
@@ -2311,11 +2332,10 @@ let rec estimate_type_jkind ~expand_component env ty =
23112332
Jkind.disallow_right
23122333
| Tpackage _ -> Jkind.Builtin.value ~why:First_class_module
23132334

2314-
and close_open_jkind ~expand_component ~is_open env jkind =
2315-
if is_open (* if the type has free variables, we can't let these leak into
2316-
with-bounds *)
2317-
(* CR layouts v2.8: Do better, by tracking the actual free variables and
2318-
rounding only those variables up. *)
2335+
and close_open_jkind ~expand_component ~bound_vars env jkind =
2336+
if not (TypeSet.is_empty bound_vars)
2337+
(* if the type has free variables, we can't let these leak into with-bounds *)
2338+
(* CR layouts v2.8: Do better, by rounding only the bound variables up. *)
23192339
then
23202340
let jkind_of_type ty =
23212341
Some (estimate_type_jkind ~expand_component env ty)
@@ -2324,9 +2344,9 @@ and close_open_jkind ~expand_component ~is_open env jkind =
23242344
else jkind
23252345

23262346
let estimate_type_jkind_unwrapped
2327-
~expand_component env { ty; is_open; modality } =
2347+
~expand_component env { ty; bound_vars; modality } =
23282348
estimate_type_jkind ~expand_component env ty |>
2329-
close_open_jkind ~expand_component ~is_open env |>
2349+
close_open_jkind ~expand_component ~bound_vars env |>
23302350
Jkind.apply_modality modality
23312351

23322352

@@ -2385,15 +2405,19 @@ let constrain_type_jkind ~fixed env ty jkind =
23852405
later).
23862406
23872407
As this unboxes types, it might unbox an existential type. We thus keep
2388-
track of whether [ty] [is_open]. EDIT: This is actually pointless and
2389-
could be removed: #3684.
2408+
track of the variables bound as we unbox (and look through [Tpoly]s.
2409+
Comparing these variables in jkinds is fine, and they can't escape from
2410+
this function (except harmlessly in error messages). However, we must be
2411+
sure not to change the kinds of these bound variables; that's the only
2412+
reason we track them here. See Test 47 in typing-layouts/basics.ml for an
2413+
example.
23902414
23912415
As this unboxed types, it might also encounter modalities. These modalities
23922416
are accommodated by changing [jkind], the expected jkind of the type.
23932417
Trying to apply the modality to the jkind extracted from [ty] would be
23942418
wrong, as it would incorrectly change the jkind on a [Tvar] to mode-cross
23952419
more than necessary. *)
2396-
let rec loop ~fuel ~expanded ty ~is_open ty's_jkind jkind =
2420+
let rec loop ~fuel ~expanded ty ~bound_vars ty's_jkind jkind =
23972421
(* Just succeed if we're comparing against [any] *)
23982422
if Jkind.is_obviously_max jkind then Ok () else
23992423
if fuel < 0 then
@@ -2405,7 +2429,8 @@ let constrain_type_jkind ~fixed env ty jkind =
24052429
(* The [ty's_jkind] we get here is an **r** jkind, necessary for
24062430
the call to [intersection_or_error]. And even if [ty] has unbound
24072431
variables, [ty's_jkind] can't have any variables in it, so we're OK. *)
2408-
| Tvar { jkind = ty's_jkind } when not fixed ->
2432+
| Tvar { jkind = ty's_jkind } when not fixed &&
2433+
not (TypeSet.mem ty bound_vars) ->
24092434
(* Unfixed tyvars are special in at least two ways:
24102435
24112436
1) Suppose we're processing [type 'a t = 'a list]. The ['a] on the
@@ -2434,9 +2459,9 @@ let constrain_type_jkind ~fixed env ty jkind =
24342459

24352460
(* Handle the [Tpoly] case out here so [Tvar]s wrapped in [Tpoly]s can get
24362461
the treatment above. *)
2437-
| Tpoly (t, bound_vars) ->
2438-
let is_open = is_open || not (Misc.Stdlib.List.is_empty bound_vars) in
2439-
loop ~fuel ~expanded:false t ~is_open ty's_jkind jkind
2462+
| Tpoly (t, bound_vars2) ->
2463+
let bound_vars = TypeSet.union bound_vars (mk_bound_vars bound_vars2) in
2464+
loop ~fuel ~expanded:false t ~bound_vars ty's_jkind jkind
24402465

24412466
| _ ->
24422467
match
@@ -2461,11 +2486,12 @@ let constrain_type_jkind ~fixed env ty jkind =
24612486
let recur ty's_jkinds jkinds =
24622487
let results =
24632488
Misc.Stdlib.List.map3
2464-
(fun { ty; is_open = _; modality } ty's_jkind jkind ->
2489+
(fun { ty; bound_vars = bound_vars2; modality } ty's_jkind jkind ->
2490+
let bound_vars = TypeSet.union bound_vars bound_vars2 in
24652491
let jkind =
24662492
Jkind.apply_modality_to_expected modality jkind
24672493
in
2468-
loop ~fuel ~expanded:false ~is_open ty ty's_jkind jkind)
2494+
loop ~fuel ~expanded:false ~bound_vars ty ty's_jkind jkind)
24692495
tys ty's_jkinds jkinds
24702496
in
24712497
if List.for_all Result.is_ok results
@@ -2498,7 +2524,7 @@ let constrain_type_jkind ~fixed env ty jkind =
24982524
if not expanded
24992525
then
25002526
let ty = expand_head_opt env ty in
2501-
loop ~fuel ~expanded:true ty ~is_open
2527+
loop ~fuel ~expanded:true ty ~bound_vars
25022528
(estimate_type_jkind env ty) jkind
25032529
else
25042530
begin match unbox_once env ty with
@@ -2510,10 +2536,10 @@ let constrain_type_jkind ~fixed env ty jkind =
25102536
Error
25112537
(Jkind.Violation.of_ ~jkind_of_type
25122538
(Not_a_subjkind (ty's_jkind, jkind, sub_failure_reasons)))
2513-
| Stepped { ty; is_open = is_open2; modality } ->
2514-
let is_open = is_open || is_open2 in
2539+
| Stepped { ty; bound_vars = bound_vars2; modality } ->
2540+
let bound_vars = TypeSet.union bound_vars bound_vars2 in
25152541
let jkind = Jkind.apply_modality_to_expected modality jkind in
2516-
loop ~fuel:(fuel - 1) ~expanded:false ty ~is_open
2542+
loop ~fuel:(fuel - 1) ~expanded:false ty ~bound_vars
25172543
(estimate_type_jkind env ty) jkind
25182544
| Stepped_record_unboxed_product tys_modalities ->
25192545
product ~fuel:(fuel - 1) tys_modalities
@@ -2529,7 +2555,7 @@ let constrain_type_jkind ~fixed env ty jkind =
25292555
Error (Jkind.Violation.of_ ~jkind_of_type
25302556
(Not_a_subjkind (ty's_jkind, jkind, sub_failure_reasons)))
25312557
in
2532-
loop ~fuel:100 ~expanded:false ty ~is_open:false
2558+
loop ~fuel:100 ~expanded:false ty ~bound_vars:TypeSet.empty
25332559
(estimate_type_jkind env ty) jkind
25342560

25352561
let type_sort ~why ~fixed env ty =
@@ -6163,10 +6189,10 @@ let rec build_subtype env (visited : transient_expr list)
61636189
begin try match get_desc t' with
61646190
Tobject _ when posi && not (opened_object t') ->
61656191
let cl_abbr, body = find_cltype_for_path env p in
6166-
let ty =
6192+
let ty, _existentials =
61676193
try
61686194
subst env !current_level Public abbrev None
6169-
cl_abbr.type_params tl body
6195+
cl_abbr.type_params tl body ~existentials:[]
61706196
with Cannot_subst -> assert false in
61716197
let ty1, tl1 =
61726198
match get_desc ty with
@@ -7142,6 +7168,9 @@ let print_global_state fmt global_state =
71427168
in
71437169
Format.fprintf fmt "@[<1>{@;%a}@]" print_fields global_state
71447170

7171+
let apply ?use_current_level env params body args =
7172+
fst (apply ?use_current_level env params body args ~existentials:[])
7173+
71457174
(*******************************)
71467175
(* checking declaration jkinds *)
71477176
(* this is down here so it can use [is_equal] *)

typing/ctype.mli

+4-1
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,10 @@ val mcomp : Env.t -> type_expr -> type_expr -> unit
574574
types and [Tpoly]s *)
575575
type unwrapped_type_expr =
576576
{ ty : type_expr
577-
; is_open : bool (* are there any unbound variables in this type? *)
577+
; bound_vars : Btype.TypeSet.t
578+
(* vars in scope in [ty] but not in the context to which this
579+
[unwrapped_type_expr] is returned. These come from unwrapped existential
580+
variables and variables bound in [Tpoly] nodes. *)
578581
; modality : Mode.Modality.Value.Const.t }
579582

580583
val get_unboxed_type_representation :

0 commit comments

Comments
 (0)