Browse Source

[matcher] fix non-exhaustive guard cases (closes #4473)

Simon Krajewski 9 years ago
parent
commit
e05f2b6438
2 changed files with 82 additions and 9 deletions
  1. 15 9
      matcher.ml
  2. 67 0
      tests/unit/src/unit/issues/Issue4473.hx

+ 15 - 9
matcher.ml

@@ -113,6 +113,7 @@ type type_finiteness =
 	| RunTimeFinite (* type is truly finite (Bool, enums) *)
 	| RunTimeFinite (* type is truly finite (Bool, enums) *)
 
 
 exception Not_exhaustive of pat * st
 exception Not_exhaustive of pat * st
+exception Not_exhaustive_default
 exception Unrecognized_pattern of Ast.expr
 exception Unrecognized_pattern of Ast.expr
 
 
 let arity con = match con.c_def with
 let arity con = match con.c_def with
@@ -872,6 +873,12 @@ let rec compile mctx stl pmat toplevel =
 	let expr id = get_cache mctx (Expr id) in
 	let expr id = get_cache mctx (Expr id) in
 	let bind bl dt = get_cache mctx (Bind(bl,dt)) in
 	let bind bl dt = get_cache mctx (Bind(bl,dt)) in
 	let switch st cl = get_cache mctx (Switch(st,cl)) in
 	let switch st cl = get_cache mctx (Switch(st,cl)) in
+	let compile mctx stl pmat toplevel =
+		try
+			compile mctx stl pmat toplevel
+		with Not_exhaustive_default when stl <> [] ->
+			raise (Not_exhaustive(any,List.hd stl))
+	in
 	get_cache mctx (match pmat with
 	get_cache mctx (match pmat with
 	| [] ->
 	| [] ->
 		(match stl with
 		(match stl with
@@ -888,7 +895,7 @@ let rec compile mctx stl pmat toplevel =
 		| _ ->
 		| _ ->
 			(* This can happen in cases a value is required and all default cases are guarded (issue #3150).
 			(* This can happen in cases a value is required and all default cases are guarded (issue #3150).
 			   Not a particularly elegant solution, may want to revisit this later. *)
 			   Not a particularly elegant solution, may want to revisit this later. *)
-			raise Exit)
+			raise Not_exhaustive_default)
 	| ([|{p_def = PTuple pt}|],out) :: pl ->
 	| ([|{p_def = PTuple pt}|],out) :: pl ->
 		compile mctx stl ((pt,out) :: pl) toplevel
 		compile mctx stl ((pt,out) :: pl) toplevel
 	| (pv,out) :: pl ->
 	| (pv,out) :: pl ->
@@ -900,9 +907,10 @@ let rec compile mctx stl pmat toplevel =
 				| None ->
 				| None ->
 					expr out.o_id
 					expr out.o_id
 				| Some _ ->
 				| Some _ ->
-					let dt = match pl,mctx.need_val with
-						| [],false ->
-							None
+					let dt = match pl with
+						| [] ->
+							if mctx.need_val then raise Not_exhaustive_default
+							else None
 						| _ ->
 						| _ ->
 							Some (compile mctx stl pl false)
 							Some (compile mctx stl pl false)
 					in
 					in
@@ -951,11 +959,7 @@ let rec compile mctx stl pmat toplevel =
 				compile mctx st_tail def false
 				compile mctx st_tail def false
 			| def,_ ->
 			| def,_ ->
 				let cdef = mk_con CAny t_dynamic st_head.st_pos in
 				let cdef = mk_con CAny t_dynamic st_head.st_pos in
-				let def = try
-					compile mctx st_tail def false
-				with Exit ->
-					raise (Not_exhaustive(any,st_head))
-				in
+				let def = compile mctx st_tail def false in
 				let cases = cases @ [cdef,def] in
 				let cases = cases @ [cdef,def] in
 				switch st_head cases
 				switch st_head cases
 			in
 			in
@@ -1378,6 +1382,8 @@ let match_expr ctx e cases def with_type p =
 			error "Note: Patterns with extractors may require a default pattern" st.st_pos;
 			error "Note: Patterns with extractors may require a default pattern" st.st_pos;
 		end else
 		end else
 			error msg st.st_pos
 			error msg st.st_pos
+	| Not_exhaustive_default ->
+		error "Unmatched patterns: _" p;
 	in
 	in
 	save();
 	save();
 	(* check for unused patterns *)
 	(* check for unused patterns *)

+ 67 - 0
tests/unit/src/unit/issues/Issue4473.hx

@@ -0,0 +1,67 @@
+package unit.issues;
+
+class Issue4473 extends Test {
+	function test1() {
+		var results = [
+			"home" => [
+				1 => 1,
+				2 => 3
+			],
+			"home2" => [
+				1 => 3,
+				2 => 2
+			],
+			"contact" => [
+				1 => 3,
+				2 => 3
+			]
+		];
+		var scenes = ["home", "home2", "contact"];
+		var indices = [1, 2];
+
+		for (scene in scenes) {
+			for (index in indices) {
+				eq(results[scene][index], f1(scene, index));
+			}
+		}
+	}
+
+	function test2() {
+		var results = [
+			"home" => [
+				1 => 1,
+				2 => 2
+			],
+			"home2" => [
+				1 => 3,
+				2 => 3
+			]
+		];
+		var scenes = ["home", "home2"];
+		var indices = [1, 2];
+
+		for (scene in scenes) {
+			for (index in indices) {
+				eq(results[scene][index], f2(scene, index));
+			}
+		}
+	}
+
+	static function f1(scene:String, index:Int) {
+		var x = switch(scene) {
+			case "home" if (index < 2): 1;
+			case "home2" if (index >= 2): 2;
+			case _: 3;
+		}
+		return x;
+	}
+
+	static function f2(scene:String, index:Int) {
+		var x = switch(scene) {
+			case "home" if (index < 2): 1;
+			case "home" if (index >= 2): 2;
+			case _: 3;
+		}
+		return x;
+	}
+}