package visitors

  1. Overview
  2. Docs

Source file Visitors.ml

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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
open VisitorsString
open VisitorsList
open Ppxlib
open List
open Asttypes
open Parsetree
open Ast_helper
open Ppx_deriving.Ast_convenience
open Ppx_deriving
open VisitorsPlugin
open VisitorsCompatibility
open VisitorsAnalysis
open VisitorsGeneration
open VisitorsSettings

(* -------------------------------------------------------------------------- *)

(* Per-run global state. *)

module Setup (X : SETTINGS) = struct

let arity =
  X.arity

(* -------------------------------------------------------------------------- *)

(* If the [public] option is absent, then every method is public. If it is
   present, then every method is private, unless its name occurs in the list
   [X.public]. *)

let visibility m =
  match X.public with
  | None ->
      Public
  | Some ms ->
      if List.mem m ms then Public else Private

(* The following brings [generate] and [dump] into scope. *)

include ClassFieldStore()

let annotation (ty : core_type) : core_type option =
  (* A type annotation is generated only in [polymorphic] mode. *)
  if X.polymorphic then Some ty else None

let generate_concrete_method m e ty =
  generate (concrete_method (visibility m) m e (annotation ty))

let generate_virtual_method m ty =
  generate (virtual_method (visibility m) m (annotation ty))

(* -------------------------------------------------------------------------- *)

(* The following brings [warning] and [warnings] into scope. *)

include WarningStore()

(* [datacon_opacity_warning cd] emits a warning (if necessary) about the
   following issue. One should not write "A of int[@opaque]". Instead, one
   should write "A of (int[@opaque])". In the case of records fields, we fix
   this silently, by moving the attribute from the record field to the type,
   but in the case of data constructors with multiple fields, it is preferable
   to be strict. *)

let datacon_opacity_warning (cd : constructor_declaration) : unit =
  if opacity cd.pcd_attributes = Opaque then
    warning cd.pcd_loc
      "%s: @opaque, attached to a data constructor, is ignored.\n\
       It should be attached to a type. Please use parentheses."
      plugin

(* [sum_build_warning decl] emits emits a warning (if necessary) about the
   following issue. One should not attach a [@@build] attribute to a sum type.
   Instead, one should attach a [@build] attribute to every data constructor.
   Note that one can attach a [@@build] attribute to a record type. *)

let sum_build_warning (decl : type_declaration) : unit =
  if build decl.ptype_attributes <> None then
    warning decl.ptype_loc
      "%s: @@build, attached to a sum type, is ignored.\n\
       Instead, @build should be attached to each data constructor."
      plugin

(* -------------------------------------------------------------------------- *)

(* Shared glue code for detecting and warning against name clashes. *)

type 'a wrapper =
  'a -> 'a

type tycon_visitor_method =
  Location.t * attributes * Longident.t -> methode

let protect_tycon_visitor_method : tycon_visitor_method wrapper =
  fun tycon_visitor_method ->
    let format : (_, _, _, _) format4 =
      "%s: name clash: the types %s and %s\n\
       both have visitor methods named %s.\n\
       Please consider using [@@name] at type declaration sites\n\
       or [@name] at type reference sites."
    in
    let id = print_longident in
    protect tycon_visitor_method
      (fun (_, _, x) (_, _, y) -> x = y)
      (fun (_, _, x) (loc, _, y) m -> warning loc format plugin (id x) (id y) m)

type datacon_descending_method =
  constructor_declaration -> methode

let protect_datacon_descending_method : datacon_descending_method wrapper =
  fun datacon_descending_method ->
    let format : (_, _, _, _) format4 =
      "%s: name clash: the data constructors %s and %s\n\
       both have visitor methods named %s.\n\
       Please consider using [@name] at data constructor declaration sites."
    in
    let id cd = cd.pcd_name.txt in
    protect datacon_descending_method
      (fun cd1 cd2 -> cd1 == cd2)
      (fun cd1 cd2 m -> warning cd2.pcd_loc format plugin (id cd1) (id cd2) m)

(* -------------------------------------------------------------------------- *)

