Skip to content

Commit 05de766

Browse files
committed
Add a new attribute for allowing any mode crossing
Replace the [@@unsafe_allow_any_kind_in_{impl,intf}] attributes with a new [@@unsafe_allow_any_mode_crossing] attribute. This is different in that it: 1. Works on the type declaration, not the inclusion check, so is more powerful - it can be used to illegally mode cross types defined in the same module, or illegally mode cross non-abstract types in interfaces. The latter especially is necessary to fully subsume -allow-illegal-crossing in stdlib 2. Only allows changing the modal bounds of a kind, not the layout - it's unclear that anyone should ever want to unsafely change the layout of a kind; I personally can't think of any sound reason to do so. Some [past discussion][0] on the specific syntax for this attribute suggested specifying the bounds directly on the attribute, rather than using the jkind annotation, but I and others feel reasonably strongly that this way of doing things is a better design for the sake of consistency. [0]: #3385 (comment)
1 parent 4de5a72 commit 05de766

File tree

9 files changed

+178
-185
lines changed

9 files changed

+178
-185
lines changed

parsing/builtin_attributes.ml

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,8 @@ let has_unboxed attrs = has_attribute "unboxed" attrs
504504

505505
let has_boxed attrs = has_attribute "boxed" attrs
506506

507-
let has_unsafe_allow_any_kind_in_intf attrs = has_attribute "unsafe_allow_any_kind_in_intf" attrs
508-
509-
let has_unsafe_allow_any_kind_in_impl attrs = has_attribute "unsafe_allow_any_kind_in_impl" attrs
507+
let has_unsafe_allow_any_mode_crossing attrs =
508+
has_attribute "unsafe_allow_any_mode_crossing" attrs
510509

511510
let parse_empty_payload attr =
512511
match attr.attr_payload with
@@ -609,11 +608,8 @@ let zero_alloc_attribute (attr : Parsetree.attribute) =
609608
let attribute_with_ignored_payload name attr =
610609
when_attribute_is [name; "ocaml." ^ name] attr ~f:(fun () -> ())
611610

612-
let unsafe_allow_any_kind_in_impl_attribute =
613-
attribute_with_ignored_payload "unsafe_allow_any_kind_in_impl"
614-
615-
let unsafe_allow_any_kind_in_intf_attribute =
616-
attribute_with_ignored_payload "unsafe_allow_any_kind_in_intf"
611+
let unsafe_allow_any_mode_crossing_attribute =
612+
attribute_with_ignored_payload "unsafe_allow_any_mode_crossing"
617613

618614
let afl_inst_ratio_attribute attr =
619615
clflags_attribute_with_int_payload attr
@@ -624,7 +620,7 @@ let parse_standard_interface_attributes attr =
624620
principal_attribute attr;
625621
noprincipal_attribute attr;
626622
nolabels_attribute attr;
627-
unsafe_allow_any_kind_in_intf_attribute attr
623+
unsafe_allow_any_mode_crossing_attribute attr
628624

629625
let parse_standard_implementation_attributes attr =
630626
warning_attribute attr;
@@ -636,7 +632,7 @@ let parse_standard_implementation_attributes attr =
636632
flambda_o3_attribute attr;
637633
flambda_oclassic_attribute attr;
638634
zero_alloc_attribute attr;
639-
unsafe_allow_any_kind_in_impl_attribute attr
635+
unsafe_allow_any_mode_crossing_attribute attr
640636

641637
let has_no_mutable_implied_modalities attrs =
642638
has_attribute "no_mutable_implied_modalities" attrs

parsing/builtin_attributes.mli

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
- ocaml.tailcall
3636
- ocaml.tail_mod_cons
3737
- ocaml.unboxed
38-
- ocaml.unsafe_allow_any_kind_in_impl
39-
- ocaml.unsafe_allow_any_kind_in_intf
38+
- ocaml.unsafe_allow_any_mode_crossing
4039
- ocaml.untagged
4140
- ocaml.unrolled
4241
- ocaml.warnerror
@@ -200,8 +199,7 @@ val explicit_arity: Parsetree.attributes -> bool
200199
val has_unboxed: Parsetree.attributes -> bool
201200
val has_boxed: Parsetree.attributes -> bool
202201

