Skip to content

Commit 805d06c

Browse files
committed
Merge pull request ocaml-ppx#29 from Twinside/master
Adding handling of shadowed standard types in mutually recursive types.
2 parents 7800f30 + e46ac61 commit 805d06c

8 files changed

Lines changed: 356 additions & 153 deletions

File tree

src/ppx_deriving.ml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ open Parsetree
55
open Ast_helper
66
open Ast_convenience
77

8+
module StringSet = Set.Make(String)
9+
810
type deriver = {
911
name : string ;
1012
core_type : (core_type -> expression) option;
@@ -269,3 +271,36 @@ let strong_type_of_type ty =
269271
let free_vars = free_vars_in_core_type ty in
270272
Typ.force_poly @@ Typ.poly free_vars ty
271273

274+
let predefined_types =
275+
[
276+
"unit";
277+
"int";
278+
"int32";
279+
"int64";
280+
"nativeint";
281+
"float";
282+
"bool";
283+
"char";
284+
"string";
285+
"bytes"
286+
]
287+
288+
let predefined_set =
289+
List.fold_right StringSet.add predefined_types StringSet.empty
290+
291+
let extract_typename_of_type_group deriver ~allow_shadowing type_list =
292+
let add_name acc ty =
293+
let typename = ty.ptype_name.txt in
294+
let is_shadowing_predefined =
295+
StringSet.mem typename predefined_set in
296+
if is_shadowing_predefined && not allow_shadowing then
297+
raise_errorf
298+
~loc:ty.ptype_loc
299+
("%s don't allow derivation of shadowed standard type %s. " ^^
300+
"Use option 'allow_std_type_masking' to lift the restriction.")
301+
deriver
302+
typename
303+
else
304+
StringSet.add ty.ptype_name.txt acc in
305+
List.fold_left add_name StringSet.empty type_list
306+

src/ppx_deriving.mli

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
open Parsetree
44

5+
module StringSet : Set.S with type elt = string
6+
57
(** {2 Registration} *)
68

79
(** A type of deriving plugins.
@@ -241,3 +243,10 @@ val binop_reduce : expression -> expression -> expression -> expression
241243
[freevars . ty], giving a strong polymorphic type *)
242244
val strong_type_of_type: core_type -> core_type
243245

246+
(** [extract_typename_of_type_group ~allow_shadowing tys] will extract
247+
the set of all types in a type group. Will raise an error in case
248+
of type shadowing standard types, unless [~allow_shadowing] is set
249+
to true. *)
250+
val extract_typename_of_type_group : string -> allow_shadowing:bool ->
251+
type_declaration list -> StringSet.t
252+

src_plugins/ppx_deriving_eq.ml

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,28 @@ open Parsetree
55
open Ast_helper
66
open Ast_convenience
77

8+
module StringSet = Ppx_deriving.StringSet
9+
810
let deriver = "eq"
911
let raise_errorf = Ppx_deriving.raise_errorf
1012

13+
type eq_options =
14+
{
15+
allow_std_type_shadowing: bool;
16+
}
17+
18+
let default_eq_options =
19+
{
20+
allow_std_type_shadowing= false;
21+
}
22+
1123
let parse_options options =
12-
options |> List.iter (fun (name, expr) ->
24+
let option_parser acc (name, expr) =
1325
match name with
14-
| _ -> raise_errorf ~loc:expr.pexp_loc "%s does not support option %s" deriver name)
26+
| "allow_std_type_shadowing" -> { allow_std_type_shadowing = true }
27+
| _ ->
28+
raise_errorf ~loc:expr.pexp_loc "%s does not support option %s" deriver name in
29+
List.fold_left option_parser default_eq_options options
1530

1631
let attr_equal attrs =
1732
Ppx_deriving.(attrs |> attr ~deriver "equal" |> Arg.(get_attr ~deriver expr))
@@ -23,58 +38,63 @@ let pattn side typs =
2338
List.mapi (fun i _ -> pvar (argn side i)) typs
2439

2540
let core_type_of_decl ~options ~path type_decl =
26-
parse_options options;
41+
ignore (parse_options options);
2742
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
2843
Ppx_deriving.poly_arrow_of_type_decl
2944
(fun var -> [%type: [%t var] -> [%t var] -> bool])
3045
type_decl
3146
[%type: [%t typ] -> [%t typ] -> bool]
3247

3348
let sig_of_type ~options ~path type_decl =
34-
parse_options options;
49+
ignore (parse_options options);
3550
[Sig.value (Val.mk (mknoloc (Ppx_deriving.mangle_type_decl (`Prefix "equal") type_decl))
3651
(core_type_of_decl ~options ~path type_decl))]
3752

38-
let rec exprsn typs =
53+
let rec exprsn group_def typs =
3954
typs |> List.mapi (fun i typ ->
40-
app (expr_of_typ typ) [evar (argn `lhs i); evar (argn `rhs i)])
55+
app (expr_of_typ group_def typ) [evar (argn `lhs i); evar (argn `rhs i)])
4156

42-
and expr_of_typ typ =
57+
and expr_of_typ group_def typ =
4358
match attr_equal typ.ptyp_attributes with
4459
| Some fn -> fn
4560
| None ->
4661
match typ with
62+
| { ptyp_desc = Ptyp_constr ({ txt = (Lident id as lid) }, args) }
63+
when StringSet.mem id group_def ->
64+
let equal_fn = Exp.ident (mknoloc (Ppx_deriving.mangle_lid (`Prefix "equal") lid)) in
65+
app equal_fn (List.map (expr_of_typ group_def) args)
4766
| [%type: _] | [%type: unit] -> [%expr fun _ _ -> true]
4867
| [%type: int] | [%type: int32] | [%type: Int32.t]
4968
| [%type: int64] | [%type: Int64.t] | [%type: nativeint] | [%type: Nativeint.t]
50-
| [%type: float] | [%type: bool] | [%type: char] | [%type: string] | [%type: bytes] ->
69+
| [%type: float] | [%type: bool] | [%type: char] | [%type: string] |
70+
[%type: String.t] | [%type: bytes] ->
5171
[%expr (fun (a:[%t typ]) b -> a = b)]
52-
| [%type: [%t? typ] ref] -> [%expr fun a b -> [%e expr_of_typ typ] !a !b]
72+
| [%type: [%t? typ] ref] -> [%expr fun a b -> [%e expr_of_typ group_def typ] !a !b]
5373
| [%type: [%t? typ] list] ->
5474
[%expr
5575
let rec loop x y =
5676
match x, y with
5777
| [], [] -> true
58-
| a :: x, b :: y -> [%e expr_of_typ typ] a b && loop x y
78+
| a :: x, b :: y -> [%e expr_of_typ group_def typ] a b && loop x y
5979
| _ -> false
6080
in (fun x y -> loop x y)]
6181
| [%type: [%t? typ] array] ->
6282
[%expr fun x y ->
6383
let rec loop i =
64-
(i = Array.length x || [%e expr_of_typ typ] x.(i) y.(i)) && loop (i + 1)
84+
(i = Array.length x || [%e expr_of_typ group_def typ] x.(i) y.(i)) && loop (i + 1)
6585
in Array.length x = Array.length y && loop 0]
6686
| [%type: [%t? typ] option] ->
6787
[%expr fun x y ->
6888
match x, y with
6989
| None, None -> true
70-
| Some a, Some b -> [%e expr_of_typ typ] a b
90+
| Some a, Some b -> [%e expr_of_typ group_def typ] a b
7191
| _ -> false]
7292
| { ptyp_desc = Ptyp_constr ({ txt = lid }, args) } ->
7393
let equal_fn = Exp.ident (mknoloc (Ppx_deriving.mangle_lid (`Prefix "equal") lid)) in
74-
app equal_fn (List.map expr_of_typ args)
94+
app equal_fn (List.map (expr_of_typ group_def) args)
7595
| { ptyp_desc = Ptyp_tuple typs } ->
7696
[%expr fun [%p ptuple (pattn `lhs typs)] [%p ptuple (pattn `rhs typs)] ->
77-
[%e exprsn typs |> Ppx_deriving.(fold_exprs (binop_reduce [%expr (&&)]))]]
97+
[%e exprsn group_def typs |> Ppx_deriving.(fold_exprs (binop_reduce [%expr (&&)]))]]
7898
| { ptyp_desc = Ptyp_variant (fields, _, _); ptyp_loc } ->
7999
let cases =
80100
(fields |> List.map (fun field ->
@@ -84,42 +104,44 @@ and expr_of_typ typ =
84104
Exp.case (pdup (fun _ -> Pat.variant label None)) [%expr true]
85105
| Rtag (label, _, false, [typ]) ->
86106
Exp.case (pdup (fun var -> Pat.variant label (Some (pvar var))))
87-
(app (expr_of_typ typ) [evar "lhs"; evar "rhs"])
107+
(app (expr_of_typ group_def typ) [evar "lhs"; evar "rhs"])
88108
| Rinherit ({ ptyp_desc = Ptyp_constr (tname, _) } as typ) ->
89109
Exp.case (pdup (fun var -> Pat.alias (Pat.type_ tname) (mknoloc var)))
90-
(app (expr_of_typ typ) [evar "lhs"; evar "rhs"])
110+
(app (expr_of_typ group_def typ) [evar "lhs"; evar "rhs"])
91111
| _ ->
92112
raise_errorf ~loc:ptyp_loc "%s cannot be derived for %s"
93113
deriver (Ppx_deriving.string_of_core_type typ))) @
94114
[Exp.case (pvar "_") [%expr false]]
95115
in
96116
[%expr fun lhs rhs -> [%e Exp.match_ [%expr lhs, rhs] cases]]
97117
| { ptyp_desc = Ptyp_var name } -> evar ("poly_"^name)
98-
| { ptyp_desc = Ptyp_alias (typ, _) } -> expr_of_typ typ
118+
| { ptyp_desc = Ptyp_alias (typ, _) } -> expr_of_typ group_def typ
99119
| { ptyp_loc } ->
100120
raise_errorf ~loc:ptyp_loc "%s cannot be derived for %s"
101121
deriver (Ppx_deriving.string_of_core_type typ)
102122

103-
let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
104-
parse_options options;
123+
let str_of_type ~options ~path group_def ({ ptype_loc = loc } as type_decl) =
124+
ignore (parse_options options);
105125
let comparator =
106126
match type_decl.ptype_kind, type_decl.ptype_manifest with
107-
| Ptype_abstract, Some manifest -> expr_of_typ manifest
127+
| Ptype_abstract, Some manifest -> expr_of_typ group_def manifest
108128
| Ptype_variant constrs, _ ->
129+
let wildcard = match constrs with
130+
| [] | [_] -> []
131+
| _ :: _ :: _ -> [Exp.case (pvar "_") [%expr false]] in
109132
let cases =
110133
(constrs |> List.map (fun { pcd_name = { txt = name }; pcd_args = typs } ->
111-
exprsn typs |>
134+
exprsn group_def typs |>
112135
Ppx_deriving.(fold_exprs ~unit:[%expr true] (binop_reduce [%expr (&&)])) |>
113136
Exp.case (ptuple [pconstr name (pattn `lhs typs);
114-
pconstr name (pattn `rhs typs)]))) @
115-
[Exp.case (pvar "_") [%expr false]]
137+
pconstr name (pattn `rhs typs)]))) @ wildcard
116138
in
117139
[%expr fun lhs rhs -> [%e Exp.match_ [%expr lhs, rhs] cases]]
118140
| Ptype_record labels, _ ->
119141
let exprs =
120142
labels |> List.map (fun { pld_name = { txt = name }; pld_type } ->
121143
let field obj = Exp.field obj (mknoloc (Lident name)) in
122-
app (expr_of_typ pld_type) [field (evar "lhs"); field (evar "rhs")])
144+
app (expr_of_typ group_def pld_type) [field (evar "lhs"); field (evar "rhs")])
123145
in
124146
[%expr fun lhs rhs -> [%e exprs |> Ppx_deriving.(fold_exprs (binop_reduce [%expr (&&)]))]]
125147
| Ptype_abstract, None ->
@@ -135,11 +157,26 @@ let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
135157
pvar (Ppx_deriving.mangle_type_decl (`Prefix "equal") type_decl) in
136158
[Vb.mk (Pat.constraint_ eq_var out_type) (polymorphize comparator)]
137159

160+
let type_decl_str ~options ~path type_decls =
161+
let opts = parse_options options in
162+
let typename_set =
163+
Ppx_deriving.extract_typename_of_type_group
164+
deriver
165+
~allow_shadowing:opts.allow_std_type_shadowing
166+
type_decls in
167+
let here_loc = (List.hd type_decls).ptype_loc in
168+
if StringSet.mem "bool" typename_set then
169+
raise_errorf
170+
~loc:here_loc
171+
"%s can't derivate types when shadowing bool (even with option)" deriver;
172+
let code =
173+
List.map (str_of_type ~options ~path typename_set) type_decls in
174+
[Str.value Recursive (List.concat code)]
175+
138176
let () =
139-
Ppx_deriving.(register (create "eq"
140-
~core_type: expr_of_typ
141-
~type_decl_str: (fun ~options ~path type_decls ->
142-
[Str.value Recursive (List.concat (List.map (str_of_type ~options ~path) type_decls))])
177+
Ppx_deriving.(register (create deriver
178+
~core_type:(expr_of_typ StringSet.empty)
179+
~type_decl_str: type_decl_str
143180
~type_decl_sig: (fun ~options ~path type_decls ->
144181
List.concat (List.map (sig_of_type ~options ~path) type_decls))
145182
()

0 commit comments

Comments
 (0)