Skip to content

Commit c7c1170

Browse files
authored
Merge pull request #12561 from art-w/instantiate-parameterized
feat(oxcaml): instantiate parameterised libraries
2 parents b08266c + bbc8334 commit c7c1170

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2917
-154
lines changed

bin/describe/describe_external_lib_deps.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ let resolve_lib_deps db lib_deps =
9797
let open Memo.O in
9898
Memo.parallel_map lib_deps ~f:(fun (lib : Lib_dep.t) ->
9999
match lib with
100-
| Direct (_, name) | Re_export (_, name) ->
100+
| Direct (_, name) | Re_export (_, name) | Instantiate { lib = name; _ } ->
101101
let+ v = resolve_lib db name Kind.Required in
102102
[ v ]
103103
| Select select ->

doc/changes/added/12561.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Add support for instantiating OxCaml parameterised libraries.
2+
(#12561, @art-w)

doc/reference/dune/library.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ order to declare a multi-directory library, you need to use the
222222
List the library parameters used by the library and its dependencies.
223223

224224
This feature is experimental and requires the compiler you are using to
225-
support parameterized libraries.
225+
support parameterised libraries.
226226
See :doc:`/reference/dune/library_parameter`.
227227

228228
.. describe:: (js_of_ocaml ...)

doc/reference/dune/library_parameter.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ library_parameter
44
.. warning::
55

66
This feature is experimental and requires the compiler you are using to
7-
support parameterized libraries.
7+
support parameterised libraries.
88

99
The ``library_parameter`` stanza describes a parameter interface defined in a single ``.mli`` file. To enable this feature,
1010
you need to add ``(using oxcaml 0.1)`` :doc:`extension

doc/reference/library-dependencies.rst

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,86 @@ be able to see ``foo`` independently of whether :doc:`implicit
6868
transitive dependencies<dune-project/implicit_transitive_deps>` are
6969
allowed or not. When they are allowed, which is the default, all transitive
7070
dependencies are visible, whether they are marked as re-exported or not.
71+
72+
Instantiating Parameterised Dependencies
73+
----------------------------------------
74+
75+
This feature requires OxCaml, see :doc:`/reference/dune/library_parameter`.
76+
77+
A parameterised dependency ``foo`` can be instantiated with the arguments
78+
``bar``, ``qux`` using the syntax:
79+
80+
.. code:: dune
81+
82+
(foo bar qux)
83+
84+
For example:
85+
86+
.. code:: dune
87+
88+
(library
89+
(name test)
90+
(libraries (foo bar qux)))
91+
92+
The library ``foo`` must have declared the set of parameters it expects, and
93+
the arguments given to the instantiation must implement a subset of these
94+
parameters. The ordering of the arguments does not matter, as the instantiation
95+
relies on the implemented parameter to uniquely identify each argument.
96+
For executables, the parameterised dependencies must be fully instantiated.
97+
98+
In the OCaml code, the instantiated library will be available under the module
99+
name ``Foo``. To avoiding overlapping module names when instantiating the same
100+
dependency multiple times, the syntax ``:as`` allows renaming the module. For
101+
example:
102+
103+
.. code:: dune
104+
105+
(library
106+
(name test)
107+
(libraries
108+
(foo a b :as foo_a_b)
109+
(foo bar qux :as foo_bar_qux)))
110+
111+
Then the instantiations will be available under the names ``Foo_a_b`` and
112+
``Foo_bar_qux``.
113+
114+
Dependencies automatically inherit the parameters of their parent library.
115+
For example, assuming the parameterised library ``foo`` requires two
116+
parameters ``p`` and ``q``:
117+
118+
.. code:: dune
119+
120+
(library
121+
(name test)
122+
(parameters p q)
123+
(libraries
124+
(foo :as foo_implicit)
125+
(foo an_implementation_of_q :as foo_q)
126+
(foo bar qux :as foo_bar_qux)
127+
other_foo))
128+
129+
Then ``foo_implicit`` is implicitly ``(foo p q)``,
130+
while ``(foo an_implementation_of_q)`` will only inherit the parameter ``p``.
131+
132+
If ``other_foo``, which is not explicitly instantiated here, is also
133+
parameterised by the parameters ``p`` (and) or ``q``, it will also inherit
134+
its parent arguments. Dune will report an error if a dependency requires
135+
parameters which have neither been given explicitly given via an instantiation
136+
and are not listed in the parent library parameters.
137+
138+
For unwrapped libaries, the instantiation of parameterised libraries is not
139+
currently generated. This is subject to change soon, but in the mean time,
140+
you'll need to manually declare the instantiations: If you depend on the
141+
instantiation ``(foo bar qux :as new_name)`` with ``bar`` an implementation of
142+
the parameter ``param_bar`` and ``qux`` an implementation of ``param_qux``,
143+
then you'll need to write the following:
144+
145+
.. code:: ocaml
146+
147+
module New_name = Foo (Param_bar) (Bar) (Param_qux) (Qux) [@jane.non_erasable.instances]
148+
149+
.. note::
150+
151+
While this reuses the OCaml functor application syntax, the attribute changes
152+
the meaning: The ``(Param) (Impl)`` must go together as a pair, but the
153+
ordering of the arguments otherwise does not matter.

src/dune_lang/lib_dep.ml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ type t =
9898
| Direct of (Loc.t * Lib_name.t)
9999
| Re_export of (Loc.t * Lib_name.t)
100100
| Select of Select.t
101+
| Instantiate of
102+
{ loc : Loc.t
103+
; lib : Lib_name.t
104+
; arguments : (Loc.t * Lib_name.t) list
105+
; new_name : Module_name.t option
106+
}
101107

102108
let equal = Poly.equal
103109

@@ -107,6 +113,13 @@ let to_dyn =
107113
| Direct (_, name) -> Lib_name.to_dyn name
108114
| Re_export (_, name) -> variant "re_export" [ Lib_name.to_dyn name ]
109115
| Select s -> variant "select" [ Select.to_dyn s ]
116+
| Instantiate { lib; arguments; new_name; loc = _ } ->
117+
variant
118+
"instantiate"
119+
[ Lib_name.to_dyn lib
120+
; list (fun (_, arg) -> Lib_name.to_dyn arg) arguments
121+
; option Module_name.to_dyn new_name
122+
]
110123
;;
111124

112125
let direct x = Direct x
@@ -126,6 +139,16 @@ let decode ~allow_re_export =
126139
, let+ select = Select.decode in
127140
Select select )
128141
]
142+
<|> enter
143+
(let+ () = Syntax.since Oxcaml.syntax (0, 1)
144+
and+ loc, lib = located Lib_name.decode
145+
and+ arguments, new_name =
146+
until_keyword
147+
":as"
148+
~before:(located Lib_name.decode)
149+
~after:Module_name.decode
150+
in
151+
Instantiate { loc; lib; arguments; new_name })
129152
<|> let+ loc, name = located Lib_name.decode in
130153
Direct (loc, name))
131154
in
@@ -144,11 +167,22 @@ let encode =
144167
Code_error.raise
145168
"Lib_dep.encode: cannot encode select"
146169
[ "select", Select.to_dyn select ]
170+
| Instantiate { lib; arguments; new_name; loc = _ } ->
171+
let as_name =
172+
match new_name with
173+
| None -> []
174+
| Some new_name -> [ string ":as"; Module_name.encode new_name ]
175+
in
176+
list
177+
sexp
178+
((Lib_name.encode lib :: List.map arguments ~f:(fun (_, arg) -> Lib_name.encode arg))
179+
@ as_name)
147180
;;
148181

149182
module L = struct
150183
type kind =
151184
| Required
185+
| Required_multiple
152186
| Optional
153187
| Forbidden
154188

@@ -186,12 +220,21 @@ module L = struct
186220
[ Pp.textf
187221
"library %S is present both as a forbidden and required dependency"
188222
(Lib_name.to_string name)
223+
]
224+
| Required_multiple, Required_multiple -> acc
225+
| Required_multiple, _ | _, Required_multiple ->
226+
User_error.raise
227+
~loc
228+
[ Pp.textf
229+
"parameterised library %S is present in multiple forms"
230+
(Lib_name.to_string name)
189231
])
190232
in
191233
ignore
192234
(List.fold_left t ~init:Lib_name.Map.empty ~f:(fun acc x ->
193235
match x with
194236
| Re_export (_, s) | Direct (_, s) -> add Required s acc
237+
| Instantiate { lib = s; _ } -> add Required_multiple s acc
195238
| Select { choices; _ } ->
196239
List.fold_left choices ~init:acc ~f:(fun acc (c : Select.Choice.t) ->
197240
let acc = Lib_name.Set.fold c.required ~init:acc ~f:(add Optional) in

src/dune_lang/lib_dep.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ type t =
2222
| Direct of (Loc.t * Lib_name.t)
2323
| Re_export of (Loc.t * Lib_name.t)
2424
| Select of Select.t
25+
| Instantiate of
26+
{ loc : Loc.t
27+
; lib : Lib_name.t
28+
; arguments : (Loc.t * Lib_name.t) list
29+
; new_name : Module_name.t option
30+
}
2531

2632
val equal : t -> t -> bool
2733
val to_dyn : t -> Dyn.t

src/dune_lang/oxcaml.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
open Import
22

3+
let latest_version = 0, 1
4+
35
let syntax =
46
Syntax.create
57
~name:"oxcaml"
68
~desc:"experimental support for OxCaml"
79
~experimental:true
810
[ (0, 1), `Since (3, 20) ]
911
;;
12+
13+
let parameterised_dir = ".parameterised"

src/dune_lang/oxcaml.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
open Import
22

33
val syntax : Syntax.t
4+
val latest_version : Syntax.Version.t
5+
val parameterised_dir : string

src/dune_rules/compilation_context.ml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ type t =
8989
; requires_link : Lib.t list Resolve.t Memo.Lazy.t
9090
; implements : Virtual_rules.t
9191
; parameters : Module_name.t list Resolve.Memo.t
92+
; instances : Parameterised_rules.instances list Resolve.Memo.t option
9293
; includes : Includes.t
9394
; preprocessing : Pp_spec.t
9495
; opaque : bool
@@ -162,6 +163,7 @@ let create
162163
?modes
163164
?bin_annot
164165
?loc
166+
?instances
165167
()
166168
=
167169
let project = Scope.project scope in
@@ -236,6 +238,7 @@ let create
236238
; bin_annot
237239
; loc
238240
; ocaml
241+
; instances
239242
}
240243
;;
241244

@@ -256,6 +259,15 @@ let for_alias_module t alias_module =
256259
let profile = Super_context.context t.super_context |> Context.profile in
257260
Ocaml_flags.default ~dune_version ~profile)
258261
in
262+
let flags =
263+
match t.instances with
264+
| None -> flags
265+
| Some _ ->
266+
(* If the alias file instantiates parameterised libraries,
267+
the [misplace-attribute] warning is currently raised on
268+
[@jane.non_erasable.instances] *)
269+
Ocaml_flags.append_common flags [ "-w"; "-53" ]
270+
in
259271
let sandbox =
260272
(* If the compiler reads the cmi for module alias even with [-w -49
261273
-no-alias-deps], we must sandbox the build of the alias module since the
@@ -342,3 +354,4 @@ let for_plugin_executable t ~embed_in_plugin_libraries =
342354
let without_bin_annot t = { t with bin_annot = false }
343355
let set_obj_dir t obj_dir = { t with obj_dir }
344356
let set_modes t ~modes = { t with modes }
357+
let instances t = t.instances

0 commit comments

Comments
 (0)