ソースを参照

fix stringification of null values of abstract types with `toString` defined
fixes 6880; better fix for #8716

Aleksandr Kuzmenko 5 年 前
コミット
66a3522aea

+ 34 - 5
src/typing/calls.ml

@@ -119,11 +119,40 @@ let mk_array_set_call ctx (cf,tf,r,e1,e2o) c ebase p =
 			make_call ctx ef [ebase;e1;evalue] r p
 
 let call_to_string ctx ?(resume=false) e =
-	(* Ignore visibility of the toString field. *)
-	ctx.meta <- (Meta.PrivateAccess,[],e.epos) :: ctx.meta;
-	let acc = type_field (TypeFieldConfig.create resume) ctx e "toString" e.epos MCall in
-	ctx.meta <- List.tl ctx.meta;
-	!build_call_ref ctx acc [] (WithType.with_type ctx.t.tstring) e.epos
+	let gen_to_string e =
+		(* Ignore visibility of the toString field. *)
+		ctx.meta <- (Meta.PrivateAccess,[],e.epos) :: ctx.meta;
+		let acc = type_field (TypeFieldConfig.create resume) ctx e "toString" e.epos MCall in
+		ctx.meta <- List.tl ctx.meta;
+		!build_call_ref ctx acc [] (WithType.with_type ctx.t.tstring) e.epos
+	in
+	if ctx.com.config.pf_static && not (is_nullable e.etype) then
+		gen_to_string e
+	else begin (* generate `if(e == null) 'null' else e.toString()` *)
+		let rec needs_temp_var e =
+			match e.eexpr with
+			| TLocal _ | TTypeExpr _ | TConst _ -> false
+			| TField (e, _) | TParenthesis e -> needs_temp_var e
+			| _ -> true
+		in
+		let string_null = mk (TConst (TString "null")) ctx.t.tstring e.epos in
+		if needs_temp_var e then
+			let tmp = alloc_var VGenerated "tmp" e.etype e.epos in
+			let tmp_local = mk (TLocal tmp) tmp.v_type tmp.v_pos in
+			let check_null = mk (TBinop (OpEq, tmp_local, mk (TConst TNull) tmp.v_type tmp.v_pos)) ctx.t.tbool e.epos in
+			{
+				eexpr = TBlock([
+					mk (TVar (tmp, Some e)) tmp.v_type tmp.v_pos;
+					mk (TIf (check_null, string_null, Some (gen_to_string tmp_local))) ctx.t.tstring tmp.v_pos;
+
+				]);
+				etype = ctx.t.tstring;
+				epos = e.epos;
+			}
+		else
+			let check_null = mk (TBinop (OpEq, e, mk (TConst TNull) e.etype e.epos)) ctx.t.tbool e.epos in
+			mk (TIf (check_null, string_null, Some (gen_to_string e))) ctx.t.tstring e.epos
+	end
 
 let rec unify_call_args' ctx el args r callp inline force_inline =
 	let in_call_args = ctx.in_call_args in

+ 0 - 4
std/UInt.hx

@@ -299,11 +299,7 @@ abstract UInt(Int) from Int to Int {
 
 	// TODO: radix is just defined to deal with doc_gen issues
 	private inline function toString(?radix:Int):String {
-		#if static
 		return Std.string(toFloat());
-		#else
-		return Std.string(this == null ? null : toFloat());
-		#end
 	}
 
 	private inline function toInt():Int {

+ 19 - 0
tests/unit/src/unit/issues/Issue6880.hx

@@ -0,0 +1,19 @@
+package unit.issues;
+
+private enum abstract JsonTypeKind<T>(String) {
+	var TMono;
+}
+
+class Issue6880 extends unit.Test {
+	function test() {
+		var u:Null<AInt> = null;
+		eq('null', '$u');
+	}
+}
+
+private abstract AInt(Int) from Int {
+	public inline function toString() {
+		var result = this + 100;
+		return '$result';
+	}
+}

+ 8 - 1
tests/unit/src/unit/issues/Issue8716.hx

@@ -7,8 +7,15 @@ private enum abstract JsonTypeKind<T>(String) {
 class Issue8716 extends unit.Test {
 #if !static
 	function test() {
-		var u:UInt = null;
+		var u:AInt = null;
 		eq('null', '$u');
 	}
 #end
+}
+
+private abstract AInt(Int) from Int {
+	public inline function toString() {
+		var result = this + 100;
+		return '$result';
+	}
 }