瀏覽代碼

[abstracts] add support for constructor forwarding (#9735)

Dmitrii Maganov 5 年之前
父節點
當前提交
63077e3145

+ 6 - 0
src-json/meta.json

@@ -394,6 +394,12 @@
 		"targets": ["TAbstract"],
 		"links": ["https://haxe.org/manual/types-abstract-forward.html"]
 	},
+	{
+		"name": "ForwardNew",
+		"metadata": ":forward.new",
+		"doc": "Forwards constructor call to underlying type.",
+		"targets": ["TAbstract"]
+	},
 	{
 		"name": "ForwardStatics",
 		"metadata": ":forwardStatics",

+ 13 - 0
src/core/abstract.ml

@@ -111,6 +111,19 @@ let rec get_underlying_type ?(return_first=false) a pl =
 		else
 			maybe_recurse (apply_params a.a_params pl a.a_this)
 
+let rec follow_with_forward_ctor ?(build=false) t = match follow t with
+	| TAbstract(a,tl) as t ->
+		if build then build_abstract a;
+		if Meta.has Meta.ForwardNew a.a_meta && not (match a.a_impl with
+			| Some c -> PMap.mem "_new" c.cl_statics
+			| None -> false
+		) then
+			follow_with_forward_ctor (get_underlying_type ~return_first:true a tl)
+		else
+			t
+	| t ->
+		t
+
 let rec follow_with_abstracts t = match follow t with
 	| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
 		follow_with_abstracts (get_underlying_type a tl)

+ 10 - 3
src/core/display/completionItem.ml

@@ -193,7 +193,7 @@ module CompletionModuleType = struct
 			with Not_found ->
 				No
 		in
-		let is_extern,is_final,kind,ctor = match mt with
+		let rec ctor_info = function
 			| TClassDecl c ->
 				(has_class_flag c CExtern),has_class_flag c CFinal,(if (has_class_flag c CInterface) then Interface else Class),ctor c
 			| TEnumDecl en ->
@@ -202,13 +202,20 @@ module CompletionModuleType = struct
 				let kind,ctor = match follow td.t_type with
 					| TAnon _ -> Struct,No
 					| TInst(c,_) -> TypeAlias,ctor c
-					| TAbstract(a,_) -> TypeAlias,actor a
+					| TAbstract(a,_) -> let _,_,_,ctor = ctor_info (TAbstractDecl a) in TypeAlias,ctor
 					| _ -> TypeAlias,No
 				in
 				false,false,kind,ctor
 			| TAbstractDecl a ->
-				false,false,(if a.a_enum then EnumAbstract else Abstract),actor a
+				let kind = if a.a_enum then EnumAbstract else Abstract in
+				let is_extern,is_final,ctor = match Abstract.follow_with_forward_ctor (TAbstract(a,List.map snd a.a_params)) with
+					| TInst(c,_) -> let is_extern,is_final,_,ctor = ctor_info (TClassDecl c) in is_extern,is_final,ctor
+					| TAbstract(a,_) -> false,false,actor a
+					| _ -> false,false,No
+				in
+				is_extern,is_final,kind,ctor
 		in
+		let is_extern,is_final,kind,ctor = ctor_info mt in
 		let infos = t_infos mt in
 		let convert_type_param (s,t) = match follow t with
 			| TInst(c,_) -> {

+ 20 - 8
src/typing/fieldAccess.ml

@@ -123,14 +123,26 @@ let resolve_accessor fa mode = match fa.fa_field.cf_kind with
 	| _ ->
 		AccessorInvalid
 
-let get_constructor_access c params p =
-	match c.cl_kind with
-	| KAbstractImpl a ->
-		let cf = (try PMap.find "_new" c.cl_statics with Not_found -> raise_error (No_constructor (TAbstractDecl a)) p) in
-		create (Builder.make_static_this c p) cf (FHAbstract(a,params,c)) false p
-	| _ ->
-		let cf = (try Type.get_constructor c with Not_found -> raise_error (No_constructor (TClassDecl c)) p) in
-		create (Builder.make_static_this c p) cf (FHInstance(c,params)) false p
+let get_constructor_access c tl p =
+	try
+		let e_static = Builder.make_static_this c p in
+		let c, tl = match c.cl_kind with
+			| KAbstractImpl a -> (match Abstract.follow_with_forward_ctor (TAbstract(a,tl)) with
+				| TInst(c,tl) -> c, tl
+				| TAbstract({a_impl = Some c},tl) -> c, tl
+				| _ -> c, tl)
+			| _ -> c, tl
+		in
+		let cf, fh = match c.cl_kind with
+			| KAbstractImpl a -> PMap.find "_new" c.cl_statics, FHAbstract(a,tl,c)
+			| _ -> Type.get_constructor c, FHInstance(c,tl)
+		in
+		create e_static cf fh false p
+	with Not_found ->
+		raise_error (No_constructor (match c.cl_kind with
+			| KAbstractImpl a -> TAbstractDecl a
+			| _ -> TClassDecl c
+		)) p
 
 let make_static_extension_access c cf e_this inline p =
 	let e_static = Texpr.Builder.make_static_this c p in

+ 2 - 2
src/typing/typer.ml

@@ -992,7 +992,7 @@ and type_new ctx path el with_type force_inline p =
 		end;
 		unify_constructor_call c fa
 	in
-	try begin match t with
+	try begin match Abstract.follow_with_forward_ctor t with
 	| TInst ({cl_kind = KTypeParameter tl} as c,params) ->
 		if not (TypeloadCheck.is_generic_parameter ctx c) then error "Only generic type parameters can be constructed" p;
  		begin match get_constructible_constraint ctx tl p with
@@ -1004,7 +1004,7 @@ and type_new ctx path el with_type force_inline p =
 		end
 	| TAbstract({a_impl = Some c} as a,tl) when not (Meta.has Meta.MultiType a.a_meta) ->
 		let fcc = build_constructor_call (Some a) c tl in
-		fcc.fc_data ();
+		{ (fcc.fc_data()) with etype = t }
 	| TInst (c,params) | TAbstract({a_impl = Some c},params) ->
 		let fcc = build_constructor_call None c params in
 		let el = List.map fst fcc.fc_args in

+ 6 - 5
src/typing/typerDisplay.ml

@@ -272,7 +272,7 @@ let rec handle_signature_display ctx e_ast with_type =
 			in
 			handle_call tl el e1.epos
 		| ENew(tpath,el) ->
-			let t = Typeload.load_instance ctx tpath true in
+			let t = Abstract.follow_with_forward_ctor (Typeload.load_instance ctx tpath true) in
 			handle_call (find_constructor_types t) el (pos tpath)
 		| EArray(e1,e2) ->
 			let e1 = type_expr ctx e1 WithType.value in
@@ -596,10 +596,11 @@ let handle_display ctx e_ast dk mode with_type =
 					begin try
 						let mt = ctx.g.do_load_type_def ctx null_pos {tpackage=mt.pack;tname=mt.module_name;tsub=Some mt.name;tparams=[]} in
 						begin match resolve_typedef mt with
-						| TClassDecl c when has_constructor c -> true
-						| TAbstractDecl {a_impl = Some c} ->
-							ignore(c.cl_build());
-							PMap.mem "_new" c.cl_statics
+						| TClassDecl c -> has_constructor c
+						| TAbstractDecl a -> (match Abstract.follow_with_forward_ctor ~build:true (TAbstract(a,List.map snd a.a_params)) with
+							| TInst(c,_) -> has_constructor c
+							| TAbstract({a_impl = Some c},_) -> PMap.mem "_new" c.cl_statics
+							| _ -> false)
 						| _ -> false
 						end
 					with _ ->

+ 26 - 0
tests/unit/src/unit/issues/Issue9735.hx

@@ -0,0 +1,26 @@
+package unit.issues;
+
+class Issue9735 extends Test {
+  function test() {
+    var array = new Foo<Array<Int>>();
+    eq(0, array.foo());
+
+    var regexp = new Foo<EReg>("ereg", "");
+    eq(0, regexp.foo());
+
+    var string = new Foo<Foo<String>>("string");
+    eq("string", string.t().t());
+
+    var bar = new Foo<Bar>(2);
+    eq(4, bar.t());
+  }
+}
+
+@:forward.new private abstract Foo<T>(T) to T {
+  public function foo() return 0;
+  public function t() return this;
+}
+
+private abstract Bar(Int) to Int {
+  public inline function new(int: Int) this = int * int;
+}