فهرست منبع

allow abstract variance (closes #2584) (closes #2063)

Simon Krajewski 11 سال پیش
والد
کامیت
a7e881e7a5
3فایلهای تغییر یافته به همراه170 افزوده شده و 47 حذف شده
  1. 50 0
      tests/unit/issues/Issue2063.hx
  2. 68 0
      tests/unit/issues/Issue2584.hx
  3. 52 47
      type.ml

+ 50 - 0
tests/unit/issues/Issue2063.hx

@@ -0,0 +1,50 @@
+package unit.issues;
+
+private abstract A(Int) {}
+private abstract B(Int) from Int {}
+private abstract C(Int) to Int {}
+private abstract D(Int) from C to B {}
+
+private abstract Arr<T>(Dynamic) from Array<T> to Array<T> {}
+private abstract Brr<T>(Dynamic) from Array<Int> to Array<Int> {}
+
+class Issue2063 extends Test {
+	function test() {
+		var x:{a:Int} = {a:9};
+		var y:{a:A} = {a:untyped 9};
+		var z:{a:B} = {a:untyped 9};
+		var w:{a:C} = {a:untyped 9};
+		t(unit.TestType.typeError(x = y));
+		t(unit.TestType.typeError(y = x));
+		t(unit.TestType.typeError(x = z));
+		z = x;
+		x = w;
+		t(unit.TestType.typeError(w = x));
+
+		// should fail (transitive cast)
+		t(unit.TestType.typeError(z = w));
+
+		// should succeed
+		var p = {xs:[0,1,2]};
+		var q:{xs:Arr<Int>} = p;
+		p = q;
+		var p = {xs:[0.0,1.0,2.0]};
+		var q:{xs:Arr<Float>} = p;
+		p = q;
+
+		// should fail (transitive cast)
+		var p = {xs:[0,1,2]};
+		t(unit.TestType.typeError(var q:{xs:Arr<Float>} = p));
+		t(unit.TestType.typeError(p = q));
+
+		// should succeed
+		var p = {xs:[0,1,2]};
+		var q:{xs:Brr<Int>} = p;
+		p = q;
+
+		// should fail (wrong param)
+		var p = {xs:[0.0,1.0,2.0]};
+		t(unit.TestType.typeError(var q:{xs:Brr<String>} = p));
+		t(unit.TestType.typeError(p = q));
+	}
+}

+ 68 - 0
tests/unit/issues/Issue2584.hx

@@ -0,0 +1,68 @@
+package unit.issues;
+import haxe.ds.Option;
+
+private abstract X (String) to String {
+    public function new (s:String) this = s;
+}
+
+private abstract XX (String) {
+    public function new (s:String) this = s;
+
+	@:to function toString():String {
+		return cast this;
+	}
+}
+
+private abstract X2(X) to X {}
+
+private abstract XArr(Array<X>) to Array<X> {}
+
+private class A {
+    public var x : X;
+}
+
+private typedef B = {
+    var x : String;
+}
+
+private typedef B1 = {
+    var x(default, null) : String;
+}
+
+class Issue2584 extends Test {
+	function test() {
+		var a : { x: X } = { x: new X("foo") };
+		var b : { x: String } = a;
+		eq("foo", a.x);
+		eq("foo", b.x);
+
+		var a : { x: XX } = { x: new XX("foo") };
+		t(unit.TestType.typeError((a : { x: String })));
+	}
+
+	function test2() {
+		var x : Option<Array<Array<X>>> = null;
+		var a : Option<Array<Array<String>>> = x;
+
+		var x : { a : Option<Array<Array<X>>> } = null;
+		var a : { a : Option<Array<Array<String>>> } = x;
+
+		var x : A = null;
+		var a : B = x;
+
+		var x : A = null;
+		var a : B1 = x;
+
+		var x : XArr = null;
+		var a : Array<String> = x;
+
+		var x : X2 = null;
+		var a : String = x;
+
+		var x : Array<Option<{ x : X}>> = null;
+		var a : Array<Option<{ x : String}>> = x;
+
+		var xx : Array<Option<{ x : XX}>> = null;
+		t(unit.TestType.typeError((xx : Array<Option<{ x : String}>>)));
+	}
+}

+ 52 - 47
type.ml

@@ -1366,48 +1366,7 @@ let rec unify a b =
 		with
 			Unify_error l -> error (cannot_unify a b :: l))
 	| TAnon a1, TAnon a2 ->
