Explorar o código

Remove err_depth (#12167)

* remove err_depth

* Update macro API to handle sub errors

* err_sub is a reversed list of sub errors

* Add test

* Update test

---------

Co-authored-by: Rudy Ges <[email protected]>
Simon Krajewski hai 1 mes
pai
achega
b6a2db6514

+ 2 - 2
src/compiler/compiler.ml

@@ -252,7 +252,7 @@ module Setup = struct
 				()
 		);
 		com.error_ext <- error_ext ctx;
-		com.error <- (fun ?(depth = 0) msg p -> com.error_ext (Error.make_error ~depth (Custom msg) p));
+		com.error <- (fun msg p -> com.error_ext (Error.make_error (Custom msg) p));
 		let filter_messages = (fun keep_errors predicate -> (List.filter (fun cm ->
 			(match cm.cm_severity with
 			| MessageSeverity.Error -> keep_errors;
@@ -447,7 +447,7 @@ with
 			ctx.has_error <- false;
 			ctx.messages <- [];
 		end else begin
-			let sub = List.map (fun p -> Error.make_error ~depth:1 (Error.Custom (Error.compl_msg "referenced here")) p) pl in
+			let sub = List.map (fun p -> Error.make_error (Error.Custom (Error.compl_msg "referenced here")) p) pl in
 			error_ext ctx (Error.make_error (Error.Custom (Printf.sprintf "You cannot access the %s package while %s (for %s)" pack (if pf = "macro" then "in a macro" else "targeting " ^ pf) (s_type_path m))) ~sub p)
 		end
 	| Error.Error err ->

+ 1 - 1
src/compiler/messageReporting.ml

@@ -340,7 +340,7 @@ let get_formatter defines def default =
 
 let print_error (err : Error.error) =
 	let ret = ref "" in
-	Error.recurse_error (fun depth err ->
+	Error.recurse_error (fun _ err ->
 		ret := !ret ^ (Lexer.get_error_pos (Printf.sprintf "%s:%d: ") err.err_pos) ^ (Error.error_msg err.err_message) ^ "\n"
 	) err;
 	!ret

+ 4 - 3
src/context/common.ml

@@ -18,6 +18,7 @@
  *)
 open Ast
 open Type
+open Error
 open Globals
 open Lookup
 open Define
@@ -773,7 +774,7 @@ let create timer_ctx compilation_step cs version args display_mode =
 		info = (fun ?depth ?from_macro _ _ -> die "" __LOC__);
 		warning = (fun ?depth ?from_macro _ _ _ -> die "" __LOC__);
 		warning_options = [List.map (fun w -> {wo_warning = w;wo_mode = WMDisable}) WarningList.disabled_warnings];
-		error = (fun ?depth _ _ -> die "" __LOC__);
+		error = (fun _ _ -> die "" __LOC__);
 		error_ext = (fun _ -> die "" __LOC__);
 		get_messages = (fun() -> []);
 		filter_messages = (fun _ -> ());
@@ -1092,8 +1093,8 @@ let display_error_ext com err =
 	end else
 		com.error_ext err
 
-let display_error com ?(depth = 0) msg p =
-	display_error_ext com (Error.make_error ~depth (Custom msg) p)
+let display_error com ?(sub:macro_error list = []) msg pos =
+	display_error_ext com (convert_error {msg; pos; sub})
 
 let adapt_defines_to_macro_context defines =
 	let to_remove = "java" :: List.map Globals.platform_name Globals.platforms in

+ 2 - 2
src/context/typecore.ml

@@ -384,8 +384,8 @@ let make_static_field_access c cf t p =
 	let ethis = Texpr.Builder.make_static_this c p in
 	mk (TField (ethis,(FStatic (c,cf)))) t p
 
-let raise_with_type_error ?(depth = 0) msg p =
-	raise (WithTypeError (make_error ~depth (Custom msg) p))
+let raise_with_type_error msg p =
+	raise (WithTypeError (make_error (Custom msg) p))
 
 let raise_or_display ctx l p =
 	if ctx.f.untyped then ()

+ 21 - 12
src/core/error.ml

@@ -28,30 +28,39 @@ and type_not_found_reason =
 type error = {
 	err_message : error_msg;
 	err_pos : pos;
-	(* TODO Should probably be deprecated at some point and be derived from err_sub *)
-	err_depth : int;
 	(* Reverse list of sub errors. Use Error.recurse_error to handle an error and its sub errors with depth. *)
 	err_sub : error list;
 	err_from_macro : bool;
 }
 
-let make_error ?(depth = 0) ?(from_macro = false) ?(sub = []) msg p = {
+type macro_error = {
+	msg : string;
+	pos : pos;
+	sub : macro_error list;
+}
+
+let make_error ?(from_macro = false) ?(sub = []) msg p = {
 	err_message = msg;
 	err_pos = p;
-	err_depth = depth;
 	err_from_macro = from_macro;
 	err_sub = sub;
 }
 
-let rec recurse_error ?(depth = 0) cb err =
-	let depth = if depth > 0 then depth else err.err_depth in
-	cb depth err;
-	List.iter (recurse_error ~depth:(depth+1) cb) (List.rev err.err_sub);
+let rec convert_error (err:macro_error) =
+	let sub = List.rev_map convert_error err.sub in
+	make_error ~sub (Custom err.msg) err.pos
+
+let recurse_error cb err =
+	let rec loop depth err =
+		cb depth err;
+		List.iter (loop (depth+1)) (List.rev err.err_sub)
+	in
+	loop 0 err
 
 exception Fatal_error of error
 exception Error of error
 
-let abort ?(depth = 0) msg p = raise (Fatal_error (make_error ~depth (Custom msg) p))
+let abort msg p = raise (Fatal_error (make_error (Custom msg) p))
 
 let string_source t = match follow t with
 	| TInst(c,tl) -> PMap.foldi (fun s _ acc -> s :: acc) (TClass.get_all_fields c tl) []
@@ -320,10 +329,10 @@ and s_call_error = function
 
 (* Global error helpers *)
 let raise_error err = raise (Error err)
-let raise_error_msg ?(depth = 0) msg p = raise_error (make_error ~depth msg p)
-let raise_msg ?(depth = 0) msg p = raise_error_msg ~depth (Custom msg) p
+let raise_error_msg msg p = raise_error (make_error msg p)
+let raise_msg msg p = raise_error_msg (Custom msg) p
 
-let raise_typing_error ?(depth = 0) msg p = raise_msg ~depth msg p
+let raise_typing_error msg p = raise_msg msg p
 let raise_typing_error_ext err = raise_error err
 
 let raise_std_not_found () =

+ 1 - 1
src/filters/safe/localStatic.ml

@@ -14,7 +14,7 @@ let promote_local_static lsctx run v eo =
 	begin try
 		let cf = PMap.find name c.cl_statics in
 		raise_typing_error_ext (make_error (Custom (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name)) ~sub:[
-			make_error ~depth:1 (Custom "Conflicting field was found here") cf.cf_name_pos
+			make_error (Custom "Conflicting field was found here") cf.cf_name_pos
 		] v.v_pos);
 	with Not_found ->
 		let cf = mk_field name ~static:true v.v_type v.v_pos v.v_pos in

+ 2 - 2
src/generators/gctx.ml

@@ -9,7 +9,7 @@ type context_main = {
 }
 
 type warning_function = ?depth:int -> ?from_macro:bool -> warning -> warning_option list list -> string -> pos -> unit
-type error_function = ?depth:int -> string -> pos -> unit
+type error_function = string -> pos -> unit
 
 type t = {
 	platform : platform;
@@ -118,4 +118,4 @@ let get_es_version defines =
 let map_source_header defines f =
 	match Define.defined_value_safe defines Define.SourceHeader with
 	| "" -> ()
-	| s -> f s
+	| s -> f s

+ 1 - 3
src/macro/eval/evalMain.ml

@@ -423,10 +423,8 @@ let compiler_error (err : Error.error) =
 
 		| _ ->
 			let stack = ref [] in
-			let depth = err.err_depth + 1 in
-
 			List.iter (fun err ->
-				Error.recurse_error ~depth (fun depth err ->
+				Error.recurse_error (fun depth err ->
 					(* TODO indent child errors depending on depth *)
 					stack := make_runtime_error (Error.error_msg err.err_message) err.err_pos :: !stack;
 				) err;

+ 21 - 12
src/macro/macroApi.ml

@@ -2,6 +2,7 @@ open Ast
 open DisplayTypes.DisplayMode
 open Type
 open Common
+open Error
 open PlatformConfig
 open DefineList
 open MetaList
@@ -65,7 +66,7 @@ type 'value compiler_api = {
 	decode_type : 'value -> t;
 	info : ?depth:int -> string -> pos -> unit;
 	warning : ?depth:int -> Warning.warning -> string -> pos -> unit;
-	display_error : ?depth:int -> (string -> pos -> unit);
+	display_error : ?sub:macro_error list -> string -> pos -> unit;
 	with_imports : 'a . import list -> placed_name list list -> (unit -> 'a) -> 'a;
 	with_options : 'a . compiler_options -> (unit -> 'a) -> 'a;
 	exc_string : 'a . string -> 'a;
@@ -73,7 +74,6 @@ type 'value compiler_api = {
 	set_hxb_writer_config : 'value -> unit;
 }
 
-
 type enum_type =
 	| IExpr
 	| IEFieldKind
@@ -747,6 +747,15 @@ let decode_placed_name vp v =
 let decode_opt_array f v =
 	if v = vnull then [] else List.map f (decode_array v)
 
+let decode_sub_errors sub =
+	let rec decode_sub o =
+		let msg = decode_string (field o "msg") in
+		let pos = decode_pos (field o "pos") in
+		let sub = decode_opt_array decode_sub (field o "sub") in
+		{msg; pos; sub}
+	in
+	decode_opt_array decode_sub sub
+
 (* Ast.placed_type_path *)
 let rec decode_ast_path t =
 	let pack = List.map decode_string (decode_array (field t "pack"))
@@ -1800,24 +1809,24 @@ let macro_api ccom get_api =
 		"init_macros_done", vfun0 (fun () ->
 			vbool ((get_api()).init_macros_done ())
 		);
-		"error", vfun3 (fun msg p depth ->
+		"error", vfun3 (fun msg p sub ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
-			let depth = decode_int depth in
-			(get_api()).display_error ~depth msg p;
+			let sub = decode_sub_errors sub in
+			(get_api()).display_error ~sub msg p;
 			raise Abort
 		);
-		"fatal_error", vfun3 (fun msg p depth ->
+		"fatal_error", vfun3 (fun msg p sub ->
 			let msg = decode_string msg in
-			let p = decode_pos p in
-			let depth = decode_int depth in
-			raise (Error.Fatal_error (Error.make_error ~depth (Custom msg) p))
+			let pos = decode_pos p in
+			let sub = decode_sub_errors sub in
+			raise (Error.Fatal_error (Error.convert_error {msg; pos; sub}))
 		);
-		"report_error", vfun3 (fun msg p depth ->
+		"report_error", vfun3 (fun msg p sub ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
-			let depth = decode_int depth in
-			(get_api()).display_error ~depth msg p;
+			let sub = decode_sub_errors sub in
+			(get_api()).display_error ~sub msg p;
 			vnull
 		);
 		"warning", vfun3 (fun msg p depth ->

+ 1 - 1
src/optimization/analyzerTexpr.ml

@@ -1257,7 +1257,7 @@ module Purity = struct
 					apply_to_class c
 				with Purity_conflict(impure,p) ->
 					Error.raise_typing_error_ext (Error.make_error (Custom "Impure field overrides/implements field which was explicitly marked as @:pure") ~sub:[
-						Error.make_error ~depth:1 (Custom (Error.compl_msg "Pure field is here")) p
+						Error.make_error (Custom (Error.compl_msg "Pure field is here")) p
 					] impure.pn_field.cf_pos)
 				end
 			| _ -> ()

+ 1 - 1
src/optimization/inlineConstructors.ml

@@ -131,7 +131,7 @@ let inline_constructors (scom : SafeCom.t) original_e =
 				List.iter (fun v -> if v.v_id < 0 then cancel_v v p) io.io_dependent_vars;
 				if ioc.ioc_forced then begin
 					SafeCom.add_error scom (make_error (Custom "Forced inline constructor could not be inlined") ~sub:([
-						(make_error ~depth:1 (Custom (compl_msg "Cancellation happened here")) p)
+						(make_error (Custom (compl_msg "Cancellation happened here")) p)
 					]) io.io_pos);
 				end
 			| _ -> ()

+ 3 - 4
src/typing/callUnification.ml

@@ -407,13 +407,12 @@ let unify_field_call ctx fa el_typed el p inline =
 				| None ->
 					let sub = List.fold_left (fun acc (cf,err) ->
 						(make_error
-							~depth:1 (* pretty much optional here *)
 							~sub:[err]
 							(Custom ("Overload resolution failed for " ^ (s_type (print_context()) cf.cf_type)))
 							p
 						) :: acc
 					) [] failures in
-					let sub = (make_error ~depth:1 (Custom "End of overload failure reasons") p) :: sub in
+					let sub = (make_error (Custom "End of overload failure reasons") p) :: sub in
 					raise_typing_error_ext (make_error ~sub (Custom "Could not find a suitable overload, reasons follow") p)
 				| Some err ->
 					raise_typing_error_ext err
@@ -428,7 +427,7 @@ let unify_field_call ctx fa el_typed el p inline =
 			| fcc :: l ->
 				let st = s_type (print_context()) in
 				let sub = List.map (fun fcc ->
-					make_error ~depth:1 (Custom (compl_msg (st fcc.fc_type))) fcc.fc_field.cf_name_pos
+					make_error (Custom (compl_msg (st fcc.fc_type))) fcc.fc_field.cf_name_pos
 				) (fcc :: l) in
 				display_error_ext ctx.com (make_error (Custom "Ambiguous overload, candidates follow") ~sub:(List.rev sub) p);
 				commit_delayed_display fcc
@@ -512,7 +511,7 @@ object(self)
 				if ep = null_pos then
 					old { err with err_pos = p }
 				else
-					old { err with err_sub = (make_error ~depth:(err.err_depth+1) (Custom (compl_msg "Called from macro here")) p) :: err.err_sub }
+					old { err with err_sub = (make_error (Custom (compl_msg "Called from macro here")) p) :: err.err_sub }
 			end else
 				old err;
 		);

+ 1 - 1
src/typing/fields.ml

@@ -249,7 +249,7 @@ let field_access ctx mode f fh e pfield =
 				(match e.eexpr with TLocal _ when Common.defined ctx.com Define.Haxe3Compat -> warning ctx WTemp "Field set has changed here in Haxe 4: call setter explicitly to keep Haxe 3.x behaviour" pfield | _ -> ());
 				if not (is_physical_field f) then begin
 					display_error_ext ctx.com (make_error (Custom "This field cannot be accessed because it is not a real variable") ~sub:[
-						make_error ~depth:1 (Custom "Add @:isVar here to enable it") f.cf_pos
+						make_error (Custom "Add @:isVar here to enable it") f.cf_pos
 					] pfield);
 				end;
 				normal false

+ 1 - 1
src/typing/forLoop.ml

@@ -119,7 +119,7 @@ module IterationKind = struct
 						| Some e -> e
 						| None ->
 							if resume then raise Not_found;
-							display_error_ext ctx.com (make_error ~depth:err.err_depth ~sub:[err] (Custom "Field iterator has an invalid type") acc_expr.epos);
+							display_error_ext ctx.com (make_error ~sub:[err] (Custom "Field iterator has an invalid type") acc_expr.epos);
 							mk (TConst TNull) t_dynamic p
 					)
 			in

+ 6 - 7
src/typing/generic.ml

@@ -358,7 +358,7 @@ let build_generic_class ctx c p tl =
 								begin match cf_old.cf_kind with
 									| Method _ when not (has_class_flag c CInterface) && not (has_class_flag c CExtern) && not (has_class_field_flag cf_old CfAbstract) ->
 										display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Field %s has no expression (possible typing order issue)" cf_new.cf_name)) ~sub:([
-											(make_error ~depth:1 (Custom (compl_msg (Printf.sprintf "While building %s" (s_type_path cg.cl_path)))) p)
+											(make_error (Custom (compl_msg (Printf.sprintf "While building %s" (s_type_path cg.cl_path)))) p)
 										]) cf_new.cf_pos);
 									| _ ->
 										()
@@ -459,10 +459,9 @@ let type_generic_function ctx fa fcc with_type p =
 	let params = extract_type_parameters monos in
 	let unify_existing_field tcf pcf = try
 		unify_raise tcf fc_type p
-	with Error ({ err_message = Unify _; err_depth = depth } as err) ->
+	with Error ({ err_message = Unify _ } as err) ->
 		raise (Error { err with err_sub = (make_error
-			~depth
-			~sub:[make_error ~depth:(depth+1) (Custom (compl_msg "Conflicting field was defined here")) pcf]
+			~sub:[make_error (Custom (compl_msg "Conflicting field was defined here")) pcf]
 			(Custom ("Cannot create field " ^ name ^ " due to type mismatch"))
 			p
 		) :: err.err_sub })
@@ -492,7 +491,7 @@ let type_generic_function ctx fa fcc with_type p =
 			let rec check e = match e.eexpr with
 				| TNew({cl_kind = KTypeParameter _} as c,_,_) when not (TypeloadCheck.is_generic_parameter ctx c) ->
 					display_error_ext ctx.com (make_error (Custom "Only generic type parameters can be constructed") ~sub:([
-						(make_error ~depth:1 (Custom (compl_msg "While specializing this call")) p)
+						(make_error (Custom (compl_msg "While specializing this call")) p)
 					]) e.epos);
 				| _ ->
 					Type.iter check e
@@ -504,8 +503,8 @@ let type_generic_function ctx fa fcc with_type p =
 						Printf.sprintf "%s = %s" ttp.ttp_name (st t)
 					) cf.cf_params monos in
 					let sub = [
-						(Error.make_error ~depth:1 (Custom (Printf.sprintf "Mapping: %s" (String.concat ", " mappings))) p);
-						(Error.make_error ~depth:1 (Custom (Printf.sprintf "For function %s.%s" (s_type_path c.cl_path) cf.cf_name)) p);
+						(Error.make_error (Custom (Printf.sprintf "Mapping: %s" (String.concat ", " mappings))) p);
+						(Error.make_error (Custom (Printf.sprintf "For function %s.%s" (s_type_path c.cl_path) cf.cf_name)) p);
 					] in
 					display_error_ext ctx.com (Error.make_error ~sub (Custom "Recursive @:generic function") p); None;
 				| Some e ->

+ 2 - 2
src/typing/macroContext.ml

@@ -469,7 +469,7 @@ let make_macro_api ctx mctx p =
 					let m = ctx.com.module_lut#find mpath in
 					let pos = { pfile = (Path.UniqueKey.lazy_path m.m_extra.m_file); pmin = 0; pmax = 0 } in
 					Interp.compiler_error (make_error ~sub:[
-						make_error ~depth:1 (Custom "Previously defined here") pos
+						make_error (Custom "Previously defined here") pos
 					] (Custom (Printf.sprintf "Cannot redefine module %s" (s_type_path mpath))) p);
 				with Not_found ->
 					ctx.com.cs#taint_module mpath DefineType;
@@ -506,7 +506,7 @@ let make_macro_api ctx mctx p =
 				if m != ctx.m.curmod then begin
 					let pos = { pfile = (Path.UniqueKey.lazy_path m.m_extra.m_file); pmin = 0; pmax = 0 } in
 					Interp.compiler_error (make_error ~sub:[
-						make_error ~depth:1 (Custom "Previously defined here") pos
+						make_error (Custom "Previously defined here") pos
 					] (Custom (Printf.sprintf "Cannot redefine module %s" (s_type_path mpath))) p);
 				end else
 					ignore(TypeloadModule.type_types_into_module ctx.com ctx.g ctx.m.curmod types pos)

+ 2 - 2
src/typing/operators.ml

@@ -421,13 +421,13 @@ let find_abstract_binop_overload ctx op e1 e2 a c tl left is_assign_op p =
 				let t_expected = BinopResult.get_type result in
 				begin try
 					unify_raise tret t_expected p
-				with Error { err_message = Unify _; err_depth = depth } ->
+				with Error { err_message = Unify _ } ->
 					match follow tret with
 						| TAbstract(a,tl) when type_iseq (Abstract.get_underlying_type a tl) t_expected ->
 							()
 						| _ ->
 							let st = s_type (print_context()) in
-							raise_typing_error ~depth (Printf.sprintf "The result of this operation (%s) is not compatible with declared return type %s" (st t_expected) (st tret)) p
+							raise_typing_error (Printf.sprintf "The result of this operation (%s) is not compatible with declared return type %s" (st t_expected) (st tret)) p
 				end;
 			end;
 			(*

+ 6 - 6
src/typing/typeload.ml

@@ -56,7 +56,7 @@ let check_field_access ctx cff =
 				let _,p2 = List.find (fun (access',_) -> access = access') acc in
 				if p1 <> null_pos && p2 <> null_pos then begin
 					display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Duplicate access modifier %s" (Ast.s_access access))) ~sub:([
-						(make_error ~depth:1 (Custom (compl_msg "Previously defined here")) p2);
+						(make_error (Custom (compl_msg "Previously defined here")) p2);
 					]) p1);
 				end;
 				loop p1 acc l
@@ -65,7 +65,7 @@ let check_field_access ctx cff =
 					begin try
 						let _,p2 = List.find (fun (access',_) -> match access' with APublic | APrivate -> true | _ -> false) acc in
 						display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access))) ~sub:([
-							(make_error ~depth:1 (Custom (compl_msg "Conflicts with this")) p2);
+							(make_error (Custom (compl_msg "Conflicts with this")) p2);
 						]) p1);
 						loop p1 acc l
 					with Not_found ->
