Skip to content

Commit 293b522

Browse files
author
Armaël Guéneau
committed
Implement ghost bindings as a separate category in the environment
This replaces the trick consisting of using a special string prefix added to normal identifiers.
1 parent 89a8d84 commit 293b522

6 files changed

Lines changed: 66 additions & 33 deletions

File tree

typing/env.ml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ type summary =
165165
| Env_functor_arg of summary * Ident.t
166166
| Env_constraints of summary * type_declaration PathMap.t
167167
| Env_copy_types of summary * string list
168+
| Env_ghost_value of summary * Ident.t * value_description
168169

169170
module TycompTbl =
170171
struct
@@ -450,6 +451,7 @@ type t = {
450451
components: module_components IdTbl.t;
451452
classes: class_declaration IdTbl.t;
452453
cltypes: class_type_declaration IdTbl.t;
454+
ghost_values: value_description IdTbl.t;
453455
functor_args: unit Ident.tbl;
454456
summary: summary;
455457
local_constraints: type_declaration PathMap.t;
@@ -483,6 +485,7 @@ and structure_components = {
483485
mutable comp_components: module_components comp_tbl;
484486
mutable comp_classes: class_declaration comp_tbl;
485487
mutable comp_cltypes: class_type_declaration comp_tbl;
488+
mutable comp_ghost_values : value_description comp_tbl;
486489
}
487490

488491
and functor_components = {
@@ -536,6 +539,7 @@ let empty = {
536539
modules = IdTbl.empty; modtypes = IdTbl.empty;
537540
components = IdTbl.empty; classes = IdTbl.empty;
538541
cltypes = IdTbl.empty;
542+
ghost_values = IdTbl.empty;
539543
summary = Env_empty; local_constraints = PathMap.empty; gadt_instances = [];
540544
flags = 0;
541545
functor_args = Ident.empty;
@@ -624,7 +628,8 @@ let empty_structure =
624628
comp_types = Tbl.empty;
625629
comp_modules = Tbl.empty; comp_modtypes = Tbl.empty;
626630
comp_components = Tbl.empty; comp_classes = Tbl.empty;
627-
comp_cltypes = Tbl.empty }
631+
comp_cltypes = Tbl.empty;
632+
comp_ghost_values = Tbl.empty; }
628633

629634
let get_components c =
630635
match get_components_opt c with
@@ -910,6 +915,8 @@ and find_class =
910915
find (fun env -> env.classes) (fun sc -> sc.comp_classes)
911916
and find_cltype =
912917
find (fun env -> env.cltypes) (fun sc -> sc.comp_cltypes)
918+
and find_ghost_value =
919+
find (fun env -> env.ghost_values) (fun sc -> sc.comp_ghost_values)
913920

914921
let type_of_cstr path = function
915922
| {cstr_inlined = Some d; _} ->
@@ -1281,6 +1288,8 @@ let lookup_class =
12811288
lookup (fun env -> env.classes) (fun sc -> sc.comp_classes)
12821289
let lookup_cltype =
12831290
lookup (fun env -> env.cltypes) (fun sc -> sc.comp_cltypes)
1291+
let lookup_ghost_value =
1292+
lookup (fun env -> env.ghost_values) (fun sc -> sc.comp_ghost_values)
12841293

12851294
let copy_types l env =
12861295
let f desc = {desc with val_type = Subst.type_expr Subst.identity desc.val_type} in
@@ -1680,7 +1689,8 @@ and components_of_module_maker (env, sub, path, mty) =
16801689
comp_labels = Tbl.empty; comp_types = Tbl.empty;
16811690
comp_modules = Tbl.empty; comp_modtypes = Tbl.empty;
16821691
comp_components = Tbl.empty; comp_classes = Tbl.empty;
1683-
comp_cltypes = Tbl.empty } in
1692+
comp_cltypes = Tbl.empty;
1693+
comp_ghost_values = Tbl.empty; } in
16841694
let pl, sub = prefix_idents path sub sg in
16851695
let env = ref env in
16861696
let pos = ref 0 in
@@ -1798,6 +1808,11 @@ and store_value ?check id decl env =
17981808
values = IdTbl.add id decl env.values;
17991809
summary = Env_value(env.summary, id, decl) }
18001810