-		(try
-			PMap.iter (fun n f2 ->
-			try
-				let f1 = PMap.find n a1.a_fields in
-				if not (unify_kind f1.cf_kind f2.cf_kind) then
-					(match !(a1.a_status), f1.cf_kind, f2.cf_kind with
-					| Opened, Var { v_read = AccNormal; v_write = AccNo }, Var { v_read = AccNormal; v_write = AccNormal } ->
-						f1.cf_kind <- f2.cf_kind;
-					| _ -> error [invalid_kind n f1.cf_kind f2.cf_kind]);
-				if f2.cf_public && not f1.cf_public then error [invalid_visibility n];
-				try
-					unify_with_access f1.cf_type f2;
-					(match !(a1.a_status) with
-					| Statics c when not (Meta.has Meta.MaybeUsed f1.cf_meta) -> f1.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: f1.cf_meta
-					| _ -> ());
-				with
-					Unify_error l -> error (invalid_field n :: l)
-			with
-				Not_found ->
-					match !(a1.a_status) with
-					| Opened ->
-						if not (link (ref None) a f2.cf_type) then error [];
-						a1.a_fields <- PMap.add n f2 a1.a_fields
-					| Const when Meta.has Meta.Optional f2.cf_meta ->
-						()
-					| _ ->
-						error [has_no_field a n];
-			) a2.a_fields;
-			(match !(a1.a_status) with
-			| Const when not (PMap.is_empty a2.a_fields) ->
-				PMap.iter (fun n _ -> if not (PMap.mem n a2.a_fields) then error [has_extra_field a n]) a1.a_fields;
-			| Opened ->
-				a1.a_status := Closed
-			| _ -> ());
-			(match !(a2.a_status) with
-			| Statics c -> (match !(a1.a_status) with Statics c2 when c == c2 -> () | _ -> error [])
-			| EnumStatics e -> (match !(a1.a_status) with EnumStatics e2 when e == e2 -> () | _ -> error [])
-			| AbstractStatics a -> (match !(a1.a_status) with AbstractStatics a2 when a == a2 -> () | _ -> error [])
-			| Opened -> a2.a_status := Closed
-			| Const | Extend _ | Closed -> ())
-		with
-			Unify_error l -> error (cannot_unify a b :: l))
+		unify_anons a b a1 a2
 	| TAnon an, TAbstract ({ a_path = [],"Class" },[pt]) ->
 		(match !(an.a_status) with
 		| Statics cl -> unify (TInst (cl,List.map (fun _ -> mk_mono()) cl.cl_params)) pt
@@ -1473,6 +1432,50 @@ let rec unify a b =
 	| _ , _ ->
 		error [cannot_unify a b]
 
+and unify_anons a b a1 a2 =
+	(try
+		PMap.iter (fun n f2 ->
+		try
+			let f1 = PMap.find n a1.a_fields in
+			if not (unify_kind f1.cf_kind f2.cf_kind) then
+				(match !(a1.a_status), f1.cf_kind, f2.cf_kind with
+				| Opened, Var { v_read = AccNormal; v_write = AccNo }, Var { v_read = AccNormal; v_write = AccNormal } ->
+					f1.cf_kind <- f2.cf_kind;
+				| _ -> error [invalid_kind n f1.cf_kind f2.cf_kind]);
+			if f2.cf_public && not f1.cf_public then error [invalid_visibility n];
+			try
+				unify_with_access f1.cf_type f2;
+				(match !(a1.a_status) with
+				| Statics c when not (Meta.has Meta.MaybeUsed f1.cf_meta) -> f1.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: f1.cf_meta
+				| _ -> ());
+			with
+				Unify_error l -> error (invalid_field n :: l)
+		with
+			Not_found ->
+				match !(a1.a_status) with
+				| Opened ->
+					if not (link (ref None) a f2.cf_type) then error [];
+					a1.a_fields <- PMap.add n f2 a1.a_fields
+				| Const when Meta.has Meta.Optional f2.cf_meta ->
+					()
+				| _ ->
+					error [has_no_field a n];
+		) a2.a_fields;
+		(match !(a1.a_status) with
+		| Const when not (PMap.is_empty a2.a_fields) ->
+			PMap.iter (fun n _ -> if not (PMap.mem n a2.a_fields) then error [has_extra_field a n]) a1.a_fields;
+		| Opened ->
+			a1.a_status := Closed
+		| _ -> ());
+		(match !(a2.a_status) with
+		| Statics c -> (match !(a1.a_status) with Statics c2 when c == c2 -> () | _ -> error [])
+		| EnumStatics e -> (match !(a1.a_status) with EnumStatics e2 when e == e2 -> () | _ -> error [])
+		| AbstractStatics a -> (match !(a1.a_status) with AbstractStatics a2 when a == a2 -> () | _ -> error [])
+		| Opened -> a2.a_status := Closed
+		| Const | Extend _ | Closed -> ())
+	with
+		Unify_error l -> error (cannot_unify a b :: l))
+
 and unify_from ab tl a b ?(allow_transitive_cast=true) t =
 	let t = apply_params ab.a_params tl t in
 	let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
@@ -1564,11 +1567,13 @@ and unify_with_variance f t1 t2 =
 		if not (List.exists (allows_variance_to t2) a1.a_to) && not (List.exists (allows_variance_to t1) a2.a_from) then
 			error [cannot_unify t1 t2]
 	| TAbstract(a,pl),t ->
-		type_eq EqStrict (apply_params a.a_params pl a.a_this) t;
-		if not (List.exists (allows_variance_to t) a.a_to) then error [cannot_unify t1 t2]
+		type_eq EqBothDynamic (apply_params a.a_params pl a.a_this) t;
+		if not (List.exists (fun t2 -> allows_variance_to t (apply_params a.a_params pl t2)) a.a_to) then error [cannot_unify t1 t2]
 	| t,TAbstract(a,pl) ->
-		type_eq EqStrict t (apply_params a.a_params pl a.a_this);
-		if not (List.exists (allows_variance_to t) a.a_from) then error [cannot_unify t1 t2]
+		type_eq EqBothDynamic t (apply_params a.a_params pl a.a_this);
+		if not (List.exists (fun t2 -> allows_variance_to t (apply_params a.a_params pl t2)) a.a_from) then error [cannot_unify t1 t2]
+	| TAnon a1,TAnon a2 ->
+		unify_anons t1 t2 a1 a2
 	| _ ->
 		error [cannot_unify t1 t2]
 
@@ -1596,7 +1601,7 @@ and unify_with_access t1 f2 =
 	(* read only *)
 	| Method MethNormal | Method MethInline | Var { v_write = AccNo } | Var { v_write = AccNever } -> unify t1 f2.cf_type
 	(* read/write *)
-	| _ -> type_eq EqBothDynamic t1 f2.cf_type
+	| _ -> with_variance (type_eq EqBothDynamic) t1 f2.cf_type
 
 module Abstract = struct
 	open Ast