Skip to content

Commit 728ff82

Browse files
committed
Use a more efficient implementation of bound vars
1 parent 9b02a9f commit 728ff82

File tree

2 files changed

+79
-22
lines changed

2 files changed

+79
-22
lines changed

typing/ctype.ml

+70-21
Original file line numberDiff line numberDiff line change
@@ -1829,8 +1829,7 @@ let subst env level priv abbrev oty params args body =
18291829
| _ -> assert false
18301830
in
18311831
abbreviations := abbrev;
1832-
let (params', body') = instance_parameterized_type params body
1833-
in
1832+
let (params', body') = instance_parameterized_type params body in
18341833
abbreviations := ref Mnil;
18351834
let uenv = Expression {env; in_subst = true} in
18361835
try
@@ -2117,16 +2116,66 @@ let expand_head_opt env ty =
21172116
let is_principal ty =
21182117
not !Clflags.principal || get_level ty = generic_level
21192118

2119+
module Bound_vars : sig
2120+
type t
2121+
2122+
val empty : t
2123+
val of_list : type_expr list -> t
2124+
val add : t -> type_expr list -> t
2125+
val union : t -> t -> t
2126+
val mem : t -> type_expr -> bool
2127+
val is_empty : t -> bool
2128+
end = struct
2129+
(* It is rare to care about which variables have been bound. This
2130+
implementation prioritizes the common case of caring only about the
2131+
presence of variables. *)
2132+
type t = { is_empty : bool; var_set : TypeSet.t Lazy.t }
2133+
2134+
let empty = { is_empty = true; var_set = Lazy.from_val TypeSet.empty }
2135+
2136+
let of_list = function
2137+
| [] -> empty
2138+
| new_ones ->
2139+
let var_set =
2140+
lazy (TypeSet.of_list (List.map Transient_expr.repr new_ones))
2141+
in
2142+
{ is_empty = false; var_set }
2143+
2144+
let add t = function
2145+
| [] -> t
2146+
| new_ones ->
2147+
let var_set = lazy begin
2148+
let new_ones_set =
2149+
TypeSet.of_list (List.map Transient_expr.repr new_ones)
2150+
in
2151+
TypeSet.union new_ones_set (Lazy.force t.var_set)
2152+
end in
2153+
{ is_empty = false; var_set }
2154+
2155+
let union ({ is_empty = empty1; var_set = set1 } as t1)
2156+
({ is_empty = empty2; var_set = set2 } as t2) =
2157+
match empty1, empty2 with
2158+
| true, true -> empty
2159+
| true, false -> t2
2160+
| false, true -> t1
2161+
| false, false ->
2162+
let var_set = lazy (TypeSet.union (Lazy.force set1) (Lazy.force set2)) in
2163+
{ is_empty = false; var_set }
2164+
2165+
let mem { is_empty; var_set } ty =
2166+
not is_empty &&
2167+
TypeSet.mem ty (Lazy.force var_set)
2168+
2169+
let is_empty { is_empty } = is_empty
2170+
end
2171+
21202172
type unwrapped_type_expr =
21212173
{ ty : type_expr
2122-
; bound_vars : TypeSet.t
2174+
; bound_vars : Bound_vars.t
21232175
; modality : Mode.Modality.Value.Const.t }
21242176

21252177
let mk_unwrapped_type_expr ty =
2126-
{ ty; bound_vars = TypeSet.empty; modality = Mode.Modality.Value.Const.id }
2127-
2128-
let mk_bound_vars vars_list =
2129-
TypeSet.of_list (List.map Transient_expr.repr vars_list)
2178+
{ ty; bound_vars = Bound_vars.empty; modality = Mode.Modality.Value.Const.id }
21302179

