Skip to content

Commit 458d293

Browse files
committed
Refactoring: Make file for zero_alloc typing stuff
1 parent adcf36d commit 458d293

File tree

12 files changed

+148
-90
lines changed

12 files changed

+148
-90
lines changed

ocaml/compilerlibs/Makefile.compilerlibs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ TYPING = \
8989
typing/jkind_types.cmo \
9090
typing/primitive.cmo \
9191
typing/shape.cmo \
92+
typing/zero_alloc.cmo \
9293
typing/types.cmo \
9394
typing/jkind.cmo \
9495
typing/btype.cmo \

ocaml/dune

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@
8888
jane_syntax ; manual update: mli only files asttypes parsetree
8989

9090
;; TYPING
91-
ident path jkind primitive shape shape_reduce types btype oprint subst predef datarepr
91+
ident path jkind primitive shape shape_reduce zero_alloc types btype oprint subst
92+
predef datarepr
9293
global_module cmi_format persistent_env env errortrace mode jkind_types jkind_intf
9394
typedtree printtyped ctype printtyp includeclass mtype envaux includecore
9495
tast_iterator tast_mapper signature_group cmt_format cms_format untypeast
@@ -291,6 +292,7 @@
291292
(jkind.mli as compiler-libs/jkind.mli)
292293
(jkind_types.mli as compiler-libs/jkind_types.mli)
293294
(primitive.mli as compiler-libs/primitive.mli)
295+
(zero_alloc.mli as compiler-libs/zero_alloc.mli)
294296
(types.mli as compiler-libs/types.mli)
295297
(btype.mli as compiler-libs/btype.mli)
296298
(binutils.mli as compiler-libs/binutils.mli)

ocaml/otherlibs/dynlink/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ COMPILERLIBS_SOURCES=\
121121
typing/shape.ml \
122122
typing/jkind_types.ml \
123123
typing/primitive.ml \
124+
typing/zero_alloc.ml \
124125
typing/types.ml \
125126
typing/jkind.ml \
126127
typing/typedtree.ml \

ocaml/otherlibs/dynlink/dune

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
jkind_intf
9595
jkind_types
9696
primitive
97+
zero_alloc
9798
types
9899
jkind
99100
value_rec_types
@@ -198,6 +199,7 @@
198199
(copy_files ../../typing/solver.ml)
199200
(copy_files ../../typing/shape_reduce.ml)
200201
(copy_files ../../typing/mode.ml)
202+
(copy_files ../../typing/zero_alloc.ml)
201203
(copy_files ../../typing/types.ml)
202204
(copy_files ../../typing/btype.ml)
203205
(copy_files ../../typing/subst.ml)
@@ -267,6 +269,7 @@
267269
(copy_files ../../typing/solver.mli)
268270
(copy_files ../../typing/shape_reduce.mli)
269271
(copy_files ../../typing/mode.mli)
272+
(copy_files ../../typing/zero_alloc.mli)
270273
(copy_files ../../typing/types.mli)
271274
(copy_files ../../typing/btype.mli)
272275
(copy_files ../../typing/subst.mli)
@@ -379,6 +382,7 @@
379382
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Mode.cmo
380383
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Jkind_intf.cmo
381384
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Jkind_types.cmo
385+
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Zero_alloc.cmo
382386
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Types.cmo
383387
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Attr_helper.cmo
384388
.dynlink_compilerlibs.objs/byte/dynlink_compilerlibs__Primitive.cmo
@@ -462,6 +466,7 @@
462466
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Mode.cmx
463467
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Jkind_intf.cmx
464468
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Jkind_types.cmx
469+
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Zero_alloc.cmx
465470
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Types.cmx
466471
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Attr_helper.cmx
467472
.dynlink_compilerlibs.objs/native/dynlink_compilerlibs__Primitive.cmx

ocaml/typing/includecore.ml

Lines changed: 5 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ open Typedtree
2323

2424
type position = Errortrace.position = First | Second
2525

26-
module ZA = Zero_alloc_utils
27-
2826
(* Inclusion between value descriptions *)
2927

3028
type primitive_mismatch =
@@ -43,8 +41,7 @@ type value_mismatch =
4341
| Primitive_mismatch of primitive_mismatch
4442
| Not_a_primitive
4543
| Type of Errortrace.moregen_error
46-
| Zero_alloc of { missing_entirely : bool }
47-
| Zero_alloc_arity of int * int
44+
| Zero_alloc of Zero_alloc.error
4845
| Modality of Mode.Modality.Value.error
4946