@@ -222,8 +222,8 @@ let is_redefined ctx cf1 fields p =
 		let st = s_type (print_context()) in
 		if not (type_iseq cf1.cf_type cf2.cf_type) then begin
 			raise_typing_error_ext (make_error (Custom ("Cannot redefine field " ^ cf1.cf_name ^ " with different type")) ~sub:([
-				(make_error ~depth:1 (Custom (compl_msg ("Second type was " ^ (st cf2.cf_type)))) cf2.cf_pos);
-				(make_error ~depth:1 (Custom (compl_msg ("First type was " ^ (st cf1.cf_type)))) cf1.cf_pos);
+				(make_error (Custom (compl_msg ("Second type was " ^ (st cf2.cf_type)))) cf2.cf_pos);
+				(make_error (Custom (compl_msg ("First type was " ^ (st cf1.cf_type)))) cf1.cf_pos);
 			]) p)
 		end else
 			true
@@ -836,7 +836,7 @@ let init_core_api ctx c =
 					raise_typing_error "Type parameters must have the same number of constraints as core type" c.cl_pos
 				| Unify_error l ->
 					display_error_ext ctx.com (make_error (Custom ("Type parameter " ^ ttp2.ttp_name ^ " has different constraint than in core type")) ~sub:([
-						(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) c.cl_pos);
+						(make_error (Custom (compl_msg (error_msg (Unify l)))) c.cl_pos);
 					]) c.cl_pos);
 		) ccore.cl_params c.cl_params;
 	with Invalid_argument _ ->
