Skip to content

Commit cde7931

Browse files
Multiway match typechecking and translation (#1683)
Multiway match typechecking and translation for pattern guards (#2) * multiway typechecking and translation * update jane test output * self-review: format and style in translcore * more translcore/typedtree cleanup * expose value `is_guarded_rhs` * fix typedtree printer * make discussed changes to ocamldoc * format: remove unnecessary parens in pattern * improve parmatch variable naming * explain [exp_attributes] and [exp_extra] weirdness * improve translcore [event_function*] naming * inlined transl_body in transl_rhs * rename pats_exp... to use "rhs" naming * added test for guarded value/exception or-patterns * address ocamldoc CRs --------- Co-authored-by: Nick Roberts <[email protected]>
1 parent 6d702c8 commit cde7931

File tree

17 files changed

+450
-404
lines changed

17 files changed

+450
-404
lines changed

ocaml/lambda/translcore.ml

Lines changed: 138 additions & 97 deletions
Large diffs are not rendered by default.

ocaml/ocamldoc/odoc_ast.ml

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -301,31 +301,35 @@ module Analyser =
301301
(* we look if the name of the parameter we just add is "*opt*", which means
302302
that there is a let param_name = ... in ... just right now *)
303303
let (p, next_exp) =
304-
match parameter with
305-
Simple_name { sn_name = "*opt*" } ->
306-
(
307-
(
308-
match func_body.exp_desc with
309-
Typedtree.Texp_let (_, {vb_pat={pat_desc = Typedtree.Tpat_var (id, _, _) };
310-
vb_expr=exp} :: _, func_body2) ->
311-
let name = Name.from_ident id in
312-
let new_param = Simple_name
313-
{ sn_name = name ;
314-
sn_text = Odoc_parameter.desc_from_info_opt current_comment_opt name ;
315-
sn_type = Odoc_env.subst_type env exp.exp_type
316-
}
317-
in
318-
(new_param, func_body2)
319-
| _ ->
320-
(parameter, func_body)
321-
)
322-
)
323-
| _ ->
324-
(parameter, func_body)
304+
match func_body with
305+
| Pattern_guarded_rhs _ -> parameter, None
306+
| Simple_rhs func_body
307+
| Boolean_guarded_rhs { bg_rhs = func_body; _ } ->
308+
match parameter with
309+
Simple_name { sn_name = "*opt*" } ->
310+
(
311+
(
312+
match func_body.exp_desc with
313+
Typedtree.Texp_let (_, {vb_pat={pat_desc = Typedtree.Tpat_var (id, _, _) };
314+
vb_expr=exp} :: _, func_body2) ->
315+
let name = Name.from_ident id in
316+
let new_param = Simple_name
317+
{ sn_name = name ;
318+
sn_text = Odoc_parameter.desc_from_info_opt current_comment_opt name ;
319+
sn_type = Odoc_env.subst_type env exp.exp_type
320+
}
321+
in
322+
(new_param, Some func_body2)
323+
| _ ->
324+
(parameter, Some func_body)
325+
)
326+
)
327+
| _ ->
328+
parameter, Some func_body
325329
in
326330
(* continue if the body is still a function *)
327-
match next_exp.exp_desc with
328-
Texp_function { cases = pat_exp_list ; _ } ->
331+
match next_exp with
332+
| Some { exp_desc = Texp_function { cases = pat_exp_list ; _ } } ->
329333
p :: (tt_analyse_function_parameters env current_comment_opt pat_exp_list)
330334
| _ ->
331335
(* something else ; no more parameter *)
@@ -445,8 +449,8 @@ module Analyser =
445449
sn_type = Odoc_env.subst_type env pattern_param.Typedtree.pat_type }
446450
in
447451
[ new_param ]
448-
449-
| {c_lhs=pattern_param; c_rhs=body} :: [] ->
452+
| {c_rhs=Pattern_guarded_rhs _} :: [] -> []
453+
| {c_lhs=pattern_param; c_rhs=Simple_rhs body | Boolean_guarded_rhs {bg_rhs = body}} :: [] ->
450454
(* if this is the first call to the function, this is the first parameter and we skip it *)
451455
if not first then
452456
(

ocaml/testsuite/tests/pattern-guards/jane_test.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,5 @@ let pattern_guard_doesnt_return_local f x =
3333
| _ -> 3
3434
;;
3535
[%%expect{|
36-
>> Fatal error: typechecking for multicase pattern guards unimplemented
37-
Uncaught exception: Misc.Fatal_error
38-
36+
val pattern_guard_doesnt_return_local : 'a -> 'b option -> int = <fun>
3937
|}]

ocaml/testsuite/tests/pattern-guards/test.ml

Lines changed: 40 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,19 @@ Line 3, characters 4-32:
298298
Error: Mixing value and exception patterns under when-guards is not supported.
299299
|}];;
300300

