Browse Source

allow matching on getters (closes #3088)

Simon Krajewski 11 years ago
parent
commit
0e038d1f48
2 changed files with 131 additions and 35 deletions
  1. 33 35
      matcher.ml
  2. 98 0
      tests/unit/issues/Issue3088.hx

+ 33 - 35
matcher.ml

@@ -44,7 +44,7 @@ and con = {
 
 and st_def =
 	| SVar of tvar
-	| SField of st * string
+	| SField of st * tclass_field
 	| SEnum of st * tenum_field * int
 	| SArray of st * int
 	| STuple of st * int * int
@@ -198,7 +198,7 @@ let mk_subs st con =
 		| _ -> fun t -> t
 	in
 	match con.c_def with
-	| CFields (_,fl) -> List.map (fun (s,cf) -> mk_st (SField(st,s)) (map cf.cf_type) st.st_pos) fl
+	| CFields (_,fl) -> List.map (fun (s,cf) -> mk_st (SField(st,cf)) (map cf.cf_type) st.st_pos) fl
 	| CEnum (en,({ef_type = TFun _} as ef)) ->
 		let rec loop t = match follow t with
 			| TEnum(_,pl) -> pl
@@ -265,7 +265,7 @@ let rec s_st st =
 	| SEnum (st,ef,i) -> s_st st ^ "." ^ ef.ef_name ^ "." ^ (string_of_int i)
 	| SArray (st,i) -> s_st st ^ "[" ^ (string_of_int i) ^ "]"
 	| STuple (st,i,a) -> "(" ^ (st_args i (a - i - 1) (s_st st)) ^ ")"
-	| SField (st,n) -> s_st st ^ "." ^ n)
+	| SField (st,cf) -> s_st st ^ "." ^ cf.cf_name)
 
 (* Pattern parsing *)
 
@@ -475,11 +475,10 @@ let to_pattern ctx e t =
 				end
 			end
 		| (EObjectDecl fl) ->
-			let is_matchable cf = match cf.cf_kind with Method _ | Var {v_read = AccCall} -> false | _ -> true in
+			let is_matchable cf = match cf.cf_kind with Method _ -> false | _ -> true in
 			let is_valid_field_name fields co n p =
 				try
 					let cf = PMap.find n fields in
-					if not (is_matchable cf) then error ("Cannot match against method or property with getter " ^ n) p;
 					begin match co with
 					| Some c when not (Typer.can_access ctx c cf false) -> error ("Cannot match against private field " ^ n) p
 					| _ -> ()
@@ -488,37 +487,37 @@ let to_pattern ctx e t =
 					error ((s_type t) ^ " has no field " ^ n ^ " that can be matched against") p;
 			in
 			pctx.pc_is_complex <- true;
-			begin match follow t with
-			| TAnon {a_fields = fields} ->
-				List.iter (fun (n,(_,p)) -> is_valid_field_name fields None n p) fl;
+			let loop_fields fields f =
 				let sl,pl,i = PMap.foldi (fun n cf (sl,pl,i) ->
 					if not (is_matchable cf) then
 						sl,pl,i
 					else
-						let pat =
-							try
-								if pctx.pc_reify && cf.cf_name = "pos" then raise Not_found;
-								loop pctx (List.assoc n fl) cf.cf_type
-							with Not_found ->
-								(mk_any cf.cf_type p)
+						let pat = try
+							loop pctx (List.assoc cf.cf_name fl) (f cf)
+						with Not_found ->
+							(mk_any cf.cf_type p)
 						in
 						(n,cf) :: sl,pat :: pl,i + 1
 				) fields ([],[],0) in
 				mk_con_pat (CFields(i,sl)) pl t p
-			| TInst(c,tl) ->
-				List.iter (fun (n,(_,p)) -> is_valid_field_name c.cl_fields (Some c) n p) fl;
-				let sl,pl,i = PMap.foldi (fun n cf (sl,pl,i) ->
-					if not (is_matchable cf) then
-						sl,pl,i
-					else
-						let t = apply_params c.cl_types tl (monomorphs cf.cf_params cf.cf_type) in
-						let pat = try loop pctx (List.assoc n fl) t with Not_found -> (mk_any t p) in
-						(n,cf) :: sl,pat :: pl,i + 1
-				) c.cl_fields ([],[],0) in
-				mk_con_pat (CFields(i,sl)) pl t p
-			| _ ->
-				error ((s_type t) ^ " should be { }") p
-			end
+			in
+			let fields,map = match follow t with
+				| TAnon {a_fields = fields} ->
+					fields,(fun cf -> cf.cf_type)
+				| TInst(c,tl) ->
+					c.cl_fields,(fun cf -> apply_params c.cl_types tl (monomorphs cf.cf_params cf.cf_type))
+				| TAbstract({a_impl = Some c} as a,tl) ->
+					let fields = List.fold_left (fun acc cf ->
+						if Meta.has Meta.Impl cf.cf_meta then
+							PMap.add cf.cf_name cf acc
+						else acc
+					) PMap.empty c.cl_ordered_statics in
+					fields,(fun cf -> apply_params a.a_types tl (monomorphs cf.cf_params cf.cf_type))
+				| _ ->
+					error ((s_type t) ^ " cannot be matched against a structure") p
+			in
+			List.iter (fun (n,(_,p)) -> is_valid_field_name fields None n p) fl;
+			loop_fields fields map
 		| EArrayDecl [] ->
 			mk_con_pat (CArray 0) [] t p
 		| EArrayDecl el ->
