Prechádzať zdrojové kódy

Constrained monomorphs (#9549)

* let's try this again

* try to reach parity with development for now

* update uncontroversial misc test failures

* refactor a bit

* 6810 is fine like that

the "Constraint check failure for test.T" is missing, but that's a separate problem. I think it gets eaten by the BetterErrors transformation.

* I need a green CI for this

* let's get dangerous

* disable for now

* update tests

* dodge lua test issue

* clean constraints once the monomorph is bound

* add constraint classification

* try to help Detective Haxe

* bind contextual monomorphs to structures

* don't use open structures

* enabled disabled tests

* reduce diff

* restructure a bit

* hold back on the merge changes because this isn't quite right yet

* reduce diff

* awkwardly test the merge thing again

* remove percall for now

* also hold back on overload issue for now

* fix recursive printing and use `Unknown<0> : Constraints` for now

* move check to right place

* remove unused value in Monomorph.close
Simon Krajewski 5 rokov pred
rodič
commit
0ddd26d86a

+ 29 - 0
src/context/typecore.ml

@@ -519,13 +519,42 @@ let merge_core_doc ctx mt =
 		end
 	| _ -> ())
 
+let check_constraints map params tl p =
+	List.iter2 (fun (_,t) tm ->
+		begin match follow t with
+		| TInst ({ cl_kind = KTypeParameter constr; cl_path = path; cl_name_pos = p; },_) ->
+			if constr <> [] then begin match tm with
+			| TMono mono ->
+				List.iter (fun t -> Monomorph.constrain_to_type mono (Some (s_type_path path)) (map t)) constr
+			| _ ->
+				let tm = map tm in
+				check_constraint (s_type_path path) (fun () ->
+					List.iter (fun t ->
+						Type.unify tm (map t)
+					) constr
+				)
+			end
+		| _ ->
+			assert false
+		end;
+	) params tl
+
 let spawn_constrained_monos ctx p map params =
 	let monos = List.map (fun (s,_) ->
 		let mono = Monomorph.create() in
 		TMono mono
 	) params in
+	let map t = map (apply_params params monos t) in
+	check_constraints map params monos p;
 	monos
 
