Browse Source

More strict macro API for init vs non init macros (#11043)

* More strict macro API for init vs non init macros

* Macro => Macros

* Simplify wording

* [tests] adjust tests positions

* Documentation

* Let addNativeArg and nullSafety be  called from outside init macros

* [tests] adjust tests positions

* Mention Context functions being flagged as deprecated from init macros in their doc

* Add more init macro checks

* [tests] adjust tests positions

* Doc adjustments

* Warn when calling Context.onAfterInitMacros after init macros

* Remove commented out code

* Add test
Rudy Ges 2 years ago
parent
commit
4c1c12121e

+ 5 - 0
src-json/define.json

@@ -261,6 +261,11 @@
 		"doc": "The current Haxe version value in SemVer format.",
 		"doc": "The current Haxe version value in SemVer format.",
 		"reserved": true
 		"reserved": true
 	},
 	},
+	{
+		"name": "HaxeNext",
+		"define": "haxe-next",
+		"doc": "Enable experimental features that are meant to be released on next Haxe version."
+	},
 	{
 	{
 		"name": "HlVer",
 		"name": "HlVer",
 		"define": "hl-ver",
 		"define": "hl-ver",

+ 15 - 8
src/macro/macroApi.ml

@@ -21,8 +21,11 @@ type compiler_options = {
 type 'value compiler_api = {
 type 'value compiler_api = {
 	pos : Globals.pos;
 	pos : Globals.pos;
 	get_com : unit -> Common.context;
 	get_com : unit -> Common.context;
+	get_macro_stack : unit -> pos list;
+	init_macros_done : unit -> bool;
 	get_type : string -> Type.t option;
 	get_type : string -> Type.t option;
 	get_module : string -> Type.t list;
 	get_module : string -> Type.t list;
+	after_init_macros : (unit -> unit) -> unit;
 	after_typing : (module_type list -> unit) -> unit;
 	after_typing : (module_type list -> unit) -> unit;
 	on_generate : (Type.t list -> unit) -> bool -> unit;
 	on_generate : (Type.t list -> unit) -> bool -> unit;
 	after_generate : (unit -> unit) -> unit;
 	after_generate : (unit -> unit) -> unit;
@@ -1690,6 +1693,13 @@ let macro_api ccom get_api =
 		"current_pos", vfun0 (fun() ->
 		"current_pos", vfun0 (fun() ->
 			encode_pos (get_api()).pos
 			encode_pos (get_api()).pos
 		);
 		);
+		"get_macro_stack", vfun0 (fun () ->
+			let stack = ((get_api()).get_macro_stack ()) in
+			encode_array (List.map encode_pos stack)
+		);
+		"init_macros_done", vfun0 (fun () ->
+			vbool ((get_api()).init_macros_done ())
+		);
 		"error", vfun3 (fun msg p depth ->
 		"error", vfun3 (fun msg p depth ->
 			let msg = decode_string msg in
 			let msg = decode_string msg in
 			let p = decode_pos p in
 			let p = decode_pos p in
@@ -1744,10 +1754,6 @@ let macro_api ccom get_api =
 		"define", vfun2 (fun s v ->
 		"define", vfun2 (fun s v ->
 			let s = decode_string s in
 			let s = decode_string s in
 			let com = ccom() in
 			let com = ccom() in
-			if com.stage <> CInitMacrosStart then begin
-				let v = if v = vnull then "" else ", " ^ (decode_string v) in
-				(get_api()).warning WMacro ("Should be used in initialization macros only: haxe.macro.Compiler.define(" ^ s ^ v ^ ")") Globals.null_pos;
-			end;
 			(* TODO: use external_define and external_define_value for #8690 *)
 			(* TODO: use external_define and external_define_value for #8690 *)
 			if v = vnull then
 			if v = vnull then
 				Common.external_define_no_check com s
 				Common.external_define_no_check com s
@@ -1773,6 +1779,11 @@ let macro_api ccom get_api =
 		"get_module", vfun1 (fun s ->
 		"get_module", vfun1 (fun s ->
 			encode_array (List.map encode_type ((get_api()).get_module (decode_string s)))
 			encode_array (List.map encode_type ((get_api()).get_module (decode_string s)))
 		);
 		);
+		"on_after_init_macros", vfun1 (fun f ->
+			let f = prepare_callback f 1 in
+			(get_api()).after_init_macros (fun tl -> ignore(f []));
+			vnull
+		);
 		"on_after_typing", vfun1 (fun f ->
 		"on_after_typing", vfun1 (fun f ->
 			let f = prepare_callback f 1 in
 			let f = prepare_callback f 1 in
 			(get_api()).after_typing (fun tl -> ignore(f [encode_array (List.map encode_module_type tl)]));
 			(get_api()).after_typing (fun tl -> ignore(f [encode_array (List.map encode_module_type tl)]));
@@ -2052,8 +2063,6 @@ let macro_api ccom get_api =
 		"add_class_path", vfun1 (fun cp ->
 		"add_class_path", vfun1 (fun cp ->
 			let com = ccom() in
 			let com = ccom() in
 			let cp = decode_string cp in
 			let cp = decode_string cp in
-			if com.stage <> CInitMacrosStart then
-				(get_api()).warning WMacro ("Should be used in initialization macros only: haxe.macro.Compiler.addClassPath(" ^ cp ^ ")") Globals.null_pos;
 			let cp = Path.add_trailing_slash cp in
 			let cp = Path.add_trailing_slash cp in
 			com.class_path <- cp :: com.class_path;
 			com.class_path <- cp :: com.class_path;
 			(match com.get_macros() with
 			(match com.get_macros() with
@@ -2068,8 +2077,6 @@ let macro_api ccom get_api =
 		"add_native_lib", vfun1 (fun file ->
 		"add_native_lib", vfun1 (fun file ->
 			let file = decode_string file in
 			let file = decode_string file in
 			let com = ccom() in
 			let com = ccom() in
-			if com.stage <> CInitMacrosStart then
-				(get_api()).warning WMacro ("Should be used in initialization macros only: haxe.macro.Compiler.addNativeLib(" ^ file ^ ")") Globals.null_pos;
 			NativeLibraryHandler.add_native_lib com file false ();
 			NativeLibraryHandler.add_native_lib com file false ();
 			vnull
 			vnull
 		);
 		);

+ 16 - 0
src/typing/macroContext.ml

@@ -134,6 +134,15 @@ let make_macro_api ctx p =
 	{
 	{
 		MacroApi.pos = p;
 		MacroApi.pos = p;
 		MacroApi.get_com = (fun() -> ctx.com);
 		MacroApi.get_com = (fun() -> ctx.com);
+		MacroApi.get_macro_stack = (fun () ->
+			let envs = Interp.call_stack (Interp.get_eval (Interp.get_ctx ())) in
+			let envs = match envs with
+				| _ :: envs -> envs (* Skip call to getMacroStack() *)
+				| _ -> envs
+			in
+			List.map (fun (env:Interp.env) -> {pfile = EvalHash.rev_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax}) envs
+		);
+		MacroApi.init_macros_done = (fun () -> ctx.com.stage >= CInitMacrosDone);
 		MacroApi.get_type = (fun s ->
 		MacroApi.get_type = (fun s ->
 			typing_timer ctx false (fun() ->
 			typing_timer ctx false (fun() ->
 				let path = parse_path s in
 				let path = parse_path s in
@@ -160,6 +169,13 @@ let make_macro_api ctx p =
 				m
 				m
 			)
 			)
 		);
 		);
+		MacroApi.after_init_macros = (fun f ->
+			ctx.com.callbacks#add_after_init_macros (fun () ->
+				let t = macro_timer ctx ["afterInitMacros"] in
+				f ();
+				t()
+			)
+		);
 		MacroApi.after_typing = (fun f ->
 		MacroApi.after_typing = (fun f ->
 			ctx.com.callbacks#add_after_typing (fun tl ->
 			ctx.com.callbacks#add_after_typing (fun tl ->
 				let t = macro_timer ctx ["afterTyping"] in
 				let t = macro_timer ctx ["afterTyping"] in

+ 67 - 56
std/haxe/macro/Compiler.hx

@@ -70,6 +70,7 @@ class Compiler {
 	**/
 	**/
 	public static function define(flag:String, ?value:String) {
 	public static function define(flag:String, ?value:String) {
 		#if (neko || eval)
 		#if (neko || eval)
+		Context.assertInitMacro();
 		load("define", 2)(flag, value);
 		load("define", 2)(flag, value);
 		#end
 		#end
 	}
 	}
@@ -137,6 +138,7 @@ class Compiler {
 	**/
 	**/
 	public static function addClassPath(path:String) {
 	public static function addClassPath(path:String) {
 		#if (neko || eval)
 		#if (neko || eval)
+		Context.assertInitMacro();
 		load("add_class_path", 1)(path);
 		load("add_class_path", 1)(path);
 		#end
 		#end
 	}
 	}
@@ -183,6 +185,7 @@ class Compiler {
 	**/
 	**/
 	public static function addNativeLib(name:String) {
 	public static function addNativeLib(name:String) {
 		#if (neko || eval)
 		#if (neko || eval)
+		Context.assertInitMacro();
 		load("add_native_lib", 1)(name);
 		load("add_native_lib", 1)(name);
 		#end
 		#end
 	}
 	}
@@ -215,69 +218,77 @@ class Compiler {
 		@param strict If true and given package wasn't found in any of class paths, fail with an error.
 		@param strict If true and given package wasn't found in any of class paths, fail with an error.
 	**/
 	**/
 	public static function include(pack:String, ?rec = true, ?ignore:Array<String>, ?classPaths:Array<String>, strict = false) {
 	public static function include(pack:String, ?rec = true, ?ignore:Array<String>, ?classPaths:Array<String>, strict = false) {
-		var ignoreWildcard:Array<String> = [];
-		var ignoreString:Array<String> = [];
-		if (ignore != null) {
-			for (ignoreRule in ignore) {
-				if (StringTools.endsWith(ignoreRule, "*")) {
-					ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1));
-				} else {
-					ignoreString.push(ignoreRule);
+		function include(pack:String, ?rec = true, ?ignore:Array<String>, ?classPaths:Array<String>, strict = false) {
+			var ignoreWildcard:Array<String> = [];
+			var ignoreString:Array<String> = [];
+			if (ignore != null) {
+				for (ignoreRule in ignore) {
+					if (StringTools.endsWith(ignoreRule, "*")) {
+						ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1));
+					} else {
+						ignoreString.push(ignoreRule);
+					}
 				}
 				}
 			}
 			}
-		}
-		var skip = if (ignore == null) {
-			function(c) return false;
-		} else {
-			function(c:String) {
-				if (Lambda.has(ignoreString, c))
-					return true;
-				for (ignoreRule in ignoreWildcard)
-					if (StringTools.startsWith(c, ignoreRule))
+			var skip = if (ignore == null) {
+				function(c) return false;
+			} else {
+				function(c:String) {
+					if (Lambda.has(ignoreString, c))
 						return true;
 						return true;
-				return false;
+					for (ignoreRule in ignoreWildcard)
+						if (StringTools.startsWith(c, ignoreRule))
+							return true;
+					return false;
+				}
 			}
 			}
-		}
-		var displayValue = Context.definedValue("display");
-		if (classPaths == null) {
-			classPaths = Context.getClassPath();
-			// do not force inclusion when using completion
-			switch (displayValue) {
-				case null:
-				case "usage":
-				case _:
-					return;
+			var displayValue = Context.definedValue("display");
+			if (classPaths == null) {
+				classPaths = Context.getClassPath();
+				// do not force inclusion when using completion
+				switch (displayValue) {
+					case null:
+					case "usage":
+					case _:
+						return;
+				}
+				// normalize class path
+				for (i in 0...classPaths.length) {
+					var cp = StringTools.replace(classPaths[i], "\\", "/");
+					if (StringTools.endsWith(cp, "/"))
+						cp = cp.substr(0, -1);
+					if (cp == "")
+						cp = ".";
+					classPaths[i] = cp;
+				}
 			}
 			}
-			// normalize class path
-			for (i in 0...classPaths.length) {
-				var cp = StringTools.replace(classPaths[i], "\\", "/");
-				if (StringTools.endsWith(cp, "/"))
-					cp = cp.substr(0, -1);
-				if (cp == "")
-					cp = ".";
-				classPaths[i] = cp;
+			var prefix = pack == '' ? '' : pack + '.';
+			var found = false;
+			for (cp in classPaths) {
+				var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/");
+				if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path))
+					continue;
+				found = true;
+				for (file in sys.FileSystem.readDirectory(path)) {
+					if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) {
+						if( file == "import.hx" ) continue;
+						var cl = prefix + file.substr(0, file.length - 3);
+						if (skip(cl))
+							continue;
+						Context.getModule(cl);
+					} else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file))
+						include(prefix + file, true, ignore, classPaths);
+				}
 			}
 			}
+			if (strict && !found)
+				Context.error('Package "$pack" was not found in any of class paths', Context.currentPos());
 		}
 		}
-		var prefix = pack == '' ? '' : pack + '.';
-		var found = false;
-		for (cp in classPaths) {
-			var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/");
-			if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path))
-				continue;
-			found = true;
-			for (file in sys.FileSystem.readDirectory(path)) {
-				if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) {
-					if( file == "import.hx" ) continue;
-					var cl = prefix + file.substr(0, file.length - 3);
-					if (skip(cl))
-						continue;
-					Context.getModule(cl);
-				} else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file))
-					include(prefix + file, true, ignore, classPaths);
-			}
+
+		if (!Context.initMacrosDone()) {
+			Context.onAfterInitMacros(() -> include(pack, rec, ignore, classPaths, strict));
+		} else {
+			include(pack, rec, ignore, classPaths, strict);
 		}
 		}
-		if (strict && !found)
-			Context.error('Package "$pack" was not found in any of class paths', Context.currentPos());
 	}
 	}
 
 
 	/**
 	/**
@@ -707,7 +718,7 @@ typedef CompilerConfiguration = {
 	final platform:haxe.display.Display.Platform;
 	final platform:haxe.display.Display.Platform;
 
 
 	/**
 	/**
-		The compilation configuration for the target platform. 
+		The compilation configuration for the target platform.
 	**/
 	**/
 	final platformConfig:PlatformConfig;
 	final platformConfig:PlatformConfig;
 
 

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

@@ -96,6 +96,14 @@ class Context {
 		load("filter_messages", 1)(predicate);
 		load("filter_messages", 1)(predicate);
 	}
 	}
 
 
+	/**
+		Check if compiler is past initializations macros or not.
+		When it is, configuration phase is over and parsing/typing can start.
+	**/
+	public static function initMacrosDone():Bool {
+		return load("init_macros_done", 0)();
+	}
+
 	/**
 	/**
 		Resolves a file name `file` based on the current class paths.
 		Resolves a file name `file` based on the current class paths.
 
 
@@ -138,6 +146,14 @@ class Context {
 		return load("current_pos", 0)();
 		return load("current_pos", 0)();
 	}
 	}
 
 
+	/**
+		Get the call stack (excluding the call to `Context.getMacroStack()`
+		that led to current macro.
+	**/
+	public static function getMacroStack():Array<Position> {
+		return load("get_macro_stack", 0)();
+	}
+
 	/**
 	/**
 		Returns the type which is expected at the place the macro is called.
 		Returns the type which is expected at the place the macro is called.
 
 
@@ -148,6 +164,7 @@ class Context {
 		macro is not an expression-macro.
 		macro is not an expression-macro.
 	**/
 	**/
 	public static function getExpectedType():Null<Type> {
 	public static function getExpectedType():Null<Type> {
+		assertInitMacrosDone(false);
 		return load("get_expected_type", 0)();
 		return load("get_expected_type", 0)();
 	}
 	}
 
 
@@ -291,8 +308,13 @@ class Context {
 		declared class path has priority.
 		declared class path has priority.
 
 
 		If no type can be found, an exception of type `String` is thrown.
 		If no type can be found, an exception of type `String` is thrown.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function getType(name:String):Type {
 	public static function getType(name:String):Type {
+		assertInitMacrosDone();
 		return load("get_type", 1)(name);
 		return load("get_type", 1)(name);
 	}
 	}
 
 
@@ -304,8 +326,13 @@ class Context {
 		declared class path has priority.
 		declared class path has priority.
 
 
 		If no module can be found, an exception of type `String` is thrown.
 		If no module can be found, an exception of type `String` is thrown.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function getModule(name:String):Array<Type> {
 	public static function getModule(name:String):Array<Type> {
+		assertInitMacrosDone();
 		return load("get_module", 1)(name);
 		return load("get_module", 1)(name);
 	}
 	}
 
 
@@ -327,8 +354,13 @@ class Context {
 		should not be treated as conclusive until the generation phase.
 		should not be treated as conclusive until the generation phase.
 
 
 		Modifying the returned array has no effect on the compilation.
 		Modifying the returned array has no effect on the compilation.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function getAllModuleTypes():Array<haxe.macro.Type.ModuleType> {
 	public static function getAllModuleTypes():Array<haxe.macro.Type.ModuleType> {
+		assertInitMacrosDone();
 		return load("get_module_types", 0)();
 		return load("get_module_types", 0)();
 	}
 	}
 
 
@@ -369,6 +401,7 @@ class Context {
 		Returns a hashed MD5 signature of value `v`.
 		Returns a hashed MD5 signature of value `v`.
 	**/
 	**/
 	public static function signature(v:Dynamic):String {
 	public static function signature(v:Dynamic):String {
+		assertInitMacrosDone(false);
 		return load("signature", 1)(v);
 		return load("signature", 1)(v);
 	}
 	}
 
 
@@ -417,6 +450,19 @@ class Context {
 		load("on_after_typing", 1)(callback);
 		load("on_after_typing", 1)(callback);
 	}
 	}
 
 
+	/**
+		Adds a callback function `callback` which is invoked after the compiler
+		is done running initialization macros, when typing begins.
+
+		`onAfterInitMacros` should be used to delay typer-dependant code from
+		your initalization macros, to properly separate configuration phase and
+		actual typing.
+	**/
+	public static function onAfterInitMacros(callback:Void->Void):Void {
+		assertInitMacro();
+		load("on_after_init_macros", 1)(callback);
+	}
+
 	/**
 	/**
 		Adds a callback function `callback` which is invoked when a type name
 		Adds a callback function `callback` which is invoked when a type name
 		cannot be resolved.
 		cannot be resolved.
@@ -434,8 +480,13 @@ class Context {
 
 
 		Typing the expression may result in a compiler error which can be
 		Typing the expression may result in a compiler error which can be
 		caught using `try ... catch`.
 		caught using `try ... catch`.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function typeof(e:Expr):Type {
 	public static function typeof(e:Expr):Type {
+		assertInitMacrosDone();
 		return load("typeof", 1)(e);
 		return load("typeof", 1)(e);
 	}
 	}
 
 
@@ -447,8 +498,13 @@ class Context {
 		be caught this way because the compiler might delay various checks
 		be caught this way because the compiler might delay various checks
 		to a later stage, at which point the exception handler is no longer
 		to a later stage, at which point the exception handler is no longer
 		active.
 		active.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function typeExpr(e:Expr):TypedExpr {
 	public static function typeExpr(e:Expr):TypedExpr {
+		assertInitMacrosDone();
 		return load("type_expr", 1)(e);
 		return load("type_expr", 1)(e);
 	}
 	}
 
 
@@ -458,8 +514,13 @@ class Context {
 		Resolving the type may result in a compiler error which can be
 		Resolving the type may result in a compiler error which can be
 		caught using `try ... catch`.
 		caught using `try ... catch`.
 		Resolution is performed based on the current context in which the macro is called.
 		Resolution is performed based on the current context in which the macro is called.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function resolveType(t:ComplexType, p:Position):Type {
 	public static function resolveType(t:ComplexType, p:Position):Type {
+		assertInitMacrosDone();
 		return load("resolve_type", 2)(t, p);
 		return load("resolve_type", 2)(t, p);
 	}
 	}
 
 
@@ -474,8 +535,13 @@ class Context {
 
 
 	/**
 	/**
 		Tries to unify `t1` and `t2` and returns `true` if successful.
 		Tries to unify `t1` and `t2` and returns `true` if successful.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function unify(t1:Type, t2:Type):Bool {
 	public static function unify(t1:Type, t2:Type):Bool {
+		assertInitMacrosDone();
 		return load("unify", 2)(t1, t2);
 		return load("unify", 2)(t1, t2);
 	}
 	}
 
 
@@ -483,8 +549,13 @@ class Context {
 		Follows a type.
 		Follows a type.
 
 
 		See `haxe.macro.TypeTools.follow` for details.
 		See `haxe.macro.TypeTools.follow` for details.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function follow(t:Type, ?once:Bool):Type {
 	public static function follow(t:Type, ?once:Bool):Type {
+		assertInitMacrosDone();
 		return load("follow", 2)(t, once);
 		return load("follow", 2)(t, once);
 	}
 	}
 
 
@@ -492,8 +563,13 @@ class Context {
 		Follows a type, including abstracts' underlying implementation
 		Follows a type, including abstracts' underlying implementation
 
 
 		See `haxe.macro.TypeTools.followWithAbstracts` for details.
 		See `haxe.macro.TypeTools.followWithAbstracts` for details.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function followWithAbstracts(t:Type, once:Bool = false):Type {
 	public static function followWithAbstracts(t:Type, once:Bool = false):Type {
+		assertInitMacrosDone();
 		return load("follow_with_abstracts", 2)(t, once);
 		return load("follow_with_abstracts", 2)(t, once);
 	}
 	}
 
 
@@ -552,8 +628,13 @@ class Context {
 		If `moduleDependency` is given and is not `null`, it should contain
 		If `moduleDependency` is given and is not `null`, it should contain
 		a module path that will be used as a dependency for the newly defined module
 		a module path that will be used as a dependency for the newly defined module
 		instead of the current module.
 		instead of the current module.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function defineType(t:TypeDefinition, ?moduleDependency:String):Void {
 	public static function defineType(t:TypeDefinition, ?moduleDependency:String):Void {
+		assertInitMacrosDone();
 		load("define_type", 2)(t, moduleDependency);
 		load("define_type", 2)(t, moduleDependency);
 	}
 	}
 
 
@@ -562,8 +643,13 @@ class Context {
 
 
 		Returned monomorph can be used with e.g. `Context.unify` to make the compiler
 		Returned monomorph can be used with e.g. `Context.unify` to make the compiler
 		bind the monomorph to an actual type and let macro further process the resulting type.
 		bind the monomorph to an actual type and let macro further process the resulting type.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function makeMonomorph():Type {
 	public static function makeMonomorph():Type {
+		assertInitMacrosDone();
 		return load("make_monomorph", 0)();
 		return load("make_monomorph", 0)();
 	}
 	}
 
 
@@ -574,12 +660,17 @@ class Context {
 		The individual `types` can reference each other and any identifier
 		The individual `types` can reference each other and any identifier
 		respects the `imports` and `usings` as usual, expect that imports are
 		respects the `imports` and `usings` as usual, expect that imports are
 		not allowed to have `.*` wildcards or `as s` shorthands.
 		not allowed to have `.*` wildcards or `as s` shorthands.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function defineModule(modulePath:String, types:Array<TypeDefinition>, ?imports:Array<ImportExpr>, ?usings:Array<TypePath>):Void {
 	public static function defineModule(modulePath:String, types:Array<TypeDefinition>, ?imports:Array<ImportExpr>, ?usings:Array<TypePath>):Void {
 		if (imports == null)
 		if (imports == null)
 			imports = [];
 			imports = [];
 		if (usings == null)
 		if (usings == null)
 			usings = [];
 			usings = [];
+		assertInitMacrosDone();
 		load("define_module", 4)(modulePath, types, imports, usings);
 		load("define_module", 4)(modulePath, types, imports, usings);
 	}
 	}
 
 
@@ -587,8 +678,13 @@ class Context {
 		Returns a syntax-level expression corresponding to typed expression `t`.
 		Returns a syntax-level expression corresponding to typed expression `t`.
 
 
 		This process may lose some information.
 		This process may lose some information.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function getTypedExpr(t:Type.TypedExpr):Expr {
 	public static function getTypedExpr(t:Type.TypedExpr):Expr {
+		assertInitMacrosDone();
 		return load("get_typed_expr", 1)(t);
 		return load("get_typed_expr", 1)(t);
 	}
 	}
 
 
@@ -603,8 +699,13 @@ class Context {
 		that is reset between compilations, so care should be taken when storing
 		that is reset between compilations, so care should be taken when storing
 		the expression returned by this method in a static variable and using the
 		the expression returned by this method in a static variable and using the
 		compilation server.
 		compilation server.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function storeTypedExpr(t:Type.TypedExpr):Expr {
 	public static function storeTypedExpr(t:Type.TypedExpr):Expr {
+		assertInitMacrosDone();
 		return load("store_typed_expr", 1)(t);
 		return load("store_typed_expr", 1)(t);
 	}
 	}
 
 
@@ -622,16 +723,26 @@ class Context {
 		that is reset between compilations, so care should be taken when storing
 		that is reset between compilations, so care should be taken when storing
 		the expression returned by this method in a static variable and using the
 		the expression returned by this method in a static variable and using the
 		compilation server.
 		compilation server.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function storeExpr(e:Expr):Expr {
 	public static function storeExpr(e:Expr):Expr {
+		assertInitMacrosDone();
 		return load("store_expr", 1)(e);
 		return load("store_expr", 1)(e);
 	}
 	}
 
 
 	/**
 	/**
 		This function works like `storeExpr`, but also returns access to the expression's
 		This function works like `storeExpr`, but also returns access to the expression's
 		type through the `type` field of the return value.
 		type through the `type` field of the return value.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function typeAndStoreExpr(e:Expr):{final type:Type.Ref<Type>; final expr:Expr;} {
 	public static function typeAndStoreExpr(e:Expr):{final type:Type.Ref<Type>; final expr:Expr;} {
+		assertInitMacrosDone();
 		return load("type_and_store_expr", 1)(e);
 		return load("type_and_store_expr", 1)(e);
 	}
 	}
 
 
@@ -643,8 +754,13 @@ class Context {
 		`externFile` has changed.
 		`externFile` has changed.
 
 
 		Has no effect if the compilation cache is not used.
 		Has no effect if the compilation cache is not used.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function registerModuleDependency(modulePath:String, externFile:String) {
 	public static function registerModuleDependency(modulePath:String, externFile:String) {
+		assertInitMacrosDone();
 		load("register_module_dependency", 2)(modulePath, externFile);
 		load("register_module_dependency", 2)(modulePath, externFile);
 	}
 	}
 
 
@@ -674,8 +790,13 @@ class Context {
 		is true even if `code` throws an exception.
 		is true even if `code` throws an exception.
 
 
 		If any argument is `null`, the result is unspecified.
 		If any argument is `null`, the result is unspecified.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function withImports<X>(imports:Array<String>, usings:Array<String>, code:() -> X):X {
 	public static function withImports<X>(imports:Array<String>, usings:Array<String>, code:() -> X):X {
+		assertInitMacrosDone();
 		return load("with_imports", 3)(imports, usings, code);
 		return load("with_imports", 3)(imports, usings, code);
 	}
 	}
 
 
@@ -688,8 +809,13 @@ class Context {
 
 
 		`allowTransform`: when disabled, the code typed with `typeExpr` will be almost exactly the same
 		`allowTransform`: when disabled, the code typed with `typeExpr` will be almost exactly the same
 		as the input code. This will disable some abstract types transformations.
 		as the input code. This will disable some abstract types transformations.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	**/
 	public static function withOptions<X>(options:{?allowInlining:Bool,?allowTransform:Bool}, code : () -> X) : X {
 	public static function withOptions<X>(options:{?allowInlining:Bool,?allowTransform:Bool}, code : () -> X) : X {
+		assertInitMacrosDone();
 		return load("with_options", 2)(options, code);
 		return load("with_options", 2)(options, code);
 	}
 	}
 
 
@@ -724,5 +850,33 @@ class Context {
 	private static function sExpr(e:TypedExpr, pretty:Bool):String {
 	private static function sExpr(e:TypedExpr, pretty:Bool):String {
 		return haxe.macro.Context.load("s_expr", 2)(e, pretty);
 		return haxe.macro.Context.load("s_expr", 2)(e, pretty);
 	}
 	}
+
+	@:allow(haxe.macro.Compiler)
+	private static function assertInitMacro():Void {
+		if (initMacrosDone()) {
+			var stack = getMacroStack();
+
+			warning(
+				"This API should only be used from initialization macros.",
+				if (stack.length > 2) stack[2] else currentPos()
+			);
+		}
+	}
+
+	private static function assertInitMacrosDone(includeSuggestion = true):Void {
+		#if haxe_next
+		if (!initMacrosDone()) {
+			var stack = getMacroStack();
+			var suggestion = includeSuggestion
+				? "\nUse `Context.onAfterInitMacros` to register a callback to run when context is ready."
+				: "";
+
+			warning(
+				"Cannot use this API from initialization macros." + suggestion,
+				if (stack.length > 2) stack[2] else currentPos()
+			);
+		}
+		#end
+	}
 	#end
 	#end
 }
 }

+ 1 - 1
tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml.stderr

@@ -1,3 +1,3 @@
 (unknown) : Uncaught exception Could not read file define.jsno
 (unknown) : Uncaught exception Could not read file define.jsno
-$$normPath(::std::)/haxe/macro/Compiler.hx:482: characters 11-39 : Called from here
+$$normPath(::std::)/haxe/macro/Compiler.hx:493: characters 11-39 : Called from here
 (unknown) : Called from here
 (unknown) : Called from here

+ 1 - 1
tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml.stderr

@@ -1,3 +1,3 @@
 (unknown) : Uncaught exception Could not read file meta.jsno
 (unknown) : Uncaught exception Could not read file meta.jsno
-$$normPath(::std::)/haxe/macro/Compiler.hx:472: characters 11-39 : Called from here
+$$normPath(::std::)/haxe/macro/Compiler.hx:483: characters 11-39 : Called from here
 (unknown) : Called from here
 (unknown) : Called from here

+ 1 - 1
tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml.stderr

@@ -1,3 +1,3 @@
 (unknown) : Uncaught exception Could not read file meta.jsno
 (unknown) : Uncaught exception Could not read file meta.jsno
-  $$normPath(::std::)/haxe/macro/Compiler.hx:472: characters 11-39 : Called from here
+  $$normPath(::std::)/haxe/macro/Compiler.hx:483: characters 11-39 : Called from here
   (unknown) : Called from here
   (unknown) : Called from here

+ 2 - 2
tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml.stderr

@@ -2,9 +2,9 @@
 
 
    | Uncaught exception Could not read file meta.jsno
    | Uncaught exception Could not read file meta.jsno
 
 
-    ->  $$normPath(::std::)/haxe/macro/Compiler.hx:472: characters 11-39
+    ->  $$normPath(::std::)/haxe/macro/Compiler.hx:483: characters 11-39
 
 
-    472 |   var f = sys.io.File.getContent(path);
+    483 |   var f = sys.io.File.getContent(path);
         |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         | Called from here
         | Called from here
 
 

+ 19 - 0
tests/misc/projects/Issue7871/Macro.hx

@@ -0,0 +1,19 @@
+import haxe.macro.Compiler;
+import haxe.macro.Context;
+
+function init() {
+	var e = macro 42;
+	Context.typeof(e);
+	if (Context.defined("haxe")) Context.warning("ok", (macro 0).pos);
+	Compiler.define("foo", "foo");
+
+	Context.onAfterInitMacros(() -> {
+		Context.warning("after init 1", (macro 0).pos);
+		Compiler.define("bar", "bar");
+		Context.typeof(e);
+	});
+	Compiler.include("hax.ds", true, true);
+	Context.onAfterInitMacros(() -> {
+		Context.warning("after init 2", (macro 0).pos);
+	});
+}

+ 1 - 0
tests/misc/projects/Issue7871/compile-fail.hxml

@@ -0,0 +1 @@
+--macro Macro.init()

+ 5 - 0
tests/misc/projects/Issue7871/compile-fail.hxml.stderr

@@ -0,0 +1,5 @@
+Macro.hx:7: characters 60-61 : Warning : ok
+Macro.hx:11: characters 42-43 : Warning : after init 1
+Macro.hx:12: characters 3-32 : Warning : This API should only be used from initialization macros.
+Package "hax.ds" was not found in any of class paths
+Macro.hx:17: characters 42-43 : Warning : after init 2

+ 2 - 0
tests/misc/projects/Issue7871/compile-next-fail.hxml

@@ -0,0 +1,2 @@
+compile-fail.hxml
+-D haxe-next

+ 7 - 0
tests/misc/projects/Issue7871/compile-next-fail.hxml.stderr

@@ -0,0 +1,7 @@
+Macro.hx:6: characters 2-19 : Warning : Cannot use this API from initialization macros.
+Macro.hx:6: characters 2-19 : ... Use `Context.onAfterInitMacros` to register a callback to run when context is ready.
+Macro.hx:7: characters 60-61 : Warning : ok
+Macro.hx:11: characters 42-43 : Warning : after init 1
+Macro.hx:12: characters 3-32 : Warning : This API should only be used from initialization macros.
+Package "hax.ds" was not found in any of class paths
+Macro.hx:17: characters 42-43 : Warning : after init 2