Преглед на файлове

support `@:from macro` (closes #4306)

Simon Krajewski преди 10 години
родител
ревизия
bf385fba1d
променени са 4 файла, в които са добавени 108 реда и са изтрити 9 реда
  1. 21 7
      codegen.ml
  2. 42 0
      tests/unit/src/unit/issues/Issue4306.hx
  3. 44 0
      tests/unit/src/unit/issues/misc/Issue4306Macro.hx
  4. 1 2
      typeload.ml

+ 21 - 7
codegen.ml

@@ -700,7 +700,21 @@ module AbstractCast = struct
 	let cast_stack = ref []
 
 	let make_static_call ctx c cf a pl args t p =
-		make_static_call ctx c cf (apply_params a.a_params pl) args t p
+		if cf.cf_kind = Method MethMacro then begin
+			match args with
+				| [e] ->
+					let e,f = push_this ctx e in
+					ctx.with_type_stack <- (WithType t) :: ctx.with_type_stack;
+					let e = match ctx.g.do_macro ctx MExpr c.cl_path cf.cf_name [e] p with
+						| Some e -> type_expr ctx e Value
+						| None ->  type_expr ctx (EConst (Ident "null"),p) Value
+					in
+					ctx.with_type_stack <- List.tl ctx.with_type_stack;
+					f();
+					e
+				| _ -> assert false
+		end else
+			make_static_call ctx c cf (apply_params a.a_params pl) args t p
 
 	let do_check_cast ctx tleft eright p =
 		let recurse cf f =
@@ -1494,12 +1508,12 @@ let dump_types com =
 			| Some f -> print_field false f);
 			List.iter (print_field false) c.cl_ordered_fields;
 			List.iter (print_field true) c.cl_ordered_statics;
-            (match c.cl_init with
-            | None -> ()
-            | Some e ->
-                print "\n\n\t__init__ = ";
-                print "%s" (s_expr s_type e);
-                print "}\n");
+			(match c.cl_init with
+			| None -> ()
+			| Some e ->
+				print "\n\n\t__init__ = ";
+				print "%s" (s_expr s_type e);
+				print "}\n");
 			print "}";
 		| Type.TEnumDecl e ->
 			print "%s%senum %s%s {\n" (if e.e_private then "private " else "") (if e.e_extern then "extern " else "") (s_type_path path) (params e.e_params);

+ 42 - 0
tests/unit/src/unit/issues/Issue4306.hx

@@ -0,0 +1,42 @@
+package unit.issues;
+
+import unit.issues.misc.Issue4306Macro.TypeName;
+import unit.issues.misc.Issue4306Macro.Arity;
+import unit.issues.misc.Issue4306Macro.EnumListener;
+
+private enum E {
+	Message(s:String);
+	NoMessage;
+}
+
+class Issue4306 extends Test {
+	function test() {
+		eq("String", getTypeName("foo"));
+		eq("Int", getTypeName(12));
+		eq("haxe.Template", getTypeName(new haxe.Template("foo")));
+
+		eq(0, getArity(test));
+		eq(1, getArity(getTypeName));
+		eq(2, getArity(function(x, y) { }));
+
+		function onMessage(s:String) { }
+		function onNoMessage() { }
+
+		eq("Message", addEnumListener(Message, onMessage));
+		t(unit.TestType.typeError(addEnumListener(Message, onNoMessage)));
+		eq("NoMessage", addEnumListener(NoMessage, onNoMessage));
+		t(unit.TestType.typeError(addEnumListener(NoMessage, noMessage)));
+	}
+
+	static function getTypeName(t:TypeName) {
+		return t;
+	}
+
+	static function getArity(a:Arity) {
+		return a;
+	}
+
+	static function addEnumListener<T>(e:EnumListener<T>, f:T) {
+		return e;
+	}
+}

+ 44 - 0
tests/unit/src/unit/issues/misc/Issue4306Macro.hx

@@ -0,0 +1,44 @@
+package unit.issues.misc;
+
+import haxe.macro.Expr;
+import haxe.macro.Context;
+import haxe.macro.Type;
+
+using haxe.macro.Tools;
+
+abstract TypeName(String) to String {
+	@:from static macro function fromE(e:Expr) {
+		return macro $v{Context.typeof(e).toString()};
+	}
+}
+
+abstract Arity(Int) to Int {
+	@:from static macro function fromE(e:Expr) {
+		return switch (Context.typeof(e).follow()) {
+			case TFun(tl, _): macro $v{tl.length};
+			case _: Context.error("Expected function type", e.pos);
+		}
+	}
+}
+
+abstract EnumListener<T>(String) to String {
+	@:from static macro function fromE(e:Expr) {
+		var e = Context.typeExpr(e);
+		var ef = switch (e.expr) {
+			case TField(_, FEnum(_, ef)): ef;
+			case _: Context.error("Expected enum constructor", e.pos);
+		}
+		var t = switch (Context.getExpectedType().follow()) {
+			case TAbstract(_, [t]): t;
+			case _: Context.error("Something went wrong", e.pos);
+		}
+		var tvoid = Context.typeof(macro (null : Void));
+		var t2 = switch (e.t.follow()) {
+			case TFun(tl, _): TFun(tl, tvoid);
+			case TEnum(_): TFun([], tvoid);
+			case _: Context.error("Something went wrong, again", e.pos);
+		}
+		Context.unify(t, t2);
+		return macro $v{ef.name};
+	}
+}

+ 1 - 2
typeload.ml

@@ -2289,10 +2289,9 @@ let init_class ctx c p context_init herits fields =
 					let allows_no_expr = ref (Meta.has Meta.CoreType a.a_meta) in
 					let rec loop ml = match ml with
 						| (Meta.From,_,_) :: _ ->
-							if is_macro then error (f.cff_name ^ ": Macro cast functions are not supported") p;
 							let r = fun () ->
 								(* the return type of a from-function must be the abstract, not the underlying type *)
-								(try type_eq EqStrict ret ta with Unify_error l -> error (error_msg (Unify l)) p);
+								if not is_macro then (try type_eq EqStrict ret ta with Unify_error l -> error (error_msg (Unify l)) p);
 								match t with
 									| TFun([_,_,t],_) -> t
 									| _ -> error (f.cff_name ^ ": @:from cast functions must accept exactly one argument") p