Ver código fonte

don't cast null coal operands before null-checking

closes #11706
Simon Krajewski 1 ano atrás
pai
commit
861a199a35
2 arquivos alterados com 51 adições e 9 exclusões
  1. 14 9
      src/typing/typer.ml
  2. 37 0
      tests/unit/src/unit/issues/Issue11706.hx

+ 14 - 9
src/typing/typer.ml

@@ -1563,17 +1563,21 @@ and type_cast ctx e t p =
 	mk (TCast (type_expr ctx e WithType.value,Some texpr)) t p
 
 and get_if_then_else_operands ctx e1 e2 with_type = match with_type with
-	| WithType.NoValue -> e1,e2,ctx.t.tvoid
-	| WithType.Value _ -> e1,e2,unify_min ctx [e1; e2]
+	| WithType.NoValue ->
+		ctx.t.tvoid,(fun e -> e)
+	| WithType.Value _ ->
+		unify_min ctx [e1; e2],(fun e -> e)
 	| WithType.WithType(t,src) when (match follow t with TMono _ -> true | t -> ExtType.is_void t) ->
-		e1,e2,unify_min_for_type_source ctx [e1; e2] src
+		unify_min_for_type_source ctx [e1; e2] src,(fun e -> e)
 	| WithType.WithType(t,_) ->
-		let e1 = AbstractCast.cast_or_unify ctx t e1 e1.epos in
-		let e2 = AbstractCast.cast_or_unify ctx t e2 e2.epos in
-		e1,e2,t
+		t,(fun e ->
+			AbstractCast.cast_or_unify ctx t e e.epos
+		)
 
 and make_if_then_else ctx e0 e1 e2 with_type p =
-	let e1,e2,t = get_if_then_else_operands ctx e1 e2 with_type in
+	let t,cast = get_if_then_else_operands ctx e1 e2 with_type in
+	let e1 = cast e1 in
+	let e2 = cast e2 in
 	mk (TIf (e0,e1,Some e2)) t p
 
 and type_if ctx e e1 e2 with_type is_ternary p =
@@ -1854,7 +1858,8 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) =
 		let vr = new value_reference ctx in
 		let e1 = type_expr ctx (Expr.ensure_block e1) with_type in
 		let e2 = type_expr ctx (Expr.ensure_block e2) (WithType.with_type e1.etype) in
-		let e1,e2,tmin = get_if_then_else_operands ctx e1 e2 with_type in
+		let tmin,cast = get_if_then_else_operands ctx e1 e2 with_type in
+		let e2 = cast e2 in
 		let rec follow_null t =
 			match t with
 			| TAbstract({a_path = [],"Null"},[t]) -> follow_null t
@@ -1869,7 +1874,7 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) =
 		let e1 = vr#as_var "tmp" {e1 with etype = ctx.t.tnull tmin} in
 		let e_null = Builder.make_null e1.etype e1.epos in
 		let e_cond = mk (TBinop(OpNotEq,e1,e_null)) ctx.t.tbool e1.epos in
-		let e_if = mk (TIf(e_cond,e1,Some e2)) iftype p in
+		let e_if = mk (TIf(e_cond,cast e1,Some e2)) iftype p in
 		vr#to_texpr e_if
 	| EBinop (OpAssignOp OpNullCoal,e1,e2) ->
 		let e_cond = EBinop(OpNotEq,e1,(EConst(Ident "null"), p)) in

+ 37 - 0
tests/unit/src/unit/issues/Issue11706.hx

@@ -0,0 +1,37 @@
+package unit.issues;
+
+import utest.Assert;
+import utest.UTest;
+
+@:callable
+private abstract EndFun(Void->Void) {
+	@:from public static function fromFun(f:Void->Void):EndFun {
+		return cast function() {
+			f();
+			f = () -> throw "double end";
+		};
+	}
+}
+
+private class C {
+	var onEnd:Void->Void = null;
+
+	public function new() {}
+
+	public function call(f:EndFun) {
+		f();
+	}
+
+	static public function run() {
+		var m = new C();
+		m.call(m.onEnd == null ? function() {} : m.onEnd);
+		m.call(m.onEnd ?? function() {});
+	}
+}
+
+class Issue11706 extends Test {
+	function test() {
+		C.run();
+		Assert.pass();
+	}
+}