203-
val has_unsafe_allow_any_kind_in_impl: Parsetree.attributes -> bool
204-
val has_unsafe_allow_any_kind_in_intf: Parsetree.attributes -> bool
202+
val has_unsafe_allow_any_mode_crossing : Parsetree.attributes -> bool
205203

206204
val parse_standard_interface_attributes : Parsetree.attribute -> unit
207205
val parse_standard_implementation_attributes : Parsetree.attribute -> unit
Lines changed: 124 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,166 @@
11
(* TEST
2-
flags = "-extension layouts_beta";
2+
flags = "-extension layouts_alpha";
33
expect;
44
*)
55

6+
let use_as_value : ('a : value) -> 'a = fun x -> x
7+
let use_uncontended : 'a @ uncontended -> 'a = fun x -> x
8+
69
(* Baseline: if the jkind doesn't match, we should get an error. *)
7-
module Mismatched_no_attrs : sig
8-
type t : float64
9-
end = struct
10-
type t = string
11-
end
10+
type t : value mod uncontended = { mutable contents : string }
1211
[%%expect{|
13-
Lines 3-5, characters 6-3:
14-
3 | ......struct
15-
4 | type t = string
16-
5 | end
17-
Error: Signature mismatch:
18-
Modules do not match:
19-
sig type t = string end
20-
is not included in
21-
sig type t : float64 end
22-
Type declarations do not match:
23-
type t = string
24-
is not included in
25-
type t : float64
26-
The layout of the first is value
27-
because it is the primitive type string.
28-
But the layout of the first must be a sublayout of float64
29-
because of the definition of t at line 2, characters 2-18.
12+
val use_as_value : 'a -> 'a = <fun>
13+
val use_uncontended : ('a : value_or_null). 'a -> 'a = <fun>
14+
Line 5, characters 0-62:
15+
5 | type t : value mod uncontended = { mutable contents : string }
16+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
Error: The kind of type "t" is value
18+
because it's a boxed record type.
19+
But the kind of type "t" must be a subkind of value mod uncontended
20+
because of the annotation on the declaration of the type t.
3021
|}]
3122

32-
(* On the other hand, if we set the correct attributes on both the impl and the intf, we
33-
shouldn't get an error (though, obviously, this is completely unsound!) *)
34-
module Mismatched_with_both_attrs : sig
35-
type t : float64
36-
[@@unsafe_allow_any_kind_in_impl "I love segfaults"]
37-
end = struct
38-
type t = string
39-
[@@unsafe_allow_any_kind_in_intf "I love segfaults"]
40-
end
23+
(* On the other hand, if we set the attribute, we shouldn't get an error. *)
24+
type t : value mod uncontended = { mutable contents : string }
25+
[@@unsafe_allow_any_mode_crossing]
26+
let f (x : t @@ contended) = use_uncontended x
4127
[%%expect{|
42-
module Mismatched_with_both_attrs : sig type t : float64 end
28+
type t : value mod uncontended = { mutable contents : string; }
29+
val f : t @ contended -> t = <fun>
4330
|}]
4431

45-
(* If we set the attributes but *don't* get a kind mismatch, we ought to be fine *)
46-
module Matching : sig
47-
type t : value
48-
[@@unsafe_allow_any_kind_in_impl "I love segfaults"]
49-
end = struct
50-
type t = string
51-
[@@unsafe_allow_any_kind_in_intf "I love segfaults"]
52-
end
32+
(* If we set the attribute but *don't* get a kind mismatch, we ought to be fine *)
33+
type t : value mod many portable uncontended = string
34+
[@@unsafe_allow_any_mode_crossing]
5335
[%%expect{|
54-
Lines 2-3, characters 2-54:
55-
2 | ..type t : value
56-
3 | [@@unsafe_allow_any_kind_in_impl "I love segfaults"]
57-
Warning 212 [unnecessary-allow-any-kind]: [@@allow_any_kind_in_intf] and [@@allow_any_kind_in_impl] set on a
58-
type, but the kind matches. The attributes can be removed.
59-
60-
module Matching : sig type t end
36+
type t = string
6137
|}]
6238

63-
(* If the attr is only on the signature we should get an error *)
64-
module Mismatched_with_attr_on_intf : sig
65-
type t : float64
66-
[@@unsafe_allow_any_kind_in_impl "I love segfaults"]
67-
end = struct
68-
type t = string
69-
end
39+
(* The attribute shouldn't allow us to change the layout *)
40+
type t : float64 mod uncontended = { mutable contents : string }
41+
[@@unsafe_allow_any_mode_crossing]
7042
[%%expect{|
71-
Lines 4-6, characters 6-3:
72-
4 | ......struct
73-
5 | type t = string
74-
6 | end
75-
Error: Signature mismatch:
76-
Modules do not match:
77-
sig type t = string end
78-
is not included in
79-
sig type t : float64 end
80-
Type declarations do not match:
81-
type t = string
82-
is not included in
83-
type t : float64
84-
The layout of the first is value
85-
because it is the primitive type string.
86-
But the layout of the first must be a sublayout of float64
87-
because of the definition of t at lines 2-3, characters 2-54.
43+
Lines 1-2, characters 0-34:
44+
1 | type t : float64 mod uncontended = { mutable contents : string }
45+
2 | [@@unsafe_allow_any_mode_crossing]
46+
Error: The layout of type "t" is value
47+
because it's a boxed record type.
48+
But the layout of type "t" must be a sublayout of float64
49+
because of the annotation on the declaration of the type t.
8850
|}]
8951

90-
(* If the attr is only on the struct we should get an error *)
91-
module Mismatched_with_attr_on_impl : sig
92-
type t : float64
52+
(* Abstract types in signatures should work with the unsafe kind *)
53+
module M : sig
54+
type t : value mod uncontended
9355
end = struct
94-
type t = string
95-
[@@unsafe_allow_any_kind_in_intf "I love segfaults"]
56+
type t : value mod uncontended = { mutable contents : string }
57+
[@@unsafe_allow_any_mode_crossing]
58+
59+
let f (x : t @@ contended) = use_uncontended x
9660
end
9761
[%%expect{|
98-
Lines 3-6, characters 6-3:
99-
3 | ......struct
100-
4 | type t = string
101-
5 | [@@unsafe_allow_any_kind_in_intf "I love segfaults"]
102-
6 | end
103-
Error: Signature mismatch:
104-
Modules do not match:
105-
sig type t = string end
106-
is not included in
107-
sig type t : float64 end
108-
Type declarations do not match:
109-
type t = string
110-
is not included in
111-
type t : float64
112-
The layout of the first is value
113-
because it is the primitive type string.
114-
But the layout of the first must be a sublayout of float64
115-
because of the definition of t at line 2, characters 2-18.
62+
module M : sig type t : value mod uncontended end
11663
|}]
11764

118-
(* Some more complex stuff with functors *)
119-
120-
module type S1 = sig
121-
type t : value
122-
end
65+
module M1 : sig
66+
type t : value mod uncontended = { mutable contents : string }
67+
[@@unsafe_allow_any_mode_crossing]
68+
end = struct
69+
type t : value mod uncontended = { mutable contents : string }
70+
[@@unsafe_allow_any_mode_crossing]
12371

124-
module type S2 = sig
125-
type t : float64
126-
[@@unsafe_allow_any_kind_in_impl]
72+
let f (x : t @@ contended) = use_uncontended x
12773
end
74+
module M2 : sig
75+
type t : value mod uncontended = M1.t = { mutable contents : string }
76+
[@@unsafe_allow_any_mode_crossing]
77+
end = struct
78+
type t : value mod uncontended = M1.t = { mutable contents : string }
79+
[@@unsafe_allow_any_mode_crossing]
12880

129-
module type S1 = sig
130-
type t : value
131-
[@@unsafe_allow_any_kind_in_intf]
81+
let f (x : t @@ contended) = use_uncontended x
13282
end
133-
134-
module F1 (X : S1) : S2 = X
135-
13683
[%%expect{|
137-
module type S1 = sig type t end
138-
module type S2 = sig type t : float64 end
139-
module type S1 = sig type t end
140-
module F1 : functor (X : S1) -> S2
84+
module M1 :
85+
sig type t : value mod uncontended = { mutable contents : string; } end
86+
module M2 :
87+
sig
88+
type t = M1.t : value mod uncontended = { mutable contents : string; }
89+
end
14190
|}]
14291

143-
module F2 (X : S2) : S1 = X
92+
(* Private types still require the attribute *)
93+
module Private : sig
94+
type t : value mod uncontended = private { mutable contents : string }
95+
[@@unsafe_allow_any_mode_crossing]
96+
end = struct
97+
type t : value mod uncontended = { mutable contents : string }
98+
[@@unsafe_allow_any_mode_crossing]
99+
100+
let f (x : t @@ contended) = use_uncontended x
101+
end
144102
[%%expect{|
145-
Line 1, characters 26-27:
146-
1 | module F2 (X : S2) : S1 = X
147-
^
148-
Error: Signature mismatch:
149-
Modules do not match: sig type t = X.t end is not included in S1
150-
Type declarations do not match: type t = X.t is not included in type t
151-
The layout of the first is float64
152-
because of the definition of t at lines 6-7, characters 2-35.
153-
But the layout of the first must be a sublayout of value
154-
because of the definition of t at lines 11-12, characters 2-35.
103+
module Private :
104+
sig
105+
type t : value mod uncontended = private { mutable contents : string; }
106+
end
155107
|}]
156108

157-
(* Non-abstract types can be annotated with [@@unsafe_allow_any_kind_in_intf] too, and get
158-
checked against signatures during inclusion. *)
109+
(* Non-abstract types in signatures should work as long as they specify the attribute *)
110+
module M : sig
111+
type t1 : value mod uncontended = { mutable contents : string }
112+
[@@unsafe_allow_any_mode_crossing]
159113

160-
module M1 : sig
161-
type t : value = string [@@unsafe_allow_any_kind_in_intf]
114+
type t2 : value mod uncontended = private { mutable contents : string }
115+
[@@unsafe_allow_any_mode_crossing]
116+
117+
type t3 : value mod uncontended =
118+
| Immut of string
119+
| Mut of { mutable contents : string }
120+
[@@unsafe_allow_any_mode_crossing]
162121
end = struct
163-
type t = string
164-
end
122+
type t1 : value mod uncontended = { mutable contents : string }
123+
[@@unsafe_allow_any_mode_crossing]
124+
125+
type t2 : value mod uncontended = { mutable contents : string }
126+
[@@unsafe_allow_any_mode_crossing]
165127

166-
module M2 : S2 = M1
128+
type t3 : value mod uncontended =
129+
| Immut of string
130+
| Mut of { mutable contents : string }
131+
[@@unsafe_allow_any_mode_crossing]
167132

133+
let f1 (x : t1 @@ contended) = use_uncontended x
134+
let f2 (x : t2 @@ contended) = use_uncontended x
135+
let f3 (x : t3 @@ contended) = use_uncontended x
136+
end
168137
[%%expect{|
169-
module M1 : sig type t = string end
170-
module M2 : S2
138+
module M :
139+
sig
140+
type t1 : value mod uncontended = { mutable contents : string; }
141+
type t2 : value mod uncontended = private { mutable contents : string; }
142+
type t3
143+
: value mod uncontended =
144+
Immut of string
145+
| Mut of { mutable contents : string; }
146+
end
171147
|}]
172148

173-
module type S3 = sig
174-
type t : value
175-
[@@unsafe_allow_any_kind_in_impl]
176-
end
149+
(* If the kind annotation on a (non-abstract) type has a more general layout than the one
150+
inferred from the declaration, we should be able to use the type as if it had the
151+
inferred layout - but also able to use it as if it had the modal bounds on the
152+
annotation.
153+
*)
154+
module Weaker_layout_stronger_modes : sig
155+
type t : any mod uncontended
156+
end = struct
157+
type t : any mod uncontended = { mutable contents : string }
158+
[@@unsafe_allow_any_mode_crossing]
177159

178-
module M3 : S3 = M1
179-
(* CR aspsmith: This is somewhat unfortunate, if S3 and M1 are defined far away, but it's
180-
unclear how to squash the warning *)
160+
(* The actual kind here looks more like [value mod uncontended] *)
161+
let f1 (x : t @@ contended) = use_uncontended x
162+
let _ = use_as_value ({ contents = "foo" } : t)
163+
end
181164
[%%expect{|
182-
module type S3 = sig type t end
183-
Lines 2-3, characters 2-35:
184-
2 | ..type t : value
185-
3 | [@@unsafe_allow_any_kind_in_impl]
186-
Warning 212 [unnecessary-allow-any-kind]: [@@allow_any_kind_in_intf] and [@@allow_any_kind_in_impl] set on a
187-
type, but the kind matches. The attributes can be removed.
188-
189-
module M3 : S3
165+
module Weaker_layout_stronger_modes : sig type t : any mod uncontended end
190166
|}]

0 commit comments

Comments
 (0)