5047
exception Dont_match of value_mismatch
@@ -92,72 +89,6 @@ let primitive_descriptions pd1 pd2 =
9289
else
9390
native_repr_args pd1.prim_native_repr_args pd2.prim_native_repr_args
9491

95-
let zero_alloc za1 za2 =
96-
(* The core of the check here is that we translate both attributes into the
97-
abstract domain and use the existing inclusion check from there, ensuring
98-
what we do in the typechecker matches the backend.
99-
100-
There are a few additional details:
101-
102-
- [opt] is not captured by the abstract domain, so we need a special check
103-
for it. But it doesn't interact at all with the abstract domain - it's
104-
just about whether or not the check happens - so this special check can
105-
be fully separate.
106-
- [arity] is also not captured by the abstract domain - it exists only for
107-
use here, in typechecking. If the arities do not match, we issue an
108-
error. It's essential for the soundness of the way we (will, in the next
109-
PR) use zero_alloc in signatures that the apparent arity of the type in
110-
the signature matches the syntactic arity of the function.
111-
- [ignore] can not appear in zero_alloc attributes in signatures, and is
112-
erased from structure items when computing their signature, so we don't
113-
need to consider it here.
114-
*)
115-
let open Builtin_attributes in
116-
(* abstract domain check *)
117-
let abstract_value za =
118-
match za with
119-
| Default_zero_alloc | Ignore_assert_all -> ZA.Assume_info.Value.top ()
120-
| Check { strict; _ } ->
121-
ZA.Assume_info.Value.of_annotation ~strict ~never_returns_normally:false
122-
~never_raises:false
123-
| Assume { strict; never_returns_normally; never_raises; } ->
124-
ZA.Assume_info.Value.of_annotation ~strict ~never_returns_normally
125-
~never_raises
126-
in
127-
let v1 = abstract_value za1 in
128-
let v2 = abstract_value za2 in
129-
if not (ZA.Assume_info.Value.lessequal v1 v2) then
130-
begin let missing_entirely =
131-
match za1 with
132-
| Default_zero_alloc -> true
133-
| Ignore_assert_all | Check _ | Assume _ -> false
134-
in
135-
raise (Dont_match (Zero_alloc {missing_entirely}))
136-
end;
137-
(* opt check *)
138-
begin match za1, za2 with
139-
| Check { opt = opt1; _ }, Check { opt = opt2; _ } ->
140-
if opt1 && not opt2 then
141-
raise (Dont_match (Zero_alloc {missing_entirely = false}))
142-
| (Check _ | Default_zero_alloc | Assume _ | Ignore_assert_all), _ -> ()
143-
end;
144-
(* arity check *)
145-
let get_arity = function
146-
| Check { arity; _ } | Assume { arity; _ } -> Some arity
147-
| Default_zero_alloc | Ignore_assert_all -> None
148-
in
149-
match get_arity za1, get_arity za2 with
150-
| Some arity1, Some arity2 ->
151-
(* Check *)
152-
if not (arity1 = arity2) then
153-
raise (Dont_match (Zero_alloc_arity (arity1, arity2)))
154-
| Some _, None -> ()
155-
(* Forgetting zero_alloc info is fine *)
156-
| None, Some _ ->
157-
(* Fabricating it is not, but earlier cases should have ruled this out *)
158-
Misc.fatal_error "Includecore.check_attributes"
159-
| None, None -> ()
160-
16192
let value_descriptions ~loc env name
16293
(vd1 : Types.value_description)
16394
(vd2 : Types.value_description) =
@@ -167,7 +98,9 @@ let value_descriptions ~loc env name
16798
loc
16899
vd1.val_attributes vd2.val_attributes
169100
name;
170-
zero_alloc vd1.val_zero_alloc vd2.val_zero_alloc;
101+
begin try Zero_alloc.sub_exn vd1.val_zero_alloc vd2.val_zero_alloc with
102+
| Zero_alloc.Error e -> raise (Dont_match (Zero_alloc e))
103+
end;
171104
begin match Mode.Modality.Value.sub vd1.val_modalities vd2.val_modalities with
172105
| Ok () -> ()
173106
| Error e -> raise (Dont_match (Modality e))
@@ -386,16 +319,7 @@ let report_value_mismatch first second env ppf err =
386319
Printtyp.report_moregen_error ppf Type_scheme env trace
387320
(fun ppf -> Format.fprintf ppf "The type")
388321
(fun ppf -> Format.fprintf ppf "is not compatible with the type")
389-
| Zero_alloc { missing_entirely } ->
390-
pr "The former provides a weaker \"zero_alloc\" guarantee than the latter.";
391-
if missing_entirely then
392-
pr "@ Hint: Add a \"zero_alloc\" attribute to the implementation."
393-
| Zero_alloc_arity (n1, n2) ->
394-
pr "zero_alloc arity mismatch:@ \
395-
When using \"zero_alloc\" in a signature, the syntactic arity of@ \
396-
the implementation must match the function type in the interface.@ \
397-
Here the former is %d and the latter is %d."
398-
n1 n2
322+
| Zero_alloc e -> Zero_alloc.print_error ppf e
399323
| Modality e -> report_modality_sub_error first second ppf e
400324

