Browse Source

Add TAnon comparison to fast_eq (#8592)

* add TAnon comparison to fast_eq

* extract into fast_eq_anon

* refactoring

* sort field lists

* handle monomorph arguments while typing `@:from` (#8588)

* fixed hangs on type_array_decl and type_ident

* no need for `fields_eq` in some cases

* less PMap lookups
Aleksandr Kuzmenko 6 years ago
parent
commit
3d2e48742b

+ 45 - 18
src/core/type.ml

@@ -634,7 +634,7 @@ let map loop t =
 	| TDynamic t2 ->
 	| TDynamic t2 ->
 		if t == t2 then	t else TDynamic (loop t2)
 		if t == t2 then	t else TDynamic (loop t2)
 
 
-let dup t =
+let duplicate t =
 	let monos = ref [] in
 	let monos = ref [] in
 	let rec loop t =
 	let rec loop t =
 		match t with
 		match t with
@@ -1742,42 +1742,69 @@ let link_dynamic a b = match follow a,follow b with
 	| TDynamic _,TMono r -> r := Some a
 	| TDynamic _,TMono r -> r := Some a
 	| _ -> ()
 	| _ -> ()
 
 
-let rec fast_eq a b =
+let fast_eq_check type_param_check a b =
 	if a == b then
 	if a == b then
 		true
 		true
 	else match a , b with
 	else match a , b with
 	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
 	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
-		List.for_all2 (fun (_,_,t1) (_,_,t2) -> fast_eq t1 t2) l1 l2 && fast_eq r1 r2
+		List.for_all2 (fun (_,_,t1) (_,_,t2) -> type_param_check t1 t2) l1 l2 && type_param_check r1 r2
 	| TType (t1,l1), TType (t2,l2) ->
 	| TType (t1,l1), TType (t2,l2) ->
-		t1 == t2 && List.for_all2 fast_eq l1 l2
+		t1 == t2 && List.for_all2 type_param_check l1 l2
 	| TEnum (e1,l1), TEnum (e2,l2) ->
 	| TEnum (e1,l1), TEnum (e2,l2) ->
-		e1 == e2 && List.for_all2 fast_eq l1 l2
+		e1 == e2 && List.for_all2 type_param_check l1 l2
 	| TInst (c1,l1), TInst (c2,l2) ->
 	| TInst (c1,l1), TInst (c2,l2) ->
-		c1 == c2 && List.for_all2 fast_eq l1 l2
+		c1 == c2 && List.for_all2 type_param_check l1 l2
 	| TAbstract (a1,l1), TAbstract (a2,l2) ->
 	| TAbstract (a1,l1), TAbstract (a2,l2) ->
-		a1 == a2 && List.for_all2 fast_eq l1 l2
+		a1 == a2 && List.for_all2 type_param_check l1 l2
 	| _ , _ ->
 	| _ , _ ->
 		false
 		false
 
 
+let rec fast_eq a b = fast_eq_check fast_eq a b
+
 let rec fast_eq_mono ml a b =
 let rec fast_eq_mono ml a b =
-	if a == b then
+	if fast_eq_check (fast_eq_mono ml) a b then
 		true
 		true
 	else match a , b with
 	else match a , b with
-	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
-		List.for_all2 (fun (_,_,t1) (_,_,t2) -> fast_eq_mono ml t1 t2) l1 l2 && fast_eq_mono ml r1 r2
-	| TType (t1,l1), TType (t2,l2) ->
-		t1 == t2 && List.for_all2 (fast_eq_mono ml) l1 l2
-	| TEnum (e1,l1), TEnum (e2,l2) ->
-		e1 == e2 && List.for_all2 (fast_eq_mono ml) l1 l2
-	| TInst (c1,l1), TInst (c2,l2) ->
-		c1 == c2 && List.for_all2 (fast_eq_mono ml) l1 l2
-	| TAbstract (a1,l1), TAbstract (a2,l2) ->
-		a1 == a2 && List.for_all2 (fast_eq_mono ml) l1 l2
 	| TMono _, _ ->
 	| TMono _, _ ->
 		List.memq a ml
 		List.memq a ml
 	| _ , _ ->
 	| _ , _ ->
 		false
 		false
 
 
+let rec fast_eq_anon a b =
+	if fast_eq_check fast_eq_anon a b then
+		true
+	else match a , b with
+	| TMono { contents = Some t1 }, TMono { contents = Some t2 } ->
+		fast_eq_anon t1 t2
+	| TAnon a1, TAnon a2 ->
+		let fields_eq() =
+			let rec loop fields1 fields2 =
+				match fields1, fields2 with
+				| [], [] -> true
+				| _, [] | [], _ -> false
+				| f1 :: rest1, f2 :: rest2 ->
+					f1.cf_name = f2.cf_name
+					&& (try fast_eq_anon f1.cf_type f2.cf_type with Not_found -> false)
+					&& loop rest1 rest2
+			in
+			let fields1 = PMap.fold (fun field fields -> field :: fields) a1.a_fields []
+			and fields2 = PMap.fold (fun field fields -> field :: fields) a2.a_fields []
+			and sort_compare f1 f2 = compare f1.cf_name f2.cf_name in
+			loop (List.sort sort_compare fields1) (List.sort sort_compare fields2)
+		in
+		(match !(a2.a_status), !(a1.a_status) with
+		| Statics c, Statics c2 -> c == c2
+		| EnumStatics e, EnumStatics e2 -> e == e2
+		| AbstractStatics a, AbstractStatics a2 -> a == a2
+		| Extend tl1, Extend tl2 -> fields_eq() && List.for_all2 fast_eq_anon tl1 tl2
+		| Closed, Closed -> fields_eq()
+		| Opened, Opened -> fields_eq()
+		| Const, Const -> fields_eq()
+		| _ -> false
+		)
+	| _ , _ ->
+		false
+
 (* perform unification with subtyping.
 (* perform unification with subtyping.
    the first type is always the most down in the class hierarchy
    the first type is always the most down in the class hierarchy
    it's also the one that is pointed by the position.
    it's also the one that is pointed by the position.

+ 4 - 4
src/typing/typer.ml

@@ -107,7 +107,7 @@ let maybe_type_against_enum ctx f with_type iscall p =
 				| TAbstract (a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
 				| TAbstract (a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
 					begin match get_abstract_froms a pl with
 					begin match get_abstract_froms a pl with
 						| [t2] ->
 						| [t2] ->
-							if (List.exists (fast_eq t) stack) then raise Exit;
+							if (List.exists (fast_eq_anon t) stack) then raise Exit;
 							loop (t :: stack) t2
 							loop (t :: stack) t2
 						| _ -> raise Exit
 						| _ -> raise Exit
 					end
 					end
@@ -1610,7 +1610,7 @@ and type_object_decl ctx fl with_type p =
 		let rec loop seen t =
 		let rec loop seen t =
 			match follow t with
 			match follow t with
 			| TAnon a -> ODKWithStructure a
 			| TAnon a -> ODKWithStructure a
-			| TAbstract (a,pl) as t when not (Meta.has Meta.CoreType a.a_meta) && not (List.exists (fun t' -> fast_eq t t') seen) ->
+			| TAbstract (a,pl) as t when not (Meta.has Meta.CoreType a.a_meta) && not (List.exists (fun t' -> fast_eq_anon t t') seen) ->
 				(match List.fold_left (fun acc t' -> match loop (t :: seen) t' with ODKPlain -> acc | t -> t :: acc) [] (get_abstract_froms a pl) with
 				(match List.fold_left (fun acc t' -> match loop (t :: seen) t' with ODKPlain -> acc | t -> t :: acc) [] (get_abstract_froms a pl) with
 				| [t] -> t
 				| [t] -> t
 				| _ -> ODKPlain)
 				| _ -> ODKPlain)
@@ -2096,10 +2096,10 @@ and type_array_decl ctx el with_type p =
 					Some (get_iterable_param t)
 					Some (get_iterable_param t)
 				with Not_found ->
 				with Not_found ->
 					None)
 					None)
-			| TAbstract (a,pl) as t when not (List.exists (fun t' -> fast_eq t (follow t')) seen) ->
+			| TAbstract (a,pl) as t when not (List.exists (fun t' -> fast_eq_anon t (follow t')) seen) ->
 				let types =
 				let types =
 					List.fold_left
 					List.fold_left
-						(fun acc t -> match loop (t :: seen) t with
+						(fun acc t' -> match loop (t :: seen) t' with
 							| None -> acc
 							| None -> acc
 							| Some t -> t :: acc
 							| Some t -> t :: acc
 						)
 						)

+ 1 - 1
src/typing/typerBase.ml

@@ -203,7 +203,7 @@ let get_abstract_froms a pl =
 		match follow (Type.field_type f) with
 		match follow (Type.field_type f) with
 		| TFun ([_,_,v],t) ->
 		| TFun ([_,_,v],t) ->
 			(try
 			(try
-				ignore(type_eq EqStrict t (TAbstract(a,List.map dup pl))); (* unify fields monomorphs *)
+				ignore(type_eq EqStrict t (TAbstract(a,List.map duplicate pl))); (* unify fields monomorphs *)
 				v :: acc
 				v :: acc
 			with Unify_error _ ->
 			with Unify_error _ ->
 				acc)
 				acc)

+ 9 - 0
tests/misc/compiler_loops/projects/Issue8588/Main.hx

@@ -0,0 +1,9 @@
+abstract Under<T>(T) {
+	@:from public static function from<T>(v:T) return v;
+}
+
+class Main {
+	static public function main() {
+		var a1:Under<{}> = { };
+	}
+}

+ 9 - 0
tests/misc/compiler_loops/projects/Issue8588/Main2.hx

@@ -0,0 +1,9 @@
+abstract Under<T>(T) {
+	@:from public static function from<T>(v) return (v:Under<T>);
+}
+
+class Main {
+	static function main() {
+		var a1:Under<{}> = {};
+	}
+}

+ 9 - 0
tests/misc/compiler_loops/projects/Issue8588/Main3.hx

@@ -0,0 +1,9 @@
+abstract Under<T>(T) {
+	@:from public static function from<T>(v:Under<T>) { return v; }
+}
+
+class Main3 {
+	static public function main() {
+		var a1:Under<{}> = [];
+	}
+}

+ 9 - 0
tests/misc/compiler_loops/projects/Issue8588/Main4.hx

@@ -0,0 +1,9 @@
+abstract Under<T>(T) {
+	@:from public static function from<T>(v:Under<T>) { return v; }
+}
+
+class Main3 {
+	static public function main() {
+		var a1:Under<{}> = ident;
+	}
+}

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile-fail.hxml

@@ -0,0 +1 @@
+-main Main

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:2: characters 9-53 : from.T should be Under<Unknown<0>>

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile2-fail.hxml

@@ -0,0 +1 @@
+-main Main2

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile2-fail.hxml.stderr

@@ -0,0 +1 @@
+Main2.hx:7: characters 3-25 : { } should be Under<{ }>

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile3-fail.hxml

@@ -0,0 +1 @@
+-main Main3

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile3-fail.hxml.stderr

@@ -0,0 +1 @@
+Main3.hx:7: characters 3-25 : Array<Unknown<0>> should be Under<{ }>

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile4-fail.hxml

@@ -0,0 +1 @@
+-main Main4

+ 1 - 0
tests/misc/compiler_loops/projects/Issue8588/compile4-fail.hxml.stderr

@@ -0,0 +1 @@
+Main4.hx:7: characters 22-27 : Unknown identifier : ident