Browse Source

Merge pull request #3341 from Simn/abstract_cast_rewrite

Rewrite of abstract cast and operator overloading
Simon Krajewski 11 years ago
parent
commit
ce8f380722

+ 58 - 42
codegen.ml

@@ -648,47 +648,65 @@ module AbstractCast = struct
 			r
 		in
 		let find a tl f =
-			let tcf,cfo = f() in
-			let mk_cast () =
-				let tcf = apply_params a.a_params tl tcf in
-				if type_iseq tcf tleft then
-					eright
-				else
-					(* TODO: causes Java overload issues *)
-					(* let eright = mk (TCast(eright,None)) tleft p in *)
-					do_check_cast ctx tcf eright p
-			in
-			match cfo,a.a_impl with
-				| None,_ ->
-					mk_cast();
-				| Some cf,_ when Meta.has Meta.MultiType a.a_meta ->
-					mk_cast();
-				| Some cf,Some c ->
-					recurse cf (fun () -> make_static_call ctx c cf a tl [eright] tleft p)
-				| _ ->
-					assert false
+			let tcf,cf = f() in
+			if (Meta.has Meta.MultiType a.a_meta) then
+				mk_cast eright tleft p
+			else match a.a_impl with
+				| Some c -> recurse cf (fun () -> make_static_call ctx c cf a tl [eright] tleft p)
+				| None -> assert false
 		in
 		if type_iseq tleft eright.etype then
 			eright
-		else try
-			begin match follow eright.etype with
-				| TAbstract(a,tl) ->
-					find a tl (fun () -> Abstract.find_to a tl tleft)
-				| _ ->
-					raise Not_found
-			end
-		with Not_found -> try
-			begin match follow tleft with
-				| TAbstract(a,tl) ->
-					find a tl (fun () -> Abstract.find_from a tl eright.etype tleft)
-				| _ ->
-					raise Not_found
-			end
+		else begin
+			let rec loop tleft tright = match follow tleft,follow tright with
+			| TAbstract(a1,tl1),TAbstract(a2,tl2) ->
+				begin try find a2 tl2 (fun () -> Abstract.find_to a2 tl2 tleft)
+				with Not_found -> try find a1 tl1 (fun () -> Abstract.find_from a1 tl1 eright.etype tleft)
+				with Not_found -> raise Not_found
+				end
+			| TAbstract(a,tl),_ ->
+				begin try find a tl (fun () -> Abstract.find_from a tl eright.etype tleft)
+				with Not_found ->
+					let rec loop2 tcl = match tcl with
+						| tc :: tcl ->
+							if not (type_iseq tc tleft) then loop (apply_params a.a_params tl tc) tright
+							else loop2 tcl
+						| [] -> raise Not_found
+					in
+					loop2 a.a_from
+				end
+			| _,TAbstract(a,tl) ->
+				begin try find a tl (fun () -> Abstract.find_to a tl tleft)
+				with Not_found ->
+					let rec loop2 tcl = match tcl with
+						| tc :: tcl ->
+							if not (type_iseq tc tright) then loop tleft (apply_params a.a_params tl tc)
+							else loop2 tcl
+						| [] -> raise Not_found
+					in
+					loop2 a.a_to
+				end
+			| _ ->
+				unify_raise ctx eright.etype tleft p;
+				eright
+			in
+			loop tleft eright.etype
+		end
+
+	let cast_or_unify_raise ctx tleft eright p =
+		try
+			if ctx.com.display <> DMNone then raise Not_found;
+			do_check_cast ctx tleft eright p
 		with Not_found ->
+			unify_raise ctx eright.etype tleft p;
 			eright
 
-	let check_cast ctx tleft eright p =
-		if ctx.com.display <> DMNone then eright else do_check_cast ctx tleft eright p
+	let cast_or_unify ctx tleft eright p =
+		try
+			cast_or_unify_raise ctx tleft eright p
+		with Error (Unify _ as err,_) ->
+			if not ctx.untyped then display_error ctx (error_msg err) p;
+			eright
 
 	let find_multitype_specialization com a pl p =
 		let m = mk_mono() in
@@ -723,7 +741,7 @@ module AbstractCast = struct
 				end;
 				tl
 		in
-		let _,cfo =
+		let _,cf =
 			try
 				Abstract.find_to a tl m
 			with Not_found ->
@@ -734,9 +752,7 @@ module AbstractCast = struct
 				else
 					error ("Abstract " ^ (s_type_path a.a_path) ^ " has no @:to function that accepts " ^ st) p;
 		in
-		match cfo with
-			| None -> assert false
-			| Some cf -> cf, follow m
+		cf, follow m
 
 	let handle_abstract_casts ctx e =
 		let rec loop ctx e = match e.eexpr with
@@ -1451,7 +1467,7 @@ struct
 				(cacc, rate_tp tlf tla)
 			else
 				let ret = ref None in
