summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuc Maranget <luc.maranget@inria.fr>2013-07-02 16:05:48 +0000
committerLuc Maranget <luc.maranget@inria.fr>2013-07-02 16:05:48 +0000
commit32bcc186ea16dd932f5369f5f5e7933b44ec4a9c (patch)
tree58f8b25bae63acf42a59a39df30f31dbe6863b7d
parentc77d5ac9c302e8e007c2fdd1f3c83e629a1ecc31 (diff)
Patch for PR#5788
(Exception binding fools pattern matching optimisations) Hopefully it's complete... git-svn-id: http://caml.inria.fr/svn/ocaml/trunk@13871 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02
-rw-r--r--bytecomp/matching.ml183
-rw-r--r--testsuite/tests/basic/patmatch.ml25
-rw-r--r--testsuite/tests/basic/patmatch.reference2
3 files changed, 181 insertions, 29 deletions
diff --git a/bytecomp/matching.ml b/bytecomp/matching.ml
index b5ba51001..42e761a91 100644
--- a/bytecomp/matching.ml
+++ b/bytecomp/matching.ml
@@ -21,6 +21,8 @@ open Lambda
open Parmatch
open Printf
+let dbg = false
+
(* See Peyton-Jones, ``The Implementation of functional programming
languages'', chapter 5. *)
(*
@@ -161,11 +163,23 @@ let ctx_matcher p =
let p = normalize_pat p in
match p.pat_desc with
| Tpat_construct (_, cstr,omegas,_) ->
- (fun q rem -> match q.pat_desc with
- | Tpat_construct (_, cstr',args,_) when cstr.cstr_tag=cstr'.cstr_tag ->
- p,args @ rem
- | Tpat_any -> p,omegas @ rem
- | _ -> raise NoMatch)
+ begin match cstr.cstr_tag with
+ | Cstr_exception _ -> (* exception matching *)
+ let nargs = List.length omegas in
+ (fun q rem -> match q.pat_desc with
+ | Tpat_construct (_, cstr',args,_)
+ when List.length args = nargs ->
+ p,args @ rem
+ | Tpat_any -> p,omegas @ rem
+ | _ -> raise NoMatch)
+ | _ ->
+ (fun q rem -> match q.pat_desc with
+ | Tpat_construct (_, cstr',args,_)
+ when cstr.cstr_tag=cstr'.cstr_tag ->
+ p,args @ rem
+ | Tpat_any -> p,omegas @ rem
+ | _ -> raise NoMatch)
+ end
| Tpat_constant cst ->
(fun q rem -> match q.pat_desc with
| Tpat_constant cst' when const_compare cst cst' = 0 ->
@@ -412,6 +426,7 @@ let rec pretty_precompiled = function
| PmOr x ->
prerr_endline "++++ OR ++++" ;
pretty_pm x.body ;
+ pretty_matrix x.or_matrix ;
List.iter
(fun (_,i,_,pm) ->
eprintf "++ Handler %d ++\n" i ;
@@ -483,12 +498,35 @@ let up_ok_action act1 act2 =
with
| Not_simple -> false
+(* Nothing is kown about exeception patterns, because of potential rebind *)
+let rec exc_inside p = match p.pat_desc with
+ | Tpat_construct (_,{cstr_tag=Cstr_exception _},_,_) -> true
+ | Tpat_any|Tpat_constant _|Tpat_var _
+ | Tpat_construct (_,_,[],_)
+ | Tpat_variant (_,None,_)
+ -> false
+ | Tpat_construct (_,_,ps,_)
+ | Tpat_tuple ps
+ | Tpat_array ps
+ -> exc_insides ps
+ | Tpat_variant (_, Some q,_)
+ | Tpat_alias (q,_,_)
+ | Tpat_lazy q
+ -> exc_inside q
+ | Tpat_record (lps,_) ->
+ List.exists (fun (_,_,p) -> exc_inside p) lps
+ | Tpat_or (p1,p2,_) -> exc_inside p1 || exc_inside p2
+
+and exc_insides ps = List.exists exc_inside ps
+
let up_ok (ps,act_p) l =
- List.for_all
- (fun (qs,act_q) ->
- up_ok_action act_p act_q ||
- not (Parmatch.compats ps qs))
- l
+ if exc_insides ps then match l with [] -> true | _::_ -> false
+ else
+ List.for_all
+ (fun (qs,act_q) ->
+ up_ok_action act_p act_q ||
+ not (Parmatch.compats ps qs))
+ l
(*
@@ -584,6 +622,16 @@ let rec what_is_cases cases = match cases with
(* A few operation on default environments *)
let as_matrix cases = get_mins le_pats (List.map (fun (ps,_) -> ps) cases)
+(* For exception matching, record no imformation in matrix *)
+let as_matrix_omega cases =
+ get_mins le_pats
+ (List.map
+ (fun (ps,_) ->
+ match ps with
+ | [] -> assert false
+ | _::ps -> omega::ps)
+ cases)
+
let cons_default matrix raise_num default =
match matrix with
| [] -> default
@@ -658,13 +706,16 @@ let pm_free_variables {cases=cases} =
(* Basic grouping predicates *)
+let pat_as_constr = function
+ | {pat_desc=Tpat_construct (_, cstr,_,_)} -> cstr
+ | _ -> fatal_error "Matching.pat_as_constr"
let group_constant = function
| {pat_desc= Tpat_constant _} -> true
| _ -> false
and group_constructor = function
- | {pat_desc = Tpat_construct _} -> true
+ | {pat_desc = Tpat_construct (_,_,_,_)} -> true
| _ -> false
and group_variant = function
@@ -847,10 +898,75 @@ let rec split_or argo cls args def =
do_split [] [] [] cls
+(* Ultra-naive spliting, close to semantics,
+ used for exception, as potential rebind prevents any kind of
+ optimisation *)
+
+and split_naive cls args def k =
+
+ let rec split_exc cstr0 yes = function
+ | [] ->
+ let yes = List.rev yes in
+ { me = Pm {cases=yes; args=args; default=def;} ;
+ matrix = as_matrix_omega yes ;
+ top_default=def},
+ k
+ | (p::_,_ as cl)::rem ->
+ if group_constructor p then
+ let cstr = pat_as_constr p in
+ if cstr = cstr0 then split_exc cstr0 (cl::yes) rem
+ else
+ let yes = List.rev yes in
+ let {me=next ; matrix=matrix ; top_default=def}, nexts =
+ split_exc cstr [cl] rem in
+ let idef = next_raise_count () in
+ let def = cons_default matrix idef def in
+ { me = Pm {cases=yes; args=args; default=def} ;
+ matrix = as_matrix_omega yes ;
+ top_default = def; },
+ (idef,next)::nexts
+ else
+ let yes = List.rev yes in
+ let {me=next ; matrix=matrix ; top_default=def}, nexts =
+ split_noexc [cl] rem in
+ let idef = next_raise_count () in
+ let def = cons_default matrix idef def in
+ { me = Pm {cases=yes; args=args; default=def} ;
+ matrix = as_matrix_omega yes ;
+ top_default = def; },
+ (idef,next)::nexts
+ | _ -> assert false
+
+ and split_noexc yes = function
+ | [] -> precompile_var args (List.rev yes) def k
+ | (p::_,_ as cl)::rem ->
+ if group_constructor p then
+ let yes= List.rev yes in
+ let {me=next; matrix=matrix; top_default=def;},nexts =
+ split_exc (pat_as_constr p) [cl] rem in
+ let idef = next_raise_count () in
+ precompile_var
+ args yes
+ (cons_default matrix idef def)
+ ((idef,next)::nexts)
+ else split_noexc (cl::yes) rem
+ | _ -> assert false in
+
+ match cls with
+ | [] -> assert false
+ | (p::_,_ as cl)::rem ->
+ if group_constructor p then
+ split_exc (pat_as_constr p) [cl] rem
+ else
+ split_noexc [cl] rem
+ | _ -> assert false
+
and split_constr cls args def k =
let ex_pat = what_is_cases cls in
match ex_pat.pat_desc with
| Tpat_any -> precompile_var args cls def k
+ | Tpat_construct (_,{cstr_tag=Cstr_exception _},_,_) ->
+ split_naive cls args def k
| _ ->
let group = get_group ex_pat in
@@ -956,12 +1072,21 @@ and dont_precompile_var args cls def k =
matrix=as_matrix cls ;
top_default=def},k
+and is_exc p = match p.pat_desc with
+| Tpat_or (p1,p2,_) -> is_exc p1 || is_exc p2
+| Tpat_alias (p,v,_) -> is_exc p
+| Tpat_construct (_,{cstr_tag = Cstr_exception _},_,_) -> true
+| _ -> false
+
and precompile_or argo cls ors args def k = match ors with
| [] -> split_constr cls args def k
| _ ->
let rec do_cases = function
| ({pat_desc=Tpat_or _} as orp::patl, action)::rem ->
- let others,rem = get_equiv orp rem in
+ let do_opt = not (is_exc orp) in
+ let others,rem =
+ if do_opt then get_equiv orp rem
+ else [],rem in
let orpm =
{cases =
(patl, action)::
@@ -971,7 +1096,7 @@ and precompile_or argo cls ors args def k = match ors with
| _ -> assert false)
others ;
args = (match args with _::r -> r | _ -> assert false) ;
- default = default_compat orp def} in
+ default = default_compat (if do_opt then orp else omega) def} in
let vars =
IdentSet.elements
(IdentSet.inter
@@ -984,17 +1109,19 @@ and precompile_or argo cls ors args def k = match ors with
Lstaticraise
(or_num, List.map (fun v -> Lvar v) vs) in
- let body,handlers = do_cases rem in
+ let do_optrec,body,handlers = do_cases rem in
+ do_opt && do_optrec,
explode_or_pat
argo new_patl mk_new_action body vars [] orp,
- (([[orp]], or_num, vars , orpm):: handlers)
+ let mat = if do_opt then [[orp]] else [[omega]] in
+ ((mat, or_num, vars , orpm):: handlers)
| cl::rem ->
- let new_ord,new_to_catch = do_cases rem in
- cl::new_ord,new_to_catch
- | [] -> [],[] in
+ let b,new_ord,new_to_catch = do_cases rem in
+ b,cl::new_ord,new_to_catch
+ | [] -> true,[],[] in
- let end_body, handlers = do_cases ors in
- let matrix = as_matrix (cls@ors)
+ let do_opt,end_body, handlers = do_cases ors in
+ let matrix = (if do_opt then as_matrix else as_matrix_omega) (cls@ors)
and body = {cases=cls@end_body ; args=args ; default=def} in
{me = PmOr {body=body ; handlers=handlers ; or_matrix=matrix} ;
matrix=matrix ;
@@ -1003,13 +1130,12 @@ and precompile_or argo cls ors args def k = match ors with
let split_precompile argo pm =
let {me=next}, nexts = split_or argo pm.cases pm.args pm.default in
-(*
- if nexts <> [] || (match next with PmOr _ -> true | _ -> false) then begin
+ if dbg && (nexts <> [] || (match next with PmOr _ -> true | _ -> false))
+ then begin
prerr_endline "** SPLIT **" ;
pretty_pm pm ;
pretty_precompiled_res next nexts
end ;
-*)
next, nexts
@@ -1136,10 +1262,6 @@ let get_args_constr p rem = match p with
| {pat_desc=Tpat_construct (_, _, args, _)} -> args @ rem
| _ -> assert false
-let pat_as_constr = function
- | {pat_desc=Tpat_construct (_, cstr,_,_)} -> cstr
- | _ -> fatal_error "Matching.pat_as_constr"
-
let matcher_constr cstr = match cstr.cstr_arity with
| 0 ->
@@ -1920,6 +2042,11 @@ let mk_failaction_neg partial ctx def = match partial with
(* Conforme a l'article et plus simple qu'avant *)
and mk_failaction_pos partial seen ctx defs =
+ if dbg then begin
+ prerr_endline "**POS**" ;
+ pretty_def defs ;
+ ()
+ end ;
let rec scan_def env to_test defs = match to_test,defs with
| ([],_)|(_,[]) ->
List.fold_left
@@ -2385,8 +2512,6 @@ let arg_to_var arg cls = match arg with
Output: a lambda term, a jump summary {..., exit number -> context, .. }
*)
-let dbg = false
-
let rec compile_match repr partial ctx m = match m with
| { cases = [] } -> comp_exit ctx m
| { cases = ([], action) :: rem } ->
diff --git a/testsuite/tests/basic/patmatch.ml b/testsuite/tests/basic/patmatch.ml
index fbb0870a1..b5977b24e 100644
--- a/testsuite/tests/basic/patmatch.ml
+++ b/testsuite/tests/basic/patmatch.ml
@@ -131,3 +131,28 @@ let () =
printf "PR#5992=Ok\n"
+(* PR #5788, was giving wrong result 3 *)
+exception Foo
+exception Bar = Foo
+
+let test e b =
+ match e, b with
+ | Foo, true -> 1
+ | Bar, false -> 2
+ | _, _ -> 3
+
+let () =
+ let r = test Bar false in
+ if r = 2 then printf "PR#5788=Ok\n"
+
+let test e b =
+ match e, b with
+ | Bar, false -> 0
+ | (Foo|Bar), true -> 1
+ | Foo, false -> 2
+ | _, _ -> 3
+
+
+let () =
+ let r = test Foo false in
+ if r = 0 then printf "PR#5788=Ok\n"
diff --git a/testsuite/tests/basic/patmatch.reference b/testsuite/tests/basic/patmatch.reference
index 3cae3a361..069400b10 100644
--- a/testsuite/tests/basic/patmatch.reference
+++ b/testsuite/tests/basic/patmatch.reference
@@ -67,3 +67,5 @@ l([|1|]) = 2
l([|2;3|]) = 5
l([|4;5;6|]) = 15
PR#5992=Ok
+PR#5788=Ok
+PR#5788=Ok