301+
(* Test rejection of pattern guards on mixed exception/value or-patterns *)
302+
let reject_guarded_val_exn_orp k =
303+
match k () with
304+
| Some s | exception Failure s when s match "foo" -> s
305+
| _ -> "Not foo"
306+
;;
307+
[%%expect{|
308+
Line 3, characters 4-32:
309+
3 | | Some s | exception Failure s when s match "foo" -> s
310+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
311+
Error: Mixing value and exception patterns under when-guards is not supported.
312+
|}];;
313+
301314
module M : sig
302315
type 'a t
303316

@@ -509,52 +522,36 @@ let collatz = function
509522
;;
510523

511524
[%%expect{|
512-
>> Fatal error: typechecking for multicase pattern guards unimplemented
513-
Uncaught exception: Misc.Fatal_error
514-
525+
val nested_singleway :
526+
('a -> 'b option) ->
527+
('b -> 'c option) -> ('c -> 'd option) -> default:'d -> 'a option -> 'd =
528+
<fun>
529+
val collatz : int -> int option = <fun>
515530
|}];;
516531

517532
nested_singleway collatz collatz collatz ~default:~-1 None;;
518533
[%%expect{|
519-
Line 1, characters 0-16:
520-
1 | nested_singleway collatz collatz collatz ~default:~-1 None;;
521-
^^^^^^^^^^^^^^^^
522-
Error: Unbound value nested_singleway
534+
- : int = -1
523535
|}];;
524536
nested_singleway collatz collatz collatz ~default:~-1 (Some 1);;
525537
[%%expect{|
526-
Line 1, characters 0-16:
527-
1 | nested_singleway collatz collatz collatz ~default:~-1 (Some 1);;
528-
^^^^^^^^^^^^^^^^
529-
Error: Unbound value nested_singleway
538+
- : int = -1
530539
|}];;
531540
nested_singleway collatz collatz collatz ~default:~-1 (Some 2);;
532541
[%%expect{|
533-
Line 1, characters 0-16:
534-
1 | nested_singleway collatz collatz collatz ~default:~-1 (Some 2);;
535-
^^^^^^^^^^^^^^^^
536-
Error: Unbound value nested_singleway
542+
- : int = -1
537543
|}];;
538544
nested_singleway collatz collatz collatz ~default:~-1 (Some 3);;
539545
[%%expect{|
540-
Line 1, characters 0-16:
541-
1 | nested_singleway collatz collatz collatz ~default:~-1 (Some 3);;
542-
^^^^^^^^^^^^^^^^
543-
Error: Unbound value nested_singleway
546+
- : int = 16
544547
|}];;
545548
nested_singleway collatz collatz collatz ~default:~-1 (Some 4);;
546549
[%%expect{|
547-
Line 1, characters 0-16:
548-
1 | nested_singleway collatz collatz collatz ~default:~-1 (Some 4);;
549-
^^^^^^^^^^^^^^^^
550-
Error: Unbound value nested_singleway
550+
- : int = -1
551551
|}];;
552552
nested_singleway collatz collatz collatz ~default:~-1 (Some 8);;
553553
[%%expect{|
554-
Line 1, characters 0-16:
555-
1 | nested_singleway collatz collatz collatz ~default:~-1 (Some 8);;
556-
^^^^^^^^^^^^^^^^
557-
Error: Unbound value nested_singleway
554+
- : int = 1
558555
|}];;
559556

560557
let find_multiway ~eq ~flag ~finish ~default = function
@@ -565,9 +562,9 @@ let find_multiway ~eq ~flag ~finish ~default = function
565562
| _ -> default
566563
;;
567564
[%%expect{|
568-
>> Fatal error: typechecking for multicase pattern guards unimplemented
569-
Uncaught exception: Misc.Fatal_error
570-
565+
val find_multiway :
566+
eq:('a -> 'a -> bool) ->
567+
flag:'a -> finish:('a -> 'b) -> default:'b -> 'a list -> 'b = <fun>
571568
|}];;
572569

573570
let eq n m = (n - m) mod 100 = 0;;
@@ -583,31 +580,19 @@ val default : string = "No match found"
583580

