Procházet zdrojové kódy

Merge pull request #3183 from Simn/get_constructor_arguments

add Context.get_constructor_arguments for use in @:genericBuild macros
Simon Krajewski před 11 roky
rodič
revize
d367b2bfdd

+ 6 - 0
interp.ml

@@ -110,6 +110,7 @@ type extern_api = {
 	set_js_generator : (value -> unit) -> unit;
 	get_local_type : unit -> t option;
 	get_expected_type : unit -> t option;
+	get_constructor_arguments : unit -> Ast.expr list option;
 	get_local_method : unit -> string;
 	get_local_using : unit -> tclass list;
 	get_local_vars : unit -> (string, Type.tvar) PMap.t;
@@ -2437,6 +2438,11 @@ let macro_lib =
 			| None -> VNull
 			| Some t -> encode_type t
 		);
+		"constructor_arguments", Fun0 (fun() ->
+			match (get_ctx()).curapi.get_constructor_arguments() with
+			| None -> VNull
+			| Some el -> enc_array (List.map encode_expr el)
+		);
 		"local_method", Fun0 (fun() ->
 			VString ((get_ctx()).curapi.get_local_method())
 		);

+ 12 - 0
std/haxe/macro/Context.hx

@@ -113,6 +113,18 @@ class Context {
 		return l;
 	}
 
