فهرست منبع

[generic] also determine overload calls when mapping @:generic

see #7599
Simon Krajewski 5 سال پیش
والد
کامیت
adff6d36fb
5فایلهای تغییر یافته به همراه203 افزوده شده و 130 حذف شده
  1. 1 0
      src/context/typecore.ml
  2. 1 17
      src/optimization/inline.ml
  3. 21 112
      src/typing/callUnification.ml
  4. 116 1
      src/typing/generic.ml
  5. 64 0
      tests/unit/src/unit/issues/Issue7599.hx

+ 1 - 0
src/context/typecore.ml

@@ -203,6 +203,7 @@ let unify_min_ref : (typer -> texpr list -> t) ref = ref (fun _ _ -> die "" __LO
 let unify_min_for_type_source_ref : (typer -> texpr list -> WithType.with_type_source option -> t) ref = ref (fun _ _ _ -> die "" __LOC__)
 let analyzer_run_on_expr_ref : (Common.context -> texpr -> texpr) ref = ref (fun _ _ -> die "" __LOC__)
 let cast_or_unify_raise_ref : (typer -> ?uctx:unification_context option -> Type.t -> texpr -> pos -> texpr) ref = ref (fun _ ?uctx _ _ _ -> assert false)
+let type_generic_function_ref : (typer -> field_access -> texpr list -> expr list -> WithType.t -> pos -> texpr) ref = ref (fun _ _ _ _ _ _ -> assert false)
 
 let pass_name = function
 	| PBuildModule -> "build-module"

+ 1 - 17
src/optimization/inline.ml

@@ -620,23 +620,7 @@ class inline_state ctx ethis params cf f p = object(self)
 				else map_type
 			in
 			let e = Type.map_expr_type (map_expr_type map_type) map_type (map_var map_type) e in
-			match e.eexpr with
-			| TCall({eexpr = TField(e1,fa)} as ef,el) ->
-				let recall fh cf =
-					let fa = FieldAccess.create e1 cf fh false ef.epos in
-					let fcc = CallUnification.unify_field_call ctx fa el [] e.epos false in
-					fcc.fc_data()
-				in
-				begin match fa with
-				| FStatic(c,cf) when has_class_field_flag cf CfOverload ->
-					recall (FHStatic c) cf
-				| FInstance(c,tl,cf) when has_class_field_flag cf CfOverload ->
-					recall (FHInstance(c,tl)) cf
-				| _ ->
-					e
-				end
-			| _ ->
-				e
+			CallUnification.maybe_reapply_overload_call ctx e
 		in
 		let e = map_expr_type map_type e in
 		let rec drop_unused_vars e =

+ 21 - 112
src/typing/callUnification.ml

@@ -311,116 +311,6 @@ let unify_field_call ctx fa el_typed el p inline =
 			| fcc :: _ -> fcc
 		end
 
-let type_generic_function ctx fa el_typed el with_type p =
-	let c,stat = match fa.fa_host with
-		| FHInstance(c,tl) -> c,false
-		| FHStatic c -> c,true
-		| FHAbstract(a,tl,c) -> c,true
-		| _ -> die "" __LOC__
-	in
-	let cf = fa.fa_field in
-	if cf.cf_params = [] then error "Function has no type parameters and cannot be generic" p;
-	let fcc = unify_field_call ctx fa el_typed el p false in
-	begin match with_type with
-		| WithType.WithType(t,_) -> unify ctx fcc.fc_ret t p
-		| _ -> ()
-	end;
-	let monos = fcc.fc_monos in
-	List.iter (fun t -> match follow t with
-		| TMono m -> safe_mono_close ctx m p
-		| _ -> ()
-	) monos;
-	let el = List.map fst fcc.fc_args in
-	(try
-		let gctx = Generic.make_generic ctx cf.cf_params monos p in
-		let name = cf.cf_name ^ "_" ^ gctx.Generic.name in
-		let unify_existing_field tcf pcf = try
-			unify_raise ctx tcf fcc.fc_type p
-		with Error(Unify _,_) as err ->
-			display_error ctx ("Cannot create field " ^ name ^ " due to type mismatch") p;
-			display_error ctx (compl_msg "Conflicting field was defined here") pcf;
-			raise err
-		in
-		let fa = try
-			let cf2 = if stat then
-				let cf2 = PMap.find name c.cl_statics in
-				unify_existing_field cf2.cf_type cf2.cf_pos;
-				cf2
-			else
-				let cf2 = PMap.find name c.cl_fields in
-				unify_existing_field cf2.cf_type cf2.cf_pos;
-				cf2
-			in
-			{fa with fa_field = cf2}
-			(*
-				java.Lib.array() relies on the ability to shadow @:generic function for certain types
-				see https://github.com/HaxeFoundation/haxe/issues/8393#issuecomment-508685760
-			*)
-			(* if cf.cf_name_pos = cf2.cf_name_pos then
-				cf2
-			else
-				error ("Cannot specialize @:generic because the generated function name is already used: " ^ name) p *)
-		with Not_found ->
-			let finalize_field c cf2 =
-				ignore(follow cf.cf_type);
-				let rec check e = match e.eexpr with
-					| TNew({cl_kind = KTypeParameter _} as c,_,_) when not (TypeloadCheck.is_generic_parameter ctx c) ->
-						display_error ctx "Only generic type parameters can be constructed" e.epos;
-						display_error ctx "While specializing this call" p;
-					| _ ->
-						Type.iter check e
-				in
-				cf2.cf_expr <- (match cf.cf_expr with
-					| None ->
-						display_error ctx "Recursive @:generic function" p; None;
-					| Some e ->
-						let e = Generic.generic_substitute_expr gctx e in
-						check e;
-						Some e
-				);
-				cf2.cf_kind <- cf.cf_kind;
-				if not (has_class_field_flag cf CfPublic) then remove_class_field_flag cf2 CfPublic;
-				cf2.cf_meta <- (Meta.NoCompletion,[],p) :: (Meta.NoUsing,[],p) :: (Meta.GenericInstance,[],p) :: cf.cf_meta
-			in
-			let mk_cf2 name =
-				mk_field ~static:stat name fcc.fc_type cf.cf_pos cf.cf_name_pos
-			in
-			if stat then begin
-				if Meta.has Meta.GenericClassPerMethod c.cl_meta then begin
-					let c = Generic.static_method_container gctx c cf p in
-					let cf2 = try
-						let cf2 = PMap.find cf.cf_name c.cl_statics in
-						unify_existing_field cf2.cf_type cf2.cf_pos;
-						cf2
-					with Not_found ->
-						let cf2 = mk_cf2 cf.cf_name in
-						c.cl_statics <- PMap.add cf2.cf_name cf2 c.cl_statics;
-						c.cl_ordered_statics <- cf2 :: c.cl_ordered_statics;
-						finalize_field c cf2;
-						cf2
-					in
-					{fa with fa_host = FHStatic c;fa_field = cf2;fa_on = Builder.make_static_this c p}
-				end else begin
-					let cf2 = mk_cf2 name in
-					c.cl_statics <- PMap.add cf2.cf_name cf2 c.cl_statics;
-					c.cl_ordered_statics <- cf2 :: c.cl_ordered_statics;
-					finalize_field c cf2;
-					{fa with fa_field = cf2}
-				end
-			end else begin
-				let cf2 = mk_cf2 name in
-				if has_class_field_flag cf CfOverride then add_class_field_flag cf2 CfOverride;
-				c.cl_fields <- PMap.add cf2.cf_name cf2 c.cl_fields;
-				c.cl_ordered_fields <- cf2 :: c.cl_ordered_fields;
-				finalize_field c cf2;
-				{fa with fa_field = cf2}
-			end
-		in
-		let e = FieldAccess.get_field_expr fa FCall in
-		make_call ctx e el fcc.fc_ret p
-	with Generic.Generic_Exception (msg,p) ->
-		error msg p)
-
 class call_dispatcher
 	(ctx : typer)
 	(mode : access_mode)
@@ -568,7 +458,7 @@ object(self)
 		| Method (MethNormal | MethInline | MethDynamic) ->
 			check_assign();
 			 if has_class_field_flag fa.fa_field CfGeneric then begin
-				type_generic_function ctx fa el_typed el with_type p
+				!type_generic_function_ref ctx fa el_typed el with_type p
 			end else
 				self#make_field_call fa el_typed el
 		| Method MethMacro ->
@@ -589,4 +479,23 @@ object(self)
 			| _ ->
 				self#expr_call (FieldAccess.get_field_expr fa FCall) el
 			end
-end
+end
+
+let maybe_reapply_overload_call ctx e =
+	match e.eexpr with
+		| TCall({eexpr = TField(e1,fa)} as ef,el) ->
+			let recall fh cf =
+				let fa = FieldAccess.create e1 cf fh false ef.epos in
+				let fcc = unify_field_call ctx fa el [] e.epos false in
+				fcc.fc_data()
+			in
+			begin match fa with
+			| FStatic(c,cf) when has_class_field_flag cf CfOverload ->
+				recall (FHStatic c) cf
+			| FInstance(c,tl,cf) when has_class_field_flag cf CfOverload ->
+				recall (FHInstance(c,tl)) cf
+			| _ ->
+				e
+			end
+		| _ ->
+			e

+ 116 - 1
src/typing/generic.ml

@@ -99,7 +99,7 @@ let generic_substitute_expr gctx e =
 			v2
 	in
 	let rec build_expr e =
-		match e.eexpr with
+		let e = match e.eexpr with
 		| TField(e1, FInstance({cl_kind = KGeneric} as c,tl,cf)) ->
 			let _, _, f = gctx.ctx.g.do_build_instance gctx.ctx (TClassDecl c) gctx.p in
 			let t = f (List.map (generic_substitute_type gctx) tl) in
@@ -135,6 +135,8 @@ let generic_substitute_expr gctx e =
 			end
 		| _ ->
 			map_expr_type build_expr (generic_substitute_type gctx) build_var e
+		in
+		CallUnification.maybe_reapply_overload_call gctx.ctx e
 	in
 	build_expr e
 
@@ -338,3 +340,116 @@ let rec build_generic ctx c p tl =
 		end;
 		TInst (cg,[])
 	end
+
+let type_generic_function ctx fa el_typed el with_type p =
+	let c,stat = match fa.fa_host with
+		| FHInstance(c,tl) -> c,false
+		| FHStatic c -> c,true
+		| FHAbstract(a,tl,c) -> c,true
+		| _ -> die "" __LOC__
+	in
+	let cf = fa.fa_field in
+	if cf.cf_params = [] then error "Function has no type parameters and cannot be generic" p;
+	let fcc = CallUnification.unify_field_call ctx fa el_typed el p false in
+	begin match with_type with
+		| WithType.WithType(t,_) -> unify ctx fcc.fc_ret t p
+		| _ -> ()
+	end;
+	let monos = fcc.fc_monos in
+	List.iter (fun t -> match follow t with
+		| TMono m -> safe_mono_close ctx m p
+		| _ -> ()
+	) monos;
+	let el = List.map fst fcc.fc_args in
+	(try
+		let gctx = make_generic ctx cf.cf_params monos p in
+		let name = cf.cf_name ^ "_" ^ gctx.name in
+		let unify_existing_field tcf pcf = try
+			unify_raise ctx tcf fcc.fc_type p
+		with Error(Unify _,_) as err ->
+			display_error ctx ("Cannot create field " ^ name ^ " due to type mismatch") p;
+			display_error ctx (compl_msg "Conflicting field was defined here") pcf;
+			raise err
+		in
+		let fa = try
+			let cf2 = if stat then
+				let cf2 = PMap.find name c.cl_statics in
+				unify_existing_field cf2.cf_type cf2.cf_pos;
+				cf2
+			else
+				let cf2 = PMap.find name c.cl_fields in
+				unify_existing_field cf2.cf_type cf2.cf_pos;
+				cf2
+			in
+			{fa with fa_field = cf2}
+			(*
+				java.Lib.array() relies on the ability to shadow @:generic function for certain types
+				see https://github.com/HaxeFoundation/haxe/issues/8393#issuecomment-508685760
+			*)
+			(* if cf.cf_name_pos = cf2.cf_name_pos then
+				cf2
+			else
+				error ("Cannot specialize @:generic because the generated function name is already used: " ^ name) p *)
+		with Not_found ->
+			let finalize_field c cf2 =
+				ignore(follow cf.cf_type);
+				let rec check e = match e.eexpr with
+					| TNew({cl_kind = KTypeParameter _} as c,_,_) when not (TypeloadCheck.is_generic_parameter ctx c) ->
+						display_error ctx "Only generic type parameters can be constructed" e.epos;
+						display_error ctx "While specializing this call" p;
+					| _ ->
+						Type.iter check e
+				in
+				cf2.cf_expr <- (match cf.cf_expr with
+					| None ->
+						display_error ctx "Recursive @:generic function" p; None;
+					| Some e ->
+						let e = generic_substitute_expr gctx e in
+						check e;
+						Some e
+				);
+				cf2.cf_kind <- cf.cf_kind;
+				if not (has_class_field_flag cf CfPublic) then remove_class_field_flag cf2 CfPublic;
+				cf2.cf_meta <- (Meta.NoCompletion,[],p) :: (Meta.NoUsing,[],p) :: (Meta.GenericInstance,[],p) :: cf.cf_meta
+			in
+			let mk_cf2 name =
+				mk_field ~static:stat name fcc.fc_type cf.cf_pos cf.cf_name_pos
+			in
+			if stat then begin
+				if Meta.has Meta.GenericClassPerMethod c.cl_meta then begin
+					let c = static_method_container gctx c cf p in
+					let cf2 = try
+						let cf2 = PMap.find cf.cf_name c.cl_statics in
+						unify_existing_field cf2.cf_type cf2.cf_pos;
+						cf2
+					with Not_found ->
+						let cf2 = mk_cf2 cf.cf_name in
+						c.cl_statics <- PMap.add cf2.cf_name cf2 c.cl_statics;
+						c.cl_ordered_statics <- cf2 :: c.cl_ordered_statics;
+						finalize_field c cf2;
+						cf2
+					in
+					{fa with fa_host = FHStatic c;fa_field = cf2;fa_on = Builder.make_static_this c p}
+				end else begin
+					let cf2 = mk_cf2 name in
+					c.cl_statics <- PMap.add cf2.cf_name cf2 c.cl_statics;
+					c.cl_ordered_statics <- cf2 :: c.cl_ordered_statics;
+					finalize_field c cf2;
+					{fa with fa_field = cf2}
+				end
+			end else begin
+				let cf2 = mk_cf2 name in
+				if has_class_field_flag cf CfOverride then add_class_field_flag cf2 CfOverride;
+				c.cl_fields <- PMap.add cf2.cf_name cf2 c.cl_fields;
+				c.cl_ordered_fields <- cf2 :: c.cl_ordered_fields;
+				finalize_field c cf2;
+				{fa with fa_field = cf2}
+			end
+		in
+		let e = FieldAccess.get_field_expr fa FCall in
+		make_call ctx e el fcc.fc_ret p
+	with Generic_Exception (msg,p) ->
+		error msg p)
+
+;;
+Typecore.type_generic_function_ref := type_generic_function

+ 64 - 0
tests/unit/src/unit/issues/Issue7599.hx

@@ -0,0 +1,64 @@
+package unit.issues;
+
+#if (java || cs)
+
+@:keep
+private class Overloader {
+	public function new() {
+
+	}
+
+	public function toString() {
+		return "Overloader!";
+	}
+
+	@:generic static public function genericMe<T>(t:T) {
+		return test(t);
+	}
+
+	@:generic static public function genericMeMember<T>(m:Overloader, t:T) {
+		return m.testMember(t);
+	}
+
+	overload static public function test(d:Dynamic) {
+		return "Dynamic: " + d;
+	}
+
+	overload static public function test(i:Int) {
+		return "Int: " + i;
+	}
+
+	overload static public function test(s:String) {
+		return "String: " + s;
+	}
+
+	overload public function testMember(d:Dynamic) {
+		return "Dynamic: " + d;
+	}
+
+	overload public function testMember(i:Int) {
+		return "Int: " + i;
+	}
+
+	overload public function testMember(s:String) {
+		return "String: " + s;
+	}
+}
+
+#end
+
+class Issue7599 extends unit.Test {
+	#if (java || cs)
+
+	function testGeneric() {
+		var overloader = new Overloader();
+		eq("String: foo", Overloader.genericMe("foo"));
+		eq("Int: 12", Overloader.genericMe(12));
+		eq("Dynamic: Overloader!", Overloader.genericMe(overloader));
+		eq("String: foo", Overloader.genericMeMember(overloader, "foo"));
+		eq("Int: 12", Overloader.genericMeMember(overloader, 12));
+		eq("Dynamic: Overloader!", Overloader.genericMeMember(overloader, overloader));
+	}
+
+	#end
+}