Skip to content

Commit cc58003

Browse files
flambda-backend: Erasability namespace for Jane Syntax attributes/extensions (#1421)
* Add `erasable` and `non_erasable` namespaces for embeddings * Add an attribute test * Rename namespace to erasability * Change Jane_syntax_parsing to take a Feature.t instead of a string * Minimize diff * Update ocaml/parsing/jane_syntax.ml Co-authored-by: Antal Spector-Zabusky <[email protected]> * Update ocaml/parsing/jane_syntax.ml Co-authored-by: Antal Spector-Zabusky <[email protected]> * Apply suggestions from code review Co-authored-by: Antal Spector-Zabusky <[email protected]> * Respond to review comments * make comprehensions examples look non-erasable * hide Erasability definition from mli and move docs accordingly * Update ocaml/parsing/jane_syntax.ml Co-authored-by: Antal Spector-Zabusky <[email protected]> * update tests according to review * Actually hide erasability --------- Co-authored-by: Antal Spector-Zabusky <[email protected]>
1 parent ae9099a commit cc58003

24 files changed

+360
-133
lines changed

parsing/builtin_attributes.ml

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,42 @@ let builtin_attrs =
108108
; "tail_mod_cons"; "ocaml.tail_mod_cons"
109109
]
110110

111-
let builtin_attrs =
112-
let tbl = Hashtbl.create 128 in
113-
List.iter (fun attr -> Hashtbl.add tbl attr ()) builtin_attrs;
114-
tbl
111+
(* nroberts: When we upstream the builtin-attribute whitelisting, we shouldn't
112+
upstream the "jane" prefix.
113+
- Internally, we use "jane.*" to encode our changes to the parsetree,
114+
and our compiler should not drop these attributes.
115+
- Upstream, ppxes may produce attributes with the "jane.*" prefix.
116+
The upstream compiler does not use these attributes. We want it to be
117+
able to drop these attributes without a warning.
118+
119+
It's an error for an upstream ppx to create an attribute that corresponds to
120+
a *non-erasable* Jane language extension, like list comprehensions, which
121+
should never reach the upstream compiler. So, we distinguish that in the
122+
attribute prefix: upstream ppxlib will error out if it sees a ppx creating a
123+
"jane.non_erasable" attribute and be happy to accept a "jane.erasable"
124+
attribute. Meanwhile, an internal patched version of ppxlib will be happy for
125+
a ppx to produce either of these attributes.
126+
*)
127+
let builtin_attr_prefixes =
128+
[ "jane"
129+
]
115130

116-
let is_builtin_attr s = Hashtbl.mem builtin_attrs s
131+
let is_builtin_attr =
132+
let builtin_attrs =
133+
let tbl = Hashtbl.create 128 in
134+
List.iter
135+
(fun attr -> Hashtbl.add tbl attr ())
136+
(builtin_attr_prefixes @ builtin_attrs);
137+
tbl
138+
in
139+
let builtin_attr_prefixes_with_trailing_dot =
140+
List.map (fun x -> x ^ ".") builtin_attr_prefixes
141+
in
142+
fun s ->
143+
Hashtbl.mem builtin_attrs s
144+
|| List.exists
145+
(fun prefix -> String.starts_with ~prefix s)
146+
builtin_attr_prefixes_with_trailing_dot
117147

118148
type attr_tracking_time = Parser | Invariant_check
119149