21312180
type unbox_result =
21322181
(* unboxing process made a step: either an unboxing or removal of a [Tpoly] *)
@@ -2163,7 +2212,7 @@ let unbox_once env ty =
21632212
| exception Not_found -> (* but we found it earlier! *) assert false
21642213
in
21652214
Stepped { ty = apply ty2 existentials;
2166-
bound_vars = mk_bound_vars existentials;
2215+
bound_vars = Bound_vars.of_list existentials;
21672216
modality }
21682217
| None -> begin match decl.type_kind with
21692218
| Type_record_unboxed_product ([_], Record_unboxed_product, _) ->
@@ -2173,7 +2222,7 @@ let unbox_once env ty =
21732222
((_::_::_ as lbls), Record_unboxed_product, _) ->
21742223
Stepped_record_unboxed_product
21752224
(List.map (fun ld -> { ty = apply ld.ld_type [];
2176-
bound_vars = TypeSet.empty;
2225+
bound_vars = Bound_vars.empty;
21772226
modality = ld.ld_modalities }) lbls)
21782227
| Type_record_unboxed_product ([], _, _) ->
21792228
Misc.fatal_error "Ctype.unboxed_once: fieldless record"
@@ -2184,7 +2233,7 @@ let unbox_once env ty =
21842233
end
21852234
| Tpoly (ty, bound_vars) ->
21862235
Stepped { ty;
2187-
bound_vars = mk_bound_vars bound_vars;
2236+
bound_vars = Bound_vars.of_list bound_vars;
21882237
modality = Mode.Modality.Value.Const.id }
21892238
| _ -> Final_result
21902239

@@ -2215,7 +2264,7 @@ let rec get_unboxed_type_representation
22152264
let ty = expand_head_opt env ty in
22162265
match unbox_once env ty with
22172266
| Stepped { ty = ty2; bound_vars = bound_vars2; modality = modality2 } ->
2218-
let bound_vars = TypeSet.union bound_vars bound_vars2 in
2267+
let bound_vars = Bound_vars.union bound_vars bound_vars2 in
22192268
let modality =
22202269
Mode.Modality.Value.Const.concat modality ~then_:modality2
22212270
in
@@ -2228,7 +2277,7 @@ let rec get_unboxed_type_representation
22282277
let get_unboxed_type_representation env ty =
22292278
(* Do not give too much fuel: PR#7424 *)
22302279
get_unboxed_type_representation
2231-
~bound_vars:TypeSet.empty
2280+
~bound_vars:Bound_vars.empty
22322281
~modality:Mode.Modality.Value.Const.id
22332282
env ty ty 100
22342283

@@ -2267,8 +2316,8 @@ let rec estimate_type_jkind ~expand_component env ty =
22672316
let { ty; bound_vars = bound_vars2; modality } =
22682317
expand_component ty
22692318
in
2270-
TypeSet.union bound_vars1 bound_vars2, (ty, modality))
2271-
TypeSet.empty ltys
2319+
Bound_vars.union bound_vars1 bound_vars2, (ty, modality))
2320+
Bound_vars.empty ltys
22722321
in
22732322
(* CR layouts v2.8: This pretty ridiculous use of [estimate_type_jkind]
22742323
just to throw most of it away will go away once we get [layout_of]. *)
@@ -2325,7 +2374,7 @@ let rec estimate_type_jkind ~expand_component env ty =
23252374
| Tpackage _ -> Jkind.Builtin.value ~why:First_class_module
23262375

