forked from ocaml-flambda/flambda-backend
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathglobal_module.mli
161 lines (124 loc) · 5.94 KB
/
global_module.mli
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
[@@@ocaml.warning "+a-9-40-41-42"]
type ('name, 'value) duplicate =
| Duplicate of { name : 'name; value1 : 'value; value2 : 'value }
module Argument : sig
type ('param, 'value) t = {
param : 'param;
value : 'value;
}
end
module Name : sig
type t = private {
head : string;
args : argument list;
}
and argument = (t, t) Argument.t
include Identifiable.S with type t := t
val create : string -> argument list -> (t, (t, t) duplicate) Result.t
val create_exn : string -> argument list -> t
val create_no_args : string -> t
val to_string : t -> string
end
(** An elaborated form of name in which all arguments are expressed, including
those being passed implicitly from one module to another by the subset rule
for parameterised modules. Normally, these "hidden" arguments simply say to
pass [X] as [X] for some module [X], but if there are parameterised
parameters, the hidden arguments can get more complex.
Suppose [M] takes parameters [X] and [Y], neither of which is itself
parameterised. If someone is passing [Foo] as the value of [X], then, we
will have (abbreviating nested records):
{[
{ head: M; visible_args: [ X, Foo ]; hidden_args: [ Y, Y ] }
]}
This represents that [X] is explicitly being given the value [Foo] and [Y]
(the parameter) is implicitly getting the value [Y] (the argument currently
in scope).
However, suppose instead [Y] is parameterised by [X]. Then [M] still takes
two parameters [X] and [Y], but now once [X] has the value [Foo], [Y]
requires _that particular_ [X]:
{[
{ head: M; visible_args: [ X, Foo ]; hidden_args: [ Y, Y[X:Foo] ] }
]}
Importantly, the _parameters_ [X] and [Y] never change: they are names that
appear in [m.ml] and [m.cmi]. But further specialisation requires passing
specifically a [Y[X:Foo]] rather than a [Y]. (Here, [Y[X:Foo]] stands for
the record [{ head = Y; visible_args = [ X, Foo ]; hidden_args = [] }] of
type [t].)
*)
type t = private {
head : string;
visible_args : argument list;
hidden_args : argument list;
}
and argument = (Name.t, t) Argument.t
include Identifiable.S with type t := t
val create
: string -> argument list -> hidden_args:argument list
-> (t, (Name.t, t) duplicate) Result.t
val create_exn : string -> argument list -> hidden_args:argument list -> t
val to_string : t -> string
val to_name : t -> Name.t
val all_args : t -> argument list
(** A map from parameter names to their values. Hidden arguments aren't relevant
in the parameter names, so they're represented by [Name.t]s here. *)
type subst = t Name.Map.t
(** Apply a substitution to the given global. If it appears in the substitution
directly (that is, its [Name.t] form is a key in the map), this simply
performs a lookup. Otherwise, we perform a _revealing substitution_: if the
value of a hidden argument is a key in the substitution, the argument becomes
visible. Otherwise, substitution recurses into arguments (both hidden and
visible) as usual. See [global_test.ml] for examples. *)
val subst : t -> subst -> t * [ `Changed | `Did_not_change ]
(** Apply a substitution to the arguments and parameters in [t] but not to [t]
itself. Useful if [subst] is constructed from some parameter-argument pairs
and [t] is one of the parameters, since we want to handle any
interdependencies but the substitution applied to [t] itself is
uninterestingly just the corresponding value. *)
val subst_inside : t -> subst -> t
(** Check that a substitution is a valid (possibly partial) instantiation of
a module with the given parameter list. Each name being substituted must
appear in the list. *)
val check : subst -> t list -> bool
(** Returns [true] if [hidden_args] is empty and all argument values (if any)
are also complete. This is a stronger condition than full application, and
(unless the whole global is itself a parameter) it's equivalent to the
global being a static constant, since any parameters being used would have
to show up in a [hidden_args] somewhere. (Importantly, it's not possible
that a parameter is being used as an argument to a different parameter,
since a module can be declared to be an argument for up to one parameter.)
CR lmaurer: Make sure we're checking for the user redundantly passing an
parameter as an argument. This should be accepted and ignored, lest we
count the parameter as filled and consider something completely
instantiated. *)
val is_complete : t -> bool
(** Returns [true] if this name has at least one argument (either hidden or
visible). *)
val has_arguments : t -> bool
module Precision : sig
(** Whether a global's elaborated form is known exactly. For example, given
the elaborated form [Foo{Bar; Baz}], if we never loaded foo.cmi then
we don't actually know whether [Foo] takes [Bar] or [Baz]. *)
type t =
| Exact (** The base module takes exactly the arguments being passed. *)
| Approximate
(** The base module takes some subset of the arguments being passed
(possibly all of them). *)
val print : Format.formatter -> t -> unit
val output : out_channel -> t -> unit
end
module With_precision : sig
type nonrec t = t * Precision.t
val equal : t -> t -> bool
exception Inconsistent
(** Given two elaborated forms of the same name and their precision, reconcile
them. In any case, if the visible parts of the globals disagree, raise
[Inconsistent] (because they don't in fact elaborate the same [Name.t]).
For the hidden parts, we treat an exact [t] as requiring equality and an
approximate [t] as specifying an upper bound. Thus exact vs. exact checks
for equality, exact vs. approximate checks the upper bound, and
approximate vs. approximate takes the least upper bound (that is, the
intersection). *)
val meet : t -> t -> t
val print : Format.formatter -> t -> unit
val output : out_channel -> t -> unit
end