Prechádzať zdrojové kódy

do not resolve abstract overloads on `null` expressions unless the argument type is `Null<T>` (closes #3376)

Simon Krajewski 10 rokov pred
rodič
commit
f1df1aa11e
2 zmenil súbory, kde vykonal 59 pridanie a 8 odobranie
  1. 45 0
      tests/unit/src/unit/issues/Issue3376.hx
  2. 14 8
      typer.ml

+ 45 - 0
tests/unit/src/unit/issues/Issue3376.hx

@@ -0,0 +1,45 @@
+package unit.issues;
+
+private abstract Vec3(Array<Float>) from Array<Float> to Array<Float> {
+	inline public function new(_x:Float, _y:Float, _z:Float):Vec3 {
+		this = [_x, _y, _z];
+	}
+
+	@:op(A == B)
+	public static function eq1(lhs:Vec3, rhs:Vec3):String {
+		return "eq1";
+	}
+
+	@:op(A == B)
+	public static function eq2(lhs:Vec3, rhs:Null<Vec3>):String {
+		return "eq2";
+	}
+
+	@:op(A == B)
+	public static function eq3(lhs:Null<Vec3>, rhs:Vec3):String {
+		return "eq3";
+	}
+
+	@:op(A != B)
+	public static function neq1(lhs:Vec3, rhs:Vec3):String {
+		return "neq1";
+	}
+
+	@:op(A != B)
+	public static function neq2(lhs:Vec3, rhs:Null<Vec3>):String {
+		return "neq2";
+	}
+}
+
+class Issue3376 extends Test {
+	function test() {
+		var vnull:Vec3 = null;
+		var v:Vec3 = new Vec3(1, 2, 3);
+		eq("eq1", v == v);
+		eq("eq2", vnull == null);
+		eq("eq3", null == vnull);
+		eq("neq1", v != v);
+		eq("neq2", vnull != null);
+		eq(false, null != vnull);
+	}
+}

+ 14 - 8
typer.ml

@@ -2203,14 +2203,10 @@ and type_binop2 ctx op (e1 : texpr) (e2 : Ast.expr) is_assign_op wt p =
 		in
 		(* 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,follow e2.etype with
-					| TMono _,_ | _,TMono _ ->
-						Type.unify e1.etype e2.etype
-					| _ ->
-						()
-				end
+		let is_eq_op = match op with OpEq | OpNotEq -> true | _ -> false in
+		if is_eq_op then begin match follow e1.etype,follow e2.etype with
+			| TMono _,_ | _,TMono _ ->
+				Type.unify e1.etype e2.etype
 			| _ ->
 				()
 		end;
@@ -2241,6 +2237,16 @@ and type_binop2 ctx op (e1 : texpr) (e2 : Ast.expr) is_assign_op wt p =
 								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;
+							let check_null e t = if is_eq_op then match e.eexpr with
+								| TConst TNull when not (is_explicit_null t) -> raise (Unify_error [])
+								| _ -> ()
+							in
+							(* If either expression is `null` we only allow operator resolving if the argument type
+							   is explicitly Null<T> (issue #3376) *)
+							if is_eq_op then begin
+								check_null e2 t2;
+								check_null e1 t1;
+							end;
 							let e = if not swapped then
 								make e1 e2
 							else if not (Optimizer.has_side_effect e1) && not (Optimizer.has_side_effect e2) then