Prechádzať zdrojové kódy

generate constructor of `@:structInit` classes (closes #4526)

Simon Krajewski 9 rokov pred
rodič
commit
050257f6ae
2 zmenil súbory, kde vykonal 100 pridanie a 0 odobranie
  1. 68 0
      tests/unit/src/unit/issues/Issue4526.hx
  2. 32 0
      typeload.ml

+ 68 - 0
tests/unit/src/unit/issues/Issue4526.hx

@@ -0,0 +1,68 @@
+package unit.issues;
+
+@:structInit
+private class Struct1 {
+	var x:Int;
+	var y:Int;
+	public function get() {
+		return x + " " + y;
+	}
+}
+
+@:structInit
+private class Struct2 {
+	@:optional var x:Int;
+	var y:Int;
+	public function get() {
+		return x + " " + y;
+	}
+}
+
+@:structInit
+private class Struct3 {
+	@:optional var x:Int;
+	@:optional var y:Int;
+	public function get() {
+		return x + " " + y;
+	}
+}
+
+@:structInit
+private class Struct4 {
+	var x:Int;
+	var y:Int;
+	var z:Int;
+	public function get() {
+		return x + " " + y + " " + z;
+	}
+}
+
+class Issue4526 extends Test {
+	function test() {
+		var fieldNull = #if (cpp || flash || java || cs) 0 #else null #end;
+
+		var s1:Struct1 = { x: 12, y: 13 };
+		eq("12 13", s1.get());
+		t(unit.TestType.typeError(({x:12} : Struct1)));
+		t(unit.TestType.typeError(({y:12} : Struct1)));
+
+		var s2:Struct2 = { x: 12, y: 13 };
+		eq("12 13", s2.get());
+		var s2:Struct2 = { y: 13 };
+		eq(fieldNull + " 13", s2.get());
+		t(unit.TestType.typeError(({x:12} : Struct2)));
+
+		var s3:Struct3 = { x: 12, y: 13 };
+		eq("12 13", s3.get());
+		var s3:Struct3 = { y: 13 };
+		eq(fieldNull + " 13", s3.get());
+		var s3:Struct3 = { };
+		eq(fieldNull + " " + fieldNull, s3.get());
+	}
+
+	function testOrder() {
+		var i = 0;
+		var s4:Struct4 = { y: i++, x: i++, z: i++ };
+		eq("1 0 2", s4.get());
+	}
+}

+ 32 - 0
typeload.ml

@@ -1343,6 +1343,37 @@ let add_constructor ctx c force_constructor p =
 		(* nothing to do *)
 		()
 
+let check_struct_init_constructor ctx c p = match c.cl_constructor with
+	| Some _ ->
+		()
+	| None ->
+		let params = List.map snd c.cl_params in
+		let ethis = mk (TConst TThis) (TInst(c,params)) p in
+		let args,el,tl = List.fold_left (fun (args,el,tl) cf -> match cf.cf_kind with
+			| Var _ ->
+				let opt = Meta.has Meta.Optional cf.cf_meta in
+				let t = if opt then ctx.t.tnull cf.cf_type else cf.cf_type in
+				let v = alloc_var cf.cf_name t in
+				let ef = mk (TField(ethis,FInstance(c,params,cf))) t p in
+				let ev = mk (TLocal v) v.v_type p in
+				let e = mk (TBinop(OpAssign,ef,ev)) ev.etype p in
+				(v,None) :: args,e :: el,(cf.cf_name,opt,t) :: tl
+			| Method _ ->
+				args,el,tl
+		) ([],[],[]) (List.rev c.cl_ordered_fields) in
+		let tf = {
+			tf_args = args;
+			tf_type = ctx.t.tvoid;
+			tf_expr = mk (TBlock el) ctx.t.tvoid p
+		} in
+		let e = mk (TFunction tf) (TFun(tl,ctx.t.tvoid)) p in
+		let cf = mk_field "new" e.etype p in
+		cf.cf_expr <- Some e;
+		cf.cf_type <- e.etype;
+		cf.cf_meta <- [Meta.CompilerGenerated,[],p];
+		cf.cf_kind <- Method MethNormal;
+		c.cl_constructor <- Some cf
+
 let set_heritance ctx c herits p =
 	let is_lib = Meta.has Meta.LibType c.cl_meta in
 	let ctx = { ctx with curclass = c; type_params = c.cl_params; } in
@@ -2752,6 +2783,7 @@ module ClassInitializer = struct
 		*)
 		(* add_constructor does not deal with overloads correctly *)
 		if not ctx.com.config.pf_overload then add_constructor ctx c cctx.force_constructor p;
+		if Meta.has Meta.StructInit c.cl_meta then check_struct_init_constructor ctx c p;
 		(* check overloaded constructors *)
 		(if ctx.com.config.pf_overload && not cctx.is_lib then match c.cl_constructor with
 		| Some ctor ->