23272376
and close_open_jkind ~expand_component ~bound_vars env jkind =
2328-
if not (TypeSet.is_empty bound_vars)
2377+
if not (Bound_vars.is_empty bound_vars)
23292378
(* if the type has free variables, we can't let these leak into with-bounds *)
23302379
(* CR layouts v2.8: Do better, by rounding only the bound variables up. *)
23312380
then
@@ -2397,7 +2446,7 @@ let constrain_type_jkind ~fixed env ty jkind =
23972446
later).
23982447
23992448
As this unboxes types, it might unbox an existential type. We thus keep
2400-
track of the variables bound as we unbox (and look through [Tpoly]s.
2449+
track of the variables bound as we unbox (and look through [Tpoly]s).
24012450
Comparing these variables in jkinds is fine, and they can't escape from
24022451
this function (except harmlessly in error messages). However, we must be
24032452
sure not to change the kinds of these bound variables; that's the only
@@ -2422,7 +2471,7 @@ let constrain_type_jkind ~fixed env ty jkind =
24222471
the call to [intersection_or_error]. And even if [ty] has unbound
24232472
variables, [ty's_jkind] can't have any variables in it, so we're OK. *)
24242473
| Tvar { jkind = ty's_jkind } when not fixed &&
2425-
not (TypeSet.mem ty bound_vars) ->
2474+
not (Bound_vars.mem bound_vars ty) ->
24262475
(* Unfixed tyvars are special in at least two ways:
24272476
24282477
1) Suppose we're processing [type 'a t = 'a list]. The ['a] on the
@@ -2452,7 +2501,7 @@ let constrain_type_jkind ~fixed env ty jkind =
24522501
(* Handle the [Tpoly] case out here so [Tvar]s wrapped in [Tpoly]s can get
24532502
the treatment above. *)
24542503
| Tpoly (t, bound_vars2) ->
2455-
let bound_vars = TypeSet.union bound_vars (mk_bound_vars bound_vars2) in
2504+
let bound_vars = Bound_vars.add bound_vars bound_vars2 in
24562505
loop ~fuel ~expanded:false t ~bound_vars ty's_jkind jkind
24572506

24582507
| _ ->
@@ -2479,7 +2528,7 @@ let constrain_type_jkind ~fixed env ty jkind =
24792528
let results =
24802529
Misc.Stdlib.List.map3
24812530
(fun { ty; bound_vars = bound_vars2; modality } ty's_jkind jkind ->
2482-
let bound_vars = TypeSet.union bound_vars bound_vars2 in
2531+
let bound_vars = Bound_vars.union bound_vars bound_vars2 in
24832532
let jkind =
24842533
Jkind.apply_modality_r modality jkind
24852534
in
@@ -2529,7 +2578,7 @@ let constrain_type_jkind ~fixed env ty jkind =
25292578
(Jkind.Violation.of_ ~jkind_of_type
25302579
(Not_a_subjkind (ty's_jkind, jkind, sub_failure_reasons)))
25312580
| Stepped { ty; bound_vars = bound_vars2; modality } ->
2532-
let bound_vars = TypeSet.union bound_vars bound_vars2 in
2581+
let bound_vars = Bound_vars.union bound_vars bound_vars2 in
25332582
let jkind = Jkind.apply_modality_r modality jkind in
25342583
loop ~fuel:(fuel - 1) ~expanded:false ty ~bound_vars
25352584
(estimate_type_jkind env ty) jkind
@@ -2547,7 +2596,7 @@ let constrain_type_jkind ~fixed env ty jkind =
25472596
Error (Jkind.Violation.of_ ~jkind_of_type
25482597
(Not_a_subjkind (ty's_jkind, jkind, sub_failure_reasons)))
25492598
in
2550-
loop ~fuel:100 ~expanded:false ty ~bound_vars:TypeSet.empty
2599+
loop ~fuel:100 ~expanded:false ty ~bound_vars:Bound_vars.empty
25512600
(estimate_type_jkind env ty) (Jkind.disallow_left jkind)
25522601

25532602
let type_sort ~why ~fixed env ty =

typing/ctype.mli

+9-1
Original file line numberDiff line numberDiff line change
@@ -569,12 +569,20 @@ val package_subtype :
569569
(* Raises [Incompatible] *)
570570
val mcomp : Env.t -> type_expr -> type_expr -> unit
571571

572+
(* A set of bound variables *)
573+
module Bound_vars : sig
574+
type t
575+
576+
val is_empty : t -> bool
577+
val mem : t -> type_expr -> bool
578+
end
579+
572580
(* represents a type that has been extracted from wrappers that
573581
do not change its runtime representation, such as [@@unboxed]
574582
types and [Tpoly]s *)
575583
type unwrapped_type_expr =
576584
{ ty : type_expr
577-
; bound_vars : Btype.TypeSet.t
585+
; bound_vars : Bound_vars.t
578586
(* vars in scope in [ty] but not in the context to which this
579587
[unwrapped_type_expr] is returned. These come from unwrapped existential
580588
variables and variables bound in [Tpoly] nodes. *)

0 commit comments

Comments
 (0)