401325
let report_type_inequality env ppf err =

ocaml/typing/includecore.mli

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ type value_mismatch =
3636
| Primitive_mismatch of primitive_mismatch
3737
| Not_a_primitive
3838
| Type of Errortrace.moregen_error
39-
| Zero_alloc of { missing_entirely : bool }
40-
| Zero_alloc_arity of int * int
39+
| Zero_alloc of Zero_alloc.error
4140
| Modality of Mode.Modality.Value.error
4241

4342
exception Dont_match of value_mismatch

ocaml/typing/typedtree.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ and expression_desc =
146146
ret_mode : Mode.Alloc.l;
147147
ret_sort : Jkind.sort;
148148
alloc_mode : Mode.Alloc.r;
149-
zero_alloc : Builtin_attributes.zero_alloc_attribute;
149+
zero_alloc : Zero_alloc.t;
150150
}
151151
| Texp_apply of
152152
expression * (arg_label * apply_arg) list * apply_position *
@@ -1093,7 +1093,7 @@ let let_bound_idents_with_modes_sorts_and_checks bindings =
10931093
(fun (id, _, _, _) ->
10941094
let zero_alloc =
10951095
Option.value (Ident.Map.find_opt id checks)
1096-
~default:Builtin_attributes.Default_zero_alloc
1096+
~default:Zero_alloc.Default_zero_alloc
10971097
in
10981098
id, List.rev (Ident.Tbl.find_all modes_and_sorts id), zero_alloc)
10991099
(rev_let_bound_idents_full bindings)

ocaml/typing/typedtree.mli

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ and expression_desc =
266266
ret_sort : Jkind.sort;
267267
alloc_mode : Mode.Alloc.r;
268268
(* Mode at which the closure is allocated *)
269-
zero_alloc : Builtin_attributes.zero_alloc_attribute
269+
zero_alloc : Zero_alloc.t;
270270
(* zero-alloc attributes *)
271271
}
272272
(** fun P0 P1 -> function p1 -> e1 | p2 -> e2 (body = Tfunction_cases _)
@@ -1157,7 +1157,7 @@ val let_bound_idents_full:
11571157
val let_bound_idents_with_modes_sorts_and_checks:
11581158
value_binding list
11591159
-> (Ident.t * (Location.t * Mode.Value.l * Jkind.sort) list
1160-
* Builtin_attributes.zero_alloc_attribute) list
1160+
* Zero_alloc.t) list
11611161

11621162
(** Alpha conversion of patterns *)
11631163
val alpha_pat:

ocaml/typing/types.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ module type Wrapped = sig
441441
val_modalities : Mode.Modality.Value.t; (* Modalities on the value *)
442442
val_kind: value_kind;
443443
val_loc: Location.t;
444-
val_zero_alloc: Builtin_attributes.zero_alloc_attribute;
444+
val_zero_alloc: Zero_alloc.t;
445445
val_attributes: Parsetree.attributes;
446446
val_uid: Uid.t;
447447
}

ocaml/typing/types.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ module type Wrapped = sig
746746
val_modalities: Mode.Modality.Value.t; (* Modalities on the value *)
747747
val_kind: value_kind;
748748
val_loc: Location.t;
749-
val_zero_alloc: Builtin_attributes.zero_alloc_attribute;
749+
val_zero_alloc: Zero_alloc.t;
750750
val_attributes: Parsetree.attributes;
751751
val_uid: Uid.t;
752752
}