@@ -851,7 +851,7 @@ let init_core_api ctx c =
 			type_eq EqCoreType (apply_params ccore.cl_params (extract_param_types c.cl_params) f.cf_type) f2.cf_type
 		with Unify_error l ->
 			display_error_ext ctx.com (make_error (Custom ("Field " ^ f.cf_name ^ " has different type than in core type")) ~sub:([
-				(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) p);
+				(make_error (Custom (compl_msg (error_msg (Unify l)))) p);
 			]) p));
 		if (has_class_field_flag f2 CfPublic) <> (has_class_field_flag f CfPublic) then raise_typing_error ("Field " ^ f.cf_name ^ " has different visibility than core type") p;
 		(match f2.cf_doc with

+ 7 - 7
src/typing/typeloadCheck.ml

@@ -131,7 +131,7 @@ let copy_meta meta_src meta_target sl =
 let check_native_name_override ctx child base =
 	let error base_pos child_pos =
 		display_error_ext ctx.com (make_error (Custom ("Field " ^ child.cf_name ^ " has different @:native value than in superclass")) ~sub:([
-			(make_error ~depth:1 (Custom (compl_msg "Base field is defined here")) base_pos)
+			(make_error (Custom (compl_msg "Base field is defined here")) base_pos)
 		]) child_pos);
 	in
 	try
@@ -188,8 +188,8 @@ let check_override_field ctx p rctx =
 	with
 		Unify_error l ->
 			display_error_ext ctx.com (make_error (Custom ("Field " ^ i ^ " overrides parent class with different or incomplete type")) ~sub:([
-				(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) p);
-				(make_error ~depth:1 (Custom (compl_msg "Base field is defined here")) rctx.cf_old.cf_name_pos);
+				(make_error (Custom (compl_msg (error_msg (Unify l)))) p);
+				(make_error (Custom (compl_msg "Base field is defined here")) rctx.cf_old.cf_name_pos);
 			]) p)
 
 let find_override_field ctx c_new cf_new c_old tl get_super_field is_overload p =