+	/**
+		Returns the constructor arguments that are used to construct the
+		current `@:genericBuild` class, if available.
+
+		Returns `null` if the current macro is not a build-macro which was
+		called from constructing a `@:genericBuild` instance.
+	**/
+	@:require(haxe_ver >= 3.2)
+	public static function getConstructorArguments():Null<Array<Expr>> {
+		return load("constructor_arguments", 0)();
+	}
+
 	/**
 		Returns the current class in which the macro was called.
 

+ 23 - 0
tests/unit/issues/Issue3183.hx

@@ -0,0 +1,23 @@
+package unit.issues;
+
+@:genericBuild(unit.issues.misc.Issue3183Macro.buildTuple())
+class MyTuple<Rest> { }
+
+class Issue3183 extends Test {
+	function test() {
+		var t = new MyTuple<String, Int>("foo", 12);
+		unit.TestType.typedAs(t, (null : MyTuple<String, Int>));
+		eq("foo", t.v0);
+		eq(12, t.v1);
+
+		var t = new MyTuple("foo", 12);
+		unit.TestType.typedAs(t, (null : MyTuple<String, Int>));
+		eq("foo", t.v0);
+		eq(12, t.v1);
+
+		var t:MyTuple = new MyTuple("foo", 12);
+		unit.TestType.typedAs(t, (null : MyTuple<String, Int>));
+		eq("foo", t.v0);
+		eq(12, t.v1);
+	}
+}

+ 105 - 0
tests/unit/issues/misc/Issue3183Macro.hx

@@ -0,0 +1,105 @@
+package unit.issues.misc;
+
+import haxe.macro.Expr;
+import haxe.macro.Context;
+import haxe.macro.Type;
+
+using haxe.macro.Tools;
+
+class Issue3183Macro {
+	static var tupleMap = new Map();
+
+	macro static public function buildTuple():ComplexType {
+		switch(Context.getLocalType()) {
+			case TInst(c, args):
+				var arity = args.length;
+				if (arity == 0) {
+					var el = Context.getConstructorArguments();
+					if (el != null && el.length > 0) {
+						args = [for (e in el) Context.typeof(e)];
+						arity = args.length;
+					} else {
+						return null;
+					}
+				}
+				if (!tupleMap.exists(arity)) {
+					tupleMap[arity] = buildTupleType(c.get(), Context.getBuildFields(), arity);
+				}
+				var ct = tupleMap[arity];
+				ct.params = [for (t in args) {
+					switch (t) {
+						case TInst(_.get().kind => KExpr(e), _):
+							TPType(Context.typeof(e).toComplexType());
+						case _:
+							TPType(t.toComplexType());
+					}
+				}];
+				return TPath(ct);
+			case _:
+				return Context.error("Class expected", Context.currentPos());
+		}
+	}
+
+	static function buildTupleType(c:ClassType, fields:Array<Field>, arity:Int) {
+		var typeParams = [];
+		var tupleFields = [];
+		for (i in 0...arity) {
+			var fieldName = 'v$i';
+			var typeParamName = 'T$i';
+			var typeParam = {
+				TPath({
+					pack: [],
+					name: typeParamName,
+					sub: null,
+					params: []
+				});
+			}
+			typeParams.push({
+				name: typeParamName,
+				constraints: [],
+				params: []
+			});
+			var field = (macro class X {
+				public var $fieldName:$typeParam;
+			}).fields[0];
+			tupleFields.push(field);
+		}
+		var constructor = {
+			name: "new",
+			pos: c.pos,
+			access: [APublic, AInline],
+			kind: FFun({
+				ret: null,
+				expr: macro $b{tupleFields.map(function(field) {
+					var name = field.name;
+					return macro this.$name = $i{name};
+				})},
+				params: [],
+				args: tupleFields.map(function(field) {
+					return {
+						name: field.name,
+						type: null,
+						opt: false,
+						value: null
+					}
+				})
+			})
+		}
+		var name = c.name + "_" + arity;
+		var tDef = {
+			pack: c.pack,
+			name: name,
+			pos: c.pos,
+			kind: TDClass(),
+			params: typeParams,
+			fields: fields.concat(tupleFields).concat([constructor])
+		}
+		Context.defineType(tDef);
+		return {
+			pack: c.pack,
+			name: name,
+			params: [],
+			sub: null
+		}
+	}
+}

+ 1 - 0
typecore.ml

@@ -96,6 +96,7 @@ and typer = {
 	mutable meta : metadata;
 	mutable this_stack : texpr list;
 	mutable with_type_stack : with_type list;
+	mutable constructor_argument_stack : Ast.expr list list;
 	(* variable *)
 	mutable pass : typer_pass;
 	(* per-module *)

+ 1 - 0
typeload.ml

@@ -2619,6 +2619,7 @@ let type_module ctx m file tdecls p =
 		meta = [];
 		this_stack = [];
 		with_type_stack = [];
+		constructor_argument_stack = [];
 		pass = PBuildModule;
 		on_error = (fun ctx msg p -> ctx.com.error msg p);
 		macro_depth = ctx.macro_depth;

+ 10 - 1
typer.ml

@@ -3157,7 +3157,10 @@ and type_expr ctx (e,p) (with_type:with_type) =
 				error "Constructor is not a function" p
 		in
 		let t = try
-			follow (Typeload.load_instance ctx t p true)
+			ctx.constructor_argument_stack <- el :: ctx.constructor_argument_stack;
+			let t = follow (Typeload.load_instance ctx t p true) in
+			ctx.constructor_argument_stack <- List.tl ctx.constructor_argument_stack;
+			t
 		with Codegen.Generic_Exception _ ->
 			(* Try to infer generic parameters from the argument list (issue #2044) *)
 			match Typeload.load_type_def ctx p t with
@@ -4192,6 +4195,11 @@ let make_macro_api ctx p =
 				| (WithType t | WithTypeResume t) :: _ -> Some t
 				| _ -> None
 		);
+		Interp.get_constructor_arguments = (fun() ->
+			match ctx.constructor_argument_stack with
+				| [] -> None
+				| el :: _ -> Some el
+		);
 		Interp.get_local_method = (fun() ->
 			ctx.curfield.cf_name;
 		);
@@ -4618,6 +4626,7 @@ let rec create com =
 		meta = [];
 		this_stack = [];
 		with_type_stack = [];
+		constructor_argument_stack = [];
 		pass = PBuildModule;
 		macro_depth = 0;
 		untyped = false;