Parcourir la source

[matcher] fix handling of forward shadowing (#9628)

* [matcher] fix handling of forward shadowing

* Update matcher.ml

* move check up

* more verbose variant

* map version

* ref list version

Co-authored-by: Simon Krajewski <[email protected]>
Dmitrii Maganov il y a 5 ans
Parent
commit
b1cf6da739
2 fichiers modifiés avec 51 ajouts et 26 suppressions
  1. 28 26
      src/typing/matcher.ml
  2. 23 0
      tests/unit/src/unit/issues/Issue9627.hx

+ 28 - 26
src/typing/matcher.ml

@@ -409,43 +409,45 @@ module Pattern = struct
 				in
 				pattern t
 			| EObjectDecl fl ->
-				let rec known_fields t = match follow t with
+				let known_fields = ref [] in
+				let collect_field cf t filter =	match filter with
+					| Some sl when not (List.mem cf.cf_name sl) -> ()
+					| _ -> known_fields := (cf,t) :: (List.filter (fun (cf',_) -> cf'.cf_name <> cf.cf_name) !known_fields)
+				in
+				let rec collect_fields t filter = match follow t with
 					| TAnon an ->
-						PMap.fold (fun cf acc -> (cf,cf.cf_type) :: acc) an.a_fields []
+						PMap.iter (fun _ cf -> collect_field cf cf.cf_type filter) an.a_fields
 					| TInst(c,tl) ->
-						let rec loop fields c tl =
-							let fields = List.fold_left (fun acc cf ->
-								if Typecore.can_access ctx c cf false then (cf,apply_params c.cl_params tl cf.cf_type) :: acc
-								else acc
-							) fields c.cl_ordered_fields in
-							match c.cl_super with
-								| None -> fields
-								| Some (csup,tlsup) -> loop fields csup (List.map (apply_params c.cl_params tl) tlsup)
+						let rec loop c tl =
+							(match c.cl_super with
+								| Some (csup,tlsup) -> loop csup (List.map (apply_params c.cl_params tl) tlsup)
+								| _ -> ());
+							List.iter (fun cf ->
+								if Typecore.can_access ctx c cf false then
+									collect_field cf (apply_params c.cl_params tl cf.cf_type) filter
+							) c.cl_ordered_fields
 						in
-						loop [] c tl
+						loop c tl
 					| TAbstract({a_impl = Some c} as a,tl) ->
-						let fields = try
+						(if Meta.has Meta.Forward a.a_meta then
 							let _,el,_ = Meta.get Meta.Forward a.a_meta in
 							let sl = ExtList.List.filter_map (fun e -> match fst e with
 								| EConst(Ident s) -> Some s
 								| _ -> None
 							) el in
-							let fields = known_fields (Abstract.get_underlying_type a tl) in
-							if sl = [] then fields else List.filter (fun (cf,t) -> List.mem cf.cf_name sl) fields
-						with Not_found ->
-							[]
-						in
-						let fields = List.fold_left (fun acc cf ->
+							let filter = if sl = [] then filter else Some (match filter with
+								| Some fsl -> List.filter (fun s -> List.mem s fsl) sl
+								| None -> sl
+							) in
+							collect_fields (Abstract.get_underlying_type a tl) filter);
+						List.iter (fun cf ->
 							if Meta.has Meta.Impl cf.cf_meta then
-								(cf,apply_params a.a_params tl cf.cf_type) :: acc
-							else
-								acc
-						) fields c.cl_ordered_statics in
-						fields
+								collect_field cf (apply_params a.a_params tl cf.cf_type) filter
+						) c.cl_ordered_statics;
 					| _ ->
 						error (Printf.sprintf "Cannot field-match against %s" (s_type t)) (pos e)
 				in
-				let known_fields = known_fields t in
+				collect_fields t None;
 				let is_matchable cf =
 					match cf.cf_kind with Method _ -> false | _ -> true
 				in
@@ -459,7 +461,7 @@ module Pattern = struct
 							(PatAny,cf.cf_pos) :: patterns,cf.cf_name :: fields
 						else
 							patterns,fields
-				) ([],[]) known_fields in
+				) ([],[]) !known_fields in
 				List.iter (fun ((s,_,_),e) -> if not (List.mem s fields) then error (Printf.sprintf "%s has no field %s" (s_type t) s) (pos e)) fl;
 				PatConstructor(con_fields fields (pos e),patterns)
 			| EBinop(OpOr,e1,e2) ->
@@ -1698,4 +1700,4 @@ module Match = struct
 			type_expr ctx e WithType.value
 		| _ ->
 			match_expr ctx e cases def with_type postfix_match p
-end
+end

+ 23 - 0
tests/unit/src/unit/issues/Issue9627.hx

@@ -0,0 +1,23 @@
+package unit.issues;
+
+class Issue9627 extends Test {
+  function test() {
+    var bar: Bar = {foo: [0]};
+    var matches = switch (bar) {
+      case {foo: {it: [0]}}: true;
+      case _: false;
+    }
+    t(matches);
+  }
+}
+
+private abstract Foo<T>(T) from T to T {
+  public var it(get, never): T;
+  private inline function get_it() return this;
+}
+
+@:forward
+private abstract Bar({foo: Array<Int>}) from {foo: Array<Int>} to {foo: Array<Int>} {
+  public var foo(get, never): Foo<Array<Int>>;
+  private inline function get_foo() return this.foo;
+}