(* We support parameterized type declarations. We require them to be regular.
   That is, for instance, if a type ['a term] is being defined, then every
   use of [_ term] in the definition should be ['a term]; it cannot be, say,
   [int term] or [('a * 'a) term]. *)

(* To enforce this, we check that, in every use of a local type constructor,
   the actual type parameters coincide with the formal type parameters. *)

(* This check is imposed only on [mono] type variables. For [poly] type
   variables, irregularity is allowed. *)

(* The purpose of this check is to avoid an incomprehensible type error in
   the generated code. *)

let check_regularity loc tycon (formals : tyvars) (actuals : core_types) =
  (* Check that the numbers of parameters match. *)
  if length formals <> length actuals then
    raise_errorf ~loc
      "%s: the type constructor %s expects %s,\n\
       but is applied to %s."
      plugin tycon
      (number (length formals) "type parameter")
      (number (length actuals) "type parameter");
  (* Check that the parameters match. *)
  if not X.irregular && not (
    fold_left2 (fun ok formal actual ->
      ok && (X.poly formal || actual.ptyp_desc = Ptyp_var formal)
    ) true formals actuals
  ) then
    raise_errorf ~loc "%s: the type constructor %s is irregular." plugin tycon

(* -------------------------------------------------------------------------- *)

(* Public naming conventions. *)

(* The names of the methods associated with the type [foo] are normally based
   on (derived from) the name [foo].

   This base name can be overriden by the user via an attribute. For a local
   type, a [@@name] attribute must be attached to the type declaration. For a
   nonlocal type, a [@name] attribute must be attached to every reference to
   this type.

   The [@name] attribute can be misused: e.g., one can mistakenly use
   different visitor method names for different occurrences of a single type.
   We currently do not attempt to detect this situation.

   The prefix that is prepended to the base name can be controlled via the
   settings [visit_prefix], [build_prefix], and [fail_prefix]. *)

let tycon_modified_name (attrs : attributes) (tycon : tycon) : tycon =
  maybe (name attrs) tycon

(* Similarly, the base name of the methods associated with a data constructor
   can be altered via a [@name] attribute, which must be attached to the data
   constructor declaration. *)

let datacon_modified_name (cd : constructor_declaration) : datacon =
  maybe (name cd.pcd_attributes) cd.pcd_name.txt

(* For every type constructor [tycon], there is a visitor method, also called
   a descending method, as it is invoked when going down into the tree. *)

(* The name of this method is normally [visit_foo] if the type is named [foo]
   or [A.foo]. (A qualified name must denote a nonlocal type.) *)

(* This convention can cause name clashes, as the types [foo] and [A.foo]
   receive visitor methods by the same name. We warn if this happens.

   A name clash can also be caused by incorrect use of the [@@name] or
   [@name] attributes. We also warn if this happens. *)

(* Step 1 -- the raw convention. *)

let tycon_visitor_method : tycon_visitor_method =
  fun (_, attrs, tycon) ->
    X.visit_prefix ^ tycon_modified_name attrs (Longident.last_exn tycon)

(* Step 2 -- protect against name clashes. *)

let tycon_visitor_method =
  protect_tycon_visitor_method tycon_visitor_method

(* Step 3 -- define auxiliary functions that are easier to use. *)

let local_tycon_visitor_method (decl : type_declaration) : methode =
  tycon_visitor_method (decl.ptype_loc, decl.ptype_attributes, Lident decl.ptype_name.txt)

let nonlocal_tycon_visitor_method (ty : core_type) : methode =
  match ty.ptyp_desc with
  | Ptyp_constr (tycon, _) ->
      tycon_visitor_method (ty.ptyp_loc, ty.ptyp_attributes, tycon.txt)
  | _ ->
      assert false

(* For every local record type constructor [tycon], there is an ascending
   method, which is invoked on the way up, in order to re-build some data
   structure. This method is virtual and exists only when the scheme is
   [fold]. *)

(* The name of this method is normally [build_foo] if the type is named [foo]. *)

let tycon_ascending_method (decl : type_declaration) : methode =
  X.build_prefix ^ tycon_modified_name decl.ptype_attributes decl.ptype_name.txt

(* [mono] type variables have a virtual visitor method. We include a quote in
   the method name so as to ensure the absence of collisions. *)

let tyvar_visitor_method (alpha : tyvar) : methode =
  sprintf "%s'%s" X.visit_prefix alpha

(* For every data constructor [datacon], there is a descending visitor method,
   which is invoked on the way down, when this data constructor is discovered. *)

(* The name of this method is normally [visit_Foo] if the data constructor is
   named [Foo]. *)

let datacon_descending_method (cd : constructor_declaration) : methode =
  X.visit_prefix ^ datacon_modified_name cd

let datacon_descending_method =
  protect_datacon_descending_method datacon_descending_method

(* For every data constructor [datacon], there is a ascending visitor method,
   which is invoked on the way up, in order to re-build some data structure.
   This method is virtual and exists only when the scheme is [fold]. *)

let datacon_ascending_method (cd : constructor_declaration) : methode =
  X.build_prefix ^ datacon_modified_name cd

(* At arity 2, for every sum type constructor [tycon] which has at least two
   data constructors, there is a failure method, which is invoked when the
   left-hand and right-hand arguments do not exhibit the same tags. *)

(* The name of this method is normally [fail_foo] if the type is named [foo]. *)

let failure_method (decl : type_declaration) : methode =
  X.fail_prefix ^ tycon_modified_name decl.ptype_attributes decl.ptype_name.txt

(* When [scheme] is [Reduce], we need a monoid, that is, a unit [zero] and a
   binary operation [plus]. The names [zero] and [plus] are fixed. We assume
   that there exist virtual methods by these names. It is up to the user to
   provide these methods via inheritance, that is, via the [ancestors]
   option. *)

let zero =
  "zero"

let plus =
  "plus"

(* -------------------------------------------------------------------------- *)

(* Private naming conventions. *)

(* These conventions must be set up so as to avoid collisions within each name
   space separately: e.g., variables, methods, type variables, and so on. *)

(* We use improbable variable names, because it is possible for user code to
   be placed in the scope of these variables. (This is the case when the user
   provides [@build] annotations.) As a couple exceptions, the names [self]
   and [env] are not made improbable, and we document the existence of these
   variables, which can be used in [@build] annotations. *)

(* In a class, the variable [self] refers to self.
   The type variable [ty_self] denotes its type. *)

let self : variable =
  "self"

let ty_self : core_type =
  Typ.var "self"

let pself : pattern =
  Pat.constraint_ (pvar self) ty_self

(* The variable [env] refers to the environment that is carried down into
   recursive calls. *)

let env : variable =
  "env"

(* We sometimes need two (or more) copies of a variable: one copy for each
   index [j] ranging in the interval [0..arity). *)

let copy (j : int) (x : string) : string =
  assert (0 <= j && j < arity);
  if arity = 1 then
    (* No alteration required. *)
    x
  else
    sprintf "%s_%d" x j

(* The variables [component i j] denote tuple components. The index [i]
   ranges over tuple components; the index [j] ranges in [0..arity). *)

let component (i : int) (j : int) : variable =
  improbable (copy j (sprintf "c%d" i))

let components (i : int) : variables =
  map (component i) (interval 0 arity)

let componentss (xs : _ list) : variables list =
  mapi (fun i _ -> components i) xs

(* The variable [thing j] denotes an input value. *)

let thing (j : int) : variable =
  improbable (copy j "this")

let things : variables =
  map thing (interval 0 arity)

(* The variable [this] is used only in the generation of [endo] visitors. *)

let this =
  thing 0

(* The variables [field label j] denote record fields. *)

let field (label : label) (j : int) : variable =
  improbable (copy j (sprintf "f%s" label))

let fields (label : label) : variables =
  map (field label) (interval 0 arity)

let fieldss (labels : label list) : variables list =
  map fields labels

(* The variables [result i] denote results of recursive calls. *)

let result (i : int) : variable =
  improbable (sprintf "r%d" i)

let results (xs : _ list) : variables =
  mapi (fun i _ -> result i) xs

(* The variables [summary i] denote results of recursive calls.
   When the scheme is [MapReduce], each recursive call produces
   a pair; we use [result i] and [summary i] as the names of the
   pair components. *)

let summary (i : int) : variable =
  improbable (sprintf "s%d" i)

let summaries (xs : _ list) : variables =
  mapi (fun i _ -> summary i) xs

(* Reserved names of type variables. We forbid the user from using these
   names, and do not let them be renamed by the function [variant] below. *)

let reserved : tyvars =
  [ "s"; "env" ]

let reserved_ty_var (alpha : tyvar) : core_type =
  assert (mem alpha reserved);
  ty_var alpha

(* Naming conventions for type variables in type annotations. If ['a]
   is a type variable named by the user, we use ['a_i], where [i] is
   in [0..arity]. Indices [i] in the interval [0..arity) are used for
   the arguments of a visitor method. The index [arity] is used for
   the result of a visitor method. *)

(* If [scheme] is [Endo], then the argument and result must have the
   same type, so we do not introduce a variation in the type variables. *)

let variant (i : int) (alpha : tyvar) : tyvar =
  assert (0 <= i && i <= arity);
  if X.scheme = Endo || mem alpha reserved then
    alpha
  else
    sprintf "%s_%d" alpha i

let vary_type (i : int) (ty : core_type) : core_type =
  rename_type (variant i) ty

(* [ty_monoid] is the type of monoid elements. *)

let ty_monoid =
  reserved_ty_var "s"

(* [ty_env] is the type of the environment. *)

(* What IS the type of the environment? This is a tricky question. Two
   possibilities arise:

   1. One might wish for every visitor method to be polymorphic in the type
   ['env] of the environment. This makes the method more widely applicable,
   but means that the environment effectively cannot be used by the method
   (except by passing it on to its callees). Thus, the method cannot be
   overridden by a custom implementation that actually uses the environment.

   2. One might wish for the environment to have monomorphic type. In that
   case, one should note that there is a priori no reason why the type of
   the environment should be the same in every method. So, we must be
   careful not to use a single type variable ['env]. We must use a distinct
   type variable every time, or (easier but equivalent) use a wildcard.

   How do we let the user specify which behavior is desired? And with what
   granularity? We choose a simple approach: we treat ['env] as polymorphic
   if and only if this (reserved) type variable is declared polymorphic by
   the user. *)

let ty_env =
  if X.poly "env" then
    reserved_ty_var "env"
  else
    ty_any

(* What is the type of a virtual visitor method [visit_'a] in charge of
   dealing with a [mono] type variable ['a]?

   One might think that it must be a monomorphic type, so we can just
   issue a wildcard [_] and let OCaml infer this type.

   Yet, if the user has requested that every method be polymorphic in
   the type ['env] of the environment, then [visit_'a], too must be
   polymorphic in ['env]. (Otherwise, the generated code would be ill-typed.)
   In that case, we must generate ['env . 'env -> _].

   This implies that [visit_'a] cannot use the environment.
   So, it seems somewhat doubtful that this feature is useful.
   Perhaps we could allow the environment to consist of two components
   and to quantify universally over only one of them? *)

let tyvar_visitor_method_type =
  if X.poly "env" then
    typ_poly ["env"] (ty_arrow ty_env ty_any)
  else
    ty_any

(* [poly] type variables have a visitor function. *)

let tyvar_visitor_function (alpha : tyvar) : variable =
  tyvar_visitor_method alpha

(* -------------------------------------------------------------------------- *)

(* Construction of type annotations. *)

(* [result_type scheme ty] is the result type of a visitor method associated
   with the type [ty]. *)

(* If [ty] is of the form [decl_type decl] -- that is, if [ty] is a local type
   constructor -- then this is the result type of the visitor method associated
   with [ty]. *)

let rec result_type scheme (ty : core_type) : core_type =
  match scheme with
  | Iter ->
      (* An [iter] visitor method returns nothing. *)
      ty_unit
  | Map | Endo ->
      (* A [map] or [endo] visitor method for the type [ty] returns a
         value of type [ty]. Note that [ty] can contain type variables. *)
      ty
  | Reduce ->
      (* A [reduce] visitor method returns a monoid element. *)
      ty_monoid
  | MapReduce ->
      (* A [mapreduce] visitor method returns a pair of the results produced
         by a [map] visitor method and by a [reduce] visitor method. *)
      Typ.tuple [ result_type Map ty; result_type Reduce ty ]
  | Fold ->
      (* This is where we have a problem. We would really like to allow the
         user to specify which skeleton should be used here, as we cannot
         guess it. We might allow it in the future. For now, we impose the
         monomorphic skeleton [_], which is not as general as we would like,
         since it requires the method to have monomorphic result type. *)
      ty_any

let result_type =
  result_type X.scheme

(* [decl_result_type decl] is the result type of a visitor method associated
   with the type declaration [decl]. *)

let decl_result_type decl =
  result_type (decl_type decl)

(* A visitor function takes an environment, followed with [arity] arguments,
   and produces a result. Thus, if [argument] and [result] are types, then the
   type of a visitor function has the following shape:

     ty_env ->
     argument_0 -> ... -> argument_{arity-1} ->
     result_{arity}

   where [ty_{i}] denotes a copy of the type [ty] whose type variables have
   been renamed by the renaming [variant i]. *)

(* We generalize the above definition to allow for multiple [arguments]. This
   is used in the visitor methods associated with data constructors. Thus,
   each argument in succession is extended to [arity] arguments. *)

(* We specialize the above definition to the case where the result type
   is [result_type ty]. *)

let visitor_fun_type (arguments : core_types) (ty : core_type) : core_type =
  ty_arrows
    (ty_env :: flatten (hextend arguments arity vary_type))
    (vary_type arity (result_type ty))

(* This special case of [visitor_fun_type] is the normal form of a visitor
   function type: there is one argument of type [ty] (extended to [arity])
   and one result of type [result_type ty]. *)

let simple_visitor_fun_type (ty : core_type) : core_type =
  visitor_fun_type [ty] ty

(* [visitor_method_type decl] is the type of the visitor method associated
   with the type [decl]. This does not account for the visitor parameters
   in charge of dealing with type variables. *)

let visitor_method_type (decl : type_declaration) : core_type =
  simple_visitor_fun_type (decl_type decl)

(* [visitor_param_type alpha] is the type of the visitor function associated
   with the type variable [alpha]. *)

let visitor_param_type (alpha : tyvar) : core_type =
  simple_visitor_fun_type (ty_var alpha)

(* [fold_result_type ty] is the result type of the visitor code generated
   by [visit_type ... ty], when [scheme] is [Fold]. *)

let fold_result_type _ty =
  (* This function is currently unimplemented and unused, because we
     do not allow [polymorphic] to be [true] when [scheme] is [Fold].
     Thus, we do not generate any type annotations for ascending methods. *)
  ty_any

(* [poly_params decl] is the subset of the formal type parameters of [decl]
   which are marked [poly]. For each of these parameters, a visitor function
   should be passed. *)

let poly_params (decl : type_declaration) : tyvars =
  filter X.poly (decl_params decl)

(* [quantify alphas ty] quantifies an appropriate set of type variables in the
   method type [ty]. The parameter [alphas] is usually [poly_params decl],
   although it could in principle be a subset of it, if we can prove that
   some visitor functions are unneeded. We introduce universal quantifiers
   on (suitable variants of) the type variables [alphas] and also possibly
   on the type variable ['env]. *)

let quantify (alphas : tyvars) (ty : core_type) : core_type =
  (* Find out which variants of the type variables [alphas] we should quantify
     over. For the arguments, we need to quantify over the variants in the
     interval [0..arity). For the result, we may need to quantify over the
     variant [arity]. We try and avoid superfluous quantifiers, as that would
     decrease readability. *)
  let alphas =
    match X.scheme with
    | Iter
    | Reduce ->
        (* Just the arguments. The result contains no type variables. *)
        flatten (hextend alphas arity variant)
    | Map
    | MapReduce ->
        (* Arguments and result. *)
        flatten (hextend alphas (arity+1) variant)
    | Endo ->
        (* In this special case, there is just one variant, as the argument
           and result must have the same type. *)
        alphas
    | Fold ->
        (* Polymorphism currently not supported with [Fold]. *)
        []
  in
  (* Then, decide whether ['env] should be universally quantified. *)
  let alphas =
    if X.poly "env" then
      "env" :: alphas
    else
      alphas
  in
  (* Done. *)
  typ_poly alphas ty

(* -------------------------------------------------------------------------- *)

(* [bind rs ss] is a binding construct which, depending on the scheme, binds
   either the variables [rs], or the variables [ss], or both, using pair
   patterns. It is used to bind the results of recursive calls to visitor
   methods. *)

let bind (rs : variables) (ss : variables)
  : expressions -> expression -> expression =
  match X.scheme with
  | Iter
  | Map
  | Endo
  | Fold ->
      letn rs
  | Reduce ->
      letn ss
  | MapReduce ->
      letnp rs ss

(* -------------------------------------------------------------------------- *)

(* [call m es] emits a method call of the form [self#m es]. *)

let call (m : methode) (es : expressions) : expression =
  send self m es

(* -------------------------------------------------------------------------- *)

(* Access to the monoid operations. *)

let monoid_unit () : expression =
  assert (X.scheme = Reduce || X.scheme = MapReduce);
  call zero []

let monoid_law () : expression =
  assert (X.scheme = Reduce || X.scheme = MapReduce);
  call plus []

(* -------------------------------------------------------------------------- *)

(* [reduce es] reduces the expressions [es], that is, it combines them, using
   a monoid, which provides a unit and a binary operation. The reduction is
   performed left-to-right. This could be of importance if the monoid is not
   associative-commutative. *)

let reduce es =
  let unit = monoid_unit()
  and law = monoid_law() in
  fold_left1 (fun e1 e2 -> app law [e1; e2]) unit es

(* -------------------------------------------------------------------------- *)

(* [alias x ps] requires the pattern list [ps] to have length [arity]. If
   [scheme] is [Endo], then it further requires [arity] to be 1. It adds a
   binding of the variable [x], using an [as] pattern, at the top level of the
   pattern. The result is again packaged as a pattern list of length [arity].
   If scheme is not [Endo], then [alias x ps] is just [ps]. *)

let alias (x : variable) (ps : patterns) : patterns =
  assert (length ps = arity);
  match X.scheme with
  | Endo ->
      assert (arity = 1);
      map (fun p ->
        Pat.alias p (Ocaml_common.Location.mknoloc x)
      ) ps
  | _ ->
      ps

(* If [scheme] is [Endo], then [transmit x xs] is [x :: xs]. Otherwise, it is
   just [xs]. *)

let transmit x xs =
  match X.scheme with
  | Endo ->
      x :: xs
  | _ ->
      xs

(* -------------------------------------------------------------------------- *)

(* [hook m xs ty e] constructs a call of the form [self#m xs], and (as a side
   effect) generates a method [method m xs = e]. The free variables of the
   expression [e] must be (a subset of) [xs]. The type [ty] is the type of
   the method. It is always computed internally, but the type annotation is
   actually printed only in [polymorphic] mode. *)

(* Thus, by default, the expression [hook m xs ty e] behaves in the same way
   as the expression [e]. But a hook, named [m], allows this default to be
   overridden. *)

let hook (m : methode) (xs : variables) (ty : core_type) (e : expression) : expression =
  (* Generate a method. *)
  generate_concrete_method m (lambdas xs e) ty;
  (* Construct a method call. *)
  call m (evars xs)

(* The additional parameter [b] makes hook insertion optional. If [b] is [true],
   a hook is created; otherwise, no hook is created. *)

let hook b m xs ty e =
  if b then hook m xs ty e else e

(* -------------------------------------------------------------------------- *)

(* [vhook m xs ty] constructs a call of the form [self#m xs], and (as a side
   effect) generates a virtual method [method m: ty]. The type [ty] is the type
   of the method. It is always computed internally, but the type annotation is
   actually printed only in [polymorphic] mode. *)

let vhook (m : methode) (xs : variables) (ty : core_type) : expression =
  generate_virtual_method m ty;
  call m (evars xs)

(* -------------------------------------------------------------------------- *)

(* If a data constructor or record carries a [@build] attribute, then the
   OCaml expression carried by this attribute should be used instead of the
   default [builder] function, which rebuilds a data constructor or record.
   This concerns [map], [endo], and [mapreduce] visitors. *)

type builder =
  variables -> expression

let ifbuild (attrs : attributes) (builder : builder) : builder =
  match build attrs with
  | None ->
      builder
  | Some e ->
      fun rs -> app e (evars rs)

(* -------------------------------------------------------------------------- *)

(* The following classes help build the code that forms the ascending part of
   a visitor method, that is, the code found after the recursive calls. *)

(* There are four variants of this code, used in visitor methods for data
   constructors, in visitor methods for records, in visitor functions for
   tuples, and in visitor functions for @opaque types. *)

(* The base class, [ascend], provides as much shared behavior as possible. *)

class virtual ascend (ss : variables) = object (self)

  (* An [iter] visitor returns a unit value. *)
  method ascend_Iter =
    unit()

  (* The behavior of a [map] visitor is defined in subclasses. *)
  method virtual ascend_Map : expression

  (* By default, an [endo] visitor behaves like a [map] visitor. This behavior
  is appropriate at @opaque types. *)
  method ascend_Endo =
    self#ascend_Map

  (* A [reduce] visitor uses [zero] and [plus] to combine the results
     of the recursive calls, which are bound to the variables [ss]. *)
  method ascend_Reduce =
    reduce (evars ss)

  (* A [mapreduce] visitor returns a pair of the results that would be
     returned by a [map] visitor and by a [reduce] visitor. *)
  method ascend_MapReduce =
    tuple [ self#ascend_Map; self#ascend_Reduce ]

  (* By default, a [fold] visitor behaves like a [map] visitor, because we
     have no better choice. This behavior is used  at tuples and at @opaque
     types. *)
  method ascend_Fold =
    self#ascend_Map

  (* Dispatch. *)
  method ascend =
    match X.scheme with
    | Iter      -> self#ascend_Iter
    | Map       -> self#ascend_Map
    | Endo      -> self#ascend_Endo
    | Reduce    -> self#ascend_Reduce
    | MapReduce -> self#ascend_MapReduce
    | Fold      -> self#ascend_Fold

end

(* The subclass [ascend_opaque] defines the desired behavior at @opaque types. *)

(* In an [iter] visitor, we return a unit value, as always. This means that,
   even if [arity] is greater than 1, NO EQUALITY TEST takes place. This
   differs from the behavior of the methods [visit_int], [visit_bool], etc.,
   which perform an equality test. *)

(* In a [map] visitor, we return THE FIRST ARGUMENT of the visitor. At arity
   greater than 1, this is an ARBITRARY choice. It is not clear what else we
   could do. *)

(* In a [reduce] visitor, we return the neutral element, [self#zero]. *)

(* In a [fold] visitor, we keep the default behavior, which is to behave like
   a [map] visitor. *)

class ascend_opaque (xs : variables) = object

  inherit ascend []

  method ascend_Map =
    evar (hd xs) (* [xs] is the vector of arguments; pick the first one *)

end

(* The subclass [ascend_endo] defines the standard behavior of an [endo]
   visitor, which is to perform physical equality tests. *)

(* Its parameters are as follows:

   [this]       (a variable for) the data structure that is visited
   [subjects]   the matrix of arguments to the recursive calls
   [rs], [ss]   (vectors of variables for) the results of the recursive calls *)

class virtual ascend_endo
  (this : variable)
  (subjects : expressions list)
  (rs : variables)
  (ss : variables)
= object (self)

  inherit ascend ss

  (* An [endo] visitor first tests if the arguments of the recursive calls,
     [subjects], are physically equal to the results of these calls, [rs]. If
     that is the case, then it returns the original data structure, [this].
     Otherwise, it reconstructs a new data structure, like a [map] visitor. *)
  method! ascend_Endo =
    (* [subjects] is a matrix of width [arity], and [arity] is [1]. The first
       column of [subjects] is [map hd subjects]. *)
    assert (for_all (fun es -> length es = arity) subjects);
    assert (arity = 1);
    Exp.ifthenelse
      (eqphys (map hd subjects) (evars rs))
      (evar this)
      (Some self#ascend_Map)

end

(* The subclass [ascend_tuple] defines the desired behavior at tuple types. *)

class ascend_tuple this subjects rs ss = object

  inherit ascend_endo this subjects rs ss

  (* A [map] visitor reconstructs a tuple. *)
  method ascend_Map =
    tuple (evars rs)

end

(* The subclass [ascend_algebraic] defines the desired behavior at a sum type
   or record type. Its extra parameters are as follows:

   [builder]  how to reconstruct a data constructor or record
   [decl]     the type declaration under which we are working
   [m]        the name of the virtual ascending method
   [tys]      the types of the components of this data constructor or record *)

class ascend_algebraic this subjects rs ss
  (builder : builder)
  (decl : type_declaration)
  (m : methode)
  (tys : core_types)
= object

  inherit ascend_endo this subjects rs ss

  (* A [map] visitor reconstructs a data structure using the results [rs] of
     the recursive calls. *)
  method ascend_Map =
    builder rs

  (* A [fold] visitor invokes the virtual ascending method [m], with [env] and
     [rs] as arguments. As a side effect, [ascend_Fold] declares this virtual
     method. *)
  method! ascend_Fold =
    vhook m
      (env :: rs)
      (ty_arrows
         (ty_env :: map fold_result_type tys)
         (decl_result_type decl)
      )

end

(* -------------------------------------------------------------------------- *)

(* [visit_type env_in_scope ty] builds a small expression that represents the
   visiting code associated with the OCaml type [ty]. For instance, if [ty] is
   a local type constructor, this could be a call to the visitor method
   associated with this type constructor. *)

(* This expression may refer to the variable [self]. *)

(* If [env_in_scope] is true, then this expression may refer to the variable
   [env]. If [env_in_scope] is false, then this expression should denote a
   function of [env]. The use of [env_in_scope] complicates things slightly,
   but allows us to avoid the production of certain eta-redexes. *)

(* If [ty] carries the attribute [@opaque], then we act as if there is nothing
   to visit. The nature of the type [ty], in that case, plays no role. *)

let rec visit_type (env_in_scope : bool) (ty : core_type) : expression =
  match env_in_scope, opacity ty.ptyp_attributes, ty.ptyp_desc with

  (* A type constructor [tycon] applied to type parameters [tys]. We handle
     the case where [env_in_scope] is false, so we construct a function of
     [env]. *)
  | false,
    NonOpaque,
    Ptyp_constr ({ txt = tycon; _ }, tys) ->
      (* [tycon] is a type constructor, applied to certain types [tys]. *)
      (* We must call the visitor method associated with [tycon],
         applied to the visitor functions associated with SOME of the [tys]. *)
      let m, tys =
        match is_local X.decls tycon with
        | Some decl ->
            let formals = decl_params decl in
            (* [tycon] is a local type constructor, whose formal type parameters
               are [formals]. Among these formal type parameters, some should be
               treated in a monomorphic manner, and some should be treated in a
               polymorphic manner. The function [X.poly], applied to a type
               variable [formal], tells how it should be treated. *)
            (* The regularity check is applied only to [mono] parameters. *)
            check_regularity ty.ptyp_loc (Longident.last_exn tycon) formals tys;
            (* The visitor method should be applied to the visitor functions
               associated with the subset of [tys] that corresponds to [poly]
               variables. *)
            local_tycon_visitor_method decl,
            filter2 X.poly formals tys
        | None ->
            (* [tycon] is a nonlocal type constructor. *)
            (* The visitor method should be applied to visitor functions for all
               of the types [tys]. *)
           (* This visitor method is NOT generated by us, so it MUST be
               inherited from an ancestor class; it is up to the user to
               ensure that this method exists. (It may be virtual.) This
               method may be polymorphic, so multiple call sites do not
               pollute one another. *)
            nonlocal_tycon_visitor_method ty,
            tys
      in
      app
        (call m [])
        (map (visit_type false) tys)

  (* A type variable [alpha] must be a formal parameter of the current
     declaration. (Indeed, we do not handle GADTs yet.) Now, two cases
     arise. If [alpha] is [mono], then it is handled by a virtual visitor
     method. If [alpha] is [poly], then it is handled by a visitor function
     which we must have received as an argument. *)
  | false,
    NonOpaque,
    Ptyp_var alpha ->
      if X.poly alpha then
        evar (tyvar_visitor_function alpha)
      else
        vhook (tyvar_visitor_method alpha) [] tyvar_visitor_method_type

  (* A tuple type. We handle the case where [env_in_scope] is true, as it
     is easier. *)
  | true,
    NonOpaque,
    Ptyp_tuple tys ->
      (* Construct a function that takes [arity] tuples as arguments. *)
      (* See [constructor_declaration] for comments. *)
      let xss = componentss tys in
      let subjects = evarss xss in
      let rs = results xss
      and ss = summaries xss in
      let ascend = new ascend_tuple this subjects rs ss in
      plambdas
        (alias this (ptuples (transpose arity (pvarss xss))))
        (bulk rs ss tys subjects ascend)

  (* If [env_in_scope] does not have the desired value, wrap a recursive call
     within an application or abstraction. At most one recursive call takes
     place, so we never produce an eta-redex. *)
  | true, NonOpaque, (Ptyp_constr _ | Ptyp_var _) ->
     app (visit_type false ty) [evar env]
  | false, _, _ ->
     lambda env (visit_type true ty)

  (* If [ty] is marked opaque, then we ignore the structure of [ty] and carry
     out appropriate action, based on the current scheme. *)
  | true, Opaque, _ ->
      (* Construct a function that takes [arity] arguments. *)
      lambdas things (
        let ascend = new ascend_opaque things in
        ascend#ascend
      )

  (* An unsupported construct. *)
  | _, _, Ptyp_any
  | _, _, Ptyp_arrow _
  | _, _, Ptyp_object _
  | _, _, Ptyp_class _
  | _, _, Ptyp_alias _
  | _, _, Ptyp_variant _
  | _, _, Ptyp_poly _
  | _, _, Ptyp_package _
  | _, _, Ptyp_extension _ ->
      unsupported ty

and visit_types tys (ess : expressions list) : expressions =
  (* The matrix [ess] is indexed first by component, then by index [j].
     Thus, to each type [ty], corresponds a row [es] of expressions,
     whose length is [arity]. *)
  assert (is_matrix (length tys) arity ess);
  map2 (fun ty es ->
    app (visit_type true ty) es
  ) tys ess

(* -------------------------------------------------------------------------- *)

(* The expression [bulk rs ss tys subjects ascend] represents the bulk of a
   visitor method or visitor function. It performs the recursive calls, binds
   their results to [rs] and/or [ss], then runs the ascending code. *)

and bulk
  (rs : variables) (ss : variables)
  (tys : core_types)
  (subjects : expressions list)
  (ascend : < ascend: expression; .. >)
=
  bind rs ss
    (visit_types tys subjects)
    (ascend#ascend)

(* -------------------------------------------------------------------------- *)

(* [constructor_declaration] turns a constructor declaration (as found in a
   declaration of a sum type) into a case, that is, a branch in the case
   analysis construct that forms the body of the visitor method for this sum
   type. At the same time, it generates several auxiliary method declarations
   and definitions. *)

let constructor_declaration decl (cd : constructor_declaration) : case =
  datacon_opacity_warning cd;
  let datacon = cd.pcd_name.txt in

  (* This is either a traditional data constructor, whose components are
     anonymous, or a data constructor whose components form an ``inline
     record''. This is a new feature of OCaml 4.03. *)

  (* In order to treat these two cases uniformly, we extract the following
     information.
     [xss]      the names under which the components are known.
                this matrix has [length tys] rows -- one per component --
                and [arity] columns.
     [tys]      the types of the components.
     [pss]      the patterns that bind [xss], on the way down.
                this matrix has [arity] rows.
                it has [length tys] columns in the case of tuples,
                and 1 column in the case of inline records.
     [builder]  a function which, applied to the results [rs] of the
                recursive calls, rebuilds a data constructor, on the way up.
  *)

  let xss, tys, pss, (builder : builder) =
  match cd.pcd_args with
    (* A traditional data constructor. *)
    | Pcstr_tuple tys ->
        let xss = componentss tys in
        let pss = transpose arity (pvarss xss) in
        xss, tys, pss, fun rs -> constr datacon (evars rs)
    (* An ``inline record'' data constructor. *)
    | Pcstr_record lds ->
        let labels, tys = ld_labels lds, ld_tys lds in
        let xss = fieldss labels in
        let pss = transpose arity (pvarss xss) in
        xss, tys,
        map (fun ps -> [precord ~closed:Closed (combine labels ps)]) pss,
        fun rs -> constr datacon [record (combine labels (evars rs))]
  in
  assert (is_matrix (length tys) arity xss);
  assert (length pss = arity);
  let subjects = evarss xss in

  (* Take a [@build] attribute into account. *)
  let builder = ifbuild cd.pcd_attributes builder in

  (* Find out which type variables [alphas] are formal parameters of this
     declaration and are marked [poly]. We have to universally quantify over
     (variants of) these type variables in the type of the hook, below.
     Furthermore, we forbid these type variables from appearing under [@opaque],
     as that would cause us to generate code whose actual type is less general
     than its expected type. *)
  let alphas = poly_params decl in
  check_poly_under_opaque alphas tys;

  (* Create new names [rs] and [ss] for the results of the recursive calls of
     visitor methods. *)
  let rs = results xss
  and ss = summaries xss in

  (* Construct a case for this data constructor in the visitor method associated
     with this sum type. This case analyzes a tuple of width [arity]. After
     binding the components [xss], we call the descending method associated with
     this data constructor. The arguments of this method are:
     1. visitor functions for [poly] type variables;
     2. [env];
     3. [this] (see below);
     4. [xss].
     In this method, we bind the variables [rs] and/or [ss] to the results of
     the recursive calls to visitor methods, then produce a result (whose nature
     depends on [scheme]). *)

  (* If the variety is [endo] (which implies that [arity] is 1), then we bind
     the variable [this] to the whole memory block. This variable is transmitted
     to the descending method. When the time comes to allocate a new memory
     block, if the components of the new block are physically equal to the
     components of the existing block, then the address of the existing block is
     returned; otherwise a new block is allocated, as in [map]. *)

  let ascend =
    new ascend_algebraic
        this subjects rs ss
        builder decl (datacon_ascending_method cd) tys
  in

  Exp.case
    (ptuple (alias this (map (pconstr datacon) pss)))
    (hook X.data
      (datacon_descending_method cd)
      (map tyvar_visitor_function alphas @ env :: transmit this (flatten xss))
      (quantify alphas (ty_arrows
        (map visitor_param_type alphas)
        (visitor_fun_type (transmit (decl_type decl) tys) (decl_type decl))))
      (bulk rs ss tys subjects ascend)
    )

(* -------------------------------------------------------------------------- *)

(* [visit_decl decl] constructs an expression that represents the visiting
   code associated with the type declaration [decl]. In other words, it is
   the body of the visitor method associated with [decl]. *)

let visit_decl (decl : type_declaration) : expression =

  (* Check that the user does not use a reserved type variable name. *)
  decl_params decl |> iter (fun alpha ->
    if mem alpha reserved then
      let loc = decl.ptype_loc in
      raise_errorf ~loc "%s: the type variable name '%s is reserved."
                   plugin alpha
  );

  (* Bind the values to a vector of variables [xs]. *)
  let xs = things in
  assert (length xs = arity);

  match decl.ptype_kind, decl.ptype_manifest with

  (* A type abbreviation. *)
  | Ptype_abstract, Some ty ->
      visit_type true ty

  (* A record type. *)
  | Ptype_record (lds : label_declaration list), _ ->
      let labels, tys = ld_labels lds, ld_tys (fix lds) in
      (* See [constructor_declaration] for comments. *)
      check_poly_under_opaque (poly_params decl) tys;
      let builder rs = record (combine labels (evars rs)) in
      let builder = ifbuild decl.ptype_attributes builder in
      let subjects = accesses xs labels in
      let rs = results labels
      and ss = summaries labels in
      let ascend =
        new ascend_algebraic
          (hd xs) subjects rs ss
          builder decl (tycon_ascending_method decl) tys
      in
      lambdas xs (bulk rs ss tys subjects ascend)

  (* A sum type. *)
  | Ptype_variant (cds : constructor_declaration list), _ ->
      sum_build_warning decl;
      (* Generate one case per data constructor. Place these cases in a
         [match] construct, which itself is placed in a function body. *)
      (* If [arity] is greater than 1 and if there is more than one data
         constructor, then generate also a default case. In this default
         case, invoke the failure method, which raises an exception. The
         failure method receives [env] and [xs] as arguments. *)
      let default() : case =
        Exp.case
          (ptuple (pvars xs))
          (hook true
            (failure_method decl)
            (env :: xs)
            (quantify (poly_params decl) (visitor_method_type decl))
            (efail (local_tycon_visitor_method decl))
          )
      in
      let complete (cs : case list) : case list =
        if arity = 1 || length cs <= 1 then cs else cs @ [ default() ]
      in
      lambdas xs (
        Exp.match_
          (tuple (evars xs))
          (complete (map (constructor_declaration decl) cds))
      )

  (* Unsupported constructs. *)
  | Ptype_abstract, None ->
      let loc = decl.ptype_loc in
      raise_errorf ~loc "%s: cannot deal with abstract types." plugin

  | Ptype_open, _ ->
      let loc = decl.ptype_loc in
      raise_errorf ~loc "%s: cannot deal with open types." plugin

(* -------------------------------------------------------------------------- *)

(* [type_decl decl] generates the main visitor method associated with the type
   declaration [decl], as well as the necessary auxiliary methods. It returns
   no result. *)

let type_decl (decl : type_declaration) : unit =
  let alphas = poly_params decl in
  generate_concrete_method
    (local_tycon_visitor_method decl)
    (lambdas (map tyvar_visitor_function alphas @ [env]) (visit_decl decl))
    (quantify alphas (ty_arrows (map visitor_param_type alphas) (visitor_method_type decl)))

(* -------------------------------------------------------------------------- *)

(* [type_decls decls] processes the type declarations [decl] and produces a
   list of structure items. It is the main entry point inside the body of
   the functor [Setup]. *)

let type_decls (decls : type_declaration list) : structure =
  (* Analyze the type definitions, and populate our classes with methods. *)
  iter type_decl decls;
  (* Emit our preprocessor warnings (if any). *)
  warnings() @
  (* In the generated code, disable certain warnings, so that the user sees
     no warnings, even if she explicitly enables them. We disable warnings
     26, 27 (unused variables) and 4 (fragile pattern matching; a feature
     intentionally exploited by [iter2] and [map2]). *)
  [ with_warnings "-4-26-27" (
    (* Surround the generated code with floating attributes, which can be
       used as markers to find and review the generated code. We use this
       mechanism to show the generated code in the documentation. *)
    floating "VISITORS.BEGIN" [] ::
      (* Produce a class definition. *)
      (* Our classes are parameterized over the type variable ['env]. They are
         also parameterized over the type variable ['self], with a constraint
         that this is the type of [self]. This trick allows us to omit the types
         of the virtual methods, even if these types include type variables. *)
    dump X.concrete X.ancestors [ ty_self, (NoVariance, NoInjectivity) ] pself X.name ::
    floating "VISITORS.END" [] ::
    []
  )]

end

(* -------------------------------------------------------------------------- *)

(* [type_decls decls] produces a list of structure items (that is, toplevel
   definitions) associated with the type declarations [decls]. It is the
   main entry point outside of the functor [Setup]. *)

let type_decls ~options ~path:_ (decls : type_declaration list) : structure =
  assert (decls <> []);
  let module Process = Setup(Parse(struct
    let loc = (VisitorsList.last decls).ptype_loc (* an approximation *)
    let options = options
    let decls = decls
  end)) in
  Process.type_decls decls

(* -------------------------------------------------------------------------- *)

(* Register our plugin with [ppx_deriving]. *)

let () =
  register (create plugin ~type_decl_str:type_decls ())
OCaml

Innovation. Community. Security.