forked from ocaml/ocaml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparmatch.ml
2250 lines (1992 loc) · 73.6 KB
/
parmatch.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
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
(* Detection of partial matches and unused match cases. *)
open Misc
open Asttypes
open Types
open Typedtree
(*************************************)
(* Utilities for building patterns *)
(*************************************)
let make_pat desc ty tenv =
{pat_desc = desc; pat_loc = Location.none; pat_extra = [];
pat_type = ty ; pat_env = tenv;
pat_attributes = [];
}
let omega = make_pat Tpat_any Ctype.none Env.empty
let extra_pat =
make_pat
(Tpat_var (Ident.create "+", mknoloc "+"))
Ctype.none Env.empty
let rec omegas i =
if i <= 0 then [] else omega :: omegas (i-1)
let omega_list l = List.map (fun _ -> omega) l
let zero = make_pat (Tpat_constant (Const_int 0)) Ctype.none Env.empty
(***********************)
(* Compatibility check *)
(***********************)
(* Patterns p and q compatible means:
there exists value V that matches both, However....
The case of extension types is dubious, as constructor rebind permits
that different constructors are the same (and are thus compatible).
Compilation must take this into account, consider:
type t = ..
type t += A|B
type t += C=A
let f x y = match x,y with
| true,A -> '1'
| _,C -> '2'
| false,A -> '3'
| _,_ -> '_'
As C is bound to A the value of f false A is '2' (and not '3' as it would
be in the absence of rebinding).
Not considering rebinding, patterns "false,A" and "_,C" are incompatible
and the compiler can swap the second and third clause, resulting in the
(more efficiently compiled) matching
match x,y with
| true,A -> '1'
| false,A -> '3'
| _,C -> '2'
| _,_ -> '_'
This is not correct: when C is bound to A, "f false A" returns '2' (not '3')
However, diagnostics do not take constructor rebinding into account.
Notice, that due to module abstraction constructor rebinding is hidden.
module X : sig type t = .. type t += A|B end = struct
type t = ..
type t += A
type t += B=A
end
open X
let f x = match x with
| A -> '1'
| B -> '2'
| _ -> '_'
The second clause above will NOT (and cannot) be flagged as useless.
Finally, there are two compatibility fonction
compat p q ---> 'syntactic compatibility, used for diagnostics.
may_compat p q ---> a safe approximation of possible compat,
for compilation
*)
let is_absent tag row = Btype.row_field tag !row = Rabsent
let is_absent_pat p = match p.pat_desc with
| Tpat_variant (tag, _, row) -> is_absent tag row
| _ -> false
let const_compare x y =
match x,y with
| Const_float f1, Const_float f2 ->
Pervasives.compare (float_of_string f1) (float_of_string f2)
| Const_string (s1, _), Const_string (s2, _) ->
String.compare s1 s2
| (Const_int _
|Const_char _
|Const_string (_, _)
|Const_float _
|Const_int32 _
|Const_int64 _
|Const_nativeint _
), _ -> Pervasives.compare x y
let records_args l1 l2 =
(* Invariant: fields are already sorted by Typecore.type_label_a_list *)
let rec combine r1 r2 l1 l2 = match l1,l2 with
| [],[] -> List.rev r1, List.rev r2
| [],(_,_,p2)::rem2 -> combine (omega::r1) (p2::r2) [] rem2
| (_,_,p1)::rem1,[] -> combine (p1::r1) (omega::r2) rem1 []
| (_,lbl1,p1)::rem1, ( _,lbl2,p2)::rem2 ->
if lbl1.lbl_pos < lbl2.lbl_pos then
combine (p1::r1) (omega::r2) rem1 l2
else if lbl1.lbl_pos > lbl2.lbl_pos then
combine (omega::r1) (p2::r2) l1 rem2
else (* same label on both sides *)
combine (p1::r1) (p2::r2) rem1 rem2 in
combine [] [] l1 l2
module Compat
(Constr:sig
val equal :
Types.constructor_description ->
Types.constructor_description ->
bool
end) = struct
let rec compat p q = match p.pat_desc,q.pat_desc with
(* Variables match any value *)
| ((Tpat_any|Tpat_var _),_)
| (_,(Tpat_any|Tpat_var _)) -> true
(* Structural induction *)
| Tpat_alias (p,_,_),_ -> compat p q
| _,Tpat_alias (q,_,_) -> compat p q
| Tpat_or (p1,p2,_),_ ->
(compat p1 q || compat p2 q)
| _,Tpat_or (q1,q2,_) ->
(compat p q1 || compat p q2)
(* Constructors, with special case for extension *)
| Tpat_construct (_, c1,ps1), Tpat_construct (_, c2,ps2) ->
Constr.equal c1 c2 && compats ps1 ps2
(* More standard stuff *)
| Tpat_variant(l1,op1, _), Tpat_variant(l2,op2,_) ->
l1=l2 && ocompat op1 op2
| Tpat_constant c1, Tpat_constant c2 ->
const_compare c1 c2 = 0
| Tpat_tuple ps, Tpat_tuple qs -> compats ps qs
| Tpat_lazy p, Tpat_lazy q -> compat p q
| Tpat_record (l1,_),Tpat_record (l2,_) ->
let ps,qs = records_args l1 l2 in
compats ps qs
| Tpat_array ps, Tpat_array qs ->
List.length ps = List.length qs &&
compats ps qs
| _,_ -> assert false (* By typing *)
and ocompat op oq = match op,oq with
| None,None -> true
| Some p,Some q -> compat p q
| (None,Some _)|(Some _,None) -> false
and compats ps qs = match ps,qs with
| [], [] -> true
| p::ps, q::qs -> compat p q && compats ps qs
| _,_ -> assert false (* By typing *)
end
module SyntacticCompat =
Compat
(struct
let equal c1 c2 = Types.equal_tag c1.cstr_tag c2.cstr_tag
end)
let compat = SyntacticCompat.compat
and compats = SyntacticCompat.compats
(* Due to (potential) rebinding, two extension constructors
of the same arity type may equal *)
exception Empty (* Empty pattern *)
(****************************************)
(* Utilities for retrieving type paths *)
(****************************************)
(* May need a clean copy, cf. PR#4745 *)
let clean_copy ty =
if ty.level = Btype.generic_level then ty
else Subst.type_expr Subst.identity ty
(* As reported in PR#6394 it is possible for recursive modules to add incoherent
equations into the environment.
So assuming we're working on the same example as in PR#6394, when looking at
constructor [A] we end up calling [expand_head] on [t] (the type of [A]) and
get [int * bool].
This will result in a proper error later on during type checking, meanwhile
we need to "survive" and be somewhat aware that we're working on bogus input
*)
type constructor_type_path =
| Ok of Path.t
| Inconsistent_environment
let get_constructor_type_path ty tenv =
let ty = Ctype.repr (Ctype.expand_head tenv (clean_copy ty)) in
match ty.desc with
| Tconstr (path,_,_) -> Ok path
| _ -> Inconsistent_environment
(****************************)
(* Utilities for matching *)
(****************************)
(* Check top matching *)
let simple_match p1 p2 =
match p1.pat_desc, p2.pat_desc with
| Tpat_construct(_, c1, _), Tpat_construct(_, c2, _) ->
Types.equal_tag c1.cstr_tag c2.cstr_tag
| Tpat_variant(l1, _, _), Tpat_variant(l2, _, _) ->
l1 = l2
| Tpat_constant(c1), Tpat_constant(c2) -> const_compare c1 c2 = 0
| Tpat_tuple _, Tpat_tuple _ -> true
| Tpat_lazy _, Tpat_lazy _ -> true
| Tpat_record _ , Tpat_record _ -> true
| Tpat_array p1s, Tpat_array p2s -> List.length p1s = List.length p2s
| _, (Tpat_any | Tpat_var(_)) -> true
| _, _ -> false
(* extract record fields as a whole *)
let record_arg p = match p.pat_desc with
| Tpat_any -> []
| Tpat_record (args,_) -> args
| _ -> fatal_error "Parmatch.as_record"
(* Raise Not_found when pos is not present in arg *)
let get_field pos arg =
let _,_, p = List.find (fun (_,lbl,_) -> pos = lbl.lbl_pos) arg in
p
let extract_fields omegas arg =
List.map
(fun (_,lbl,_) ->
try
get_field lbl.lbl_pos arg
with Not_found -> omega)
omegas
(* Build argument list when p2 >= p1, where p1 is a simple pattern *)
let rec simple_match_args p1 p2 = match p2.pat_desc with
| Tpat_alias (p2,_,_) -> simple_match_args p1 p2
| Tpat_construct(_, _, args) -> args
| Tpat_variant(_, Some arg, _) -> [arg]
| Tpat_tuple(args) -> args
| Tpat_record(args,_) -> extract_fields (record_arg p1) args
| Tpat_array(args) -> args
| Tpat_lazy arg -> [arg]
| (Tpat_any | Tpat_var(_)) ->
begin match p1.pat_desc with
Tpat_construct(_, _,args) -> omega_list args
| Tpat_variant(_, Some _, _) -> [omega]
| Tpat_tuple(args) -> omega_list args
| Tpat_record(args,_) -> omega_list args
| Tpat_array(args) -> omega_list args
| Tpat_lazy _ -> [omega]
| _ -> []
end
| _ -> []
(*
Normalize a pattern ->
all arguments are omega (simple pattern) and no more variables
*)
let rec normalize_pat q = match q.pat_desc with
| Tpat_any | Tpat_constant _ -> q
| Tpat_var _ -> make_pat Tpat_any q.pat_type q.pat_env
| Tpat_alias (p,_,_) -> normalize_pat p
| Tpat_tuple (args) ->
make_pat (Tpat_tuple (omega_list args)) q.pat_type q.pat_env
| Tpat_construct (lid, c,args) ->
make_pat
(Tpat_construct (lid, c,omega_list args))
q.pat_type q.pat_env
| Tpat_variant (l, arg, row) ->
make_pat (Tpat_variant (l, may_map (fun _ -> omega) arg, row))
q.pat_type q.pat_env
| Tpat_array (args) ->
make_pat (Tpat_array (omega_list args)) q.pat_type q.pat_env
| Tpat_record (largs, closed) ->
make_pat
(Tpat_record (List.map (fun (lid,lbl,_) ->
lid, lbl,omega) largs, closed))
q.pat_type q.pat_env
| Tpat_lazy _ ->
make_pat (Tpat_lazy omega) q.pat_type q.pat_env
| Tpat_or _ -> fatal_error "Parmatch.normalize_pat"
(* Consider a pattern matrix whose first column has been simplified
to contain only _ or a head constructor
| p1, r1...
| p2, r2...
| p3, r3...
| ...
We build a normalized /discriminating/ pattern from a pattern [q] by folding
over the first column of the matrix, "refining" [q] as we go:
- when we encounter a row starting with [Tpat_tuple] or [Tpat_lazy] then we
can stop and return that pattern, as we cannot refine any further. Indeed,
these constructors are alone in their signature, so they will subsume
whatever other pattern we might find, as well as the pattern we're threading
along.
- when we find a [Tpat_record] then it is a bit more involved: it is also
alone in its signature, however it might only be matching a subset of the
record fields. We use these fields to refine our accumulator and keep going
as another row might match on different fields.
- rows starting with a wildcard do not bring any information, so we ignore
them and keep going
- if we encounter anything else (i.e. any other constructor), then we just
stop and return our accumulator.
*)
let discr_pat q pss =
let rec refine_pat acc = function
| [] -> acc
| (head, _) :: rows ->
match head.pat_desc with
| Tpat_or _ | Tpat_var _ | Tpat_alias _ -> assert false
| Tpat_any -> refine_pat acc rows
| Tpat_tuple _ | Tpat_lazy _ -> normalize_pat head
| Tpat_record (largs, closed) ->
(* N.B. we could make this case "simpler" by refining the record case
using [all_record_args].
In which case we wouldn't need to fold over the first column for
records.
However it makes the witness we generate for the exhaustivity warning
less pretty. *)
let new_omegas =
List.fold_right
(fun (lid, lbl,_) r ->
try
let _ = get_field lbl.lbl_pos r in
r
with Not_found ->
(lid, lbl,omega)::r)
largs (record_arg acc)
in
let new_acc =
make_pat (Tpat_record (new_omegas, closed)) head.pat_type head.pat_env
in
refine_pat new_acc rows
| _ -> acc
in
let q = normalize_pat q in
(* short-circuiting: clearly if we have anything other than [Tpat_record] or
[Tpat_any] to start with, we're not going to be able refine at all. So
there's no point going over the matrix. *)
match q.pat_desc with
| Tpat_any | Tpat_record _ -> refine_pat q pss
| _ -> q
(*
In case a matching value is found, set actual arguments
of the matching pattern.
*)
let rec read_args xs r = match xs,r with
| [],_ -> [],r
| _::xs, arg::rest ->
let args,rest = read_args xs rest in
arg::args,rest
| _,_ ->
fatal_error "Parmatch.read_args"
let do_set_args erase_mutable q r = match q with
| {pat_desc = Tpat_tuple omegas} ->
let args,rest = read_args omegas r in
make_pat (Tpat_tuple args) q.pat_type q.pat_env::rest
| {pat_desc = Tpat_record (omegas,closed)} ->
let args,rest = read_args omegas r in
make_pat
(Tpat_record
(List.map2 (fun (lid, lbl,_) arg ->
if
erase_mutable &&
(match lbl.lbl_mut with
| Mutable -> true | Immutable -> false)
then
lid, lbl, omega
else
lid, lbl, arg)
omegas args, closed))
q.pat_type q.pat_env::
rest
| {pat_desc = Tpat_construct (lid, c,omegas)} ->
let args,rest = read_args omegas r in
make_pat
(Tpat_construct (lid, c,args))
q.pat_type q.pat_env::
rest
| {pat_desc = Tpat_variant (l, omega, row)} ->
let arg, rest =
match omega, r with
Some _, a::r -> Some a, r
| None, r -> None, r
| _ -> assert false
in
make_pat
(Tpat_variant (l, arg, row)) q.pat_type q.pat_env::
rest
| {pat_desc = Tpat_lazy _omega} ->
begin match r with
arg::rest ->
make_pat (Tpat_lazy arg) q.pat_type q.pat_env::rest
| _ -> fatal_error "Parmatch.do_set_args (lazy)"
end
| {pat_desc = Tpat_array omegas} ->
let args,rest = read_args omegas r in
make_pat
(Tpat_array args) q.pat_type q.pat_env::
rest
| {pat_desc=Tpat_constant _|Tpat_any} ->
q::r (* case any is used in matching.ml *)
| _ -> fatal_error "Parmatch.set_args"
let set_args q r = do_set_args false q r
and set_args_erase_mutable q r = do_set_args true q r
(* Given a matrix of non-empty rows
p1 :: r1...
p2 :: r2...
p3 :: r3...
Simplify the first column [p1 p2 p3] by splitting all or-patterns.
The result is a list of couples
(simple pattern, rest of row)
where a "simple pattern" starts with either the catch-all pattern omega (_)
or a head constructor.
For example,
x :: r1
(Some _) as y :: r2
(None as x) as y :: r3
(Some x | (None as x)) :: r4
becomes
(_, r1)
(Some _, r2)
(None, r3)
(Some x, r4)
(None, r4)
*)
let rec simplify_first_col = function
| [] -> []
| [] :: _ -> assert false (* the rows are non-empty! *)
| (p::ps) :: rows -> simplify_head_pat p ps (simplify_first_col rows)
and simplify_head_pat p ps k =
match p.pat_desc with
| Tpat_alias (p,_,_) -> simplify_head_pat p ps k
| Tpat_var (_,_) -> (omega, ps) :: k
| Tpat_or (p1,p2,_) -> simplify_head_pat p1 ps (simplify_head_pat p2 ps k)
| _ -> (p, ps) :: k
(* Builds the specialized matrix of [pss] according to pattern [q].
See section 3.1 of http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
NOTES:
- expects [pss] to be a "simplified matrix", cf. [simplify_first_col]
- [q] was produced by [discr_pat]
- we are polymorphic on the type of matrices we work on, in particular a row
might not simply be a [pattern list]. That's why we have the [extend_row]
parameter.
*)
let build_specialized_submatrix ~extend_row q pss =
let rec filter_rec = function
| ({pat_desc = (Tpat_alias _ | Tpat_or _ | Tpat_var _) }, _) :: _ ->
assert false
| (p, ps) :: pss ->
if simple_match q p
then extend_row (simple_match_args q p) ps :: filter_rec pss
else filter_rec pss
| _ -> [] in
filter_rec pss
(* The "default" and "specialized" matrices of a given matrix.
See section 3.1 of http://moscova.inria.fr/~maranget/papers/warn/warn.pdf .
*)
type 'matrix specialized_matrices = {
default : 'matrix;
constrs : (pattern * 'matrix) list;
}
(* Consider a pattern matrix whose first column has been simplified
to contain only _ or a head constructor
| p1, r1...
| p2, r2...
| p3, r3...
| ...
We split this matrix into a list of /specialized/ sub-matrices, one for
each head constructor appearing in the first column. For each row whose
first column starts with a head constructor, remove this head
column, prepend one column for each argument of the constructor,
and add the resulting row in the sub-matrix corresponding to this
head constructor.
Rows whose left column is omega (the Any pattern _) may match any
head constructor, so they are added to all sub-matrices.
In the case where all the rows in the matrix have an omega on their first
column, then there is only one /specialized/ sub-matrix, formed of all these
omega rows.
This matrix is also called the /default/ matrix.
See the documentation of [build_specialized_submatrix] for an explanation of
the [extend_row] parameter.
*)
let build_specialized_submatrices ~extend_row q rows =
let extend_group discr p r rs =
let r = extend_row (simple_match_args discr p) r in
(discr, r :: rs)
in
(* insert a row of head [p] and rest [r] into the right group *)
let rec insert_constr p r = function
| [] ->
(* if no group matched this row, it has a head constructor that
was never seen before; add a new sub-matrix for this head *)
[extend_group (normalize_pat p) p r []]
| (q0,rs) as bd::env ->
if simple_match q0 p
then extend_group q0 p r rs :: env
else bd :: insert_constr p r env
in
(* insert a row of head omega into all groups *)
let insert_omega r env =
List.map (fun (q0,rs) -> extend_group q0 omega r rs) env
in
let rec form_groups constr_groups omega_tails = function
| [] -> (constr_groups, omega_tails)
| ({pat_desc=(Tpat_var _|Tpat_alias _|Tpat_or _)},_)::_ -> assert false
| ({pat_desc=Tpat_any}, tail) :: rest ->
(* note that calling insert_omega here would be wrong
as some groups may not have been formed yet, if the
first row with this head pattern comes after in the list *)
form_groups constr_groups (tail :: omega_tails) rest
| (p,r) :: rest ->
form_groups (insert_constr p r constr_groups) omega_tails rest
in
let constr_groups, omega_tails =
let initial_constr_group =
match q.pat_desc with
| Tpat_record(_) | Tpat_tuple(_) | Tpat_lazy(_) ->
(* [q] comes from [discr_pat], and in this case subsumes any of the
patterns we could find on the first column of [rows]. So it is better
to use it for our initial environment than any of the normalized
pattern we might obtain from the first column. *)
[q,[]]
| _ -> []
in
form_groups initial_constr_group [] rows
in
{
default = omega_tails;
constrs =
(* insert omega rows in all groups *)
List.fold_right insert_omega omega_tails constr_groups;
}
(* Variant related functions *)
let set_last a =
let rec loop = function
| [] -> assert false
| [_] -> [a]
| x::l -> x :: loop l
in
function
| (_, []) -> (a, [])
| (first, row) -> (first, loop row)
(* mark constructor lines for failure when they are incomplete
Precondition: the input matrix has been simplified so that its
first column only contains _ or head constructors. *)
let mark_partial =
List.map (function
| ({pat_desc=(Tpat_var _|Tpat_alias _|Tpat_or _)},_) -> assert false
| ({pat_desc = Tpat_any }, _) as ps -> ps
| ps -> set_last zero ps
)
let close_variant env row =
let row = Btype.row_repr row in
let nm =
List.fold_left
(fun nm (_tag,f) ->
match Btype.row_field_repr f with
| Reither(_, _, false, e) ->
(* m=false means that this tag is not explicitly matched *)
Btype.set_row_field e Rabsent;
None
| Rabsent | Reither (_, _, true, _) | Rpresent _ -> nm)
row.row_name row.row_fields in
if not row.row_closed || nm != row.row_name then begin
(* this unification cannot fail *)
Ctype.unify env row.row_more
(Btype.newgenty
(Tvariant {row with row_fields = []; row_more = Btype.newgenvar();
row_closed = true; row_name = nm}))
end
let row_of_pat pat =
match Ctype.expand_head pat.pat_env pat.pat_type with
{desc = Tvariant row} -> Btype.row_repr row
| _ -> assert false
(*
Check whether the first column of env makes up a complete signature or
not.
*)
let full_match closing env = match env with
| ({pat_desc = Tpat_construct(_,c,_)},_) :: _ ->
if c.cstr_consts < 0 then false (* extensions *)
else List.length env = c.cstr_consts + c.cstr_nonconsts
| ({pat_desc = Tpat_variant _} as p,_) :: _ ->
let fields =
List.map
(function ({pat_desc = Tpat_variant (tag, _, _)}, _) -> tag
| _ -> assert false)
env
in
let row = row_of_pat p in
if closing && not (Btype.row_fixed row) then
(* closing=true, we are considering the variant as closed *)
List.for_all
(fun (tag,f) ->
match Btype.row_field_repr f with
Rabsent | Reither(_, _, false, _) -> true
| Reither (_, _, true, _)
(* m=true, do not discard matched tags, rather warn *)
| Rpresent _ -> List.mem tag fields)
row.row_fields
else
row.row_closed &&
List.for_all
(fun (tag,f) ->
Btype.row_field_repr f = Rabsent || List.mem tag fields)
row.row_fields
| ({pat_desc = Tpat_constant(Const_char _)},_) :: _ ->
List.length env = 256
| ({pat_desc = Tpat_constant(_)},_) :: _ -> false
| ({pat_desc = Tpat_tuple(_)},_) :: _ -> true
| ({pat_desc = Tpat_record(_)},_) :: _ -> true
| ({pat_desc = Tpat_array(_)},_) :: _ -> false
| ({pat_desc = Tpat_lazy(_)},_) :: _ -> true
| ({pat_desc = (Tpat_any|Tpat_var _|Tpat_alias _|Tpat_or _)},_) :: _
| []
->
assert false
(* Written as a non-fragile matching, PR#7451 originated from a fragile matching below. *)
let should_extend ext env = match ext with
| None -> false
| Some ext -> begin match env with
| [] -> assert false
| (p,_)::_ ->
begin match p.pat_desc with
| Tpat_construct
(_, {cstr_tag=(Cstr_constant _|Cstr_block _|Cstr_unboxed)},_) ->
(match get_constructor_type_path p.pat_type p.pat_env with
| Ok path -> Path.same path ext
| Inconsistent_environment ->
(* returning [true] here could result in more computations being
done to check exhaustivity. Which is clearly not necessary
since the code doesn't typecheck anyway. *)
false)
| Tpat_construct
(_, {cstr_tag=(Cstr_extension _)},_) -> false
| Tpat_constant _|Tpat_tuple _|Tpat_variant _
| Tpat_record _|Tpat_array _ | Tpat_lazy _
-> false
| Tpat_any|Tpat_var _|Tpat_alias _|Tpat_or _
-> assert false
end
end
module ConstructorTagHashtbl = Hashtbl.Make(
struct
type t = Types.constructor_tag
let hash = Hashtbl.hash
let equal = Types.equal_tag
end
)
(* complement constructor tags *)
let complete_tags nconsts nconstrs tags =
let seen_const = Array.make nconsts false
and seen_constr = Array.make nconstrs false in
List.iter
(function
| Cstr_constant i -> seen_const.(i) <- true
| Cstr_block i -> seen_constr.(i) <- true
| _ -> assert false)
tags ;
let r = ConstructorTagHashtbl.create (nconsts+nconstrs) in
for i = 0 to nconsts-1 do
if not seen_const.(i) then
ConstructorTagHashtbl.add r (Cstr_constant i) ()
done ;
for i = 0 to nconstrs-1 do
if not seen_constr.(i) then
ConstructorTagHashtbl.add r (Cstr_block i) ()
done ;
r
(* build a pattern from a constructor description *)
let pat_of_constr ex_pat cstr =
{ex_pat with pat_desc =
Tpat_construct (mknoloc (Longident.Lident "?pat_of_constr?"),
cstr, omegas cstr.cstr_arity)}
let orify x y = make_pat (Tpat_or (x, y, None)) x.pat_type x.pat_env
let rec orify_many = function
| [] -> assert false
| [x] -> x
| x :: xs -> orify x (orify_many xs)
(* build an or-pattern from a constructor list *)
let pat_of_constrs ex_pat cstrs =
if cstrs = [] then raise Empty else
orify_many (List.map (pat_of_constr ex_pat) cstrs)
let pats_of_type ?(always=false) env ty =
let ty' = Ctype.expand_head env ty in
match ty'.desc with
| Tconstr (path, _, _) ->
begin try match (Env.find_type path env).type_kind with
| Type_variant cl when always || List.length cl = 1 ||
List.for_all (fun cd -> cd.Types.cd_res <> None) cl ->
let cstrs = fst (Env.find_type_descrs path env) in
List.map (pat_of_constr (make_pat Tpat_any ty env)) cstrs
| Type_record _ ->
let labels = snd (Env.find_type_descrs path env) in
let fields =
List.map (fun ld ->
mknoloc (Longident.Lident "?pat_of_label?"), ld, omega)
labels
in
[make_pat (Tpat_record (fields, Closed)) ty env]
| _ -> [omega]
with Not_found -> [omega]
end
| Ttuple tl ->
[make_pat (Tpat_tuple (omegas (List.length tl))) ty env]
| _ -> [omega]
let rec get_variant_constructors env ty =
match (Ctype.repr ty).desc with
| Tconstr (path,_,_) -> begin
try match Env.find_type path env with
| {type_kind=Type_variant _} ->
fst (Env.find_type_descrs path env)
| {type_manifest = Some _} ->
get_variant_constructors env
(Ctype.expand_head_once env (clean_copy ty))
| _ -> fatal_error "Parmatch.get_variant_constructors"
with Not_found ->
fatal_error "Parmatch.get_variant_constructors"
end
| _ -> fatal_error "Parmatch.get_variant_constructors"
(* Sends back a pattern that complements constructor tags all_tag *)
let complete_constrs p all_tags =
let c =
match p.pat_desc with Tpat_construct (_, c, _) -> c | _ -> assert false in
let not_tags = complete_tags c.cstr_consts c.cstr_nonconsts all_tags in
let constrs = get_variant_constructors p.pat_env c.cstr_res in
let others =
List.filter
(fun cnstr -> ConstructorTagHashtbl.mem not_tags cnstr.cstr_tag)
constrs in
let const, nonconst =
List.partition (fun cnstr -> cnstr.cstr_arity = 0) others in
const @ nonconst
let build_other_constrs env p =
match p.pat_desc with
Tpat_construct (_, {cstr_tag=Cstr_constant _|Cstr_block _}, _) ->
let get_tag = function
| {pat_desc = Tpat_construct (_,c,_)} -> c.cstr_tag
| _ -> fatal_error "Parmatch.get_tag" in
let all_tags = List.map (fun (p,_) -> get_tag p) env in
pat_of_constrs p (complete_constrs p all_tags)
| _ -> extra_pat
(* Auxiliary for build_other *)
let build_other_constant proj make first next p env =
let all = List.map (fun (p, _) -> proj p.pat_desc) env in
let rec try_const i =
if List.mem i all
then try_const (next i)
else make_pat (make i) p.pat_type p.pat_env
in try_const first
(*
Builds a pattern that is incompatible with all patterns in
the first column of env
*)
let build_other ext env = match env with
| ({pat_desc = Tpat_construct (lid, {cstr_tag=Cstr_extension _},_)},_) :: _ ->
(* let c = {c with cstr_name = "*extension*"} in *) (* PR#7330 *)
make_pat (Tpat_var (Ident.create "*extension*",
{lid with txt="*extension*"})) Ctype.none Env.empty
| ({pat_desc = Tpat_construct _} as p,_) :: _ ->
begin match ext with
| Some ext ->
(match get_constructor_type_path p.pat_type p.pat_env with
| Ok path when Path.same ext path -> extra_pat
| _ -> build_other_constrs env p)
| _ ->
build_other_constrs env p
end
| ({pat_desc = Tpat_variant (_,_,r)} as p,_) :: _ ->
let tags =
List.map
(function ({pat_desc = Tpat_variant (tag, _, _)}, _) -> tag
| _ -> assert false)
env
in
let row = row_of_pat p in
let make_other_pat tag const =
let arg = if const then None else Some omega in
make_pat (Tpat_variant(tag, arg, r)) p.pat_type p.pat_env in
begin match
List.fold_left
(fun others (tag,f) ->
if List.mem tag tags then others else
match Btype.row_field_repr f with
Rabsent (* | Reither _ *) -> others
(* This one is called after erasing pattern info *)
| Reither (c, _, _, _) -> make_other_pat tag c :: others
| Rpresent arg -> make_other_pat tag (arg = None) :: others)
[] row.row_fields
with
[] ->
make_other_pat "AnyExtraTag" true
| pat::other_pats ->
List.fold_left
(fun p_res pat ->
make_pat (Tpat_or (pat, p_res, None)) p.pat_type p.pat_env)
pat other_pats
end
| ({pat_desc = Tpat_constant(Const_char _)} as p,_) :: _ ->
let all_chars =
List.map
(fun (p,_) -> match p.pat_desc with
| Tpat_constant (Const_char c) -> c
| _ -> assert false)
env in
let rec find_other i imax =
if i > imax then raise Not_found
else
let ci = Char.chr i in
if List.mem ci all_chars then
find_other (i+1) imax
else
make_pat (Tpat_constant (Const_char ci)) p.pat_type p.pat_env in
let rec try_chars = function
| [] -> omega
| (c1,c2) :: rest ->
try
find_other (Char.code c1) (Char.code c2)
with
| Not_found -> try_chars rest in
try_chars
[ 'a', 'z' ; 'A', 'Z' ; '0', '9' ;
' ', '~' ; Char.chr 0 , Char.chr 255]
| ({pat_desc=(Tpat_constant (Const_int _))} as p,_) :: _ ->
build_other_constant
(function Tpat_constant(Const_int i) -> i | _ -> assert false)
(function i -> Tpat_constant(Const_int i))
0 succ p env
| ({pat_desc=(Tpat_constant (Const_int32 _))} as p,_) :: _ ->
build_other_constant
(function Tpat_constant(Const_int32 i) -> i | _ -> assert false)
(function i -> Tpat_constant(Const_int32 i))
0l Int32.succ p env
| ({pat_desc=(Tpat_constant (Const_int64 _))} as p,_) :: _ ->
build_other_constant
(function Tpat_constant(Const_int64 i) -> i | _ -> assert false)
(function i -> Tpat_constant(Const_int64 i))
0L Int64.succ p env
| ({pat_desc=(Tpat_constant (Const_nativeint _))} as p,_) :: _ ->
build_other_constant
(function Tpat_constant(Const_nativeint i) -> i | _ -> assert false)
(function i -> Tpat_constant(Const_nativeint i))
0n Nativeint.succ p env
| ({pat_desc=(Tpat_constant (Const_string _))} as p,_) :: _ ->
build_other_constant
(function Tpat_constant(Const_string (s, _)) -> String.length s
| _ -> assert false)
(function i -> Tpat_constant(Const_string(String.make i '*', None)))
0 succ p env
| ({pat_desc=(Tpat_constant (Const_float _))} as p,_) :: _ ->
build_other_constant
(function Tpat_constant(Const_float f) -> float_of_string f
| _ -> assert false)
(function f -> Tpat_constant(Const_float (string_of_float f)))
0.0 (fun f -> f +. 1.0) p env
| ({pat_desc = Tpat_array _} as p,_)::_ ->
let all_lengths =
List.map
(fun (p,_) -> match p.pat_desc with
| Tpat_array args -> List.length args
| _ -> assert false)
env in
let rec try_arrays l =
if List.mem l all_lengths then try_arrays (l+1)
else
make_pat
(Tpat_array (omegas l))
p.pat_type p.pat_env in
try_arrays 0
| [] -> omega
| _ -> omega
let rec has_instance p = match p.pat_desc with
| Tpat_variant (l,_,r) when is_absent l r -> false
| Tpat_any | Tpat_var _ | Tpat_constant _ | Tpat_variant (_,None,_) -> true
| Tpat_alias (p,_,_) | Tpat_variant (_,Some p,_) -> has_instance p
| Tpat_or (p1,p2,_) -> has_instance p1 || has_instance p2
| Tpat_construct (_,_,ps) | Tpat_tuple ps | Tpat_array ps ->
has_instances ps
| Tpat_record (lps,_) -> has_instances (List.map (fun (_,_,x) -> x) lps)
| Tpat_lazy p
-> has_instance p
and has_instances = function
| [] -> true
| q::rem -> has_instance q && has_instances rem
(*
Core function :
Is the last row of pattern matrix pss + qs satisfiable ?
That is :
Does there exists at least one value vector, es such that :
1- for all ps in pss ps # es (ps and es are not compatible)
2- qs <= es (es matches qs)
*)
let rec satisfiable pss qs = match pss with