@@ -399,8 +399,8 @@ module Inheritance = struct
 						Unify_error l ->
 							if not ((has_class_flag c CExtern)) then begin
 								display_error_ext com (make_error (Custom ("Field " ^ f.cf_name ^ " has different type than in " ^ s_type_path intf.cl_path)) ~sub:([
-									(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) p);
-									(make_error ~depth:1 (Custom (compl_msg "Interface field is defined here")) f.cf_name_pos);
+									(make_error (Custom (compl_msg (error_msg (Unify l)))) p);
+									(make_error (Custom (compl_msg "Interface field is defined here")) f.cf_name_pos);
 								]) p)
 							end
 				)
@@ -496,7 +496,7 @@ module Inheritance = struct
 					| t ->
 						s_type pctx t
 				in
-				make_error ~depth:1 (Custom (compl_msg (Printf.sprintf "%s(%s)" cf.cf_name s))) cf.cf_name_pos
+				make_error (Custom (compl_msg (Printf.sprintf "%s(%s)" cf.cf_name s))) cf.cf_name_pos
 			) !missing in
 			let singular = match l with [_] -> true | _ -> false in
 			let sub = [make_error (Custom (Printf.sprintf "Implement %s or make %s abstract as well" (if singular then "it" else "them") (s_type_path c.cl_path))) ~sub c.cl_name_pos] in