1811+
and store_ghost_value id decl env =
1812+
{ env with
1813+
ghost_values = IdTbl.add id decl env.ghost_values;
1814+
summary = Env_ghost_value(env.summary, id, decl) }
1815+
18011816
and store_type ~check id info env =
18021817
let loc = info.type_loc in
18031818
if check then
@@ -1979,6 +1994,8 @@ let add_local_constraint path info elv env =
19791994
add_local_type path info env
19801995
| _ -> assert false
19811996

1997+
let add_ghost_value id desc env =
1998+
store_ghost_value id desc env
19821999

19832000
(* Insertion of bindings by name *)
19842001

typing/env.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ type summary =
3333
| Env_functor_arg of summary * Ident.t
3434
| Env_constraints of summary * type_declaration PathMap.t
3535
| Env_copy_types of summary * string list
36+
(* Ghost bindings are used to keep track of non-recursive let-bindings that
37+
may be missing a 'rec', in order to provide a special error message in that
38+
case. *)
39+
| Env_ghost_value of summary * Ident.t * value_description
3640

3741
type t
3842

@@ -67,6 +71,7 @@ val find_module: Path.t -> t -> module_declaration
6771
val find_modtype: Path.t -> t -> modtype_declaration
6872
val find_class: Path.t -> t -> class_declaration
6973
val find_cltype: Path.t -> t -> class_type_declaration
74+
val find_ghost_value: Path.t -> t -> value_description
7075

7176
val find_type_expansion:
7277
Path.t -> t -> type_expr list * type_expr * int option
@@ -123,6 +128,8 @@ val lookup_class:
123128
?loc:Location.t -> Longident.t -> t -> Path.t * class_declaration
124129
val lookup_cltype:
125130
?loc:Location.t -> Longident.t -> t -> Path.t * class_type_declaration
131+
val lookup_ghost_value:
132+
?loc:Location.t -> Longident.t -> t -> Path.t * value_description
126133

127134
val copy_types: string list -> t -> t
128135
(* Used only in Typecore.duplicate_ident_types. *)
@@ -146,6 +153,7 @@ val add_class: Ident.t -> class_declaration -> t -> t
146153
val add_cltype: Ident.t -> class_type_declaration -> t -> t
147154
val add_local_constraint: Path.t -> type_declaration -> int -> t -> t
148155
val add_local_type: Path.t -> type_declaration -> t -> t
156+
val add_ghost_value: Ident.t -> value_description -> t -> t
149157

150158
(* Insertion of all fields of a signature. *)
151159

typing/envaux.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ let rec env_from_summary sum subst =
3939
| Env_value(s, id, desc) ->
4040
Env.add_value id (Subst.value_description subst desc)
4141
(env_from_summary s subst)
42+
| Env_ghost_value(s, id, desc) ->
43+
Env.add_ghost_value id (Subst.value_description subst desc)
44+
(env_from_summary s subst)
4245
| Env_type(s, id, desc) ->
4346
Env.add_type ~check:false id
4447
(Subst.type_declaration subst desc)

typing/typecore.ml

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -136,48 +136,38 @@ type recarg =
136136
let case lhs rhs =
137137
{c_lhs = lhs; c_guard = None; c_rhs = rhs}
138138

139-
(* Add a dummy prefix to a name in order to help track missing
140-
"rec" keywords *)
141-
let ghost_name_easytype s =
142-
"***" ^ s
143139

144-
(* Add a dummy prefix to an ident in order to help track missing
145-
"rec" keywords *)
146-
let ghost_ident_easytype id =
147-
{ id with Ident.name = ghost_name_easytype (Ident.name id) }
140+
(* For detecting missing "rec" keywords, we add "ghost" bindings in the
141+
environment for variables that are bound non-recursively. Then, when an
142+
identifier is unbound, [find_value] additionally checks if it exists as a
143+
"ghost" binding. In that case, an special-cased error message "you are
144+
probably missing the rec keyword" is displayed.
145+
*)
148146