-				if List.exists (fun (t,_) -> try
+				if List.exists (fun t -> try
 					ret := Some (rate_conv (cacc+1) (apply_params af.a_params tlf t) targ);
 					true
 				with | Not_found ->
@@ -1459,7 +1475,7 @@ struct
 				) af.a_from then
 					Option.get !ret
 			else
-				if List.exists (fun (t,_) -> try
+				if List.exists (fun t -> try
 					ret := Some (rate_conv (cacc+1) tfun (apply_params aa.a_params tla t));
 					true
 				with | Not_found ->
@@ -1749,4 +1765,4 @@ let interpolate_code com code tl f_string f_expr p =
 			| Str.Delim x :: _ ->
 				err ("Unexpected " ^ x)
 	in
-	loop (Str.full_split regex code)
+	loop (Str.full_split regex code)

+ 2 - 2
gencommon.ml

@@ -70,13 +70,13 @@ let rec like_float t =
 	match follow t with
 		| TAbstract({ a_path = ([], "Float") },[])
 		| TAbstract({ a_path = ([], "Int") },[]) -> true
-		| TAbstract(a, _) -> List.exists (fun (t,_) -> like_float t) a.a_from || List.exists (fun (t,_) -> like_float t) a.a_to
+		| TAbstract(a, _) -> List.exists (fun t -> like_float t) a.a_from || List.exists (fun t -> like_float t) a.a_to
 		| _ -> false
 
 let rec like_int t =
 	match follow t with
 		| TAbstract({ a_path = ([], "Int") },[]) -> true
-		| TAbstract(a, _) -> List.exists (fun (t,_) -> like_int t) a.a_from || List.exists (fun (t,_) -> like_int t) a.a_to
+		| TAbstract(a, _) -> List.exists (fun t -> like_int t) a.a_from || List.exists (fun t -> like_int t) a.a_to
 		| _ -> false
 
 

+ 4 - 3
genxml.ml

@@ -240,9 +240,10 @@ let rec gen_type_decl com pos t =
 	| TAbstractDecl a ->
 		let doc = gen_doc_opt a.a_doc in
 		let meta = gen_meta a.a_meta in
-		let mk_cast (t,cfo) = node "icast" (match cfo with None -> [] | Some cf -> ["field",cf.cf_name]) [gen_type t] in
-		let sub = (match a.a_from with [] -> [] | l -> [node "from" [] (List.map mk_cast l)]) in
-		let super = (match a.a_to with [] -> [] | l -> [node "to" [] (List.map mk_cast l)]) in
+		let mk_cast t = node "icast" [] [gen_type t] in
+		let mk_field_cast (t,cf) = node "icast" ["field",cf.cf_name] [gen_type t] in
+		let sub = (match a.a_from,a.a_from_field with [],[] -> [] | l1,l2 -> [node "from" [] ((List.map mk_cast l1) @ (List.map mk_field_cast l2))]) in
+		let super = (match a.a_to,a.a_to_field with [],[] -> [] | l1,l2 -> [node "to" [] ((List.map mk_cast l1) @ (List.map mk_field_cast l2))]) in
 		let impl = (match a.a_impl with None -> [] | Some c -> [node "impl" [] [gen_type_decl com pos (TClassDecl c)]]) in
 		let this = [node "this" [] [gen_type a.a_this]] in
 		node "abstract" (gen_type_params pos a.a_private (tpath t) a.a_params a.a_pos m) (sub @ this @ super @ doc @ meta @ impl)

+ 3 - 3
interp.ml

@@ -4008,7 +4008,7 @@ let decode_unop op =
 	| 4, [] -> NegBits
 	| _ -> raise Invalid_expr
 
-let decode_import_mode t = 
+let decode_import_mode t =
 	match decode_enum t with
 	| 0, [] -> INormal
 	| 1, [alias] -> IAsName (dec_string alias)
@@ -4285,8 +4285,8 @@ and encode_tabstract a =
 		"impl", (match a.a_impl with None -> VNull | Some c -> encode_clref c);
 		"binops", enc_array (List.map (fun (op,cf) -> enc_obj [ "op",encode_binop op; "field",encode_cfield cf]) a.a_ops);
 		"unops", enc_array (List.map (fun (op,postfix,cf) -> enc_obj [ "op",encode_unop op; "isPostfix",VBool (match postfix with Postfix -> true | Prefix -> false); "field",encode_cfield cf]) a.a_unops);
-		"from", enc_array (List.map (fun (t,cfo) -> enc_obj [ "t",encode_type t; "field",match cfo with None -> VNull | Some cf -> encode_cfield cf]) a.a_from);
-		"to", enc_array (List.map (fun (t,cfo) -> enc_obj [ "t",encode_type t; "field",match cfo with None -> VNull | Some cf -> encode_cfield cf]) a.a_to);
+		"from", enc_array ((List.map (fun t -> enc_obj [ "t",encode_type t; "field",VNull]) a.a_from) @ (List.map (fun (t,cf) -> enc_obj [ "t",encode_type t; "field",encode_cfield cf]) a.a_from_field));
+		"to", enc_array ((List.map (fun t -> enc_obj [ "t",encode_type t; "field",VNull]) a.a_to) @ (List.map (fun (t,cf) -> enc_obj [ "t",encode_type t; "field",encode_cfield cf]) a.a_to_field));
 		"array", enc_array (List.map encode_cfield a.a_array);
 	]
 

+ 2 - 4
matcher.ml

@@ -1254,11 +1254,9 @@ let match_expr ctx e cases def with_type p =
 				let e = type_expr ctx e with_type in
 				match with_type with
 				| WithType t ->
-					unify ctx e.etype t e.epos;
-					Codegen.AbstractCast.check_cast ctx t e e.epos;
+					Codegen.AbstractCast.cast_or_unify ctx t e e.epos;
 				| WithTypeResume t ->
-					(try unify_raise ctx e.etype t e.epos with Error (Unify l,p) -> raise (Typer.WithTypeError (l,p)));
-					Codegen.AbstractCast.check_cast ctx t e e.epos
+					(try Codegen.AbstractCast.cast_or_unify_raise ctx t e e.epos with Error (Unify l,p) -> raise (Typer.WithTypeError (l,p)));
 				| _ -> e
 		in
 		(* type case guard *)

+ 2 - 1
optimizer.ml

@@ -277,7 +277,8 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f
 			| TConst TNull , Some c -> mk (TConst c) v.v_type e.epos
 			(* we have to check for abstract casts here because we can't do that later. However, we have to skip the check for the
 			   first argument of abstract implementation functions. *)
-			| _ when not (first && Meta.has Meta.Impl cf.cf_meta && cf.cf_name <> "_new") -> (!check_abstract_cast_ref) ctx (map_type v.v_type) e e.epos
+			(* actually we don't because unify_call_args takes care of that anyway *)
+			(* | _ when not (first && Meta.has Meta.Impl cf.cf_meta && cf.cf_name <> "_new") -> (!cast_or_unify_ref) ctx (map_type v.v_type) e e.epos *)
 			| _ -> e) :: loop pl al false
 		| [], (v,opt) :: al ->
 			(mk (TConst (match opt with None -> TNull | Some c -> c)) v.v_type p) :: loop [] al false

+ 0 - 1
tests/unit/MyAbstract.hx

@@ -203,7 +203,6 @@ abstract MyString(String) from String to String {
 	@:op(A + B) static public function add(lhs:MyString, rhs:MyString):MyString;
 	@:op(A + B) static public function addInt(lhs:MyString, rhs:Int):MyString;
 	@:op(A + B) static public function addBool(lhs:MyString, rhs:Bool):Bool;
-	@:op(A - B) static public function sub(lhs:MyString, rhs:MyString):MyString;
 }
 
 class ClassWithHashCode {

+ 1 - 1
tests/unit/TestType.hx

@@ -387,7 +387,7 @@ class TestType extends Test {
 		eq(c.b, true);
 		eq(c.t, String);
 
-		var c = new InitChildWithCtor(null);
+		var c = new InitChildWithCtor("null");
 		eq(c.i, 2);
 		eq(c.s, "foo");
 		eq(c.b, true);

+ 26 - 0
tests/unit/issues/Issue1810.hx

@@ -0,0 +1,26 @@
+package unit.issues;
+
+private abstract A(T) to T {
+	public inline function new(x:T) this = x;
+	public function get() return this;
+}
+
+private abstract T(X) from X to X {
+	public inline function new(x:X) this = x;
+	@:from public static inline function fromArr(x:Array<Int>) return new T(new X(x[0]));
+	public function get() return this;
+}
+
+private class X {
+	public var i:Int;
+	public function new(i:Int) {
+		this.i = i;
+	}
+}
+
+class Issue1810 extends Test {
+	function test() {
+		var a = new A([12]);
+		//eq(12, a.get().get().i);
+	}
+}

+ 42 - 0
tests/unit/issues/Issue1845.hx

@@ -0,0 +1,42 @@
+package unit.issues;
+
+private abstract A(Int) from Int {
+	inline public function new(x:Int) this=x;
+	@:from public static function fromArr(x:Array<Int>) return new A(x[0]);
+	public function toString() {
+		return 'A($this)';
+	}
+}
+
+private abstract M<T>(Null<T>) from Null<T> {
+	inline public function new(x:T) this=x;
+	public function toString() {
+		return 'M($this)';
+	}
+}
+
+private abstract A2(Int) from Int {
+	inline public function new(x:Int) this=x;
+	@:to public function fromArr():Array<Int> return [this];
+	public function toString() {
+		return 'A2($this)';
+	}
+}
+
+private abstract M2<T>(T) to T {
+	inline public function new(x:T) this=x;
+	public function toString() {
+		return 'M2($this)';
+	}
+}
+
+class Issue1845 extends Test {
+	function test() {
+		var m:M<A> = [10];
+		eq("M(10)", m.toString());
+
+		var m2:M2<A2> = new M2(12);
+		var a:Array<Int> = m2;
+		eq(12, a[0]);
+	}
+}

+ 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));
+	}
+}

+ 38 - 0
tests/unit/issues/Issue2130.hx