@@ -644,7 +644,7 @@ let check_final_vars ctx e =
 		if Hashtbl.length final_vars > 0 then begin
 			let sub = List.filter_map (fun (c,cf) ->
 				if Hashtbl.mem final_vars cf.cf_name then
-					Some (make_error ~depth:1 (Custom "Uninitialized field") cf.cf_name_pos)
+					Some (make_error (Custom "Uninitialized field") cf.cf_name_pos)
 				else
 					None
 			) (DynArray.to_list ordered_fields) in

+ 6 - 6
src/typing/typeloadFields.ml

@@ -313,7 +313,7 @@ let build_enum_abstract ctx c a fields p =
 					()
 				| VPublic(access,p2) | VPrivate(access,p2) ->
 					display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access))) ~sub:[
-						make_error ~depth:1 (Custom (compl_msg "Conflicts with this")) p2;
+						make_error (Custom (compl_msg "Conflicts with this")) p2;
 					] p1)
 			in
 			let rec loop visibility acc = match acc with
@@ -554,7 +554,7 @@ let create_typer_context_for_field ctx cctx fctx cff cf =
 			invalid_modifier_combination fctx ctx.com fctx "abstract" "inline" (pos cff.cff_name)
 		else if not (has_class_flag c CAbstract) then begin
 			display_error_ext ctx.com (make_error (Custom "This class should be declared abstract because it has at least one abstract field") ~sub:[
-				make_error ~depth:1 (Custom (compl_msg "First abstract field was here")) (pos cff.cff_name);
+				make_error (Custom (compl_msg "First abstract field was here")) (pos cff.cff_name);
 			] c.cl_name_pos);
 			add_class_flag c CAbstract;
 		end;
