Browse Source

Fix null coal assign typing (#12425)

* Fix null coal assign typing

* use follow_without_type
RblSb 1 week ago
parent
commit
54b083b0a3
3 changed files with 16 additions and 9 deletions
  1. 2 2
      src/typing/operators.ml
  2. 2 7
      src/typing/typer.ml
  3. 12 0
      tests/unit/src/unit/TestNullCoalescing.hx

+ 2 - 2
src/typing/operators.ml

@@ -811,8 +811,8 @@ let type_op_null_coal_assign ctx e1 e2 with_type p =
 				e1,None,ctx.t.tvoid
 			| _ ->
 				let e1 = vr#as_var "tmp" e1 in
-				(* The t2 is here so that `anything ??= 2` doesn't become Null<T> *)
-				e1,Some e1,t2
+				let t = if is_nullable t2 then e1.etype else follow_without_type e1.etype in
+				e1,Some e1,t
 		in
 		let e_null = Texpr.Builder.make_null e1.etype e1.epos in
 		let e_null = Texpr.Builder.binop OpEq e1 e_null ctx.t.tbool e1.epos in

+ 2 - 7
src/typing/typer.ml

@@ -1755,16 +1755,11 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) =
 		let e2 = type_expr ctx (Expr.ensure_block e2) (WithType.with_type e1.etype) 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
-			| _ -> t
-		in
 		let iftype = if DeadEnd.has_dead_end e2 then
-			follow_null e1.etype
+			follow_without_type e1.etype
 		else match e2.etype with
 			| TAbstract({a_path = [],"Null"},[t]) -> tmin
-			| _ -> follow_null tmin
+			| _ -> follow_without_type tmin
 		in
 		let e1_null_t = if is_nullable e1.etype then e1.etype else ctx.t.tnull e1.etype in
 		let var_name = match WithType.get_expected_name with_type with

+ 12 - 0
tests/unit/src/unit/TestNullCoalescing.hx

@@ -88,6 +88,11 @@ private abstract NullCoalAbstractResolve(NullCoalAbstractData) {
 	}
 }
 
+abstract Message(String) from String {
+	public function scream()
+		trace(this.toUpperCase());
+}
+
 @:nullSafety(StrictThreaded)
 class TestNullCoalescing extends Test {
 	final nullInt:Null<Int> = null;
@@ -213,7 +218,9 @@ class TestNullCoalescing extends Test {
 		a ??= 5;
 		eq(a, 5);
 		t(HelperMacros.isNullable(a ??= null));
+		t(HelperMacros.isNullable(a ??= a));
 		f(HelperMacros.isNullable(a ??= 5));
+		f(HelperMacros.isNullable(a ??= count));
 		var a:Null<Int> = null;
 		eq(a ??= 5, 5);
 		eq(a, 5);
@@ -329,6 +336,11 @@ class TestNullCoalescing extends Test {
 		eq("value", obj.field ?? "fail");
 		eq("value", value);
 
+		var m:Message = "foo";
+		(m ??= "bar").scream();
+		eq("unit.Message", HelperMacros.typeString(m ??= "bar"));
+		eq("foo", '$m');
+
 		// TODO: this fails at the moment with some "not enough arguments error"
 		// mutAssignLeft(obj.field) ??= "not value";
 		// eq(5, obj.getGetCounter());