+let safe_mono_close ctx m p =
+	try
+		Monomorph.close m
+	with
+		Unify_error l ->
+			raise_or_display ctx l p
+
 (* -------------- debug functions to activate when debugging typer passes ------------------------------- *)
 (*/*
 

+ 1 - 0
src/core/tFunctions.ml

@@ -4,6 +4,7 @@ open TType
 
 let monomorph_create_ref : (unit -> tmono) ref = ref (fun _ -> die "" __LOC__)
 let monomorph_bind_ref : (tmono -> t -> unit) ref = ref (fun _ _ -> die "" __LOC__)
+let monomorph_classify_constraints_ref : (tmono -> tmono_constraint_kind) ref = ref (fun _ -> die "" __LOC__)
 
 let has_meta m ml = List.exists (fun (m2,_,_) -> m = m2) ml
 let get_meta m ml = List.find (fun (m2,_,_) -> m = m2) ml

+ 12 - 1
src/core/tPrinting.ml

@@ -39,7 +39,12 @@ let rec s_type ctx t =
 			with Not_found ->
 				let id = List.length !ctx in
 				ctx := (t,id) :: !ctx;
-				Printf.sprintf "Unknown<%d>" id
+			let s_const = match !monomorph_classify_constraints_ref r with
+				| CUnknown -> ""
+				| CTypes tl -> " : " ^ String.concat " & " (List.map (fun (t,_) -> s_type ctx t) tl)
+				| CStructural(fields,_) -> " : " ^ s_type ctx (mk_anon ~fields (ref Closed))
+			in
+				Printf.sprintf "Unknown<%d>%s" id s_const
 			end
 		| Some t -> s_type ctx t)
 	| TEnum (e,tl) ->
@@ -101,6 +106,12 @@ and s_type_params ctx = function
 	| [] -> ""
 	| l -> "<" ^ String.concat ", " (List.map (s_type ctx) l) ^ ">"
 
+and s_constraint = function
+	| MMono(m,_) -> Printf.sprintf "MMono %s" (s_type_kind (TMono m))
+	| MField cf -> Printf.sprintf "MField %s" cf.cf_name
+	| MType(t,_) -> Printf.sprintf "MType %s" (s_type_kind t)
+	| MOpenStructure -> "MOpenStructure"
+
 let s_access is_read = function
 	| AccNormal -> "default"
 	| AccNo -> "null"

+ 12 - 0
src/core/tType.ml

@@ -45,8 +45,20 @@ type t =
 
 and tmono = {
 	mutable tm_type : t option;
+	mutable tm_constraints : tmono_constraint list;
 }
 
+and tmono_constraint =
+	| MMono of tmono * string option
+	| MField of tclass_field
+	| MType of t * string option
+	| MOpenStructure
+
+and tmono_constraint_kind =
+	| CUnknown
+	| CStructural of (string,tclass_field) PMap.t * bool
+	| CTypes of (t * string option) list
+
 and tlazy =
 	| LAvailable of t
 	| LProcessing of (unit -> t)

+ 112 - 3
src/core/tUnification.ml

@@ -53,14 +53,122 @@ let default_unification_context = {
 module Monomorph = struct
 	let create () = {
 		tm_type = None;
+		tm_constraints = [];
 	}
 
+	(* constraining *)
+
+	let add_constraint m constr =
+		m.tm_constraints <- constr :: m.tm_constraints
+
+	let constraint_of_type name t = match follow t with
+		| TMono m2 ->
+			[MMono(m2,name)]
+		| TAnon an when not (PMap.is_empty an.a_fields) ->
+			PMap.fold (fun cf l ->
+				(MField cf) :: l
+			) an.a_fields []
+		| _ ->
+			[MType(t,name)]
+
+	let constrain_to_type m name t =
+		List.iter (add_constraint m) (constraint_of_type name t)
+
+	let classify_constraints m =
+		let types = DynArray.create () in
+		let fields = ref PMap.empty in
+		let is_open = ref false in
+		let rec check constr = match constr with
+			| MMono(m2,name) ->
+				begin match m2.tm_type with
+				| None ->
+					()
+				| Some t ->
+					List.iter (fun constr -> check constr) (constraint_of_type name t)
+				end;
+			| MField cf ->
+				fields := PMap.add cf.cf_name cf !fields;
+			| MType(t2,name) ->
+				DynArray.add types (t2,name)
+			| MOpenStructure ->
+				is_open := true
+		in
+		List.iter check m.tm_constraints;
+		if DynArray.length types > 0 then
+			CTypes (DynArray.to_list types)
+		else if not (PMap.is_empty !fields) then
+			CStructural(!fields,!is_open)
+		else
+			CUnknown
+
+	let check_constraints m t = match classify_constraints m with
+		| CUnknown ->
+			()
+		| CTypes tl ->
+			List.iter (fun (t2,name) ->
+				let f () = (!unify_ref) default_unification_context t t2 in
+				match name with
+				| Some name -> check_constraint name f
+				| None -> f()
+			) tl
+		| CStructural(fields,is_open) ->
+			let t2 = mk_anon ~fields (ref Closed) in
+			(!unify_ref) default_unification_context t t2
+
+	(* binding *)
+
 	let do_bind m t =
 		(* assert(m.tm_type = None); *) (* TODO: should be here, but matcher.ml does some weird bind handling at the moment. *)
-		m.tm_type <- Some t
+		m.tm_type <- Some t;
+		m.tm_constraints <- []
 
 	let rec bind m t =
-		m.tm_type <- Some t
+		begin match t with
+		| TAnon _ when List.mem MOpenStructure m.tm_constraints ->
+			(* If we assign an open structure monomorph to another structure, the semantics want us to merge the
+			   fields. This is kinda weird, but that's how it has always worked. *)
+			constrain_to_type m None t;
+			ignore(close m)
+		| TMono m2 ->
+			begin match m2.tm_type with
+			| None ->
+				List.iter (fun constr -> m2.tm_constraints <- constr :: m2.tm_constraints) m.tm_constraints;
+				do_bind m t;
+			| Some t ->
+				bind m t
+			end
+		| _ ->
+			check_constraints m t;
+			do_bind m t
+		end
+
+	and close m = match m.tm_type with
+		| Some _ ->
+			()
+		| None -> match classify_constraints m with
+			| CUnknown ->
+				()
+			| CTypes [(t,_)] ->
+				do_bind m t;
+				()
+			| CTypes _ ->
+				()
+			| CStructural(fields,_) ->
+				let check_recursion cf =
+					let rec loop t = match t with
+					| TMono m2 when m == m2 ->
+						let pctx = print_context() in
+						let s = Printf.sprintf "%s appears in { %s: %s }" (s_type pctx t) cf.cf_name (s_type pctx cf.cf_type) in
+						raise (Unify_error [Unify_custom "Recursive type";Unify_custom s]);
+					| _ ->
+						TFunctions.map loop t
+					in
+					ignore(loop cf.cf_type);
+				in
+				(* We found a bunch of fields but no type, create a merged structure type and bind to that *)
+				PMap.iter (fun _ cf -> check_recursion cf) fields;
+				do_bind m (mk_anon ~fields (ref Closed));
+				()
 
 	let unbind m =
 		m.tm_type <- None
@@ -883,4 +991,5 @@ let type_eq_custom = type_eq
 let type_eq param = type_eq {default_unification_context with equality_kind = param}
 
 ;;
-unify_ref := unify_custom;;
+unify_ref := unify_custom;;
+monomorph_classify_constraints_ref := Monomorph.classify_constraints

+ 4 - 5
src/typing/calls.ml

@@ -375,11 +375,10 @@ let type_generic_function ctx (e,fa) el ?(using_param=None) with_type p =
 		| _ -> ()
 	end;
 	let el,_ = unify_call_args ctx el args ret p false false in
-	begin try
-		check_constraints ctx cf.cf_name cf.cf_params monos map false p
-	with Unify_error l ->
-		display_error ctx (error_msg (Unify l)) p
-	end;
+	List.iter (fun t -> match follow t with
+		| TMono m -> safe_mono_close ctx m p
+		| _ -> ()
+	) monos;
 	let el = match using_param with None -> el | Some e -> e :: el in
 	(try
 		let gctx = Generic.make_generic ctx cf.cf_params monos p in

+ 51 - 63
src/typing/fields.ml

@@ -57,65 +57,18 @@ let remove_constant_flag t callb =
 		restore();
 		raise e
 
-let check_constraints ctx tname tpl tl map delayed p =
-	List.iter2 (fun m (name,t) ->
-		match follow t with
-		| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] ->
-			let f = (fun() ->
-				List.iter (fun ct ->
-					try
-						Type.unify (map m) (map ct)
-					with Unify_error l ->
-						let l = Constraint_failure (tname ^ "." ^ name) :: l in
-						raise (Unify_error l)
-				) constr
-			) in
-			if delayed then
-				delay ctx PCheckConstraint (fun () -> try f() with Unify_error l -> display_error ctx (error_msg (Unify l)) p)
-			else
-				f()
-		| _ ->
-			()
-	) tl tpl
-
-let enum_field_type ctx en ef tl_en tl_ef p =
-	let map t = apply_params en.e_params tl_en (apply_params ef.ef_params tl_ef t) in
-	begin try
-		check_constraints ctx (s_type_path en.e_path) en.e_params tl_en map true p;
-		check_constraints ctx ef.ef_name ef.ef_params tl_ef map true p;
-	with Unify_error l ->
-		display_error ctx (error_msg (Unify l)) p
-	end;
+let enum_field_type ctx en ef p =
+	let tl_en = spawn_constrained_monos ctx p (fun t -> t) en.e_params in
+	let map = apply_params en.e_params tl_en in
+	let tl_ef = spawn_constrained_monos ctx p map ef.ef_params in
+	let map t = map (apply_params ef.ef_params tl_ef t) in
 	map ef.ef_type
 
-let add_constraint_checks ctx ctypes pl f tl p =
-	List.iter2 (fun m (name,t) ->
-		match follow t with
-		| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] ->
-			let constr = List.map (fun t ->
-				let t = apply_params f.cf_params tl t in
-				(* only apply params if not static : in that case no param is passed *)
-				let t = (if pl = [] then t else apply_params ctypes pl t) in
-				t
-			) constr in
-			delay ctx PCheckConstraint (fun() ->
-				List.iter (fun ct ->
-					try
-						(* if has_mono m then raise (Unify_error [Unify_custom "Could not resolve full type for constraint checks"; Unify_custom ("Type was " ^ (s_type (print_context()) m))]); *)
-						Type.unify m ct
-					with Unify_error l ->
-						display_error ctx (error_msg (Unify (Constraint_failure (f.cf_name ^ "." ^ name) :: l))) p;
-				) constr
-			);
-		| _ -> ()
-	) tl f.cf_params
-
 let field_type ctx c pl f p =
 	match f.cf_params with
 	| [] -> f.cf_type
 	| l ->
-		let monos = List.map (fun _ -> mk_mono()) l in
-		if not (Meta.has Meta.Generic f.cf_meta) then add_constraint_checks ctx c.cl_params pl f monos p;
+		let monos = spawn_constrained_monos ctx p (if pl = [] then (fun t -> t) else apply_params c.cl_params pl) f.cf_params in
 		apply_params l monos f.cf_type
 
 let get_constructor ctx c params p =
@@ -488,9 +441,8 @@ let rec type_field cfg ctx e i p mode =
 				| Statics c -> FStatic (c,f), field_type ctx c [] f p
 				| EnumStatics e ->
 					let ef = try PMap.find f.cf_name e.e_constrs with Not_found -> die "" __LOC__ in
-					let monos = List.map (fun _ -> mk_mono()) e.e_params in
-					let monos2 = List.map (fun _ -> mk_mono()) ef.ef_params in
-					FEnum (e,ef), enum_field_type ctx e ef monos monos2 p
+					let t = enum_field_type ctx e ef p in
+					FEnum (e,ef),t
 				| _ ->
 					match f.cf_params with
 					| [] ->
@@ -499,7 +451,6 @@ let rec type_field cfg ctx e i p mode =
 						(* handle possible constraints *)
 						let monos = spawn_constrained_monos ctx p (fun t -> t) f.cf_params in
 						let t = apply_params f.cf_params monos f.cf_type in
-						add_constraint_checks ctx [] [] f monos p;
 						FAnon f, t
 			) in
 			field_access ctx mode f fmode ft e p
@@ -525,15 +476,52 @@ let rec type_field cfg ctx e i p mode =
 				field_access ctx mode f (FAnon f) (Type.field_type f) e p
 		)
 	| TMono r ->
-		let f = {
+		let mk_field () = {
 			(mk_field i (mk_mono()) p null_pos) with
 			cf_kind = Var { v_read = AccNormal; v_write = (match mode with MSet -> AccNormal | MGet | MCall -> AccNo) };
 		} in
-		let x = ref Opened in
-		let t = mk_anon ~fields:(PMap.add i f PMap.empty) x in
-		ctx.opened <- x :: ctx.opened;
-		Monomorph.bind r t;
-		field_access ctx mode f (FAnon f) (Type.field_type f) e p
+		let access f =
+			field_access ctx mode f (FAnon f) (Type.field_type f) e p
+		in
+		begin match Monomorph.classify_constraints r with
+		| CStructural(fields,is_open) ->
+			begin try
+				let f = PMap.find i fields in
+				if is_open && mode = MSet then begin match f.cf_kind with
+					(* We previously inferred to read-only, but now we want to write. This can happen in cases like #8079. *)
+					| Var ({v_write = AccNo} as acc) -> f.cf_kind <- Var {acc with v_write = AccNormal}
+					| _ -> ()
+				end;
+				access f
+			with Not_found ->
+				if not is_open then
+					no_field()
+				else begin
+					let f = mk_field() in
+					Monomorph.add_constraint r (MField f);
+					access f
+				end
+			end
+		| CTypes tl ->
+			let rec loop tl = match tl with
+				| [] ->
+					no_field()
+				| (t,_) :: tl ->
+					try
+						type_field (TypeFieldConfig.with_resume cfg) ctx {e with etype = t} i p mode
+					with Not_found ->
+						loop tl
+			in
+			loop tl
+		| CUnknown ->
+			if not (List.exists (fun (m,_) -> m == r) ctx.monomorphs.perfunction) && not (ctx.untyped && ctx.com.platform = Neko) then begin
+				ctx.monomorphs.perfunction <- (r,p) :: ctx.monomorphs.perfunction;
+			end;
+			let f = mk_field() in
+			Monomorph.add_constraint r (MField f);
+			Monomorph.add_constraint r MOpenStructure;
+			access f
+		end
 	| TAbstract (a,pl) ->
 		let static_abstract_access_through_instance = ref false in
 		(try

+ 27 - 52
src/typing/typeload.ml

@@ -239,36 +239,6 @@ let resolve_position_by_path ctx path p =
 	let p = (t_infos mt).mt_pos in
 	raise_positions [p]
 
-let check_param_constraints ctx types t pl c p =
-	match follow t with
-	| TMono _ -> ()
-	| _ ->
-		let ctl = (match c.cl_kind with KTypeParameter l -> l | _ -> []) in
-		List.iter (fun ti ->
-			let ti = apply_params types pl ti in
-			let ti = (match follow ti with
-				| TInst ({ cl_kind = KGeneric } as c,pl) ->
-					(* if we solve a generic contraint, let's substitute with the actual generic instance before unifying *)
-					let _,_, f = ctx.g.do_build_instance ctx (TClassDecl c) p in
-					f pl
-				| _ -> ti
-			) in
-			try
-				unify_raise ctx t ti p
-			with Error(Unify l,p) ->
-				let fail() =
-					if not ctx.untyped then display_error ctx (error_msg (Unify (Constraint_failure (s_type_path c.cl_path) :: l))) p;
-				in
-				match follow t with
-				| TInst({cl_kind = KExpr e},_) ->
-					let e = type_expr {ctx with locals = PMap.empty} e (WithType.with_type ti) in
-					begin try unify_raise ctx e.etype ti p
-					with Error (Unify _,_) -> fail() end
-				| _ ->
-					fail()
-
-		) ctl
-
 let generate_args_meta com cls_opt add_meta args =
 	let values = List.fold_left (fun acc ((name,p),_,_,_,eo) -> match eo with Some e -> ((name,p,NoQuotes),e) :: acc | _ -> acc) [] args in
 	(match values with
@@ -337,16 +307,8 @@ let rec load_instance' ctx (t,p) allow_no_params =
 		let types , path , f = ctx.g.do_build_instance ctx mt p in
 		let is_rest = is_generic_build && (match types with ["Rest",_] -> true | _ -> false) in
 		if allow_no_params && t.tparams = [] && not is_rest then begin
-			let pl = ref [] in
-			pl := List.map (fun (name,t) ->
-				match follow t with
-				| TInst (c,_) ->
-					let t = mk_mono() in
-					if c.cl_kind <> KTypeParameter [] || is_generic then delay ctx PCheckConstraint (fun() -> check_param_constraints ctx types t (!pl) c p);
-					t;
-				| _ -> die "" __LOC__
-			) types;
-			f (!pl)
+			let monos = spawn_constrained_monos ctx p (fun t -> t) types in
+			f (monos)
 		end else if path = ([],"Dynamic") then
 			match t.tparams with
 			| [] -> t_dynamic
@@ -356,7 +318,7 @@ let rec load_instance' ctx (t,p) allow_no_params =
 			let is_java_rest = ctx.com.platform = Java && is_extern in
 			let is_rest = is_rest || is_java_rest in
 			if not is_rest && ctx.com.display.dms_error_policy <> EPIgnore && List.length types <> List.length t.tparams then error ("Invalid number of type parameters for " ^ s_type_path path) p;
-			let tparams = List.map (fun t ->
+			let load_param t =
 				match t with
 				| TPExpr e ->
 					let name = (match fst e with
@@ -372,9 +334,10 @@ let rec load_instance' ctx (t,p) allow_no_params =
 					c.cl_kind <- KExpr e;
 					TInst (c,[])
 				| TPType t -> load_complex_type ctx true t
-			) t.tparams in
+			in
 			let rec loop tl1 tl2 is_rest = match tl1,tl2 with
 				| t :: tl1,(name,t2) :: tl2 ->
+					let t = load_param t in
 					let check_const c =
 						let is_expression = (match t with TInst ({ cl_kind = KExpr _ },_) -> true | _ -> false) in
 						let expects_expression = name = "Const" || Meta.has Meta.Const c.cl_meta in
@@ -387,17 +350,9 @@ let rec load_instance' ctx (t,p) allow_no_params =
 					in
 					let is_rest = is_rest || name = "Rest" && is_generic_build in
 					let t = match follow t2 with
-						| TInst ({ cl_kind = KTypeParameter [] } as c, []) when not is_generic ->
-							check_const c;
-							t
 						| TInst (c,[]) ->
 							check_const c;
-							let r = exc_protect ctx (fun r ->
-								r := lazy_available t;
-								delay ctx PCheckConstraint (fun() -> check_param_constraints ctx types t tparams c p);
-								t
-							) "constraint" in
-							TLazy r
+							t
 						| _ -> die "" __LOC__
 					in
 					t :: loop tl1 tl2 is_rest
@@ -413,6 +368,7 @@ let rec load_instance' ctx (t,p) allow_no_params =
 					else
 						error ("Not enough type parameters for " ^ s_type_path path) p
 				| t :: tl,[] ->
+					let t = load_param t in
 					if is_rest then
 						t :: loop tl [] true
 					else if ctx.com.display.dms_error_policy = EPIgnore then
@@ -420,7 +376,26 @@ let rec load_instance' ctx (t,p) allow_no_params =
 					else
 						error ("Too many parameters for " ^ s_type_path path) p
 			in
-			let params = loop tparams types false in
+			let params = loop t.tparams types false in
+			if not is_rest then begin
+				let map t =
+					let t = apply_params types params t in
+					let t = (match follow t with
+						| TInst ({ cl_kind = KGeneric } as c,pl) ->
+							(* if we solve a generic contraint, let's substitute with the actual generic instance before unifying *)
+							let _,_, f = ctx.g.do_build_instance ctx (TClassDecl c) p in
+							f pl
+						| _ -> t
+					) in
+					t
+				in
+				delay ctx PCheckConstraint (fun () ->
+					try
+						check_constraints map types params p;
+					with Unify_error l ->
+						raise_error (Unify l) p
+				);
+			end;
 			f params
 		end
 	in

+ 1 - 0
src/typing/typeloadFunction.ml

@@ -224,6 +224,7 @@ let type_function ctx args ret fmode f do_display p =
 		| _ -> e
 	in
 	List.iter (fun r -> r := Closed) ctx.opened;
+	List.iter (fun (m,p) -> safe_mono_close ctx m p) ctx.monomorphs.perfunction;
 	if is_position_debug then print_endline ("typing:\n" ^ (Texpr.dump_with_pos "" e));
 	e , fargs
 

+ 2 - 8
src/typing/typer.ml

@@ -417,10 +417,8 @@ let rec type_ident_raise ctx i p mode =
 					try
 						let ef = PMap.find i e.e_constrs in
 						let et = type_module_type ctx t None p in
-						let monos = List.map (fun _ -> mk_mono()) e.e_params in
-						let monos2 = List.map (fun _ -> mk_mono()) ef.ef_params in
 						ImportHandling.mark_import_position ctx pt;
-						wrap (mk (TField (et,FEnum (e,ef))) (enum_field_type ctx e ef monos monos2 p) p)
+						wrap (mk (TField (et,FEnum (e,ef))) (enum_field_type ctx e ef p) p)
 					with
 						Not_found -> loop l
 		in
@@ -1029,7 +1027,6 @@ and type_binop2 ?(abstract_overload_only=false) ctx op (e1 : texpr) (e2 : Ast.ex
 								Type.type_eq EqStrict e2.etype t2;
 								AbstractCast.cast_or_unify_raise ctx t1 e1 p,e2
 							end in
-							check_constraints ctx "" cf.cf_params monos (apply_params a.a_params tl) false cf.cf_pos;
 							let check_null e t = if is_eq_op then match e.eexpr with
 								| TConst TNull when not (is_explicit_null t) -> raise (Unify_error [])
 								| _ -> ()
@@ -1798,10 +1795,7 @@ and type_new ctx path el with_type force_inline p =
 			let ct, f = get_constructor ctx c monos p in
 			ignore (unify_constructor_call c monos f ct);
 			begin try
-				let t = Generic.build_generic ctx c p monos in
-				let map = apply_params c.cl_params monos in
-				check_constraints ctx (s_type_path c.cl_path) c.cl_params monos map true p;
-				t
+				Generic.build_generic ctx c p monos
 			with Generic.Generic_Exception _ as exc ->
 				(* If we have an expected type, just use that (issue #3804) *)
 				begin match with_type with

+ 3 - 2
tests/misc/projects/Issue3781/compile-fail.hxml.stderr

@@ -1,2 +1,3 @@
-Main.hx:12: characters 9-11 : Constraint check failure for E1.T
-Main.hx:12: characters 9-11 : Main.T should be String
+Main.hx:12: characters 12-13 : Constraint check failure for E1.T
+Main.hx:12: characters 12-13 : Main.T should be String
+Main.hx:12: characters 12-13 : For function argument 'v'

+ 4 - 2
tests/misc/projects/Issue4775/compile1-fail.hxml.stderr

@@ -1,2 +1,4 @@
-Main1.hx:7: characters 3-27 : Constraint check failure for Contain.T
-Main1.hx:7: characters 3-27 : Main1 should be String
+Main1.hx:7: characters 15-26 : Constraint check failure for Contain.T
+Main1.hx:7: characters 15-26 : Main1 should be String
+Main1.hx:7: characters 15-26 : For function argument 't'
+Main1.hx:7: characters 3-27 : Could not determine type for parameter T

+ 8 - 4
tests/misc/projects/Issue5946/compile-fail.hxml.stderr

@@ -1,4 +1,8 @@
-Main.hx:5: characters 3-15 : Constraint check failure for downcast.S
-Main.hx:5: characters 3-15 : ITwo should be IOne
-Main.hx:4: characters 3-15 : Constraint check failure for downcast.S
-Main.hx:4: characters 3-15 : Two should be One
+Main.hx:4: characters 28-31 : Class<Two> should be Class<Unknown<0> : One>
+Main.hx:4: characters 28-31 : Constraint check failure for downcast.S
+Main.hx:4: characters 28-31 : Two should be One
+Main.hx:4: characters 28-31 : For function argument 'c'
+Main.hx:5: characters 29-33 : Class<ITwo> should be Class<Unknown<0> : IOne>
+Main.hx:5: characters 29-33 : Constraint check failure for downcast.S
+Main.hx:5: characters 29-33 : ITwo should be IOne
+Main.hx:5: characters 29-33 : For function argument 'c'

+ 8 - 4
tests/misc/projects/Issue6810/compile-fail.hxml.stderr

@@ -1,4 +1,8 @@
-Fail.hx:8: characters 3-7 : Constraint check failure for test.T
-Fail.hx:8: characters 3-7 : FakeVoid should be haxe.NotVoid
-Fail.hx:7: characters 3-7 : Constraint check failure for test.T
-Fail.hx:7: characters 3-7 : Void should be haxe.NotVoid
+Fail.hx:7: characters 8-12 : error: Void should be haxe.NotVoid
+Fail.hx:7: characters 8-12 :  have: (...) -> Void
+Fail.hx:7: characters 8-12 :  want: (...) -> haxe.NotVoid
+Fail.hx:7: characters 8-12 : For function argument 'f'
+Fail.hx:8: characters 8-16 : error: FakeVoid should be haxe.NotVoid
+Fail.hx:8: characters 8-16 :  have: (...) -> FakeVoid
+Fail.hx:8: characters 8-16 :  want: (...) -> haxe.NotVoid
+Fail.hx:8: characters 8-16 : For function argument 'f'

+ 3 - 2
tests/misc/projects/Issue6878/compile1-fail.hxml.stderr

@@ -1,2 +1,3 @@
-Main1.hx:4: characters 3-11 : Constraint check failure for test.T
-Main1.hx:4: characters 3-11 : Int should be String
+Main1.hx:4: characters 8-10 : Constraint check failure for test.T
+Main1.hx:4: characters 8-10 : Int should be String
+Main1.hx:4: characters 8-10 : For function argument 't'

+ 2 - 2
tests/misc/projects/Issue7997/compile-fail.hxml.stderr

@@ -1,2 +1,2 @@
-Main.hx:5: characters 9-18 : {+ field : Unknown<0> } should be {+ args : {+ field : Unknown<0> } }
-Main.hx:5: characters 9-18 : For function argument 'type'
+Main.hx:4: characters 4-19 : Recursive type
+Main.hx:4: characters 4-19 : Unknown<0> appears in { args: Unknown<0> : { field : Unknown<1>, args : Unknown<0> } }

+ 3 - 2
tests/misc/projects/Issue8787/compile1-fail.hxml.stderr

@@ -1,2 +1,3 @@
-Main1.hx:7: characters 3-6 : Constraint check failure for E.T
-Main1.hx:7: characters 3-6 : Int should be String
+Main1.hx:7: characters 7-9 : Constraint check failure for E.T
+Main1.hx:7: characters 7-9 : Int should be String
+Main1.hx:7: characters 7-9 : For function argument 't'

+ 3 - 2
tests/misc/projects/Issue8787/compile2-fail.hxml.stderr

@@ -1,2 +1,3 @@
-Main2.hx:7: characters 3-6 : Constraint check failure for C.T
-Main2.hx:7: characters 3-6 : Int should be String
+Main2.hx:7: characters 7-9 : Constraint check failure for C.T
+Main2.hx:7: characters 7-9 : Int should be String
+Main2.hx:7: characters 7-9 : For function argument 't'

+ 80 - 0
tests/unit/src/unit/TestConstrainedMonomorphs.hx

@@ -0,0 +1,80 @@
+package unit;
+
+import utest.Assert;
+
+private class MyNotString {
+	var s:String;
+
+	public function new(s:String) {
+		this.s = s;
+	}
+
+	public function toUpperCase() {
+		return new MyNotString(s.toUpperCase());
+	}
+
+	public function getString() {
+		return s;
+	}
+}
+
+#if java
+@:native("unit.DetectiveHaxeExtern")
+extern private class DetectiveHaxeExtern {
+	@:overload static function itWasYou(i1:Int, i2:Int):String;
+	@:overload static function itWasYou(s1:String, s2:String):String;
+	@:overload static function itWasYou(f1:Float, f2:Float):String;
+}
+
+@:native("unit.DetectiveHaxeExtern")
+@:keep
+private class DetectiveHaxeImplementation {
+	@:overload static function itWasYou(s1:String, s2:String) {
+		return s1 + s2;
+	}
+}
+#end
+
+class TestConstrainedMonomorphs extends Test {
+
+	function infer(arg) {
+		var s1:MyNotString = arg.toUpperCase();
+		var s:MyNotString = arg;
+		HelperMacros.typedAs(arg, (null : MyNotString));
+		return s.getString() + s1.getString();
+	}
+
+	function testNarrowingInference() {
+		eq("fooFOO", infer(new MyNotString("foo")));
+	}
+
+	#if todo
+	function testDetectiveHaxe() {
+		var a = null;
+		eq("nullfoo", DetectiveHaxeExtern.itWasYou(a, "foo"));
+	}
+	#end
+
+	static function merge<A:{}, B:{}, C:A & B>(a:A, b:B):C {
+		return cast {
+			foo: (cast a).foo,
+			bar: (cast b).bar
+		};
+	}
+
+	function testMergedConstraints() {
+		var a = merge({foo: 5}, {bar: "bar"});
+		eq(5, a.foo);
+		eq("bar", a.bar);
+		t(HelperMacros.typeError(a.oh));
+	}
+
+	public static function returnC<C:{foo:Int}>():C {
+		return notMerge();
+	}
+
+	public static function notMerge<C:{foo:Int}>():C {
+		return null;
+	}
+
+}

+ 2 - 1
tests/unit/src/unit/TestMain.hx

@@ -109,7 +109,8 @@ function main() {
 		new TestMapComprehension(),
 		new TestMacro(),
 		new TestKeyValueIterator(),
-		new TestFieldVariance()
+		new TestFieldVariance(),
+		new TestConstrainedMonomorphs()
 		//new TestUnspecified(),
 		//new TestRemoting(),
 	];

+ 2 - 0
tests/unit/src/unit/issues/Issue6379.hx

@@ -1,6 +1,7 @@
 package unit.issues;
 
 class Issue6379 extends unit.Test {
+	#if (!java && !lua)
 	function test() {
         eq(g("x_x").length, 2);
     }
@@ -10,4 +11,5 @@ class Issue6379 extends unit.Test {
 		g.bind("");
         return r;
 	}
+	#end
 }