ocaml/typing/zero_alloc.ml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
module ZA = Zero_alloc_utils
2+
3+
type t = Builtin_attributes.zero_alloc_attribute =
4+
| Default_zero_alloc
5+
| Ignore_assert_all
6+
| Check of { strict: bool;
7+
opt: bool;
8+
arity: int;
9+
loc: Location.t;
10+
}
11+
| Assume of { strict: bool;
12+
never_returns_normally: bool;
13+
never_raises: bool;
14+
arity: int;
15+
loc: Location.t;
16+
}
17+
18+
type error =
19+
| Less_general of { missing_entirely : bool }
20+
| Arity_mismatch of int * int
21+
22+
exception Error of error
23+
24+
let print_error ppf error =
25+
let pr fmt = Format.fprintf ppf fmt in
26+
match error with
27+
| Less_general { missing_entirely } ->
28+
pr "The former provides a weaker \"zero_alloc\" guarantee than the latter.";
29+
if missing_entirely then
30+
pr "@ Hint: Add a \"zero_alloc\" attribute to the implementation."
31+
| Arity_mismatch (n1, n2) ->
32+
pr "zero_alloc arity mismatch:@ \
33+
When using \"zero_alloc\" in a signature, the syntactic arity of@ \
34+
the implementation must match the function type in the interface.@ \
35+
Here the former is %d and the latter is %d."
36+
n1 n2
37+
38+
let sub_exn za1 za2 =
39+
(* The core of the check here is that we translate both attributes into the
40+
abstract domain and use the existing inclusion check from there, ensuring
41+
what we do in the typechecker matches the backend.
42+
43+
There are a few additional details:
44+
45+
- [opt] is not captured by the abstract domain, so we need a special check
46+
for it. But it doesn't interact at all with the abstract domain - it's
47+
just about whether or not the check happens - so this special check can
48+
be fully separate.
49+
- [arity] is also not captured by the abstract domain - it exists only for
50+
use here, in typechecking. If the arities do not match, we issue an
51+
error. It's essential for the soundness of the way we (will, in the next
52+
PR) use zero_alloc in signatures that the apparent arity of the type in
53+
the signature matches the syntactic arity of the function.
54+
- [ignore] can not appear in zero_alloc attributes in signatures, and is
55+
erased from structure items when computing their signature, so we don't
56+
need to consider it here.
57+
*)
58+
let open Builtin_attributes in
59+
(* abstract domain check *)
60+
let abstract_value za =
61+
match za with
62+
| Default_zero_alloc | Ignore_assert_all -> ZA.Assume_info.Value.top ()
63+
| Check { strict; _ } ->
64+
ZA.Assume_info.Value.of_annotation ~strict ~never_returns_normally:false
65+
~never_raises:false
66+
| Assume { strict; never_returns_normally; never_raises; } ->
67+
ZA.Assume_info.Value.of_annotation ~strict ~never_returns_normally
68+
~never_raises
69+
in
70+
let v1 = abstract_value za1 in
71+
let v2 = abstract_value za2 in
72+
if not (ZA.Assume_info.Value.lessequal v1 v2) then
73+
begin let missing_entirely =
74+
match za1 with
75+
| Default_zero_alloc -> true
76+
| Ignore_assert_all | Check _ | Assume _ -> false
77+
in
78+
raise (Error (Less_general {missing_entirely}))
79+
end;
80+
(* opt check *)
81+
begin match za1, za2 with
82+
| Check { opt = opt1; _ }, Check { opt = opt2; _ } ->
83+
if opt1 && not opt2 then
84+
raise (Error (Less_general {missing_entirely = false}))
85+
| (Check _ | Default_zero_alloc | Assume _ | Ignore_assert_all), _ -> ()
86+
end;
87+
(* arity check *)
88+
let get_arity = function
89+
| Check { arity; _ } | Assume { arity; _ } -> Some arity
90+
| Default_zero_alloc | Ignore_assert_all -> None
91+
in
92+
match get_arity za1, get_arity za2 with
93+
| Some arity1, Some arity2 ->
94+
(* Check *)
95+
if not (arity1 = arity2) then
96+
raise (Error (Arity_mismatch (arity1, arity2)))
97+
| Some _, None -> ()
98+
(* Forgetting zero_alloc info is fine *)
99+
| None, Some _ ->
100+
(* Fabricating it is not, but earlier cases should have ruled this out *)
101+
Misc.fatal_error "Zero_alloc: sub"
102+
| None, None -> ()

ocaml/typing/zero_alloc.mli

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
type t = Builtin_attributes.zero_alloc_attribute =
2+
| Default_zero_alloc
3+
| Ignore_assert_all
4+
| Check of { strict: bool;
5+
opt: bool;
6+
arity: int;
7+
loc: Location.t;
8+
}
9+
| Assume of { strict: bool;
10+
never_returns_normally: bool;
11+
never_raises: bool;
12+
arity: int;
13+
loc: Location.t;
14+
}
15+
16+
type error
17+
18+
exception Error of error
19+
20+
val print_error : Format.formatter -> error -> unit
21+
22+
(* [sub_exn t1 t2] checks whether the zero_alloc check t1 is stronger than the
23+
zero_alloc check t2. If not, it raises [Error]. *)
24+
val sub_exn : t -> t -> unit

0 commit comments

Comments
 (0)