parsing/jane_syntax.ml

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,25 @@ open Jane_syntax_parsing
1212
that both [comprehensions] and [immutable_arrays] are enabled. But our
1313
general mechanism for checking for enabled extensions (in [of_ast]) won't
1414
work well here: it triggers when converting from
15-
e.g. [[%jane.comprehensions.array] ...] to the comprehensions-specific
16-
AST. But if we spot a [[%jane.comprehensions.immutable]], there is no
17-
expression to translate. So we just check for the immutable arrays extension
18-
when processing a comprehension expression for an immutable array.
15+
e.g. [[%jane.non_erasable.comprehensions.array] ...] to the
16+
comprehensions-specific AST. But if we spot a
17+
[[%jane.non_erasable.comprehensions.immutable]], there is no expression to
18+
translate. So we just check for the immutable arrays extension when
19+
processing a comprehension expression for an immutable array.
1920
2021
Note [Wrapping with make_entire_jane_syntax]
2122
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2223
2324
The topmost node in the encoded AST must always look like e.g.
24-
[%jane.comprehensions]. This allows the decoding machinery to know what
25-
extension is being used and what function to call to do the decoding.
26-
Accordingly, during encoding, after doing the hard work of converting the
27-
extension syntax tree into e.g. Parsetree.expression, we need to make a final
28-
step of wrapping the result in an [%jane.xyz] node. Ideally, this step would
29-
be done by part of our general structure, like we separate [of_ast] and
30-
[of_ast_internal] in the decode structure; this design would make it
31-
structurally impossible/hard to forget taking this final step.
25+
[%jane.non_erasable.comprehensions]. (More generally,
26+
[%jane.ERASABILITY.FEATURE] or [@jane.ERASABILITY.FEATURE].) This allows the
27+
decoding machinery to know what extension is being used and what function to
28+
call to do the decoding. Accordingly, during encoding, after doing the hard
29+
work of converting the extension syntax tree into e.g. Parsetree.expression,
30+
we need to make a final step of wrapping the result in a [%jane.*.xyz] node.
31+
Ideally, this step would be done by part of our general structure, like we
32+
separate [of_ast] and [of_ast_internal] in the decode structure; this design
33+
would make it structurally impossible/hard to forget taking this final step.
3234
3335
However, the final step is only one line of code (a call to
3436
[make_entire_jane_syntax]), but yet the name of the feature varies, as does
@@ -43,7 +45,8 @@ module With_attributes = With_attributes
4345

4446
(** List and array comprehensions *)
4547
module Comprehensions = struct
46-
let extension_string = Language_extension.to_string Comprehensions
48+
let feature : Feature.t = Language_extension Comprehensions
49+
let extension_string = Feature.extension_component feature
4750

