Skip to content

Commit f94a110

Browse files
committed
Adding check of standard type shadowing and code generation for eq.
1 parent 4d27ac4 commit f94a110

1 file changed

Lines changed: 60 additions & 29 deletions

File tree

src_plugins/ppx_deriving_eq.ml

Lines changed: 60 additions & 29 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_masking: bool;
16+
}
17+
18+
let default_eq_options =
19+
{
20+
allow_std_type_masking = 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_masking" -> { allow_std_type_masking = 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,62 @@ 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
29-
(fun var -> [%type: [%t var] -> [%t var] -> bool])
44+
(fun var -> [%type: [%t var] -> [%t var] -> Pervasives.bool])
3045
type_decl
31-
[%type: [%t typ] -> [%t typ] -> bool]
46+
[%type: [%t typ] -> [%t typ] -> Pervasives.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]
5069
| [%type: float] | [%type: bool] | [%type: char] | [%type: string] | [%type: bytes] ->
5170
[%expr (fun (a:[%t typ]) b -> a = b)]
52-
| [%type: [%t? typ] ref] -> [%expr fun a b -> [%e expr_of_typ typ] !a !b]
71+
| [%type: [%t? typ] ref] -> [%expr fun a b -> [%e expr_of_typ group_def typ] !a !b]
5372
| [%type: [%t? typ] list] ->
5473
[%expr
5574
let rec loop x y =
5675
match x, y with
5776
| [], [] -> true
58-
| a :: x, b :: y -> [%e expr_of_typ typ] a b && loop x y
77+
| a :: x, b :: y -> [%e expr_of_typ group_def typ] a b && loop x y
5978
| _ -> false
6079
in (fun x y -> loop x y)]
6180
| [%type: [%t? typ] array] ->
6281
[%expr fun x y ->
6382
let rec loop i =
64-
(i = Array.length x || [%e expr_of_typ typ] x.(i) y.(i)) && loop (i + 1)
83+
(i = Array.length x || [%e expr_of_typ group_def typ] x.(i) y.(i)) && loop (i + 1)
6584
in Array.length x = Array.length y && loop 0]
6685
| [%type: [%t? typ] option] ->
6786
[%expr fun x y ->
6887
match x, y with
6988
| None, None -> true
70-
| Some a, Some b -> [%e expr_of_typ typ] a b
89+
| Some a, Some b -> [%e expr_of_typ group_def typ] a b
7190
| _ -> false]
7291
| { ptyp_desc = Ptyp_constr ({ txt = lid }, args) } ->
7392
let equal_fn = Exp.ident (mknoloc (Ppx_deriving.mangle_lid (`Prefix "equal") lid)) in
74-
app equal_fn (List.map expr_of_typ args)
93+
app equal_fn (List.map (expr_of_typ group_def) args)
7594
| { ptyp_desc = Ptyp_tuple typs } ->
7695
[%expr fun [%p ptuple (pattn `lhs typs)] [%p ptuple (pattn `rhs typs)] ->
77-
[%e exprsn typs |> Ppx_deriving.(fold_exprs (binop_reduce [%expr (&&)]))]]
96+
[%e exprsn group_def typs |> Ppx_deriving.(fold_exprs (binop_reduce [%expr (&&)]))]]
7897
| { ptyp_desc = Ptyp_variant (fields, _, _); ptyp_loc } ->
7998
let cases =
8099
(fields |> List.map (fun field ->
@@ -84,42 +103,44 @@ and expr_of_typ typ =
84103
Exp.case (pdup (fun _ -> Pat.variant label None)) [%expr true]
85104
| Rtag (label, _, false, [typ]) ->
86105
Exp.case (pdup (fun var -> Pat.variant label (Some (pvar var))))
87-
(app (expr_of_typ typ) [evar "lhs"; evar "rhs"])
106+
(app (expr_of_typ group_def typ) [evar "lhs"; evar "rhs"])
88107
| Rinherit ({ ptyp_desc = Ptyp_constr (tname, _) } as typ) ->
89108
Exp.case (pdup (fun var -> Pat.alias (Pat.type_ tname) (mknoloc var)))
90-
(app (expr_of_typ typ) [evar "lhs"; evar "rhs"])
109+
(app (expr_of_typ group_def typ) [evar "lhs"; evar "rhs"])
91110
| _ ->
92111
raise_errorf ~loc:ptyp_loc "%s cannot be derived for %s"
93112
deriver (Ppx_deriving.string_of_core_type typ))) @
94113
[Exp.case (pvar "_") [%expr false]]
95114
in
96115
[%expr fun lhs rhs -> [%e Exp.match_ [%expr lhs, rhs] cases]]
97116
| { ptyp_desc = Ptyp_var name } -> evar ("poly_"^name)
98-
| { ptyp_desc = Ptyp_alias (typ, _) } -> expr_of_typ typ
117+
| { ptyp_desc = Ptyp_alias (typ, _) } -> expr_of_typ group_def typ
99118
| { ptyp_loc } ->
100119
raise_errorf ~loc:ptyp_loc "%s cannot be derived for %s"
101120
deriver (Ppx_deriving.string_of_core_type typ)
102121

103-
let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
104-
parse_options options;
122+
let str_of_type ~options ~path group_def ({ ptype_loc = loc } as type_decl) =
123+
ignore (parse_options options);
105124
let comparator =
106125
match type_decl.ptype_kind, type_decl.ptype_manifest with
107-
| Ptype_abstract, Some manifest -> expr_of_typ manifest
126+
| Ptype_abstract, Some manifest -> expr_of_typ group_def manifest
108127
| Ptype_variant constrs, _ ->
128+
let wildcard = match constrs with
129+
| [] | [_] -> []
130+
| _ :: _ :: _ -> [Exp.case (pvar "_") [%expr false]] in
109131
let cases =
110132
(constrs |> List.map (fun { pcd_name = { txt = name }; pcd_args = typs } ->
111-
exprsn typs |>
133+
exprsn group_def typs |>
112134
Ppx_deriving.(fold_exprs ~unit:[%expr true] (binop_reduce [%expr (&&)])) |>
113135
Exp.case (ptuple [pconstr name (pattn `lhs typs);
114-
pconstr name (pattn `rhs typs)]))) @
115-
[Exp.case (pvar "_") [%expr false]]
136+
pconstr name (pattn `rhs typs)]))) @ wildcard
116137
in
117138
[%expr fun lhs rhs -> [%e Exp.match_ [%expr lhs, rhs] cases]]
118139
| Ptype_record labels, _ ->
119140
let exprs =
120141
labels |> List.map (fun { pld_name = { txt = name }; pld_type } ->
121142
let field obj = Exp.field obj (mknoloc (Lident name)) in
122-
app (expr_of_typ pld_type) [field (evar "lhs"); field (evar "rhs")])
143+
app (expr_of_typ group_def pld_type) [field (evar "lhs"); field (evar "rhs")])
123144
in
124145
[%expr fun lhs rhs -> [%e exprs |> Ppx_deriving.(fold_exprs (binop_reduce [%expr (&&)]))]]
125146
| Ptype_abstract, None ->
@@ -135,11 +156,21 @@ let str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
135156
pvar (Ppx_deriving.mangle_type_decl (`Prefix "equal") type_decl) in
136157
[Vb.mk (Pat.constraint_ eq_var out_type) (polymorphize comparator)]
137158

159+
let type_decl_str ~options ~path type_decls =
160+
let opts = parse_options options in
161+
let typename_set =
162+
Ppx_deriving.extract_typename_of_type_group
163+
deriver
164+
~allow_shadowing:opts.allow_std_type_masking
165+
type_decls in
166+
let code =
167+
List.map (str_of_type ~options ~path typename_set) type_decls in
168+
[Str.value Recursive (List.concat code)]
169+
138170
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))])
171+
Ppx_deriving.(register (create deriver
172+
~core_type:(expr_of_typ StringSet.empty)
173+
~type_decl_str: type_decl_str
143174
~type_decl_sig: (fun ~options ~path type_decls ->
144175
List.concat (List.map (sig_of_type ~options ~path) type_decls))
145176
()

0 commit comments

Comments
 (0)