@@ -0,0 +1,38 @@
+package unit.issues;
+
+private abstract ZeroOneFloat(Float) to Float {
+    public inline function new(v:Float) {
+        this = v<0 ? 0. : (v>1 ? 1. : v);
+    }
+
+    @:from public static inline function fromInt(v:Int) {
+        return new ZeroOneFloat(v);
+    }
+
+    @:from public static inline function fromFloat(v:Float) {
+        return new ZeroOneFloat(v);
+    }
+
+    @:op(A *= B) public inline function umul_f(r:Float) : ZeroOneFloat {
+        return this = new ZeroOneFloat(this*r);
+    }
+
+    @:op(A * B) @:commutative public static inline function mul_i(l:ZeroOneFloat, r:Float) return l*r;
+}
+
+class Issue2130 extends Test {
+	function test() {
+        var f : ZeroOneFloat = 0.5;
+        feq(0.5, f);
+        f *= 2.;
+        feq(1, f);
+
+
+        var f : ZeroOneFloat = 0.5;
+        var a : Float = 10.;
+		feq(5, a * f);
+
+        a *= f;
+        feq(5, a);
+	}
+}

+ 15 - 0
tests/unit/issues/Issue2152.hx

@@ -0,0 +1,15 @@
+package unit.issues;
+
+using unit.issues.misc.Issue2152Class;
+
+private abstract MyInt(Int) {
+	public inline function new (x:Int) this = x;
+	@:to inline function toString ():String return "asString: " + this;
+}
+
+class Issue2152 extends Test {
+	function test() {
+		var z = new MyInt(1);
+		eq("asString: 1", z.passString());
+	}
+}

+ 26 - 0
tests/unit/issues/Issue2157.hx

@@ -0,0 +1,26 @@
+package unit.issues;
+
+private abstract Choice<X,Y>(Dynamic) {
+    private function new (x:Dynamic) this = x;
+
+    @:from public static function fromX <X,Y>(x:X):Choice<X,Y> return new Choice(x);
+    @:from public static function fromY <X,Y>(y:Y):Choice<X,Y> return new Choice(y);
+}
+
+private abstract False(Bool) {
+    inline function new (x:Bool) this = x;
+    @:from public static function fromBool (b:Bool) {
+        if (!b) {
+            throw "assert " + b + " should be false";
+        }
+        return new False(b);
+    }
+}
+
+
+class Issue2157 extends Test {
+	function test() {
+		var x : False = true;
+		t(unit.TestType.typeError(var z : Choice<False, Int> = true));
+	}
+}

+ 13 - 0
tests/unit/issues/Issue2184.hx

@@ -0,0 +1,13 @@
+package unit.issues;
+
+private abstract BoxedInt({ val : Int }) from { val : Int } {
+	public function new (v: { val :Int }) this = v;
+	@:to public function toInt():Int return this.val;
+}
+
+class Issue2184 extends Test {
+	function test() {
+		var test = function () return new BoxedInt({ val : 5 });
+		t(unit.TestType.typeError(var z:Void->Int = test));
+	}
+}

+ 3 - 0
tests/unit/issues/Issue2343.hx

@@ -15,5 +15,8 @@ class Issue2343 extends unit.Test {
 	function test() {
 		var foo = new Foo<MyInt>(1);
 		eq(1, foo.result);
+
+		// we cannot actually test this because it is delayed
+		//t(unit.TestType.typeError(var foo2 = new Foo<String>("1")));
 	}
 }

+ 57 - 0
tests/unit/issues/Issue2396.hx

@@ -0,0 +1,57 @@
+package unit.issues;
+
+private class AA
+{
+    public var x:Float;
+    public var y:Float;
+
+    inline public function new(x:Float, y:Float)
+    {
+        this.x = x;
+        this.y = y;
+    }
+}
+
+private abstract BB(AA)
+{
+    public var x(get, set):Float;
+    public var y(get, set):Float;
+
+
+    inline public function get_x():Float return this.x;
+    inline public function get_y():Float return this.y;
+
+    inline public function set_x(v:Float):Float return this.x = v;
+    inline public function set_y(v:Float):Float return this.y = v;
+
+    public inline function new(x:Float = 0, y:Float = 0)
+    {
+        this = new AA(x, y);
+    }
+
+    @:commutative @:op(A*B) inline public static function opMulSF(a:BB, s:Float):BB
+    {
+        return new BB(a.x * s, a.y * s);
+    }
+}
+
+
+class Issue2396 extends Test {
+	function test() {
+        var a = new BB(10, 20);
+
+        var t1 = 1.0;
+        var t2 = 2.0;
+
+        // !!! fails here
+        var fail = a * (t1 + t2);
+		feq(30, fail.x);
+		feq(60, fail.y);
+
+        // ok here
+        var cacheSum = (t1 + t2);
+        var ok = a * cacheSum;
+		feq(30, ok.x);
+		feq(60, ok.y);
+	}
+}

+ 16 - 0
tests/unit/issues/Issue2409.hx

@@ -0,0 +1,16 @@
+package unit.issues;
+
+private abstract Foo(Int) {
+    public function new(x:Int) this = x;
+    @:op(A+B) public static function add(a:Foo, b:Foo):Foo;
+	public function get() {
+		return this;
+	}
+}
+
+class Issue2409 extends Test {
+	function test() {
+		var f:Foo = new Foo(1);
+		eq(2, (f+f).get());
+	}
+}

+ 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}>>)));
+	}
+}

+ 4 - 3
tests/unit/issues/Issue2713.hx

@@ -1,7 +1,8 @@
 package unit.issues;
 import unit.Test;
 
