Skip to content

Commit 93c9f02

Browse files
committed
Fix polymorphic generators in mutual recursive types
1 parent 311d5cc commit 93c9f02

2 files changed

Lines changed: 38 additions & 33 deletions

File tree

src/ppx_deriving_qcheck/ppx_deriving_qcheck.ml

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ let pat ~loc ?sized s =
6161
let s = name ?sized s in
6262
A.pvar s
6363

64+
let rec add_fuel ~loc expr =
65+
match expr.pexp_desc with
66+
| Pexp_fun (x, y, z, expr) ->
67+
{ expr with pexp_desc = Pexp_fun (x, y, z, add_fuel ~loc expr) }
68+
| _ -> [%expr fun n -> [%e expr]]
69+
6470
let rec curry_args ~loc args body =
6571
match args with
6672
| [] -> body
@@ -98,7 +104,7 @@ let sized_and_gen ~loc ?(args = []) typ gen patt =
98104
let gen_expr = curry_args ~loc args gen in
99105
let gen = [%stri let [%p patt] = [%e expr_sized]] in
100106
let gen_sized = [%stri let rec [%p pat_sized] = [%e gen_expr]] in
101-
(gen_sized, Some gen)
107+
(gen_sized, gen)
102108

103109

104110
let gen ~loc ?(env = TypeGen.empty) lg =
@@ -146,25 +152,19 @@ let sized ~loc ~env typ_name (is_rec : 'a -> bool)
146152

147153
let mutually_recursive_gens ~loc typs gens =
148154
let (module A) = Ast_builder.make loc in
149-
let rec aux l typs =
150-
match (l, typs) with
151-
| ([%stri let rec [%p? ppat] = [%e? expr]], gen) :: xs, typ :: typs
152-
| ([%stri let [%p? ppat] = [%e? expr]], gen) :: xs, typ :: typs ->
153-
(match gen with
154-
| None ->
155-
(* Current generator is not recursive. However, we want the generators
156-
to share the fuel. Thus, we transform the non-recursive generator to
157-
a recursive one. *)
158-
let expr = [%expr fun n -> [%e expr]] in
159-
let ppat' = pat ~loc ~sized:true typ in
160-
let (_, gen) = sized_and_gen ~loc typ [%expr None] ppat in
161-
aux (([%stri let [%p ppat'] = [%e expr]], gen) :: xs) (typ :: typs)
162-
| Some x ->
163-
(A.value_binding ~pat:ppat ~expr, x) :: aux xs typs)
164-
| ([], []) -> []
165-
| _ -> assert false
155+
156+
let gens =
157+
List.map2 (fun x y -> match (x, y) with
158+
| ([%stri let rec [%p? ppat] = [%e? expr]], `Needed call_to_size), _ ->
159+
(A.value_binding ~pat:ppat ~expr, call_to_size)
160+
| ([%stri let [%p? _] = [%e? expr]], `Useless call_to_size), typ ->
161+
let expr = add_fuel ~loc expr in
162+
let ppat = pat ~loc ~sized:true typ in
163+
(A.value_binding ~pat:ppat ~expr, call_to_size)
164+
| (_, _) -> assert false)
165+
gens typs
166166
in
167-
let (sized_gens, gens) = List.split (aux gens typs) in
167+
let (sized_gens, gens) = List.split gens in
168168
let mutual_gens = A.pstr_value Recursive sized_gens in
169169
mutual_gens :: gens
170170

@@ -343,8 +343,7 @@ and gen_from_constr ~loc ~env ty xs =
343343
| xs ->
344344
let args = List.map (gen_from_type ~loc ~env) xs in
345345
match gen with
346-
| [%expr [%e? f] [%e? x]] ->
347-
[%expr [%e apply_args ~loc args f] [%e x]]
346+
| [%expr [%e? f] [%e? x]] -> [%expr [%e apply_args ~loc args f] [%e x]]
348347
| _ -> apply_args ~loc args gen
349348
350349
and gen_from_constr_decl ~loc ?(env = TypeGen.empty)
@@ -490,18 +489,22 @@ let gen_from_type_declaration ~loc ?(env = TypeGen.empty) td =
490489
td.ptype_params
491490
in
492491
492+
let (sized, call_to_size) = sized_and_gen ~loc ~args typ gen_expr gen_pat in
493493
match gen_expr with
494494
| [%expr fun n -> [%e? _]] ->
495-
sized_and_gen ~loc ~args typ gen_expr gen_pat
495+
(sized, `Needed call_to_size)
496496
| _ ->
497-
([%stri let [%p gen_pat] = [%e curry_args ~loc args gen_expr]], None)
498-
497+
([%stri let [%p gen_pat] = [%e curry_args ~loc args gen_expr]],
498+
`Useless call_to_size)
499499
500500
let derive_gen ~loc xs : structure =
501501
match xs with
502502
| _, [ x ] ->
503503
let (sized, gen) = gen_from_type_declaration ~loc x in
504-
sized :: Option.fold ~none:[] ~some:(fun x -> [x]) gen
504+
(match gen with
505+
| `Needed gen ->
506+
[sized; gen]
507+
| _ -> [sized])
505508
| _, xs ->
506509
let (module A) = Ast_builder.make loc in
507510
let typs = List.map (fun td -> td.ptype_name.txt) xs in

test/ppx_deriving_qcheck/deriver/test.ml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -545,14 +545,14 @@ let test_forest () =
545545
let expected =
546546
[
547547
[%stri
548-
let rec gen_tree_sized n =
548+
let rec gen_tree_sized gen_a n =
549549
QCheck.Gen.map
550550
(fun gen0 -> Node gen0)
551551
(QCheck.Gen.map
552552
(fun (gen0, gen1) -> (gen0, gen1))
553-
(QCheck.Gen.pair QCheck.Gen.int (gen_forest_sized (n / 2))))
553+
(QCheck.Gen.pair gen_a ((gen_forest_sized gen_a)(n / 2))))
554554

555-
and gen_forest_sized n =
555+
and gen_forest_sized gen_a n =
556556
match n with
557557
| 0 -> QCheck.Gen.pure Nil
558558
| n ->
@@ -564,20 +564,22 @@ let test_forest () =
564564
(fun gen0 -> Cons gen0)
565565
(QCheck.Gen.map
566566
(fun (gen0, gen1) -> (gen0, gen1))
567-
(QCheck.Gen.pair (gen_tree_sized (n / 2)) (gen_forest_sized (n / 2))))
567+
(QCheck.Gen.pair
568+
((gen_tree_sized gen_a) (n / 2))
569+
((gen_forest_sized gen_a) (n / 2))))
568570
);
569571
]];
570-
[%stri let gen_tree = QCheck.Gen.sized @@ gen_tree_sized];
571-
[%stri let gen_forest = QCheck.Gen.sized @@ gen_forest_sized];
572+
[%stri let gen_tree gen_a = QCheck.Gen.sized @@ (gen_tree_sized gen_a)];
573+
[%stri let gen_forest gen_a = QCheck.Gen.sized @@ (gen_forest_sized gen_a)];
572574
]
573575
in
574576
let actual =
575577
f
576578
@@ extract
577579
[%stri
578-
type tree = Node of (int * forest)
580+
type 'a tree = Node of ('a * 'a forest)
579581

580-
and forest = Nil | Cons of (tree * forest)]
582+
and 'a forest = Nil | Cons of ('a tree * 'a forest)]
581583
in
582584
check_eq ~expected ~actual "deriving forest"
583585

0 commit comments

Comments
 (0)