@@ -1401,7 +1401,7 @@ let create_property (ctx,cctx,fctx) c f cf (get,set,t,eo) p =
 					(match f2.cf_kind with
 						| Method MethMacro ->
 							display_error_ext ctx.com (make_error (Custom (f2.cf_name ^ ": Macro methods cannot be used as property accessor")) ~sub:[
-								make_error ~depth:1 (Custom (compl_msg (f2.cf_name ^ ": Accessor method is here"))) f2.cf_pos;
+								make_error (Custom (compl_msg (f2.cf_name ^ ": Accessor method is here"))) f2.cf_pos;
 							] p);
 						| _ -> ());
 					unify_raise t2 t f2.cf_pos;
@@ -1569,7 +1569,7 @@ let check_overload ctx f fs is_extern_class =
 			) fs
 		in
 		display_error_ext ctx.com (make_error (Custom ("Another overloaded field of same signature was already declared : " ^ f.cf_name)) ~sub:[
-			make_error ~depth:1 (Custom (compl_msg "The second field is declared here")) f2.cf_pos;
+			make_error (Custom (compl_msg "The second field is declared here")) f2.cf_pos;
 		] f.cf_pos);
 		false
 	with Not_found -> try
@@ -1587,7 +1587,7 @@ let check_overload ctx f fs is_extern_class =
 			"Another overloaded field of similar signature was already declared : " ^
 			f.cf_name ^
 			"\nThe signatures are different in Haxe, but not in the target language"