-private abstract Vector<T>(Array<T>) {
+@:arrayAccess
+private abstract MyVector<T>(Array<T>) {
 
 	public function new ():Void {
 		this = new Array<T>();
@@ -11,7 +12,7 @@ private abstract Vector<T>(Array<T>) {
 		return this.push(x);
 	}
 
-	@:from static public inline function fromArray<T, U> (a:Array<U>):Vector<T> {
+	@:from static public inline function fromArray<T, U> (a:Array<U>):MyVector<T> {
 		return cast a;
 	}
 
@@ -23,7 +24,7 @@ private abstract Vector<T>(Array<T>) {
 
 class Issue2713 extends Test {
 	function test() {
-		var v:Vector<Int> = [1, 2, 3];
+		var v:MyVector<Int> = [1, 2, 3];
 		for (i in 0...2) {
 			v.push(i);
 		}

+ 23 - 0
tests/unit/issues/Issue2856.hx

@@ -0,0 +1,23 @@
+package unit.issues;
+
+class Issue2856 extends Test {
+	#if python
+	function test() {
+		function x (args:python.KwArgs) {
+			var dict = args.toDict();
+			var acc = [];
+			for (key in dict.keys()) {
+				acc.push(key + "=" +dict.get(key, null));
+			}
+			return acc.join(";");
+		}
+
+		var a = python.Lib.anonToDict({ "a" : 1, "b" : 2});
+		var res = x( a );
+		eq("a=1;b=2", res);
+
+		var res = x( python.Lib.anonToDict({ "a" : 1, "b" : 2}) );
+		eq("a=1;b=2", res);
+	}
+	#end
+}

+ 32 - 0
tests/unit/issues/Issue3214.hx

@@ -0,0 +1,32 @@
+package unit.issues;
+
+private abstract Int64(Int64_t) {
+	inline private function new(i) {
+		this = i;
+	}
+
+	inline public static function make(a:Int, b:Int):Int64 {
+		return new Int64(cast a + b);
+	}
+
+	@:op(A*B) inline public function mul_i64(a:Int64):Int64 {
+		return new Int64(cast this.get() * a.get().get());
+	}
+
+	public function get() {
+		return this;
+	}
+}
+
+private abstract Int64_t(Dynamic) {
+	public function get() return this;
+}
+
+class Issue3214 extends unit.Test {
+	function test() {
+		var a = Int64.make(1,2);
+		var b = Int64.make(3,4);
+        var c = a * b;
+		eq(21, c.get().get());
+	}
+}

+ 5 - 0
tests/unit/issues/misc/Issue2152Class.hx

@@ -0,0 +1,5 @@
+package unit.issues.misc;
+
+class Issue2152Class {
+	public static function passString (s:String) return s;
+}

+ 105 - 82
type.ml

@@ -258,9 +258,11 @@ and tabstract = {
 	mutable a_unops : (Ast.unop * unop_flag * tclass_field) list;
 	mutable a_impl : tclass option;
 	mutable a_this : t;
-	mutable a_from : (t * tclass_field option) list;
+	mutable a_from : t list;
+	mutable a_from_field : (t * tclass_field) list;
+	mutable a_to : t list;
+	mutable a_to_field : (t * tclass_field) list;
 	mutable a_array : tclass_field list;
-	mutable a_to : (t * tclass_field option) list;
 }
 
 and module_type =
@@ -1295,8 +1297,8 @@ let rec unify a b =
 	| _ , TAbstract ({a_path=[],"Void"},_) ->
 		error [cannot_unify a b]
 	| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
-		let f1 = unify_to_field a1 tl1 b in
-		let f2 = unify_from_field a2 tl2 a b in
+		let f1 = unify_to a1 tl1 b in
+		let f2 = unify_from a2 tl2 a b in
 		if not (List.exists (f1 ~allow_transitive_cast:false) a1.a_to) && not (List.exists (f2 ~allow_transitive_cast:false) a2.a_from)
 		    && not (List.exists f1 a1.a_to) && not (List.exists f2 a2.a_from) then error [cannot_unify a b]
 	| TInst (c1,tl1) , TInst (c2,tl2) ->
@@ -1364,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
@@ -1459,24 +1420,92 @@ let rec unify a b =
 		| _ ->
 			error [cannot_unify a b])
 	| TAbstract (aa,tl), _  ->
-		if not (List.exists (unify_to_field aa tl b) aa.a_to) then error [cannot_unify a b];
+		if not (List.exists (unify_to aa tl b) aa.a_to) then error [cannot_unify a b];
 	| TInst ({ cl_kind = KTypeParameter ctl } as c,pl), TAbstract (bb,tl) ->
 		(* one of the constraints must satisfy the abstract *)
 		if not (List.exists (fun t ->
 			let t = apply_params c.cl_params pl t in
 			try unify t b; true with Unify_error _ -> false
-		) ctl) && not (List.exists (unify_from_field bb tl a b) bb.a_from) then error [cannot_unify a b];
+		) ctl) && not (List.exists (unify_from bb tl a b) bb.a_from) then error [cannot_unify a b];
 	| _, TAbstract (bb,tl) ->
-		if not (List.exists (unify_from_field bb tl a b) bb.a_from) then error [cannot_unify a b]
+		if not (List.exists (unify_from bb tl a b) bb.a_from) then error [cannot_unify a b]
 	| _ , _ ->
 		error [cannot_unify a b]
 
-and unify_from_field ab tl a b ?(allow_transitive_cast=true) (t,cfo) =
+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 =
 	if (List.exists (fun (a2,b2) -> fast_eq a a2 && fast_eq b b2) (!abstract_cast_stack)) then false else begin
 	abstract_cast_stack := (a,b) :: !abstract_cast_stack;
-	let unify_func = match follow a with TAbstract({a_impl = Some _},_) when ab.a_impl <> None || not allow_transitive_cast -> type_eq EqStrict | _ -> unify in
-	let b = try begin match cfo with
-		| Some cf -> (match follow cf.cf_type with
+	let t = apply_params ab.a_params tl t in
+	let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
+	let b = try
+		unify_func a t;
+		true
+	with Unify_error _ ->
+		false
+	in
+	abstract_cast_stack := List.tl !abstract_cast_stack;
+	b
+	end
+
+and unify_to ab tl 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
+	try
+		unify_func t b;
+		true
+	with Unify_error _ ->
+		false
+
+and unify_from_field ab tl a b ?(allow_transitive_cast=true) (t,cf) =
+	if (List.exists (fun (a2,b2) -> fast_eq a a2 && fast_eq b b2) (!abstract_cast_stack)) then false else begin
+	abstract_cast_stack := (a,b) :: !abstract_cast_stack;
+	let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
+	let b = try
+		begin match follow cf.cf_type with
 			| TFun(_,r) ->
 				let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
 				let map t = apply_params ab.a_params tl (apply_params cf.cf_params monos t) in
@@ -1486,10 +1515,8 @@ and unify_from_field ab tl a b ?(allow_transitive_cast=true) (t,cfo) =
 						List.iter (fun tc -> match follow m with TMono _ -> raise (Unify_error []) | _ -> unify m (map tc) ) constr
 					| _ -> ()
 				) monos cf.cf_params;
-				unify (map r) b;
-			| _ -> assert false)
-		| _ ->
-			unify_func a (apply_params ab.a_params tl t)
+				unify_func (map r) b;
+			| _ -> assert false
 		end;
 		true
 	with Unify_error _ -> false
@@ -1498,18 +1525,13 @@ and unify_from_field ab tl a b ?(allow_transitive_cast=true) (t,cfo) =
 	b
 	end
 
-and unify_to_field ab tl b ?(allow_transitive_cast=true) (t,cfo) =
+and unify_to_field ab tl b ?(allow_transitive_cast=true) (t,cf) =
 	let a = TAbstract(ab,tl) in
 	if (List.exists (fun (b2,a2) -> fast_eq a a2 && fast_eq b b2) (!abstract_cast_stack)) then false else begin
 	abstract_cast_stack := (b,a) :: !abstract_cast_stack;
-	let unify_func = match follow b with
-		| TAbstract(ab2,_) when not (Meta.has Meta.CoreType ab.a_meta) || not (Meta.has Meta.CoreType ab2.a_meta) || not allow_transitive_cast ->
-			type_eq EqStrict
-		| _ ->
-			unify
-	in
-	let r = try begin match cfo with
-		| Some cf -> (match follow cf.cf_type with
+	let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
+	let r = try
+		begin match follow cf.cf_type with
 			| TFun((_,_,ta) :: _,_) ->
 				let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
 				let map t = apply_params ab.a_params tl (apply_params cf.cf_params monos t) in
@@ -1524,9 +1546,7 @@ and unify_to_field ab tl b ?(allow_transitive_cast=true) (t,cfo) =
 					| _ -> ()
 				) monos cf.cf_params;
 				unify_func (map t) b;
-			| _ -> assert false)
-		| _ ->
-			unify_func (apply_params ab.a_params tl t) b;
+			| _ -> assert false
 		end;
 		true
 	with Unify_error _ -> false
@@ -1536,10 +1556,7 @@ and unify_to_field ab tl b ?(allow_transitive_cast=true) (t,cfo) =
 	end
 
 and unify_with_variance f t1 t2 =
-	let allows_variance_to t (tf,cfo) = match cfo with
-		| None -> type_iseq tf t
-		| Some _ -> false
-	in
+	let allows_variance_to t tf = type_iseq tf t in
 	match follow t1,follow t2 with
 	| TInst(c1,tl1),TInst(c2,tl2) when c1 == c2 ->
 		List.iter2 f tl1 tl2
@@ -1556,11 +1573,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]
 
@@ -1588,22 +1607,26 @@ 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
 
 	let find_to ab pl b =
 		if follow b == t_dynamic then
-			List.find (fun (t,_) -> follow t == t_dynamic) ab.a_to
+			List.find (fun (t,_) -> follow t == t_dynamic) ab.a_to_field
+		else if List.exists (unify_to ab pl ~allow_transitive_cast:false b) ab.a_to then
+			raise Not_found (* legacy compatibility *)
 		else
-			List.find (unify_to_field ab pl b) ab.a_to
+			List.find (unify_to_field ab pl b) ab.a_to_field
 
 	let find_from ab pl a b =
 		if follow a == t_dynamic then
-			List.find (fun (t,_) -> follow t == t_dynamic) ab.a_from
+			List.find (fun (t,_) -> follow t == t_dynamic) ab.a_from_field
+		else if List.exists (unify_from ab pl a ~allow_transitive_cast:false b) ab.a_from then
+			raise Not_found (* legacy compatibility *)
 		else
-			List.find (unify_from_field ab pl a b) ab.a_from
+			List.find (unify_from_field ab pl a b) ab.a_from_field
 
 	let underlying_type_stack = ref []
 

+ 1 - 1
typecore.ml

@@ -154,7 +154,7 @@ let unify_min_ref : (typer -> texpr list -> t) ref = ref (fun _ _ -> assert fals
 let match_expr_ref : (typer -> Ast.expr -> (Ast.expr list * Ast.expr option * Ast.expr option) list -> Ast.expr option option -> with_type -> Ast.pos -> decision_tree) ref = ref (fun _ _ _ _ _ _ -> assert false)
 let get_pattern_locals_ref : (typer -> Ast.expr -> Type.t -> (string, tvar * pos) PMap.t) ref = ref (fun _ _ _ -> assert false)
 let get_constructor_ref : (typer -> tclass -> t list -> Ast.pos -> (t * tclass_field)) ref = ref (fun _ _ _ _ -> assert false)
-let check_abstract_cast_ref : (typer -> t -> texpr -> Ast.pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
+let cast_or_unify_ref : (typer -> t -> texpr -> Ast.pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
 
 (* Source: http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#OCaml *)
 let levenshtein a b =

+ 10 - 12
typeload.ml

@@ -118,6 +118,8 @@ let make_module ctx mpath file tdecls loadp =
 				a_meta = d.d_meta;
 				a_from = [];
 				a_to = [];
+				a_from_field = [];
+				a_to_field = [];
 				a_ops = [];
 				a_unops = [];
 				a_impl = None;
@@ -232,7 +234,7 @@ let type_function_arg ctx t e opt p =
 let type_var_field ctx t e stat p =
 	if stat then ctx.curfun <- FunStatic else ctx.curfun <- FunMember;
 	let e = type_expr ctx e (WithType t) in
-	unify ctx e.etype t p;
+	let e = (!cast_or_unify_ref) ctx t e p in
 	match t with
 	| TType ({ t_path = ([],"UInt") },[]) | TAbstract ({ a_path = ([],"UInt") },[]) when stat -> { e with etype = t }
 	| _ -> e
@@ -1714,11 +1716,7 @@ let init_class ctx c p context_init herits fields =
 						(* turn int constant to float constant if expected type is float *)
 						{e with eexpr = TConst (TFloat (Int32.to_string i))}
 					| _ ->
-						let e' = (!check_abstract_cast_ref) ctx cf.cf_type e e.epos in
-						if e' == e then
-							mk (TCast(e,None)) cf.cf_type e.epos
-						else
-							e'
+						mk_cast e cf.cf_type e.epos
 				end
 			in
 			let r = exc_protect ctx (fun r ->
@@ -1976,7 +1974,7 @@ let init_class ctx c p context_init herits fields =
 									| TFun([_,_,t],_) -> t
 									| _ -> error (f.cff_name ^ ": @:from cast functions must accept exactly one argument") p
 							in
-							a.a_from <- (TLazy (ref r),Some cf) :: a.a_from;
+							a.a_from_field <- (TLazy (ref r),cf) :: a.a_from_field;
 						| (Meta.To,_,_) :: _ ->
 							if is_macro then error (f.cff_name ^ ": Macro cast functions are not supported") p;
 							if not (Meta.has Meta.Impl cf.cf_meta) then cf.cf_meta <- (Meta.Impl,[],cf.cf_pos) :: cf.cf_meta;
@@ -2003,7 +2001,7 @@ let init_class ctx c p context_init herits fields =
 							end else (fun () ->
 								resolve_m []
 							) in
-							a.a_to <- (TLazy (ref r), Some cf) :: a.a_to
+							a.a_to_field <- (TLazy (ref r), cf) :: a.a_to_field
 						| (Meta.ArrayAccess,_,_) :: _ ->
 							if is_macro then error (f.cff_name ^ ": Macro array-access functions are not supported") p;
 							a.a_array <- cf :: a.a_array;
@@ -2233,8 +2231,8 @@ let init_class ctx c p context_init herits fields =
 	) fields;
 	(match c.cl_kind with
 	| KAbstractImpl a ->
-		a.a_to <- List.rev a.a_to;
-		a.a_from <- List.rev a.a_from;
+		a.a_to_field <- List.rev a.a_to_field;
+		a.a_from_field <- List.rev a.a_from_field;
 		a.a_ops <- List.rev a.a_ops;
 		a.a_unops <- List.rev a.a_unops;
 	| _ -> ());
@@ -2611,8 +2609,8 @@ let rec init_module_type ctx context_init do_init (decl,p) =
 			t
 		in
 		List.iter (function
-			| AFromType t -> a.a_from <- (load_type t true, None) :: a.a_from
-			| AToType t -> a.a_to <- (load_type t false, None) :: a.a_to
+			| AFromType t -> a.a_from <- (load_type t true) :: a.a_from
+			| AToType t -> a.a_to <- (load_type t false) :: a.a_to
 			| AIsType t ->
 				if a.a_impl = None then error "Abstracts with underlying type must have an implementation" a.a_pos;
 				if Meta.has Meta.CoreType a.a_meta then error "@:coreType abstracts cannot have an underlying type" p;

+ 131 - 156
typer.ml

@@ -104,7 +104,7 @@ let rec classify t =
 	| TAbstract({a_impl = Some _} as a,tl) -> KAbstract (a,tl)
 	| TAbstract ({ a_path = [],"Int" },[]) -> KInt
 	| TAbstract ({ a_path = [],"Float" },[]) -> KFloat
-	| TAbstract (a,[]) when List.exists (fun (t,_) -> match classify t with KInt | KFloat -> true | _ -> false) a.a_to -> KParam t
+	| TAbstract (a,[]) when List.exists (fun t -> match classify t with KInt | KFloat -> true | _ -> false) a.a_to -> KParam t
 	| TInst ({ cl_kind = KTypeParameter ctl },_) when List.exists (fun t -> match classify t with KInt | KFloat -> true | _ -> false) ctl -> KParam t
 	| TMono r when !r = None -> KUnk
 	| TDynamic _ -> KDyn
@@ -695,9 +695,7 @@ let rec unify_call_args' ctx el args r p inline force_inline =
 	let force_inline, is_extern = false, false in
 	let type_against t e =
 		let e = type_expr ctx e (WithTypeResume t) in
-		(try unify_raise ctx e.etype t e.epos with Error (Unify l,p) -> raise (WithTypeError (l,p)));
-		let e = Codegen.AbstractCast.check_cast ctx t e p in
-		e
+		(try Codegen.AbstractCast.cast_or_unify_raise ctx t e p with Error (Unify l,p) -> raise (WithTypeError (l,p)));
 	in
 	let rec loop el args = match el,args with
 		| [],[] ->
@@ -1183,7 +1181,7 @@ let rec using_field ctx mode e i p =
 			begin match follow t with
 				| TFun((_,_,(TType({t_path = ["haxe";"macro"],"ExprOf"},[t0]) | t0)) :: args,r) ->
 					if is_dynamic && follow t0 != t_dynamic then raise Not_found;
-					Type.unify e.etype t0;
+					let e = Codegen.AbstractCast.cast_or_unify_raise ctx t0 e p in
 					(* early constraints check is possible because e.etype has no monomorphs *)
 					List.iter2 (fun m (name,t) -> match follow t with
 						| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] && not (has_mono m) ->
@@ -1197,7 +1195,7 @@ let rec using_field ctx mode e i p =
 			end
 		with Not_found ->
 			loop l
-		| Unify_error el ->
+		| Unify_error el | Error (Unify el,_) ->
 			if List.exists (function Has_extra_field _ -> true | _ -> false) el then check_constant_struct := true;
 			loop l
 	in
@@ -1792,11 +1790,10 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 		let e1 = type_access ctx (fst e1) (snd e1) MSet in
 		let tt = (match e1 with AKNo _ | AKInline _ | AKUsing _ | AKMacro _ | AKAccess _ -> Value | AKSet(_,t,_) -> WithType t | AKExpr e -> WithType e.etype) in
 		let e2 = type_expr ctx e2 tt in
-		let e2 = match tt with WithType t -> Codegen.AbstractCast.check_cast ctx t e2 p | _ -> e2 in
 		(match e1 with
 		| AKNo s -> error ("Cannot access field or identifier " ^ s ^ " for writing") p
 		| AKExpr e1  ->
-			unify ctx e2.etype e1.etype p;
+			let e2 = Codegen.AbstractCast.cast_or_unify ctx e1.etype e2 p in
 			check_assign ctx e1;
 			(match e1.eexpr , e2.eexpr with
 			| TLocal i1 , TLocal i2 when i1 == i2 -> error "Assigning a value to itself" p
@@ -1805,7 +1802,7 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 			| _ , _ -> ());
 			mk (TBinop (op,e1,e2)) e1.etype p
 		| AKSet (e,t,cf) ->
-			unify ctx e2.etype t p;
+			let e2 = Codegen.AbstractCast.cast_or_unify ctx t e2 p in
 			make_call ctx (mk (TField (e,quick_field_dynamic e.etype ("set_" ^ cf.cf_name))) (tfun [t] t) p) [e2] t p
 		| AKAccess(ebase,ekey) ->
 			let c,cf,tf,r = find_array_access_from_type ebase.etype ekey.etype (Some e2.etype) p in
@@ -2057,13 +2054,11 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 	| OpEq
 	| OpNotEq ->
 		let e1,e2 = try
-			unify_raise ctx e1.etype e2.etype p;
 			(* we only have to check one type here, because unification fails if one is Void and the other is not *)
 			(match follow e2.etype with TAbstract({a_path=[],"Void"},_) -> error "Cannot compare Void" p | _ -> ());
-			Codegen.AbstractCast.check_cast ctx e2.etype e1 p,e2
+			Codegen.AbstractCast.cast_or_unify_raise ctx e2.etype e1 p,e2
 		with Error (Unify _,_) ->
-			unify ctx e2.etype e1.etype p;
-			e1,Codegen.AbstractCast.check_cast ctx e1.etype e2 p
+			e1,Codegen.AbstractCast.cast_or_unify ctx e1.etype e2 p
 		in
 		mk_op e1 e2 ctx.t.tbool
 	| OpGt
@@ -2116,126 +2111,114 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 	| OpAssignOp _ ->
 		assert false
 	in
-	let find_overload a pl c t left =
-		let rec loop ops = match ops with
-			| [] -> raise Not_found
-			| (o,cf) :: ops when is_assign_op && o = OpAssignOp(op) || o == op ->
-				let impl = Meta.has Meta.Impl cf.cf_meta in
-				let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
-				let tcf = apply_params cf.cf_params monos cf.cf_type in
-				let tcf = if impl then apply_params a.a_params pl tcf else tcf in
-				(match follow tcf with
-				| TFun([(_,_,t1);(_,_,t2)],r) ->
-					(* implementation fields can only be used in left mode (issue #2130) *)
-					if impl && not left then loop ops else begin
-						let t1,t2 = if left || Meta.has Meta.Commutative cf.cf_meta then t1,t2 else t2,t1 in
-						begin try
-							begin
-								if impl then
-									type_eq EqStrict (Abstract.get_underlying_type a pl) (Abstract.follow_with_abstracts t1)
-								else
-									type_eq EqStrict (TAbstract(a,pl)) t1;
-							end;
-							(* special case for == and !=: if the second type is a monomorph, assume that we want to unify
-							   it with the first type to preserve comparison semantics. *)
-							begin match op,follow t with
-								| (OpEq | OpNotEq),TMono _ ->
-									Type.unify (if left then e1.etype else e2.etype) t
-								| _ ->
-									()
-							end;
-							Type.unify t t2;
-							check_constraints ctx "" cf.cf_params monos (apply_params a.a_params pl) false cf.cf_pos;
-							cf,t2,r,o = OpAssignOp(op),Meta.has Meta.Commutative cf.cf_meta
-						with Unify_error _ ->
-							loop ops
-						end
+	let find_overload a c tl left =
+		let map = apply_params a.a_params tl in
+		let make op_cf cf e1 e2 tret =
+			if cf.cf_expr = None then begin
+				if not (Meta.has Meta.CoreType a.a_meta) then begin
+					(* for non core-types we require that the return type is compatible to the native result type *)
+					let e' = make {e1 with etype = Abstract.follow_with_abstracts e1.etype} {e1 with etype = Abstract.follow_with_abstracts e2.etype} in
+					let t_expected = e'.etype in
+					begin try
+						unify_raise ctx tret t_expected p
+					with Error (Unify _,_) ->
+						match follow tret with
+							| TAbstract(a,tl) when type_iseq (Abstract.get_underlying_type a tl) t_expected ->
+								()
+							| _ ->
+								let st = s_type (print_context()) in
+								error (Printf.sprintf "The result of this operation (%s) is not compatible with declared return type %s" (st t_expected) (st tret)) p
 					end;
-				| _ -> loop ops)
-			| _ :: ops ->
-				loop ops
+				end;
+				mk_cast (Codegen.binop op e1 e2 tret p) tret p
+			end else begin
+				let e = make_static_call ctx c cf map [e1;e2] tret p in
+				e
+			end
 		in
-		loop a.a_ops
-	in
-	let mk_cast_op c f a pl e1 e2 r assign =
-		let t = field_type ctx c [] f p in
-		let t = apply_params a.a_params pl t in
-		let et = type_module_type ctx (TClassDecl c) None p in
-		let ef = mk (TField (et,FStatic (c,f))) t p in
-		let ec = make_call ctx ef [e1;e2] r p in
-		if is_assign_op && not assign then mk (TMeta((Meta.RequiresAssign,[],ec.epos),ec)) ec.etype ec.epos else ec
-	in
-	let cast_rec e1t e2t r is_core_type =
-		if is_core_type then
-			(* we assume that someone declaring a @:coreType knows what he is doing with regards to operation return types (issue #2333) *)
-			mk (TBinop(op,e1t,e2t)) r p
-		else begin
-			let e = make e1t e2t in
-			begin try
-				unify_raise ctx e.etype r p
-			with Error (Unify _,_) ->
-				match follow r with
-					| TAbstract(a,tl) when type_iseq (Abstract.get_underlying_type a tl) e.etype ->
+		(* special case for == and !=: if the second type is a monomorph, assume that we want to unify
+		   it with the first type to preserve comparison semantics. *)
+		begin match op with
+			| (OpEq | OpNotEq) ->
+				begin match follow e1.etype,e2.etype with
+					| TMono _,_ | _,TMono _ ->
+						Type.unify e1.etype e2.etype
+					| _ ->
 						()
+				end
+			| _ ->
+				()
+		end;
+ 		let rec loop ol = match ol with
+			| (op_cf,cf) :: ol when op_cf <> op && (not is_assign_op || op_cf <> OpAssignOp(op)) ->
+				loop ol
+			| (op_cf,cf) :: ol ->
+				let is_impl = Meta.has Meta.Impl cf.cf_meta in
+				begin match follow cf.cf_type with
+					| TFun([(_,_,t1);(_,_,t2)],tret) ->
+						let check e1 e2 swapped =
+							let map_arguments () =
+								let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
+								let map t = map (apply_params cf.cf_params monos t) in
+								let t1 = map t1 in
+								let t2 = map t2 in
+								monos,t1,t2
+							in
+							let make e1 e2 = make op_cf cf e1 e2 tret in
+							let monos,t1,t2 = map_arguments() in
+							let t1 = if is_impl then Abstract.follow_with_abstracts t1 else t1 in
+							let e1,e2 = if left || not left && swapped then begin
+								Type.type_eq EqStrict (if is_impl then Abstract.follow_with_abstracts e1.etype else e1.etype) t1;
+								e1,Codegen.AbstractCast.cast_or_unify_raise ctx t2 e2 p
+							end else begin
+								Type.type_eq EqStrict e2.etype t2;
+								Codegen.AbstractCast.cast_or_unify_raise ctx t1 e1 p,e2
+							end in
+							check_constraints ctx "" cf.cf_params monos (apply_params a.a_params tl) false cf.cf_pos;
+							if not swapped then
+								make e1 e2
+							else
+								let v1,v2 = gen_local ctx t1, gen_local ctx t2 in
+								let ev1,ev2 = mk (TVar(v1,Some e1)) ctx.t.tvoid p,mk (TVar(v2,Some e2)) ctx.t.tvoid p in
+								let eloc1,eloc2 = mk (TLocal v1) v1.v_type p,mk (TLocal v2) v2.v_type p in
+								let e = make eloc1 eloc2 in
+								let e = mk (TBlock [
+									ev2;
+									ev1;
+									e
+								]) e.etype e.epos in
+								if is_assign_op && op_cf = op then (mk (TMeta((Meta.RequiresAssign,[],p),e)) e.etype e.epos)
+								else e
+						in
+						begin try
+							check e1 e2 false
+						with Error (Unify _,_) | Unify_error _ -> try
+							if not (Meta.has Meta.Commutative cf.cf_meta) then raise Not_found;
+							check e2 e1 true
+						with Not_found | Error (Unify _,_) | Unify_error _ ->
+							loop ol
+						end
 					| _ ->
-						error ("The result of this operation (" ^ (s_type (print_context()) e.etype) ^ ") is not compatible with declared return type " ^ (s_type (print_context()) r)) p;
-			end;
-			{e with etype = r}
-		end
+						assert false
+				end
+			| [] ->
+				raise Not_found
+		in
+		loop (if left then a.a_ops else List.filter (fun (_,cf) -> not (Meta.has Meta.Impl cf.cf_meta)) a.a_ops)
 	in
-	try (match follow e1.etype with
-		| TAbstract ({a_impl = Some c} as a,pl) ->
-			let f,t2,r,assign,_ = find_overload a pl c e2.etype true in
-			let e2 = Codegen.AbstractCast.check_cast ctx t2 e2 e2.epos in
-			begin match f.cf_expr with
-				| None ->
-					let e2 = match follow e2.etype with TAbstract(a,pl) -> {e2 with etype = apply_params a.a_params pl a.a_this} | _ -> e2 in
-					cast_rec {e1 with etype = apply_params a.a_params pl a.a_this} e2 r (Meta.has Meta.CoreType a.a_meta)
-				| Some _ ->
-					mk_cast_op c f a pl e1 e2 r assign
-			end
-		| _ ->
-			raise Not_found)
-	with Not_found -> try (match follow e2.etype with
-		| TAbstract ({a_impl = Some c} as a,pl) ->
-			let f,t2,r,assign,commutative = find_overload a pl c e1.etype false in
-			(* let e1,e2 = if commutative then  else e1,Codegen.AbstractCast.check_cast ctx t2 e2 e2.epos in *)
-			let e1,e2,init = if not commutative then
-				e1,Codegen.AbstractCast.check_cast ctx t2 e2 e2.epos,None
-			else if not (Optimizer.has_side_effect e1) && not (Optimizer.has_side_effect e2) then
-				e2,Codegen.AbstractCast.check_cast ctx t2 e1 e1.epos,None
-			else begin
-				let v1,v2 = gen_local ctx e1.etype, gen_local ctx e2.etype in
-				let mk_var v e =
-					mk (TVar(v,Some e)) ctx.t.tvoid e.epos,mk (TLocal v) e.etype e.epos
-				in
-				let v1 = mk_var v1 (Codegen.AbstractCast.check_cast ctx t2 e1 e1.epos) in
-				let v2 = mk_var v2 e2 in
-				snd v2,snd v1,Some(fst v1,fst v2)
-			end in
-			let e = match f.cf_expr with
-				| None ->
-					let e1 = match follow e1.etype with TAbstract(a,pl) -> {e1 with etype = apply_params a.a_params pl a.a_this} | _ -> e1 in
-					cast_rec e1 {e2 with etype = apply_params a.a_params pl a.a_this} r (Meta.has Meta.CoreType a.a_meta)
-				| Some _ ->
-					mk_cast_op c f a pl e1 e2 r assign
-			in
-			begin match init with
-				| None ->
-					e
-				| Some(e1,e2) ->
-					mk (TBlock [
-						e1;
-						e2;
-						e
-					]) e.etype e.epos
-			end
-		| _ ->
-			raise Not_found)
+	try
+		begin match follow e1.etype with
+			| TAbstract({a_impl = Some c} as a,tl) -> find_overload a c tl true
+			| _ -> raise Not_found
+		end
+	with Not_found -> try
+		begin match follow e2.etype with
+			| TAbstract({a_impl = Some c} as a,tl) -> find_overload a c tl false
+			| _ -> raise Not_found
+		end
 	with Not_found ->
 		make e1 e2
 
-
 and type_unop ctx op flag e p =
 	let set = (op = Increment || op = Decrement) in
 	let acc = type_access ctx (fst e) (snd e) (if set then MSet else MGet) in
@@ -2618,8 +2601,8 @@ and type_vars ctx vl p in_block =
 				| None -> None
 				| Some e ->
 					let e = type_expr ctx e (WithType t) in
-					unify ctx e.etype t p;
-					Some (Codegen.AbstractCast.check_cast ctx t e p)
+					let e = Codegen.AbstractCast.cast_or_unify ctx t e p in
+					Some e
 			) in
 			if v.[0] = '$' && ctx.com.display = DMNone then error "Variables names starting with a dollar are not allowed" p;
 			add_local ctx v t, e
@@ -2827,7 +2810,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 		| WithType t | WithTypeResume t ->
 			(match follow t with
 			| TAnon a when not (PMap.is_empty a.a_fields) -> Some a
-			| TAbstract (a,tl) when not (Meta.has Meta.CoreType a.a_meta) && List.exists (fun (_,cfo) -> cfo = None) a.a_from ->
+			| TAbstract (a,tl) when not (Meta.has Meta.CoreType a.a_meta) && a.a_from <> [] ->
 				begin match follow (Abstract.get_underlying_type a tl) with
 					| TAnon a when not (PMap.is_empty a.a_fields) -> Some a
 					| _ -> None
@@ -2861,8 +2844,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 				let e = try
 					let t = (PMap.find n a.a_fields).cf_type in
 					let e = type_expr ctx e (match with_type with WithTypeResume _ -> WithTypeResume t | _ -> WithType t) in
-					let e = Codegen.AbstractCast.check_cast ctx t e p in
-					unify ctx e.etype t e.epos;
+					let e = Codegen.AbstractCast.cast_or_unify ctx t e p in
 					(try type_eq EqStrict e.etype t; e with Unify_error _ -> mk (TCast (e,None)) t e.epos)
 				with Not_found ->
 					extra_fields := n :: !extra_fields;
@@ -3002,9 +2984,8 @@ and type_expr ctx (e,p) (with_type:with_type) =
 			let el = List.map (fun e ->
 				let e = type_expr ctx e (match with_type with WithTypeResume _ -> WithTypeResume t | _ -> WithType t) in
 				(match with_type with
-				| WithTypeResume _ -> (try unify_raise ctx e.etype t e.epos with Error (Unify l,p) -> raise (WithTypeError (l,p)))
-				| _ -> unify ctx e.etype t e.epos);
-				Codegen.AbstractCast.check_cast ctx t e p
+				| WithTypeResume _ -> (try Codegen.AbstractCast.cast_or_unify_raise ctx t e p with Error (Unify l,p) -> raise (WithTypeError (l,p)))
+				| _ -> Codegen.AbstractCast.cast_or_unify ctx t e p);
 			) el in
 			mk (TArrayDecl el) (ctx.t.tarray t) p)
 	| EVars vl ->
@@ -3032,8 +3013,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 					assert false
 				| _ ->
 					(try
-						unify_raise ctx e1.etype t e1.epos;
-						Codegen.AbstractCast.check_cast ctx t e1 p
+						Codegen.AbstractCast.cast_or_unify_raise ctx t e1 p
 					with Error (Unify _,_) ->
 						let acc = build_call ctx (type_field ctx e1 "iterator" e1.epos MCall) [] Value e1.epos in
 						try
@@ -3075,8 +3055,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 		type_expr ctx (EIf (e1,e2,Some e3),p) with_type
 	| EIf (e,e1,e2) ->
 		let e = type_expr ctx e Value in
-		unify ctx e.etype ctx.t.tbool e.epos;
-		let e = Codegen.AbstractCast.check_cast ctx ctx.t.tbool e p in
+		let e = Codegen.AbstractCast.cast_or_unify ctx ctx.t.tbool e p in
 		let e1 = type_expr ctx e1 with_type in
 		(match e2 with
 		| None ->
@@ -3089,22 +3068,21 @@ and type_expr ctx (e,p) (with_type:with_type) =
 				| WithType t | WithTypeResume t when (match follow t with TMono _ -> true | _ -> false) -> e1,e2,unify_min ctx [e1; e2]
 				| WithType t | WithTypeResume t ->
 					begin try
-						unify_raise ctx e1.etype t e1.epos;
-						unify_raise ctx e2.etype t e2.epos;
+					let e1 = Codegen.AbstractCast.cast_or_unify_raise ctx t e1 e1.epos in
+					let e2 = Codegen.AbstractCast.cast_or_unify_raise ctx t e2 e2.epos in
+					e1,e2,t
 					with Error (Unify l,p) -> match with_type with
 						| WithTypeResume _ -> raise (WithTypeError (l,p))
-						| _ -> display_error ctx (error_msg (Unify l)) p
+						| _ ->
+							display_error ctx (error_msg (Unify l)) p;
+							e1,e2,t
 					end;
-					let e1 = Codegen.AbstractCast.check_cast ctx t e1 e1.epos in
-					let e2 = Codegen.AbstractCast.check_cast ctx t e2 e2.epos in
-					e1,e2,t
 			in
 			mk (TIf (e,e1,Some e2)) t p)
 	| EWhile (cond,e,NormalWhile) ->
 		let old_loop = ctx.in_loop in
 		let cond = type_expr ctx cond Value in
-		unify ctx cond.etype ctx.t.tbool cond.epos;
-		let cond = Codegen.AbstractCast.check_cast ctx ctx.t.tbool cond p in
+		let cond = Codegen.AbstractCast.cast_or_unify ctx ctx.t.tbool cond p in
 		ctx.in_loop <- true;
 		let e = type_expr ctx e NoValue in
 		ctx.in_loop <- old_loop;
@@ -3115,8 +3093,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 		let e = type_expr ctx e NoValue in
 		ctx.in_loop <- old_loop;
 		let cond = type_expr ctx cond Value in
-		unify ctx cond.etype ctx.t.tbool cond.epos;
-		let cond = Codegen.AbstractCast.check_cast ctx ctx.t.tbool cond p in
+		let cond = Codegen.AbstractCast.cast_or_unify ctx ctx.t.tbool cond p in
 		mk (TWhile (cond,e,DoWhile)) ctx.t.tvoid p
 	| ESwitch (e1,cases,def) ->
 		begin try
@@ -3134,8 +3111,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 				None , v
 			| Some e ->
 				let e = type_expr ctx e (WithType ctx.ret) in
-				unify ctx e.etype ctx.ret e.epos;
-				let e = Codegen.AbstractCast.check_cast ctx ctx.ret e p in
+				let e = Codegen.AbstractCast.cast_or_unify ctx ctx.ret e p in
 				Some e , e.etype
 		) in
 		mk (TReturn e) t_dynamic p
@@ -3449,8 +3425,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 	| ECheckType (e,t) ->
 		let t = Typeload.load_complex_type ctx p t in
 		let e = type_expr ctx e (WithType t) in
-		let e = Codegen.AbstractCast.check_cast ctx t e p in
-		unify ctx e.etype t e.epos;
+		let e = Codegen.AbstractCast.cast_or_unify ctx t e p in
 		if e.etype == t then e else mk (TCast (e,None)) t p
 	| EMeta (m,e1) ->
 		let old = ctx.meta in
@@ -3823,16 +3798,16 @@ and build_call ctx acc el (with_type:with_type) p =
 		| _ ->
 			let t = follow (field_type ctx cl [] ef p) in
 			(* for abstracts we have to apply their parameters to the static function *)
-			let t,tthis,is_abstract_impl_call = match follow eparam.etype with
-				| TAbstract(a,tl) when Meta.has Meta.Impl ef.cf_meta -> apply_params a.a_params tl t,apply_params a.a_params tl a.a_this,true
-				| te -> t,te,false
+			let t,tthis = match follow eparam.etype with
+				| TAbstract(a,tl) when Meta.has Meta.Impl ef.cf_meta -> apply_params a.a_params tl t,apply_params a.a_params tl a.a_this
+				| te -> t,te
 			in
 			let params,args,r,eparam = match t with
 				| TFun ((_,_,t1) :: args,r) ->
 					unify ctx tthis t1 eparam.epos;
 					let ef = prepare_using_field ef in
 					begin match unify_call_args ctx el args r p (ef.cf_kind = Method MethInline) (is_forced_inline (Some cl) ef) with
-					| el,TFun(args,r) -> el,args,r,(if is_abstract_impl_call then eparam else Codegen.AbstractCast.check_cast ctx t1 eparam eparam.epos)
+					| el,TFun(args,r) -> el,args,r,eparam
 					| _ -> assert false
 					end
 				| _ -> assert false
@@ -4853,5 +4828,5 @@ let rec create com =
 unify_min_ref := unify_min;
 make_call_ref := make_call;
 get_constructor_ref := get_constructor;
-check_abstract_cast_ref := Codegen.AbstractCast.check_cast;
+cast_or_unify_ref := Codegen.AbstractCast.cast_or_unify_raise;
 type_module_type_ref := type_module_type;