@@ -984,10 +983,9 @@ let mk_const ctx p = function
 
 let rec convert_st ctx st = match st.st_def with
 	| SVar v -> mk (TLocal v) v.v_type st.st_pos
-	| SField (sts,f) ->
+	| SField (sts,cf) ->
 		let e = convert_st ctx sts in
-		let fa = try quick_field e.etype f with Not_found -> FDynamic f in
-		mk (TField(e,fa)) st.st_type st.st_pos
+		Typer.acc_get ctx (Typer.type_field ctx e cf.cf_name st.st_pos Typer.MGet) st.st_pos
 	| SArray (sts,i) -> mk (TArray(convert_st ctx sts,mk_const ctx st.st_pos (TInt (Int32.of_int i)))) st.st_type st.st_pos
 	| STuple (st,_,_) -> convert_st ctx st
 	| SEnum(sts,ef,i) -> mk (TEnumParameter(convert_st ctx sts, ef, i)) st.st_type st.st_pos
@@ -1332,10 +1330,10 @@ let match_expr ctx e cases def with_type p =
 				Printf.sprintf "[%s]" (st_args i r (s_st_r top false st v))
 			| SArray(st,i) ->
 				s_st_r false true st (Printf.sprintf "[%i]%s" i (if top then " = " ^ v else v))
-			| SField({st_def = SVar v1},f) when v1.v_name.[0] = '`' ->
-				f ^ (if top then " = " ^ v else v)
-			| SField(st,f) ->
-				s_st_r false true st (Printf.sprintf ".%s%s" f (if top then " = " ^ v else v))
+			| SField({st_def = SVar v1},cf) when v1.v_name.[0] = '`' ->
+				cf.cf_name ^ (if top then " = " ^ v else v)
+			| SField(st,cf) ->
+				s_st_r false true st (Printf.sprintf ".%s%s" cf.cf_name (if top then " = " ^ v else v))
 			| SEnum(st,ef,i) ->
 				let len = match follow ef.ef_type with TFun(args,_) -> List.length args | _ -> 0 in
 				s_st_r false false st (Printf.sprintf "%s(%s)" ef.ef_name (st_args i (len - 1 - i) v))

+ 98 - 0
tests/unit/issues/Issue3088.hx

@@ -0,0 +1,98 @@
+package unit.issues;
+
+private class C {
+	var x(get, null):String;
+	function get_x() return "foo";
+	function new() { }
+
+	static public function getValue() {
+		var m = new C();
+		switch (m) {
+			case {
+				x: x = "foo"
+			}:
+				return x;
+			case _:
+				return null;
+		}
+	}
+}
+
+private typedef PublicationData = {
+    year:Int,
+    artist:String,
+    name:String,
+    type:String,
+    ?tracks:Array<String>,
+    ?bSide:String
+}
+
+private abstract Publication(PublicationData) from PublicationData {
+    public var year(get, never):Int;
+    public var artist(get, never):String;
+    public var name(get, never):String;
+    public var kind(get, never):PublicationKind;
+    inline function get_year() return this.year;
+    inline function get_artist() return this.artist;
+    inline function get_name() return this.name;
+    inline function get_kind()
+    	return
+            if (this.type == 'album')
+                Album(this.tracks);
+    		else
+                Single(this.bSide);
+}
+
+private enum PublicationKind {
+    Album(tracks:Array<String>);
+    Single(bSide:String);
+}
+
+class Issue3088 extends Test {
+	function test() {
+		eq("foo", C.getValue());
+	}
+
+	function testCrazyJuraj() {
+        var publications:Array<Publication> = [
+            {
+                year : 1967,
+                artist : "The Jimi Hendrix Experience",
+                name : "Are You Experienced?",
+                type : "album",
+                tracks : [/* ... */],
+            },
+            {
+                year : 1978,
+                artist : "Bob Marley and the Wailers",
+                name : "Babylon by Bus",
+                type : "album",
+                tracks : [/* ... */],
+            },
+            {
+                year : 1962,
+                artist : "John Lee Hooker",
+                name : "Boom Boom",
+                type : "single",
+                bSide : "Drug Store Woman",
+            },
+        ];
+        var albumsBefore1970 = [];
+     	for (p in publications)
+            switch p {
+                case {
+             		year : _ < 1970 => true,
+             		name : name,
+             		kind : kind = Album(tracks)
+            	}:
+                    albumsBefore1970.push({
+                        name : name,
+                        kind : kind
+                    });
+                default:
+            }
+
+    	eq(1, albumsBefore1970.length);
+    	eq("Are You Experienced?", albumsBefore1970[0].name);
+	}
+}