584581
find_multiway ~eq ~flag ~finish ~default [ 10; 20; 110; 100 ];;
585582
[%%expect{|
586-
Line 1, characters 0-13:
587-
1 | find_multiway ~eq ~flag ~finish ~default [ 10; 20; 110; 100 ];;
588-
^^^^^^^^^^^^^
589-
Error: Unbound value find_multiway
583+
- : string = "110"
590584
|}];;
591585
find_multiway ~eq ~flag ~finish ~default [ 10; 20; 100; 110 ];;
592586
[%%expect{|
593-
Line 1, characters 0-13:
594-
1 | find_multiway ~eq ~flag ~finish ~default [ 10; 20; 100; 110 ];;
595-
^^^^^^^^^^^^^
596-
Error: Unbound value find_multiway
587+
- : string = "0"
597588
|}];;
598589
find_multiway ~eq ~flag ~finish ~default [ 10; 20; 30; 40 ];;
599590
[%%expect{|
600-
Line 1, characters 0-13:
601-
1 | find_multiway ~eq ~flag ~finish ~default [ 10; 20; 30; 40 ];;
602-
^^^^^^^^^^^^^
603-
Error: Unbound value find_multiway
591+
- : string = "No match found"
604592
|}];;
605593
find_multiway ~eq ~flag ~finish ~default [ 0; 100 ];;
606594
[%%expect{|
607-
Line 1, characters 0-13:
608-
1 | find_multiway ~eq ~flag ~finish ~default [ 0; 100 ];;
609-
^^^^^^^^^^^^^
610-
Error: Unbound value find_multiway
595+
- : string = "0"
611596
|}];;
612597

613598
let nested_multiway f g h = function
@@ -621,9 +606,9 @@ let nested_multiway f g h = function
621606
| _ -> "not found"
622607
;;
623608
[%%expect{|
624-
>> Fatal error: typechecking for multicase pattern guards unimplemented
625-
Uncaught exception: Misc.Fatal_error
626-
609+
val nested_multiway :
610+
('a -> string) ->
611+
('a -> string list) -> ('a -> bool) -> 'a option -> string = <fun>
627612
|}];;
628613

629614
let f = function
@@ -647,45 +632,27 @@ val h : int -> bool = <fun>
647632

648633
nested_multiway f g h None;;
649634
[%%expect{|
650-
Line 1, characters 0-15:
651-
1 | nested_multiway f g h None;;
652-
^^^^^^^^^^^^^^^
653-
Error: Unbound value nested_multiway
635+
- : string = "not found"
654636
|}];;
655637
nested_multiway f g h (Some 0);;
656638
[%%expect{|
657-
Line 1, characters 0-15:
658-
1 | nested_multiway f g h (Some 0);;
659-
^^^^^^^^^^^^^^^
660-
Error: Unbound value nested_multiway
639+
- : string = "not found"
661640
|}];;
662641
nested_multiway f g h (Some 1);;
663642
[%%expect{|
664-
Line 1, characters 0-15:
665-
1 | nested_multiway f g h (Some 1);;
666-
^^^^^^^^^^^^^^^
667-
Error: Unbound value nested_multiway
643+
- : string = "foo1"
668644
|}];;
669645
nested_multiway f g h (Some 10);;
670646
[%%expect{|
671-
Line 1, characters 0-15:
672-
1 | nested_multiway f g h (Some 10);;
673-
^^^^^^^^^^^^^^^
674-
Error: Unbound value nested_multiway
647+
- : string = "bar empty"
675648
|}];;
676649
nested_multiway f g h (Some 100);;
677650
[%%expect{|
678-
Line 1, characters 0-15:
679-
1 | nested_multiway f g h (Some 100);;
680-
^^^^^^^^^^^^^^^
681-
Error: Unbound value nested_multiway
651+
- : string = "bar singleton one"
682652
|}];;
683653
nested_multiway f g h (Some 1000);;
684654
[%%expect{|
685-
Line 1, characters 0-15:
686-
1 | nested_multiway f g h (Some 1000);;
687-
^^^^^^^^^^^^^^^
688-
Error: Unbound value nested_multiway
655+
- : string = "not found"
689656
|}];;
690657

691658
(* Checks that optional arguments with defaults are correclty bound in the

ocaml/typing/cmt2annot.ml

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,13 @@ let bind_bindings scope bindings =
4343

4444
let bind_cases l =
4545
List.iter
46-
(fun {c_lhs; c_guard; c_rhs} ->
46+
(fun {c_lhs; c_rhs} ->
4747
let loc =
48-
let open Location in
49-
match c_guard with
50-
| None -> c_rhs.exp_loc
51-
| Some g ->
52-
let gexp =
53-
match g with
54-
| Predicate pred -> pred
55-
| Pattern { pg_scrutinee; pg_pattern; _ } ->
56-
bind_variables c_rhs.exp_loc pg_pattern;
57-
pg_scrutinee
58-
in
59-
{c_rhs.exp_loc with loc_start=gexp.exp_loc.loc_start}
48+
match c_rhs with
49+
| Simple_rhs rhs -> rhs.exp_loc
50+
| Boolean_guarded_rhs { bg_guard; bg_rhs } ->
51+
{ bg_rhs.exp_loc with loc_start = bg_guard.exp_loc.loc_start }
52+
| Pattern_guarded_rhs { pg_loc; _ } -> pg_loc
6053
in
6154
bind_variables loc c_lhs
6255
)

0 commit comments

Comments
 (0)