瀏覽代碼

infer `Void` return type for arrow functions (closes #9181)

Aleksandr Kuzmenko 5 年之前
父節點
當前提交
93cd4bb90e
共有 4 個文件被更改,包括 35 次插入18 次删除
  1. 1 1
      src/typing/matcher.ml
  2. 1 1
      src/typing/typeloadFunction.ml
  3. 20 16
      src/typing/typer.ml
  4. 13 0
      tests/optimization/src/TestJs.hx

+ 1 - 1
src/typing/matcher.ml

@@ -1625,7 +1625,7 @@ module Match = struct
 		let tmono,with_type,allow_min_void = match with_type with
 			| WithType.WithType(t,src) ->
 				(match follow t, src with
-				| TMono _, Some ImplicitReturn -> Some t, WithType.Value src, true
+				| ((TMono _) | (TAbstract({a_path=[],"Void"},_))), Some ImplicitReturn -> Some t, WithType.Value src, true
 				| TMono _, _ -> Some t,WithType.value,false
 				| _ -> None,with_type,false)
 			| _ -> None,with_type,false

+ 1 - 1
src/typing/typeloadFunction.ml

@@ -82,7 +82,7 @@ let type_function_arg_value ctx t c do_display =
 			let rec loop e = match e.eexpr with
 				| TConst _ -> Some e
 				| TField({eexpr = TTypeExpr _},FEnum _) -> Some e
-                | TField({eexpr = TTypeExpr _},FStatic({cl_kind = KAbstractImpl a},cf)) when Meta.has Meta.Enum a.a_meta && Meta.has Meta.Enum cf.cf_meta -> Some e
+				| TField({eexpr = TTypeExpr _},FStatic({cl_kind = KAbstractImpl a},cf)) when Meta.has Meta.Enum a.a_meta && Meta.has Meta.Enum cf.cf_meta -> Some e
 				| TCast(e,None) -> loop e
 				| _ ->
 					if ctx.com.display.dms_kind = DMNone || ctx.com.display.dms_inline && ctx.com.display.dms_error_policy = EPCollect then

+ 20 - 16
src/typing/typer.ml

@@ -1949,8 +1949,9 @@ and type_local_function ctx kind f with_type p =
 					| _ -> ()
 				) args args2;
 				(* unify for top-down inference unless we are expecting Void *)
-				begin match follow tr,follow rt with
-					| TAbstract({a_path = [],"Void"},_),_ -> ()
+				begin
+					match follow tr,follow rt with
+					| TAbstract({a_path = [],"Void"},_),_ when kind <> FKArrow -> ()
 					| _,TMono _ -> unify ctx rt tr p
 					| _ -> ()
 				end
@@ -2133,21 +2134,24 @@ and type_return ?(implicit=false) ctx e with_type p =
 				else WithType.with_type ctx.ret
 			in
 			let e = type_expr ctx e with_expected_type in
-			let e = AbstractCast.cast_or_unify ctx ctx.ret e p in
-			begin match follow e.etype with
-			| TAbstract({a_path=[],"Void"},_) ->
-				begin match (Texpr.skip e).eexpr with
-				| TConst TNull -> error "Cannot return `null` from Void-function" p
-				| _ -> ()
-				end;
-				(* if we get a Void expression (e.g. from inlining) we don't want to return it (issue #4323) *)
-				mk (TBlock [
-					e;
-					mk (TReturn None) t_dynamic p
-				]) t_dynamic e.epos;
+			match follow ctx.ret with
+			| TAbstract({a_path=[],"Void"},_) when implicit ->
+				e
 			| _ ->
-				mk (TReturn (Some e)) t_dynamic p
-			end
+				let e = AbstractCast.cast_or_unify ctx ctx.ret e p in
+				match follow e.etype with
+				| TAbstract({a_path=[],"Void"},_) ->
+					begin match (Texpr.skip e).eexpr with
+					| TConst TNull -> error "Cannot return `null` from Void-function" p
+					| _ -> ()
+					end;
+					(* if we get a Void expression (e.g. from inlining) we don't want to return it (issue #4323) *)
+					mk (TBlock [
+						e;
+						mk (TReturn None) t_dynamic p
+					]) t_dynamic e.epos;
+				| _ ->
+					mk (TReturn (Some e)) t_dynamic p
 		with Error(err,p) ->
 			check_error ctx err p;
 			(* If we have a bad return, let's generate a return null expression at least. This surpresses various

+ 13 - 0
tests/optimization/src/TestJs.hx

@@ -611,6 +611,19 @@ class TestJs {
 		var a = processNullBool(null);
 		TestJs.use(a);
 	}
+
+	@:js('
+		var produce = function(producer) {return null;};
+		produce(function(obj) {obj.id = 2;});
+	')
+	static function testIssue9181_arrowFunction_infersVoidReturn() {
+		function produce<T>(producer: T -> Void): T {
+			return null;
+		}
+		var result = produce((obj) -> {
+			obj.id = 2;
+		});
+	}
 }
 
 extern class Extern {