-		)) ~sub:[make_error ~depth:1 (Custom (compl_msg "The second field is declared here")) f2.cf_pos] f.cf_pos);
+		)) ~sub:[make_error (Custom (compl_msg "The second field is declared here")) f2.cf_pos] f.cf_pos);
 		false
 	with Not_found ->
 		true
@@ -1789,7 +1789,7 @@ let init_class ctx_c cctx c p herits fields =
 				display.module_diagnostics <- MissingFields diag :: display.module_diagnostics
 			end else begin
 				display_error_ext com (make_error (Custom "This class has uninitialized final vars, which requires a constructor") ~sub:[
-					make_error ~depth:1 (Custom "Example of an uninitialized final var") cf.cf_name_pos;
+					make_error (Custom "Example of an uninitialized final var") cf.cf_name_pos;
 				] p);
 			end
 		| _ ->

+ 1 - 1
src/typing/typeloadModule.ml

@@ -77,7 +77,7 @@ module ModuleLevel = struct
 			DeprecationCheck.check_is com ctx_m.m.curmod meta [] name meta p;
 			let error prev_pos =
 				raise_typing_error_ext (make_error (Custom ("Name " ^ name ^ " is already defined in this module")) ~sub:[
-					make_error ~depth:1 (Custom (compl_msg "Previous declaration here")) prev_pos
+					make_error (Custom (compl_msg "Previous declaration here")) prev_pos
 				] p);
 			in
 			DynArray.iter (fun t2 ->

+ 1 - 1
src/typing/typer.ml

@@ -1047,7 +1047,7 @@ and type_map_declaration ctx e1 el with_type p =
 		try
 			let p = Hashtbl.find keys e_key.eexpr in
 			raise_typing_error_ext (make_error (Custom "Duplicate key") ~sub:[
-				make_error ~depth:1 (Custom (compl_msg "Previously defined here")) p
+				make_error (Custom (compl_msg "Previously defined here")) p
 			] e_key.epos);
 		with Not_found ->
 			begin match e_key.eexpr with

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

@@ -30,6 +30,12 @@ enum Message {
 	Warning(msg:String, pos:Position);
 }
 
+typedef MacroError = {
+	msg:String,
+	pos:Position,
+	?sub:Array<MacroError>
+}
+
 /**
 	Context provides an API for macro programming.
 
@@ -47,24 +53,24 @@ class Context {
 		Displays a compilation error `msg` at the given `Position` `pos`
 		and aborts the current macro call.
 	**/
-	public static function error(msg:String, pos:Position, ?depth:Int = 0):Dynamic {
-		return load("error", 2)(msg, pos, depth);
+	public static function error(msg:String, pos:Position, ?sub:Array<MacroError>):Dynamic {
+		return load("error", 2)(msg, pos, sub);
 	}
 
 	/**
 		Displays a compilation error `msg` at the given `Position` `pos`
 		and aborts the compilation.
 	**/
-	public static function fatalError(msg:String, pos:Position, ?depth:Int = 0):Dynamic {
-		return load("fatal_error", 2)(msg, pos, depth);
+	public static function fatalError(msg:String, pos:Position, ?sub:Array<MacroError>):Dynamic {
+		return load("fatal_error", 2)(msg, pos, sub);
 	}
 
 	/**
 		Displays a compilation error `msg` at the given `Position` `pos`
 		without aborting the current macro call.
 	**/
-	public static function reportError(msg:String, pos:Position, ?depth:Int = 0):Void {
-		load("report_error", 2)(msg, pos, depth);
+	public static function reportError(msg:String, pos:Position, ?sub:Array<MacroError>):Void {
+		load("report_error", 2)(msg, pos, sub);
 	}
 
 	/**

+ 1 - 1
tests/misc/compile.hxml

@@ -1,4 +1,4 @@
 -p src
 #-D MISC_TEST_FILTER=4270
 -main Main
---interp
+--interp 

+ 13 - 0
tests/misc/projects/Issue12167/Main.hx

@@ -0,0 +1,13 @@
+#if !macro
+function main() testMacro();
+#end
+
+macro function testMacro() {
+	var p1 = (macro "1st position").pos;
+	var p2 = (macro "2nd position").pos;
+	haxe.macro.Context.error("Top level error", haxe.macro.Context.currentPos(), [
+		{msg: "1st sub error", pos: p1, sub: [{msg: "Nested sub error", pos: p2}]},
+		{msg: "2nd sub error", pos: p2}
+	]);
+	return macro null;
+}

+ 3 - 0
tests/misc/projects/Issue12167/compile-fail.hxml

@@ -0,0 +1,3 @@
+-main Main
+-D message.reporting=pretty
+-D message.no-color

+ 18 - 0
tests/misc/projects/Issue12167/compile-fail.hxml.stderr

@@ -0,0 +1,18 @@
+[ERROR] Main.hx:2: characters 17-28
+
+ 2 | function main() testMacro();
+   |                 ^^^^^^^^^^^
+   | Top level error
+
+    6 |  var p1 = (macro "1st position").pos;
+      |                  ^^^^^^^^^^^^^^
+      | 1st sub error
+
+       7 |  var p2 = (macro "2nd position").pos;
+         |                  ^^^^^^^^^^^^^^
+         | Nested sub error
+
+    7 |  var p2 = (macro "2nd position").pos;
+      |                  ^^^^^^^^^^^^^^
+      | 2nd sub error
+