4851
type iterator =
4952
| Range of { start : expression
@@ -93,7 +96,7 @@ module Comprehensions = struct
9396

9497
let comprehension_expr names x =
9598
AST.wrap_desc Expression ~attrs:[] ~loc:x.pexp_loc @@
96-
AST.make_jane_syntax Expression (extension_string :: names) x
99+
AST.make_jane_syntax Expression feature names x
97100

98101
(** First, we define how to go from the nice AST to the OCaml AST; this is
99102
the [expr_of_...] family of expressions, culminating in
@@ -142,7 +145,7 @@ module Comprehensions = struct
142145

143146
let expr_of ~loc cexpr =
144147
(* See Note [Wrapping with make_entire_jane_syntax] *)
145-
AST.make_entire_jane_syntax Expression ~loc extension_string (fun () ->
148+
AST.make_entire_jane_syntax Expression ~loc feature (fun () ->
146149
match cexpr with
147150
| Cexp_list_comprehension comp ->
148151
expr_of_comprehension ~type_:["list"] comp
@@ -180,7 +183,7 @@ module Comprehensions = struct
180183
Location.errorf ~loc
181184
"Unknown, unexpected, or malformed@ comprehension embedded term %a"
182185
Embedded_name.pp_quoted_name
183-
Embedded_name.(extension_string :: subparts)
186+
(Embedded_name.of_feature feature subparts)
184187
| No_clauses ->
185188
Location.errorf ~loc
186189
"Tried to desugar a comprehension with no clauses"
@@ -200,11 +203,14 @@ module Comprehensions = struct
200203
attribute removed. *)
201204
let expand_comprehension_extension_expr expr =
202205
match find_and_remove_jane_syntax_attribute expr.pexp_attributes with
203-
| Some (comprehensions :: names, attributes)
204-
when String.equal comprehensions extension_string ->
205-
names, { expr with pexp_attributes = attributes }
206-
| Some (ext_name, _) ->
207-
Desugaring_error.raise expr (Non_comprehension_embedding ext_name)
206+
| Some (ext_name, attributes) -> begin
207+
match Jane_syntax_parsing.Embedded_name.components ext_name with
208+
| comprehensions :: names
209+
when String.equal comprehensions extension_string ->
210+
names, { expr with pexp_attributes = attributes }
211+
| _ :: _ ->
212+
Desugaring_error.raise expr (Non_comprehension_embedding ext_name)
213+
end
208214
| None ->
209215
Desugaring_error.raise expr Non_embedding
210216

@@ -278,12 +284,12 @@ module Immutable_arrays = struct
278284
type nonrec pattern =
279285
| Iapat_immutable_array of pattern list
280286

281-
let extension_string = Language_extension.to_string Immutable_arrays
287+
let feature : Feature.t = Language_extension Immutable_arrays
282288

283289
let expr_of ~loc = function
284290
| Iaexp_immutable_array elts ->
285291
(* See Note [Wrapping with make_entire_jane_syntax] *)
286-
AST.make_entire_jane_syntax Expression ~loc extension_string (fun () ->
292+
AST.make_entire_jane_syntax Expression ~loc feature (fun () ->
287293
Ast_helper.Exp.array elts)
288294

289295
(* Returns remaining unconsumed attributes *)
@@ -294,7 +300,7 @@ module Immutable_arrays = struct
294300
let pat_of ~loc = function
295301
| Iapat_immutable_array elts ->
296302
(* See Note [Wrapping with make_entire_jane_syntax] *)
297-
AST.make_entire_jane_syntax Pattern ~loc extension_string (fun () ->
303+
AST.make_entire_jane_syntax Pattern ~loc feature (fun () ->
298304
Ast_helper.Pat.array elts)
299305

300306
(* Returns remaining unconsumed attributes *)
@@ -311,13 +317,13 @@ module Include_functor = struct
311317
type structure_item =
312318
| Ifstr_include_functor of include_declaration
313319

314-
let extension_string = Language_extension.to_string Include_functor
320+
let feature : Feature.t = Language_extension Include_functor
315321

316322
let sig_item_of ~loc = function
317323
| Ifsig_include_functor incl ->
318324
(* See Note [Wrapping with make_entire_jane_syntax] *)
319-
AST.make_entire_jane_syntax Signature_item ~loc extension_string
320-
(fun () -> Ast_helper.Sig.include_ incl)
325+
AST.make_entire_jane_syntax Signature_item ~loc feature (fun () ->
326+
Ast_helper.Sig.include_ incl)
321327

322328
let of_sig_item sigi = match sigi.psig_desc with
323329
| Psig_include incl -> Ifsig_include_functor incl
@@ -326,8 +332,8 @@ module Include_functor = struct
326332
let str_item_of ~loc = function
327333
| Ifstr_include_functor incl ->
328334
(* See Note [Wrapping with make_entire_jane_syntax] *)
329-
AST.make_entire_jane_syntax Structure_item ~loc extension_string
330-
(fun () -> Ast_helper.Str.include_ incl)
335+
AST.make_entire_jane_syntax Structure_item ~loc feature (fun () ->
336+
Ast_helper.Str.include_ incl)
331337

332338
let of_str_item stri = match stri.pstr_desc with
333339
| Pstr_include incl -> Ifstr_include_functor incl
@@ -339,15 +345,15 @@ module Strengthen = struct
339345
type nonrec module_type =
340346
{ mty : Parsetree.module_type; mod_id : Longident.t Location.loc }
341347

342-
let extension_string = Language_extension.to_string Module_strengthening
348+
let feature : Feature.t = Language_extension Module_strengthening
343349

344350
(* Encoding: [S with M] becomes [functor (_ : S) -> (module M)], where
345351
the [(module M)] is a [Pmty_alias]. This isn't syntax we can write, but
346352
[(module M)] can be the inferred type for [M], so this should be fine. *)
347353

348354
let mty_of ~loc { mty; mod_id } =
349355
(* See Note [Wrapping with make_entire_jane_syntax] *)
350-
AST.make_entire_jane_syntax Module_type ~loc extension_string (fun () ->
356+
AST.make_entire_jane_syntax Module_type ~loc feature (fun () ->
351357
Ast_helper.Mty.functor_ (Named (Location.mknoloc None, mty))
352358
(Ast_helper.Mty.alias mod_id))
353359

0 commit comments

Comments
 (0)