149147
(* Add pattern variables as ghost bindings, i.e., bindings that may be
150148
used for the error message "you are probably missing the rec keyword". *)
151-
let add_pattern_variables_ghost_easytype loc_let ?check ?check_as env pv =
152-
(* Note: the arguments ?check ?check_as might not be needed *)
149+
let add_pattern_variables_ghost loc_let env pv =
153150
List.fold_right
154-
(fun (id, ty, _name, _loc, as_var) env ->
155-
let check = if as_var then check_as else check in
156-
Env.add_value ?check (ghost_ident_easytype id)
151+
(fun (id, ty, _name, _loc, _as_var) env ->
152+
Env.add_ghost_value id
157153
{val_type = ty; val_kind = Val_reg; Types.val_loc = loc_let;
158154
val_attributes = [];
159155
} env
160156
)
161157
pv env
162158

163159
(* Lookup in the environment for a variable or a corresponding ghost variable *)
164-
let find_value_easytype env loc txt =
160+
let find_value env loc txt =
165161
try Typetexp.find_value env loc txt
166-
with | (Typetexp.Error (loc', env', Typetexp.Unbound_value lid')) as error ->
167-
let txt2 =
168-
match txt with
169-
| Longident.Lident s -> Longident.Lident (ghost_name_easytype s)
170-
| _ -> raise error
171-
in
172-
let (_path, desc) =
173-
try Typetexp.find_value env loc txt2
174-
with Typetexp.Error (_, _, Typetexp.Unbound_value _) -> raise error
175-
in
176-
let loc = desc.val_loc in
177-
raise
178-
(Typetexp.Error
179-
(loc', env', Typetexp.Unbound_value_missing_rec (lid', loc)))
180-
162+
with (Typetexp.Error (loc', env', Typetexp.Unbound_value lid')) as error ->
163+
begin match Typetexp.find_ghost_value_opt env loc txt with
164+
| None -> raise error
165+
| Some (_, desc) ->
166+
let loc = desc.val_loc in
167+
raise
168+
(Typetexp.Error
169+
(loc', env', Typetexp.Unbound_value_missing_rec (lid', loc)))
170+
end
181171

182172
(* Upper approximation of free identifiers on the parse tree *)
183173

@@ -2670,7 +2660,7 @@ and type_expect_ ?in_function ?(recarg=Rejected) env sexp ty_expected =
26702660
match sexp.pexp_desc with
26712661
| Pexp_ident lid ->
26722662
begin
2673-
let (path, desc) = find_value_easytype env lid.loc lid.txt in
2663+
let (path, desc) = find_value env lid.loc lid.txt in
26742664
if !Clflags.annotations then begin
26752665
let dloc = desc.Types.val_loc in
26762666
let annot =
@@ -4752,7 +4742,7 @@ and type_let ?(check = fun s -> Warnings.Unused_var s)
47524742
| {pvb_loc; _} :: _ -> pvb_loc
47534743
| _ -> assert false
47544744
in
4755-
add_pattern_variables_ghost_easytype loc env pv
4745+
add_pattern_variables_ghost loc env pv
47564746
end
47574747
else env in
47584748

typing/typetexp.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,15 @@ let find_class_type env loc lid =
187187
Builtin_attributes.check_deprecated loc decl.clty_attributes (Path.name path);
188188
r
189189

190+
let find_ghost_value_opt env loc lid =
191+
try
192+
Some
193+
(find_component Env.lookup_ghost_value
194+
(fun lid -> Unbound_value lid)
195+
env loc lid)
196+
with Error (_, _, Unbound_value _) ->
197+
None
198+
190199
let unbound_constructor_error env lid =
191200
narrow_unbound_lid_error env lid.loc lid.txt
192201
(fun lid -> Unbound_constructor lid)

typing/typetexp.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ val find_modtype:
111111
Env.t -> Location.t -> Longident.t -> Path.t * modtype_declaration
112112
val find_class_type:
113113
Env.t -> Location.t -> Longident.t -> Path.t * class_type_declaration
114+
(* There is no constructor in [error] for an "unbound ghost value" error. Ghost
115+
values should only be used internally, and never appear to the user: as an
116+
attempt to document that fact, we provide a [find_*] function that returns an
117+
option, instead of raising an exception. *)
118+
val find_ghost_value_opt:
119+
Env.t -> Location.t -> Longident.t -> (Path.t * value_description) option
114120

115121
val unbound_constructor_error: Env.t -> Longident.t Location.loc -> 'a
116122
val unbound_label_error: Env.t -> Longident.t Location.loc -> 'a

0 commit comments

Comments
 (0)