jon.recoil.org

Source file attribute_handler_intf.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
open! Stdppx
open! Import
open Language.Typed
module Type = Language.Type

module Definitions = struct
  type lookup =
    | Preserve_atoms
    (** When evaluating, do not expand identifiers into the set they represent. This form
        of lookup is used when evaluating set mono and poly attributes for the purposes of
        mangling, because we mangle by _names_ of sets, not canonical representations of
        their contents. *)
    | Expand_atoms_bound_to_sets
    (** When evaluating, fully expand all identifiers. This form of lookup is used when
        evaluating the right hand side of a non-set binding, since here we wish to bind
        the pattern on the left against all values in the set on the right *)

  module Binding = struct
    type 'a t =
      { pattern : 'a Pattern.t
      ; expression : ('a, Expression.set) Expression.t Loc.t
      ; lookup : lookup
      }

    module Selector = struct
      type ('bind, 'select) t =
        | Id : ('a, 'a) t (** most bindings *)
        | Fst : ('a * 'b, 'a) t (** [[@@alloc (a @ m) = ...]] and similar bindings *)
    end

    module With_selector = struct
      type nonrec ('bind, 'select) t =
        { binding : 'bind t
        ; selector : ('bind, 'select Type.non_tuple) Selector.t
        (** When an item [let lhs = rhs [@@attr pat = (expr1, expr2)]] is evaluated e.g.
            on [expr1], the expression gets evaluated to a [Value.t]. A selector
            determines what part of the [value] to use to mangle [lhs]. This is used to
            enable [[@@alloc (a @ m) = ...]] to mangle only based on [a]. *)
        }

      type 'select packed = P : (_, 'select) t -> 'select packed
    end
  end

  module Poly = struct
    type t =
      | Poly :
          'select Type.non_tuple Axis.t * 'select Binding.With_selector.packed list
          -> t
  end

  module Mono = struct
    type t = Expression.Basic.packed Loc.t list Explicitness.With.t Axis.Map.t
  end

  type poly_w =
    [ `value_binding
    | `value_description
    | `module_binding
    | `module_declaration
    | `type_declaration
    | `module_type_declaration
    | `include_infos
    ]

  type mono_w =
    [ `expression
    | `module_expr
    | `core_type
    | `module_type
    ]

  type exclave_if_w = [ `expression ]

  type zero_alloc_if_w =
    [ `expression
    | `value_binding
    | `value_description
    ]

  type any_w =
    [ poly_w
    | mono_w
    | zero_alloc_if_w
    ]

  module Context = struct
    type ('a, 'w) t =
      | Expression : (expression, [> `expression ]) t
      | Module_expr : (module_expr, [> `module_expr ]) t
      | Core_type : (core_type, [> `core_type ]) t
      | Module_type : (module_type, [> `module_type ]) t
      | Value_binding : (value_binding, [> `value_binding ]) t
      | Value_description : (value_description, [> `value_description ]) t
      | Module_binding : (module_binding, [> `module_binding ]) t
      | Module_declaration : (module_declaration, [> `module_declaration ]) t
      | Type_declaration : (type_declaration, [> `type_declaration ]) t
      | Module_type_declaration :
          (module_type_declaration, [> `module_type_declaration ]) t
      | Include_infos :
          ((module_expr, module_type) Either.t include_infos, [> `include_infos ]) t

    type 'a poly = ('a, poly_w) t
    type 'a mono = ('a, mono_w) t
    type 'a zero_alloc_if = ('a, zero_alloc_if_w) t
    type 'a any = ('a, any_w) t
  end

  module Exclave_if = struct
    module Reason = struct
      type t =
        | May_return_local
        | May_return_regional
        | Will_return_unboxed
    end

    (** Represents attributes that introduce conditional exclaves into the code. [loc] is
        the location of the attribute's payload. [expr] is the expression (mode, alloc,
        etc.) to compare against to determine whether to apply [[@@exclave_if_*]].
        [reasons] is the list of reasons for applying unconditionally, out of a list of
        pre-defined reasons. *)
    type ('typ, 'reasons) t =
      { expr : ('typ, Expression.singleton) Expression.t Loc.t
      ; reasons : 'reasons
      }
  end

  module Zero_alloc_if = struct
    (** Represents attributes that optionally annotate code as zero-alloc. [loc] is the
        location of the attribute's payload. [expr] is the expression (mode, alloc, etc.)
        to compare against to determine whether to apply [[@@zero_alloc]]. [args] is the
        arguments to be given to the [[@@zero_alloc]] attribute. *)
    type 'a t =
      { loc : location
      ; expr : ('a, Expression.singleton) Expression.t Loc.t
      ; args : expression list
      }
  end
end

module type Attribute_handler = sig
  include module type of struct
    include Definitions
  end

  module Binding : sig
    include module type of struct
      include Binding
    end

    module Selector : sig
      include module type of struct
        include Selector
      end

      val select : ('bind, 'select) t -> 'bind Value.t -> 'select Value.t
    end
  end

  module Context : sig
    include module type of struct
      include Context
    end

    type 'w packed = private T : (_, 'w) t -> 'w packed [@@unboxed]

    val poly_to_any : 'a poly -> 'a any
    val mono_to_any : 'a mono -> 'a any
    val zero_alloc_if_to_any : 'a zero_alloc_if -> 'a any
    val location : ('a, 'b) t -> 'a -> Location.t
  end

  (** A [('w, 'b) t] is a handler that knows how to consume a particular attribute on ['w]
      syntax items, and produce back ['b] values. Note: if the attribute isn't present,
      the handler will return some default ['b], whether that be [None] (if ['b] is
      [_ option]) or some semantic default for the given ['b]. *)
  type ('w, 'b) t

  (** [consume t ctx item] runs the handler [t] in the context [ctx] on [item]. The
      handler [t] strips its corresponding attributes from [item] in addition to producing
      its output. *)
  val consume : ('w, 'b) t -> ('a, 'w) Context.t -> 'a -> ('a * 'b, Syntax_error.t) result

  (** A map from [('a, 'w) Context.t] to [('a, 'b) Attribute.t]. *)
  module Attribute_map : sig
    type ('w, 'b) t

    val find_exn
      :  ('w, 'b) t
      -> ('a, 'w) Context.t
      -> ('a, 'b) Attribute.t Explicitness.Each.t
  end

  module Poly : module type of struct
    include Poly
  end

  (** A handler for attributes that make definitions/declarations polymorphic. Might
      return an [Error _] if the attribute's payload is malformed. Defaults to
      [Ok { kinds = None; modes = None }]. *)
  val poly : (poly_w, Poly.t Explicitness.With.t list) t

  module Mono : sig
    include module type of struct
      include Mono
    end

    val contexts : mono_w Context.packed list

    type attr :=
      ( mono_w
        , (Expression.Basic.packed Loc.t list, Syntax_error.t) result )
        Attribute_map.t

    val kind_attr : attr
    val kind_set_attr : attr
    val mode_attr : attr
    val modality_attr : attr
    val alloc_attr : attr
  end

  (** A handler for attributes that mangle identifiers to the correct monomorphized name.
      Defaults to [{ kinds = []; modes = [] }]. *)
  val mono : (mono_w, Mono.t) t

  module Exclave_if : sig
    include module type of struct
      include Exclave_if
    end

    module Reason : sig
      include module type of struct
        include Exclave_if.Reason
      end
    end
  end

  (** A handler for attributes that optionally insert [exclave_] markers. We expect to
      replace these attributes with mode-polymorphic tailcalls and/or unboxed types. *)
  val exclave_if_local
    : (exclave_if_w, (Type.mode, Exclave_if.Reason.t list) Exclave_if.t option) t

  (** Like {!exclave_if_local}, but for allocation identifiers. *)
  val exclave_if_stack : (exclave_if_w, (Type.alloc, unit) Exclave_if.t option) t

  module Zero_alloc_if : sig
    include module type of struct
      include Zero_alloc_if
    end
  end

  (** A handler for attributes that optionally annotate code as zero-alloc. *)
  val zero_alloc_if_local : (zero_alloc_if_w, Type.mode Zero_alloc_if.t option) t

  (** Like {!zero_alloc_if_local}, but for allocation identifiers. *)
  val zero_alloc_if_stack : (zero_alloc_if_w, Type.alloc Zero_alloc_if.t option) t

  val with_ : ([ `module_type ], signature option) t
  val with_attr : (module_type, (signature, Syntax_error.t) result) Attribute.t
  val functor_portable : ([ `module_binding | `module_declaration ], string loc option) t
  val functor_stateless : ([ `module_binding | `module_declaration ], string loc option) t

  val error_you_can_only_use_one_attribute_per_axis
    :  loc:location
    -> (_, Syntax_error.t) result

  module Floating : sig
    type poly :=
      [ `structure_item
      | `signature_item
      ]

    module Context : sig
      type ('a, 'w) t =
        | Structure_item : (structure_item, [> `structure_item ]) t
        | Signature_item : (signature_item, [> `signature_item ]) t

      type nonrec 'a poly = ('a, poly) t

      val location : ('a, 'b) t -> 'a -> Location.t
    end

    module Define : sig
      type t = Define : 'a Type.non_tuple Binding.t list -> t
    end

    module Poly : sig
      type kind =
        | Never_add_mangler
        | Always_add_mangler
        | Add_mangler_if_more_than_one_elt
        (** Behaves like [Always_add_mangler] if the set on the RHS of the binding, when
            evaluated with [Expand_atoms_bound_to_sets], has more than one element, and
            like [Never_add_mangler] otherwise.

            For [Add_mangler_if_more_than_one_elt] specifically, we only permit exactly
            one [(_, singleton) Expression.t] on the RHS. Loosely speaking, if the
            expression on the RHS contains a [Union], it is probably true that it always
            evaluates to a set with more than one element, and the [Always_add_mangler]
            version of the attribute should be used instead. *)

      type t =
        { bindings : Poly.t
        ; kind : kind
        }

      (** Check if the provided ast node is a floating poly template attribute. Does not
          mark the attribute as seen. *)
      val is_present : ('a, poly) Context.t -> 'a -> bool
    end

    type t =
      | Define of Define.t
      | Poly of Poly.t Explicitness.With.t

    (** Check if the provided ast node is a floating template attribute, and evaluate its
        contents. Marks the attribute as seen. Returns an [Error _] if the payload of the
        attribute is malformed or inconsistent. *)
    val convert : ('a, poly) Context.t -> 'a -> (t option, Syntax_error.t) result
  end
end