From 175c1ca320f5f15f7c2ff1c016eb10fd249a69c5 Mon Sep 17 00:00:00 2001 From: RblSb Date: Tue, 13 Jun 2023 15:46:45 +0300 Subject: [PATCH 01/19] Fix `??` inference and precedence (#11252) --- src/syntax/parser.ml | 13 ++++---- src/typing/typer.ml | 15 ++++++--- tests/unit/src/unit/TestMain.hx | 4 ++- tests/unit/src/unit/TestNullCoalescing.hx | 40 ++++++++++++++++++----- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/syntax/parser.ml b/src/syntax/parser.ml index 4ea6a672516..43b913c54da 100644 --- a/src/syntax/parser.ml +++ b/src/syntax/parser.ml @@ -266,12 +266,13 @@ let precedence op = | OpAdd | OpSub -> 3, left | OpShl | OpShr | OpUShr -> 4, left | OpOr | OpAnd | OpXor -> 5, left - | OpEq | OpNotEq | OpGt | OpLt | OpGte | OpLte -> 6, left - | OpInterval -> 7, left - | OpBoolAnd -> 8, left - | OpBoolOr | OpNullCoal -> 9, left - | OpArrow -> 10, right - | OpAssign | OpAssignOp _ -> 11, right + | OpNullCoal -> 6, left + | OpEq | OpNotEq | OpGt | OpLt | OpGte | OpLte -> 7, left + | OpInterval -> 8, left + | OpBoolAnd -> 9, left + | OpBoolOr -> 10, left + | OpArrow -> 11, right + | OpAssign | OpAssignOp _ -> 12, right let is_higher_than_ternary = function | OpAssign | OpAssignOp _ | OpArrow -> false diff --git a/src/typing/typer.ml b/src/typing/typer.ml index 55f7d010ae3..c087f668169 100644 --- a/src/typing/typer.ml +++ b/src/typing/typer.ml @@ -1891,19 +1891,24 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = let vr = new value_reference ctx in let e1 = type_expr ctx (Expr.ensure_block e1) with_type in let e2 = type_expr ctx (Expr.ensure_block e2) (WithType.with_type e1.etype) in - let e1 = vr#as_var "tmp" {e1 with etype = ctx.t.tnull e1.etype} in + let tmin = unify_min ctx [e1; e2] in + let e1 = vr#as_var "tmp" {e1 with etype = ctx.t.tnull tmin} in let e_null = Builder.make_null e1.etype e1.epos in let e_cond = mk (TBinop(OpNotEq,e1,e_null)) ctx.t.tbool e1.epos in - let follow_null_once t = + let rec follow_null t = match t with - | TAbstract({a_path = [],"Null"},[t]) -> t + | TAbstract({a_path = [],"Null"},[t]) -> follow_null t | _ -> t in let iftype = if DeadEnd.has_dead_end e2 then - WithType.with_type (follow_null_once e1.etype) + WithType.with_type (follow_null e1.etype) else - WithType.WithType(e2.etype,None) + let t = match e2.etype with + | TAbstract({a_path = [],"Null"},[t]) -> tmin + | _ -> follow_null tmin + in + WithType.with_type t in let e_if = make_if_then_else ctx e_cond e1 e2 iftype p in vr#to_texpr e_if diff --git a/tests/unit/src/unit/TestMain.hx b/tests/unit/src/unit/TestMain.hx index a7688bef98e..8b6cd45cd76 100644 --- a/tests/unit/src/unit/TestMain.hx +++ b/tests/unit/src/unit/TestMain.hx @@ -75,6 +75,7 @@ function main() { new TestCasts(), new TestSyntaxModule(), new TestNull(), + new TestNullCoalescing(), new TestNumericCasts(), new TestHashMap(), new TestRest(), @@ -106,7 +107,8 @@ function main() { new TestOverloadsForEveryone(), new TestInterface(), new TestNaN(), - #if ((dce == "full") && !interp) new TestDCE(), + #if ((dce == "full") && !interp) + new TestDCE(), #end new TestMapComprehension(), new TestMacro(), diff --git a/tests/unit/src/unit/TestNullCoalescing.hx b/tests/unit/src/unit/TestNullCoalescing.hx index 0189e140458..76eb4895fbc 100644 --- a/tests/unit/src/unit/TestNullCoalescing.hx +++ b/tests/unit/src/unit/TestNullCoalescing.hx @@ -1,18 +1,25 @@ package unit; +private class A {} +private class B extends A {} +private class C extends A {} + @:nullSafety(StrictThreaded) class TestNullCoalescing extends Test { final nullInt:Null = null; + final nullFloat:Null = null; final nullBool:Null = null; final nullString:Null = null; var count = 0; + function call() { count++; return "_"; } function test() { + eq(true, 0 != 1 ?? 2); var a = call() ?? "default"; eq(count, 1); @@ -30,9 +37,10 @@ class TestNullCoalescing extends Test { final s:Int = if (nullInt == null) 2; else nullInt; final s = nullInt ?? 2; - // $type(testBool); // Bool - // $type(testNullBool); // Null - // $type(s); // Int + f(HelperMacros.isNullable(testBool)); + t(HelperMacros.isNullable(testNullBool)); + f(HelperMacros.isNullable(s)); + // nullsafety filter test: final shouldBeBool:Bool = testBool; if (testNullBool == null) {} final shouldBeInt:Int = s; @@ -54,10 +62,7 @@ class TestNullCoalescing extends Test { eq(arr[1], 1); eq(arr[2], 1); - final arr = [ - nullInt ?? 2, - 2 - ]; + final arr = [nullInt ?? 2, 2]; eq(arr[0], arr[1]); var a = [0 => nullInt ?? 0 + 100]; @@ -110,7 +115,8 @@ class TestNullCoalescing extends Test { } eq(item(1) ?? item(2) ?? item(3), 1); eq(arr.length, 1); - for (i => v in [1]) eq(arr[i], v); + for (i => v in [1]) + eq(arr[i], v); final arr = []; function item(n) { @@ -119,6 +125,22 @@ class TestNullCoalescing extends Test { } eq(item(1) ?? item(2) ?? item(3), null); eq(arr.length, 3); - for (i => v in [1, 2, 3]) eq(arr[i], v); + for (i => v in [1, 2, 3]) + eq(arr[i], v); + + var b:B = cast null; + var c:C = cast null; + var a = if (b != null) b else c; + var a = b ?? c; + eq("unit._TestNullCoalescing.A", HelperMacros.typeString(a)); + + var nullF = false ? nullFloat : 0; + var nullF2 = nullFloat ?? nullInt; + var notNullF = nullFloat ?? 0; + var notNullF2 = (1 : Null) ?? throw ""; + t(HelperMacros.isNullable(nullF)); + t(HelperMacros.isNullable(nullF2)); + f(HelperMacros.isNullable(notNullF)); + f(HelperMacros.isNullable(notNullF2)); } } From ac59c250b4264d56e56a4748e314634c32e2eaec Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Sun, 18 Jun 2023 07:30:22 +0200 Subject: [PATCH 02/19] [typer] fix double check --- src/context/common.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/context/common.ml b/src/context/common.ml index d56979fa496..5eecd221283 100644 --- a/src/context/common.ml +++ b/src/context/common.ml @@ -419,8 +419,7 @@ exception Abort of Error.error let ignore_error com = let b = com.display.dms_error_policy = EPIgnore in - if b then - if b then com.has_error <- true; + if b then com.has_error <- true; b (* Defines *) From 16a300f965f24b7ab59036a5e4d5424b2360bdde Mon Sep 17 00:00:00 2001 From: Rudy Ges Date: Mon, 19 Jun 2023 12:05:39 +0200 Subject: [PATCH 03/19] [display] Avoid display issues with missing fields (#11251) * [display] only raise completion items when in completion request * Don't check interfaces during display requests * Use dms_full_typing instead --- src/syntax/grammar.mly | 2 +- src/typing/typeloadCheck.ml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/syntax/grammar.mly b/src/syntax/grammar.mly index de7fecb7274..8ff085035e6 100644 --- a/src/syntax/grammar.mly +++ b/src/syntax/grammar.mly @@ -189,7 +189,7 @@ and parse_class_content doc meta flags n p1 s = let tl = parse_constraint_params s in let rec loop had_display p0 acc = let check_display p1 = - if not had_display && !in_display_file && display_position#enclosed_in p1 then + if not had_display && !in_display_file && !display_mode = DMDefault && display_position#enclosed_in p1 then syntax_completion (if List.mem HInterface n then SCInterfaceRelation else SCClassRelation) None (display_position#with_pos p1) in match s with parser diff --git a/src/typing/typeloadCheck.ml b/src/typing/typeloadCheck.ml index c7a4ac763f1..618b8838766 100644 --- a/src/typing/typeloadCheck.ml +++ b/src/typing/typeloadCheck.ml @@ -564,8 +564,10 @@ module Inheritance = struct purpose. However, we STILL have to delay the check because at the time pending is handled, the class is not built yet. See issue #10847. *) pending := (fun () -> delay ctx PConnectField check_interfaces_or_delay) :: !pending - | _ -> + | _ when ctx.com.display.dms_full_typing -> check_interfaces ctx c + | _ -> + () in if is_extends then begin if c.cl_super <> None then raise_typing_error "Cannot extend several classes" p; From 67092f93210acaf7fe714186e1ce5fafcb456413 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Mon, 26 Jun 2023 08:58:03 +0200 Subject: [PATCH 04/19] [filters] give better unbound variable error when renaming fails --- src/filters/renameVars.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/filters/renameVars.ml b/src/filters/renameVars.ml index b23c61139f9..b7c8b882c3e 100644 --- a/src/filters/renameVars.ml +++ b/src/filters/renameVars.ml @@ -223,6 +223,9 @@ let declare_var rc scope v = let will_be_reserved rc v = rc.rc_no_shadowing || (has_var_flag v VCaptured && rc.rc_hoisting) +let unbound_variable v = + raise (Failure (Printf.sprintf "Unbound variable: %s<%i>" v.v_name v.v_id)) + (** Invoked for each `TLocal v` texr_expr *) @@ -234,7 +237,7 @@ let rec determine_overlaps rc scope v = Overlaps.add v scope.foreign_vars; (match scope.parent with | Some parent -> determine_overlaps rc parent v - | None -> raise (Failure "Failed to locate variable declaration") + | None -> unbound_variable v ) | (d, _) :: _ when d == v -> () @@ -261,7 +264,7 @@ let use_var rc scope v = | Some parent -> loop parent | None -> - raise (Failure "Failed to locate variable declaration") + unbound_variable v end in loop scope From 2d63f9098b304d5703831c485c78eb4cf159e799 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Mon, 26 Jun 2023 09:06:12 +0200 Subject: [PATCH 05/19] Allow boolean operators in patterns (#11157) * [matcher] allow boolops in patterns * add test that definitely covers all corner cases --- src/typing/matcher/exprToPattern.ml | 3 +++ tests/unit/src/unit/issues/Issue11157.hx | 25 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/unit/src/unit/issues/Issue11157.hx diff --git a/src/typing/matcher/exprToPattern.ml b/src/typing/matcher/exprToPattern.ml index c8e10d64752..491bd6d9126 100644 --- a/src/typing/matcher/exprToPattern.ml +++ b/src/typing/matcher/exprToPattern.ml @@ -412,6 +412,9 @@ let rec make pctx toplevel t e = restore(); let pat = make pctx toplevel e1.etype e2 in PatExtractor {ex_var = v; ex_expr = e1; ex_pattern = pat} + | EBinop((OpEq | OpNotEq | OpLt | OpLte | OpGt | OpGte | OpBoolAnd | OpBoolOr),_,_) -> + let e_rhs = (EConst (Ident "true"),null_pos) in + loop (EBinop(OpArrow,e,e_rhs),(pos e)) (* Special case for completion on a pattern local: We don't want to add the local to the context while displaying (#7319) *) | EDisplay((EConst (Ident _),_ as e),dk) when pctx.ctx.com.display.dms_kind = DMDefault -> diff --git a/tests/unit/src/unit/issues/Issue11157.hx b/tests/unit/src/unit/issues/Issue11157.hx new file mode 100644 index 00000000000..3f2a47cff04 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11157.hx @@ -0,0 +1,25 @@ +package unit.issues; + +private enum E { + CInt(i:Int); + CString(s:String); +} + +class Issue11157 extends Test { + function process(e:E) { + return switch e { + case CInt(_ > 0 && _ < 12): + "in range"; + case CString(_.toLowerCase() == "foo"): + return "foo"; + case _: + return "something else"; + } + } + + function test() { + eq("in range", (process(CInt(11)))); + eq("something else", (process(CInt(12)))); + eq("foo", (process(CString("FOO")))); + } +} From 83b64122ae416d3473e836bb42f3edd8a3ae51e9 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 27 Jun 2023 08:18:20 +0200 Subject: [PATCH 06/19] [typer] fix custom array access temp var handling closes #11248 --- src/typing/operators.ml | 17 ++++++----------- tests/unit/src/unit/issues/Issue11248.hx | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 tests/unit/src/unit/issues/Issue11248.hx diff --git a/src/typing/operators.ml b/src/typing/operators.ml index e399bd42c2a..3c9d92f035d 100644 --- a/src/typing/operators.ml +++ b/src/typing/operators.ml @@ -704,18 +704,15 @@ let type_assign_op ctx op e1 e2 with_type p = let cf_get,tf_get,r_get,ekey = AbstractCast.find_array_read_access ctx a tl ekey p in (* bind complex keys to a variable so they do not make it into the output twice *) let save = save_locals ctx in - let maybe_bind_to_temp e = match Optimizer.make_constant_expression ctx e with - | Some e -> e,None - | None -> - let v = gen_local ctx e.etype p in - let e' = mk (TLocal v) e.etype p in - e', Some (mk (TVar (v,Some e)) ctx.t.tvoid p) + let vr = new value_reference ctx in + let maybe_bind_to_temp name e = match Optimizer.make_constant_expression ctx e with + | Some e -> e + | None -> vr#as_var name e in - let ekey,ekey' = maybe_bind_to_temp ekey in - let ebase,ebase' = maybe_bind_to_temp ebase in + let ebase = maybe_bind_to_temp "base" ebase in + let ekey = maybe_bind_to_temp "key" ekey in let eget = mk_array_get_call ctx (cf_get,tf_get,r_get,ekey) c ebase p in let eget = type_binop2 ctx op eget e2 true WithType.value p in - let vr = new value_reference ctx in let eget = BinopResult.to_texpr vr eget (fun e -> e) in unify ctx eget.etype r_get p; let cf_set,tf_set,r_set,ekey,eget = AbstractCast.find_array_write_access ctx a tl ekey eget p in @@ -727,8 +724,6 @@ let type_assign_op ctx op e1 e2 with_type p = | Some _,Some _ -> let ef_set = mk (TField(et,(FStatic(c,cf_set)))) tf_set p in let el = [make_call ctx ef_set [ebase;ekey;eget] r_set p] in - let el = match ebase' with None -> el | Some ebase -> ebase :: el in - let el = match ekey' with None -> el | Some ekey -> ekey :: el in begin match el with | [e] -> e | el -> mk (TBlock el) r_set p diff --git a/tests/unit/src/unit/issues/Issue11248.hx b/tests/unit/src/unit/issues/Issue11248.hx new file mode 100644 index 00000000000..f198354bdee --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11248.hx @@ -0,0 +1,18 @@ +package unit.issues; + +class Issue11248 extends unit.Test { + public static var BIT_A:UInt = 1; + public static var FLAG_1:Int = 0; + + static final _flags:Map = [0 => 1010]; + public static var flags(get, never):Map; + + public static function get_flags() { + return _flags; + } + + function test() { + flags[FLAG_1] ^= BIT_A; + eq(1011, flags[FLAG_1]); + } +} From e88ed6099f7dbea832c5719d01cdf73a9e5994ca Mon Sep 17 00:00:00 2001 From: Rudy Ges Date: Tue, 4 Jul 2023 08:55:30 +0200 Subject: [PATCH 07/19] [macro] add flags to TDAbstract to be able to construct enum abstracts (#11230) * [macro] add flags to TDAbstract to be able to construct enum abstracts * Remove remnants of AbExtern * Printer: handle null from/to arrays --- src/macro/macroApi.ml | 13 +++++++++++-- std/haxe/macro/Expr.hx | 24 +++++++++++++++++++++++- std/haxe/macro/Printer.hx | 21 +++++++++++++++++---- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index 1a2d668ab99..e825da0938e 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -1664,10 +1664,19 @@ let decode_type_def v = EClass (mk flags fields) | 3, [t] -> ETypedef (mk (if isExtern then [EExtern] else []) (decode_ctype t)) - | 4, [tthis;tfrom;tto] -> - let flags = match opt decode_array tfrom with None -> [] | Some ta -> List.map (fun t -> AbFrom (decode_ctype t)) ta in + | 4, [tthis;tflags;tfrom;tto] -> + let flags = match opt decode_array tflags with + | None -> [] + | Some ta -> List.map (fun f -> match decode_enum f with + | 0, [] -> AbEnum + | 1, [ct] -> AbFrom (decode_ctype ct) + | 2, [ct] -> AbTo (decode_ctype ct) + | _ -> raise Invalid_expr + ) ta in + let flags = match opt decode_array tfrom with None -> flags | Some ta -> List.map (fun t -> AbFrom (decode_ctype t)) ta @ flags in let flags = match opt decode_array tto with None -> flags | Some ta -> (List.map (fun t -> AbTo (decode_ctype t)) ta) @ flags in let flags = match opt decode_ctype tthis with None -> flags | Some t -> (AbOver t) :: flags in + let flags = if isExtern then AbExtern :: flags else flags in EAbstract(mk flags fields) | 5, [fk;al] -> let fk = decode_class_field_kind fk in diff --git a/std/haxe/macro/Expr.hx b/std/haxe/macro/Expr.hx index 778e0473772..30d7f00e3f5 100644 --- a/std/haxe/macro/Expr.hx +++ b/std/haxe/macro/Expr.hx @@ -1004,7 +1004,7 @@ enum TypeDefKind { /** Represents an abstract kind. **/ - TDAbstract(tthis:Null, ?from:Array, ?to:Array); + TDAbstract(tthis:Null, ?flags:Array, ?from:Array, ?to:Array); /** Represents a module-level field. @@ -1013,6 +1013,28 @@ enum TypeDefKind { } +/** + Represents an abstract flag. +**/ +enum AbstractFlag { + /** + Indicates that this abstract is an `enum abstract` + **/ + AbEnum; + + /** + Indicates that this abstract can be assigned from `ct`. + This flag can be added several times to add multiple "from" types. + **/ + AbFrom(ct:ComplexType); + + /** + Indicates that this abstract can be assigned to `ct`. + This flag can be added several times to add multiple "to" types. + **/ + AbTo(ct:ComplexType); +} + /** This error can be used to handle or produce compilation errors in macros. **/ diff --git a/std/haxe/macro/Printer.hx b/std/haxe/macro/Printer.hx index e931e68cbc5..6766acb3d0f 100644 --- a/std/haxe/macro/Printer.hx +++ b/std/haxe/macro/Printer.hx @@ -385,13 +385,26 @@ class Printer { case _: printComplexType(ct); }) + ";"; - case TDAbstract(tthis, from, to): - "abstract " + case TDAbstract(tthis, tflags, from, to): + var from = from == null ? [] : from.copy(); + var to = to == null ? [] : to.copy(); + var isEnum = false; + + for (flag in tflags) { + switch (flag) { + case AbEnum: isEnum = true; + case AbFrom(ct): from.push(ct); + case AbTo(ct): to.push(ct); + } + } + + (isEnum ? "enum " : "") + + "abstract " + t.name + ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "") + (tthis == null ? "" : "(" + printComplexType(tthis) + ")") - + (from == null ? "" : [for (f in from) " from " + printComplexType(f)].join("")) - + (to == null ? "" : [for (t in to) " to " + printComplexType(t)].join("")) + + [for (f in from) " from " + printComplexType(f)].join("") + + [for (f in to) " to " + printComplexType(f)].join("") + " {\n" + [ for (f in t.fields) { From 0182b7da0bf7328e7025180c2f0e859e859247e0 Mon Sep 17 00:00:00 2001 From: Zeta <53486764+Apprentice-Alchemist@users.noreply.github.com> Date: Tue, 4 Jul 2023 19:47:38 +0200 Subject: [PATCH 08/19] Make genhl respect Meta.NoExpr. (#11257) Closes https://github.com/HaxeFoundation/haxe/issues/11196 --- src/generators/genhl.ml | 9 +++++++-- tests/misc/hl/projects/Issue11196/Issue11196.hx | 3 +++ tests/misc/hl/projects/Issue11196/compile.hxml | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/misc/hl/projects/Issue11196/Issue11196.hx create mode 100644 tests/misc/hl/projects/Issue11196/compile.hxml diff --git a/src/generators/genhl.ml b/src/generators/genhl.ml index a6de801d3de..7db70613ea8 100644 --- a/src/generators/genhl.ml +++ b/src/generators/genhl.ml @@ -3329,7 +3329,10 @@ let generate_static ctx c f = let gen_content() = op ctx (OThrow (make_string ctx ("Requires compiling with -D hl-ver=" ^ ver ^ ".0 or higher") null_pos)); in - ignore(make_fun ctx ~gen_content (s_type_path c.cl_path,f.cf_name) (alloc_fid ctx c f) (match f.cf_expr with Some { eexpr = TFunction f } -> f | _ -> abort "Missing function body" f.cf_pos) None None) + (match f.cf_expr with + | Some { eexpr = TFunction fn } -> ignore(make_fun ctx ~gen_content (s_type_path c.cl_path,f.cf_name) (alloc_fid ctx c f) fn None None) + | _ -> if not (Meta.has Meta.NoExpr f.cf_meta) then abort "Missing function body" f.cf_pos) + else add_native "std" f.cf_name | (Meta.HlNative,[] ,_ ) :: _ -> @@ -3337,7 +3340,9 @@ let generate_static ctx c f = | (Meta.HlNative,_ ,p) :: _ -> abort "Invalid @:hlNative decl" p | [] -> - ignore(make_fun ctx (s_type_path c.cl_path,f.cf_name) (alloc_fid ctx c f) (match f.cf_expr with Some { eexpr = TFunction f } -> f | _ -> abort "Missing function body" f.cf_pos) None None) + (match f.cf_expr with + | Some { eexpr = TFunction fn } -> ignore(make_fun ctx (s_type_path c.cl_path,f.cf_name) (alloc_fid ctx c f) fn None None) + | _ -> if not (Meta.has Meta.NoExpr f.cf_meta) then abort "Missing function body" f.cf_pos) | _ :: l -> loop l in diff --git a/tests/misc/hl/projects/Issue11196/Issue11196.hx b/tests/misc/hl/projects/Issue11196/Issue11196.hx new file mode 100644 index 00000000000..9126da39de6 --- /dev/null +++ b/tests/misc/hl/projects/Issue11196/Issue11196.hx @@ -0,0 +1,3 @@ +function main() { + var a:hl.I64 = 5; +} \ No newline at end of file diff --git a/tests/misc/hl/projects/Issue11196/compile.hxml b/tests/misc/hl/projects/Issue11196/compile.hxml new file mode 100644 index 00000000000..49a5d4098e6 --- /dev/null +++ b/tests/misc/hl/projects/Issue11196/compile.hxml @@ -0,0 +1,3 @@ +-m Issue11196 +-hl out.hl +-dce no \ No newline at end of file From b5048e7d9d19df33f2b16ac060e0daa33602aeb6 Mon Sep 17 00:00:00 2001 From: Zeta <53486764+Apprentice-Alchemist@users.noreply.github.com> Date: Wed, 5 Jul 2023 08:23:00 +0200 Subject: [PATCH 09/19] Add a few extra reserved keywords to gencpp (#11268) Closes https://github.com/HaxeFoundation/hxcpp/issues/1050 --- src/generators/gencpp.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/generators/gencpp.ml b/src/generators/gencpp.ml index 48cbb065e39..e417bbbb718 100644 --- a/src/generators/gencpp.ml +++ b/src/generators/gencpp.ml @@ -393,7 +393,12 @@ let keyword_remap name = | "HX_" | "HXLINE" | "HXDLIN" | "NO" | "YES" | "abstract" | "decltype" | "finally" | "nullptr" | "static_assert" - | "struct" -> "_hx_" ^ name + | "struct" | "_Atomic" + | "constexpr" | "consteval" | "constinit" + | "co_await" | "co_return" | "co_yield" + | "alignas" | "alignof" + | "_Alignas" | "_Alignof" + | "requires" -> "_hx_" ^ name | x -> x ;; From 71f36636dc75c3a767d70a8521506b8921ee0791 Mon Sep 17 00:00:00 2001 From: RblSb Date: Wed, 5 Jul 2023 09:23:41 +0300 Subject: [PATCH 10/19] Make break/continue exprs not-nullable (#11269) --- src/typing/nullSafety.ml | 2 ++ tests/nullsafety/src/cases/TestLoose.hx | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/typing/nullSafety.ml b/src/typing/nullSafety.ml index d411fcb5a95..b0683bce6d7 100644 --- a/src/typing/nullSafety.ml +++ b/src/typing/nullSafety.ml @@ -1041,6 +1041,8 @@ class expr_checker mode immediate_execution report = | TMeta (_, e) -> self#is_nullable_expr e | TThrow _ -> false | TReturn _ -> false + | TContinue -> false + | TBreak -> false | TBinop ((OpAssign | OpAssignOp _), _, right) -> self#is_nullable_expr right | TBlock exprs -> local_safety#block_declared; diff --git a/tests/nullsafety/src/cases/TestLoose.hx b/tests/nullsafety/src/cases/TestLoose.hx index 233f9c778d7..704f6521abd 100644 --- a/tests/nullsafety/src/cases/TestLoose.hx +++ b/tests/nullsafety/src/cases/TestLoose.hx @@ -113,4 +113,11 @@ class TestLoose { } shouldFail(if (foo()) {}); } + + static function nullCoal_continue_shouldPass():Void { + for (i in 0...1) { + var i:String = staticVar ?? continue; + var i2:String = staticVar ?? break; + } + } } From a8e181c2818b76be1aeaf541de3ac6ec4b79bf45 Mon Sep 17 00:00:00 2001 From: Rudy Ges Date: Wed, 5 Jul 2023 09:36:55 +0200 Subject: [PATCH 11/19] [hl/eval/neko] Fix exception stack when wrapping native exceptions (#11249) * [tests] add test for #11247 * Fix for #11247 * [hl] restore callstack fix * [tests] cpp doesn't like native exceptions in those tests either * [tests] only run new test for eval/hl/neko * Fix #11265 * [tests] don't compile test on target that won't run it --- src/filters/exceptions.ml | 6 ++-- std/cpp/_std/haxe/Exception.hx | 11 ++++++- std/eval/_std/haxe/Exception.hx | 11 ++++++- std/hl/_std/haxe/Exception.hx | 11 ++++++- std/hl/_std/haxe/NativeStackTrace.hx | 2 +- std/neko/_std/haxe/Exception.hx | 11 ++++++- tests/unit/src/unit/TestExceptions.hx | 41 ++++++++++++++++++++++----- 7 files changed, 79 insertions(+), 14 deletions(-) diff --git a/src/filters/exceptions.ml b/src/filters/exceptions.ml index fa4b0182928..d092e890d7d 100644 --- a/src/filters/exceptions.ml +++ b/src/filters/exceptions.ml @@ -480,9 +480,11 @@ let catch_native ctx catches t p = ) (* Haxe-specific wildcard catches should go to if-fest because they need additional handling *) | (v,_) :: _ when is_haxe_wildcard_catch ctx v.v_type -> - (match handle_as_value_exception with - | [] -> + (match handle_as_value_exception, value_exception_catch with + | [], None -> catches_to_ifs ctx catches t p + | [], Some catch -> + catches_to_ifs ctx [catch] t p | _ -> catches_as_value_exception ctx handle_as_value_exception None t p :: catches_to_ifs ctx catches t p diff --git a/std/cpp/_std/haxe/Exception.hx b/std/cpp/_std/haxe/Exception.hx index 4c244a7e34d..92fbaf553bd 100644 --- a/std/cpp/_std/haxe/Exception.hx +++ b/std/cpp/_std/haxe/Exception.hx @@ -19,7 +19,10 @@ class Exception { if(Std.isOfType(value, Exception)) { return value; } else { - return new ValueException(value, null, value); + var e = new ValueException(value, null, value); + // Undo automatic __shiftStack() + e.__unshiftStack(); + return e; } } @@ -63,6 +66,12 @@ class Exception { __skipStack++; } + @:noCompletion + @:ifFeature("haxe.Exception.get_stack") + inline function __unshiftStack():Void { + __skipStack--; + } + function get_message():String { return __exceptionMessage; } diff --git a/std/eval/_std/haxe/Exception.hx b/std/eval/_std/haxe/Exception.hx index 814370e7ac8..48467a04484 100644 --- a/std/eval/_std/haxe/Exception.hx +++ b/std/eval/_std/haxe/Exception.hx @@ -18,7 +18,10 @@ class Exception { if(Std.isOfType(value, Exception)) { return value; } else { - return new ValueException(value, null, value); + var e = new ValueException(value, null, value); + // Undo automatic __shiftStack() + e.__unshiftStack(); + return e; } } @@ -63,6 +66,12 @@ class Exception { __skipStack++; } + @:noCompletion + @:ifFeature("haxe.Exception.get_stack") + inline function __unshiftStack():Void { + __skipStack--; + } + function get_message():String { return __exceptionMessage; } diff --git a/std/hl/_std/haxe/Exception.hx b/std/hl/_std/haxe/Exception.hx index 8f3ae5fc734..68734c49586 100644 --- a/std/hl/_std/haxe/Exception.hx +++ b/std/hl/_std/haxe/Exception.hx @@ -18,7 +18,10 @@ class Exception { if(Std.isOfType(value, Exception)) { return value; } else { - return new ValueException(value, null, value); + var e = new ValueException(value, null, value); + // Undo automatic __shiftStack() + e.__unshiftStack(); + return e; } } @@ -62,6 +65,12 @@ class Exception { __skipStack++; } + @:noCompletion + @:ifFeature("haxe.Exception.get_stack") + inline function __unshiftStack():Void { + __skipStack--; + } + function get_message():String { return __exceptionMessage; } diff --git a/std/hl/_std/haxe/NativeStackTrace.hx b/std/hl/_std/haxe/NativeStackTrace.hx index e7cf077a142..eada6faf0be 100644 --- a/std/hl/_std/haxe/NativeStackTrace.hx +++ b/std/hl/_std/haxe/NativeStackTrace.hx @@ -29,7 +29,7 @@ class NativeStackTrace { var count = callStackRaw(null); var arr = new NativeArray(count); callStackRaw(arr); - return arr; + return arr.sub(1, arr.length - 1); } @:hlNative("std", "exception_stack_raw") diff --git a/std/neko/_std/haxe/Exception.hx b/std/neko/_std/haxe/Exception.hx index 1302231c141..0579eb835ab 100644 --- a/std/neko/_std/haxe/Exception.hx +++ b/std/neko/_std/haxe/Exception.hx @@ -18,7 +18,10 @@ class Exception { if(Std.isOfType(value, Exception)) { return value; } else { - return new ValueException(value, null, value); + var e = new ValueException(value, null, value); + // Undo automatic __shiftStack() + e.__unshiftStack(); + return e; } } @@ -63,6 +66,12 @@ class Exception { __skipStack++; } + @:noCompletion + @:ifFeature("haxe.Exception.get_stack") + inline function __unshiftStack():Void { + __skipStack--; + } + function get_message():String { return __exceptionMessage; } diff --git a/tests/unit/src/unit/TestExceptions.hx b/tests/unit/src/unit/TestExceptions.hx index 98fd5e13a48..6fb5580af80 100644 --- a/tests/unit/src/unit/TestExceptions.hx +++ b/tests/unit/src/unit/TestExceptions.hx @@ -1,4 +1,4 @@ -package unit; +package unit; import haxe.Exception; import haxe.exceptions.ArgumentException; @@ -235,17 +235,16 @@ class TestExceptions extends Test { public function testExceptionStack() { var data = [ '_without_ throws' => stacksWithoutThrowLevel1(), - '_with_ throws' => stacksWithThrowLevel1() + '_with_ throws' => stacksWithThrowLevel1(), + #if (eval || hl || neko) + 'auto wrapped' => stacksAutoWrappedLevel1() + #end ]; for(label => stacks in data) { Assert.isTrue(stacks.length > 1, '$label: wrong stacks.length'); var expected = null; var lineShift = 0; for(s in stacks) { - // TODO: fix hl vs other targets difference with callstacks - // See https://github.com/HaxeFoundation/haxe/issues/10926 - #if hl @:privateAccess s.asArray().shift(); #end - if(expected == null) { expected = stackItemData(s[0]); } else { @@ -295,6 +294,24 @@ class TestExceptions extends Test { return result; } + #if (eval || hl || neko) + function stacksAutoWrappedLevel1() { + return stacksAutoWrappedLevel2(); + } + + function stacksAutoWrappedLevel2():Array { + @:pure(false) function wrapNativeError(_) return []; + + var result:Array = []; + // It's critical for `testExceptionStack` test to keep the following lines + // order with no additional code in between. + result.push(try throw new Exception('') catch(e:Exception) e.stack); + result.push(try throw "" catch(e:Exception) e.stack); + result.push(try wrapNativeError((null:String).length) catch(e:Exception) e.stack); + return result; + } + #end + function stackItemData(item:StackItem):ItemData { var result:ItemData = {}; switch item { @@ -321,6 +338,16 @@ class TestExceptions extends Test { eq('haxe.Exception', HelperMacros.typeString(try throw new Exception('') catch(e) e)); } + function testCatchValueException() { + try { + throw ""; + } catch(e:ValueException) { + Assert.pass(); + } catch(e) { + Assert.fail(); + } + } + function testNotImplemented() { try { futureFeature(); @@ -374,4 +401,4 @@ class TestExceptions extends Test { } } #end -} \ No newline at end of file +} From d1ef1a3d61c54d063486448f7fad4efb7e6d11f2 Mon Sep 17 00:00:00 2001 From: player-03 Date: Fri, 7 Jul 2023 02:16:14 -0400 Subject: [PATCH 12/19] Account for sub-types in `ComplexTypeTools.toComplex()`. (#11273) --- std/haxe/macro/MacroStringTools.hx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/std/haxe/macro/MacroStringTools.hx b/std/haxe/macro/MacroStringTools.hx index 85e12b9b9a9..bfd422e4eb0 100644 --- a/std/haxe/macro/MacroStringTools.hx +++ b/std/haxe/macro/MacroStringTools.hx @@ -94,6 +94,10 @@ class MacroStringTools { static public function toComplex(path:String):ComplexType { var pack = path.split("."); - return TPath({pack: pack, name: pack.pop(), params: []}); + if(pack.length >= 2 && ~/^[A-Z]/.match(pack[pack.length - 2])) { + return TPath({pack: pack, sub: pack.pop(), name: pack.pop(), params: []}); + } else { + return TPath({pack: pack, name: pack.pop(), params: []}); + } } } From 777a5b9d5e53be72c9a84a641d60e38296379171 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 11 Jul 2023 08:09:10 +0200 Subject: [PATCH 13/19] [typer] don't add empty import modules to com.modules --- src/typing/typeloadModule.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/typing/typeloadModule.ml b/src/typing/typeloadModule.ml index 5b3a4cc7730..d9e2e62c8f3 100644 --- a/src/typing/typeloadModule.ml +++ b/src/typing/typeloadModule.ml @@ -294,7 +294,6 @@ module ModuleLevel = struct (* We use the file path as module name to make it unique. This may or may not be a good idea... *) let m_import = make_module ctx ([],path) path p in m_import.m_extra.m_kind <- MImport; - add_module ctx m_import p; m_import in List.fold_left (fun acc path -> @@ -310,7 +309,9 @@ module ModuleLevel = struct | ParseError(_,(msg,p),_) -> Parser.error msg p in List.iter (fun (d,p) -> match d with EImport _ | EUsing _ -> () | _ -> raise_typing_error "Only import and using is allowed in import.hx files" p) r; - add_dependency m (make_import_module path r); + let m_import = make_import_module path r in + add_module ctx m_import p; + add_dependency m m_import; r end else begin let r = [] in From de8f4730bc694a0c75d83a33873b5b0055b5010e Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 11 Jul 2023 08:55:47 +0200 Subject: [PATCH 14/19] [typer] remove wacky do_add handling --- src/typing/typeloadFields.ml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index 86bee7419da..b69c9cfc13c 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -79,7 +79,6 @@ type field_init_ctx = { field_kind : field_kind; display_modifier : placed_access option; mutable do_bind : bool; - mutable do_add : bool; (* If true, cf_expr = None makes a difference in the logic. We insert a dummy expression in display mode in order to address this. *) mutable expr_presence_matters : bool; @@ -144,7 +143,6 @@ let dump_field_context fctx = "is_field_debug",string_of_bool fctx.is_field_debug; "field_kind",s_field_kind fctx.field_kind; "do_bind",string_of_bool fctx.do_bind; - "do_add",string_of_bool fctx.do_add; "expr_presence_matters",string_of_bool fctx.expr_presence_matters; ] @@ -646,7 +644,6 @@ let create_field_context ctx cctx cff is_display_file display_modifier = is_generic = Meta.has Meta.Generic cff.cff_meta; field_kind = field_kind; do_bind = (((not ((has_class_flag c CExtern) || !is_extern) || is_inline) && not is_abstract && not (has_class_flag c CInterface)) || field_kind = FKInit); - do_add = true; expr_presence_matters = false; had_error = false; } in @@ -1222,7 +1219,6 @@ let check_abstract (ctx,cctx,fctx) a c cf fd t ret p = if !allows_no_expr then begin cf.cf_meta <- (Meta.NoExpr,[],null_pos) :: cf.cf_meta; fctx.do_bind <- false; - if not (Meta.has Meta.CoreType a.a_meta) then fctx.do_add <- false; end end @@ -1873,7 +1869,7 @@ let init_class ctx c p context_init herits fields = in display_error ctx.com ("Duplicate " ^ type_kind ^ " field declaration : " ^ s_type_path path ^ "." ^ cf.cf_name) cf.cf_name_pos else - if fctx.do_add then TClass.add_field c cf + TClass.add_field c cf end with Error ({ err_message = Custom _; err_pos = p2 } as err) when p = p2 -> display_error_ext ctx.com err From 4841072e8185f037fa0c2d62d0abc61eb2e473af Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 11 Jul 2023 09:14:20 +0200 Subject: [PATCH 15/19] [std] avoid duplicate field names --- std/UnicodeString.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/UnicodeString.hx b/std/UnicodeString.hx index 0b5a0a98b8a..896db23f48c 100644 --- a/std/UnicodeString.hx +++ b/std/UnicodeString.hx @@ -442,7 +442,7 @@ abstract UnicodeString(String) from String to String { @:op(A += B) static function assignAdd(a:UnicodeString, b:UnicodeString):UnicodeString; - @:op(A + B) @:commutative static function add(a:UnicodeString, b:String):UnicodeString; + @:op(A + B) @:commutative static function addString(a:UnicodeString, b:String):UnicodeString; - @:op(A += B) @:commutative static function assignAdd(a:UnicodeString, b:String):UnicodeString; + @:op(A += B) @:commutative static function assignAddString(a:UnicodeString, b:String):UnicodeString; } From d45708344df40a8f3dd838d2fe24bc0a56d24aef Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 11 Jul 2023 10:12:48 +0200 Subject: [PATCH 16/19] [jvm] deal with abstract no expression corner case --- src/generators/genjvm.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/generators/genjvm.ml b/src/generators/genjvm.ml index 95038772dc8..ca9c1db3470 100644 --- a/src/generators/genjvm.ml +++ b/src/generators/genjvm.ml @@ -2581,7 +2581,13 @@ class tclass_to_jvm gctx c = object(self) let field mtype cf = match cf.cf_kind with | Method (MethNormal | MethInline) -> List.iter (fun cf -> - if not (has_class_field_flag cf CfExtern) then self#generate_method gctx jc c mtype cf + let is_weird_abstract_field_without_expression = match cf.cf_expr,c.cl_kind with + | None,KAbstractImpl _ -> + true + | _ -> + false + in + if not (has_class_field_flag cf CfExtern) && not (is_weird_abstract_field_without_expression) then self#generate_method gctx jc c mtype cf ) (cf :: List.filter (fun cf -> has_class_field_flag cf CfOverload) cf.cf_overloads) | _ -> if not (has_class_flag c CInterface) && is_physical_field cf then self#generate_field gctx jc c mtype cf From 402d20167dd73f628edaae0a862bff0ca1fb4e5e Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Wed, 12 Jul 2023 10:26:48 +0200 Subject: [PATCH 17/19] [jvm] allow - in resource names closes #11275 --- src/codegen/codegen.ml | 6 +++--- src/generators/gencs.ml | 2 +- src/generators/genjava.ml | 2 +- src/generators/genjvm.ml | 2 +- src/generators/genphp7.ml | 2 +- src/generators/genpy.ml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/codegen/codegen.ml b/src/codegen/codegen.ml index 71967c57544..84509ec8276 100644 --- a/src/codegen/codegen.ml +++ b/src/codegen/codegen.ml @@ -65,12 +65,12 @@ let add_property_field com c = c.cl_statics <- PMap.add cf.cf_name cf c.cl_statics; c.cl_ordered_statics <- cf :: c.cl_ordered_statics -let escape_res_name name allow_dirs = +let escape_res_name name allowed = ExtString.String.replace_chars (fun chr -> if (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr = '_' || chr = '.' then Char.escaped chr - else if chr = '/' && allow_dirs then - "/" + else if List.mem chr allowed then + Char.escaped chr else "-x" ^ (string_of_int (Char.code chr))) name diff --git a/src/generators/gencs.ml b/src/generators/gencs.ml index fc2e4ed41cd..b56ecd99b82 100644 --- a/src/generators/gencs.ml +++ b/src/generators/gencs.ml @@ -3421,7 +3421,7 @@ let generate con = gen.gcon.file ^ "/src/Resources" in Hashtbl.iter (fun name v -> - let name = Codegen.escape_res_name name true in + let name = Codegen.escape_res_name name ['/'] in let full_path = src ^ "/" ^ name in Path.mkdir_from_path full_path; diff --git a/src/generators/genjava.ml b/src/generators/genjava.ml index 3f541643511..ce7445aa36c 100644 --- a/src/generators/genjava.ml +++ b/src/generators/genjava.ml @@ -2661,7 +2661,7 @@ let generate con = let res = ref [] in Hashtbl.iter (fun name v -> res := { eexpr = TConst(TString name); etype = gen.gcon.basic.tstring; epos = null_pos } :: !res; - let name = Codegen.escape_res_name name true in + let name = Codegen.escape_res_name name ['/'] in let full_path = gen.gcon.file ^ "/src/" ^ name in Path.mkdir_from_path full_path; diff --git a/src/generators/genjvm.ml b/src/generators/genjvm.ml index ca9c1db3470..5ff17668e28 100644 --- a/src/generators/genjvm.ml +++ b/src/generators/genjvm.ml @@ -3073,7 +3073,7 @@ let generate jvm_flag com = end ) com.native_libs.java_libs in Hashtbl.iter (fun name v -> - let filename = Codegen.escape_res_name name true in + let filename = Codegen.escape_res_name name ['/';'-'] in gctx.out#add_entry v filename; ) com.resources; let generate_real_types () = diff --git a/src/generators/genphp7.ml b/src/generators/genphp7.ml index 8496388f467..514acd4a3b3 100644 --- a/src/generators/genphp7.ml +++ b/src/generators/genphp7.ml @@ -35,7 +35,7 @@ let write_resource dir name data = let rdir = dir ^ "/res" in if not (Sys.file_exists dir) then Unix.mkdir dir 0o755; if not (Sys.file_exists rdir) then Unix.mkdir rdir 0o755; - let name = Codegen.escape_res_name name false in + let name = Codegen.escape_res_name name [] in let ch = open_out_bin (rdir ^ "/" ^ name) in output_string ch data; close_out ch diff --git a/src/generators/genpy.ml b/src/generators/genpy.ml index cc4e594b423..e5cda5ad565 100644 --- a/src/generators/genpy.ml +++ b/src/generators/genpy.ml @@ -2270,7 +2270,7 @@ module Generator = struct end else "," in - let k_enc = Codegen.escape_res_name k false in + let k_enc = Codegen.escape_res_name k [] in print ctx "%s\"%s\": open('%%s.%%s'%%(_file,'%s'),'rb').read()" prefix (StringHelper.s_escape k) k_enc; let f = open_out_bin (ctx.com.file ^ "." ^ k_enc) in From 039ac61295004e7885b240687479e915a375e36e Mon Sep 17 00:00:00 2001 From: Rudy Ges Date: Thu, 13 Jul 2023 13:30:14 +0200 Subject: [PATCH 18/19] [generics] apply generic expansion to arg default values --- src/core/texpr.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/texpr.ml b/src/core/texpr.ml index 540ede76f6c..e2149267db6 100644 --- a/src/core/texpr.ml +++ b/src/core/texpr.ml @@ -232,7 +232,7 @@ let map_expr_type f ft fv e = | TFunction fu -> let fu = { tf_expr = f fu.tf_expr; - tf_args = List.map (fun (v,o) -> fv v, o) fu.tf_args; + tf_args = List.map (fun (v,o) -> fv v, (Option.map f o)) fu.tf_args; tf_type = ft fu.tf_type; } in { e with eexpr = TFunction fu; etype = ft e.etype } From 27053aed8d810758f82ed01f066dd63bcde267c5 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Fri, 14 Jul 2023 10:30:51 +0200 Subject: [PATCH 19/19] [typer] factor out tanon_identification --- src/generators/genjvm.ml | 1 + src/generators/genshared.ml | 164 +------------------------- src/typing/tanon_identification.ml | 177 +++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 162 deletions(-) create mode 100644 src/typing/tanon_identification.ml diff --git a/src/generators/genjvm.ml b/src/generators/genjvm.ml index 5ff17668e28..05c4e54545d 100644 --- a/src/generators/genjvm.ml +++ b/src/generators/genjvm.ml @@ -31,6 +31,7 @@ open JvmSignature open JvmMethod open JvmBuilder open Genshared +open Tanon_identification (* Note: This module is the bridge between Haxe structures and JVM structures. No module in generators/jvm should reference any Haxe-specific type. *) diff --git a/src/generators/genshared.ml b/src/generators/genshared.ml index 161212acca0..0fb9d79226b 100644 --- a/src/generators/genshared.ml +++ b/src/generators/genshared.ml @@ -15,167 +15,6 @@ let is_extern_abstract a = match a.a_impl with | ([],("Void" | "Float" | "Int" | "Single" | "Bool" | "Null")) -> true | _ -> false -open OverloadResolution - -type 'a path_field_mapping = { - pfm_path : path; - pfm_params : type_params; - pfm_fields : (string,tclass_field) PMap.t; - mutable pfm_converted : (string * 'a) list option; - pfm_arity : int; -} - -let count_fields pm = - PMap.fold (fun _ i -> i + 1) pm 0 - -let pfm_of_typedef td = match follow td.t_type with - | TAnon an -> { - pfm_path = td.t_path; - pfm_params = td.t_params; - pfm_fields = an.a_fields; - pfm_converted = None; - pfm_arity = count_fields an.a_fields; - } - | _ -> - die "" __LOC__ - -class ['a] tanon_identification (empty_path : string list * string) = - let is_normal_anon an = match !(an.a_status) with - | Closed | Const -> true - | _ -> false - in -object(self) - - val pfms = Hashtbl.create 0 - val pfm_by_arity = DynArray.create () - val mutable num = 0 - - method get_pfms = pfms - - method add_pfm (path : path) (pfm : 'a path_field_mapping) = - while DynArray.length pfm_by_arity <= pfm.pfm_arity do - DynArray.add pfm_by_arity (DynArray.create ()) - done; - DynArray.add (DynArray.get pfm_by_arity pfm.pfm_arity) pfm; - Hashtbl.replace pfms path pfm - - method unify (tc : Type.t) (pfm : 'a path_field_mapping) = - let check () = - let pair_up fields = - PMap.fold (fun cf acc -> - let cf' = PMap.find cf.cf_name fields in - (cf,cf') :: acc - ) pfm.pfm_fields [] - in - let monos = match follow tc with - | TInst(c,tl) -> - let pairs = pair_up c.cl_fields in - let monos = List.map (fun _ -> mk_mono()) pfm.pfm_params in - let map = apply_params pfm.pfm_params monos in - List.iter (fun (cf,cf') -> - if not (unify_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); - Type.unify (apply_params c.cl_params tl (monomorphs cf'.cf_params cf'.cf_type)) (map (monomorphs cf.cf_params cf.cf_type)) - ) pairs; - monos - | TAnon an1 -> - let fields = ref an1.a_fields in - let pairs = pair_up an1.a_fields in - let monos = List.map (fun _ -> mk_mono()) pfm.pfm_params in - let map = apply_params pfm.pfm_params monos in - List.iter (fun (cf,cf') -> - if not (unify_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); - fields := PMap.remove cf.cf_name !fields; - Type.type_eq EqDoNotFollowNull cf'.cf_type (map (monomorphs cf.cf_params cf.cf_type)) - ) pairs; - if not (PMap.is_empty !fields) then raise (Unify_error [Unify_custom "not enough fields"]); - monos - | _ -> - raise (Unify_error [Unify_custom "bad type"]) - in - (* Check if we applied Void to a return type parameter... (#3463) *) - List.iter (fun t -> match follow t with - | TMono r -> - Monomorph.bind r t_dynamic - | t -> - if Type.ExtType.is_void t then raise(Unify_error [Unify_custom "return mono"]) - ) monos - in - try - check() - with Not_found -> - raise (Unify_error []) - - method find_compatible (arity : int) (tc : Type.t) = - if arity >= DynArray.length pfm_by_arity then - raise Not_found; - let d = DynArray.get pfm_by_arity arity in - let l = DynArray.length d in - let rec loop i = - if i >= l then - raise Not_found; - let pfm = DynArray.unsafe_get d i in - try - self#unify tc pfm; - pfm - with Unify_error _ -> - loop (i + 1) - in - loop 0 - - method identify_typedef (td : tdef) = - let rec loop t = match t with - | TAnon an when is_normal_anon an && not (PMap.is_empty an.a_fields) -> - self#add_pfm td.t_path (pfm_of_typedef td) - | TMono {tm_type = Some t} -> - loop t - | TLazy f -> - loop (lazy_type f) - | t -> - () - in - loop td.t_type - - method identify (accept_anons : bool) (t : Type.t) = - match t with - | TType(td,tl) -> - begin try - Some (Hashtbl.find pfms td.t_path) - with Not_found -> - self#identify accept_anons (apply_typedef td tl) - end - | TMono {tm_type = Some t} -> - self#identify accept_anons t - | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> - self#identify accept_anons (Abstract.get_underlying_type a tl) - | TAbstract({a_path=([],"Null")},[t]) -> - self#identify accept_anons t - | TLazy f -> - self#identify accept_anons (lazy_type f) - | TAnon an when accept_anons && not (PMap.is_empty an.a_fields) -> - let arity = PMap.fold (fun cf i -> - Gencommon.replace_mono cf.cf_type; - i + 1 - ) an.a_fields 0 in - begin try - Some (self#find_compatible arity t) - with Not_found -> - let id = num in - num <- num + 1; - let path = (["haxe";"generated"],Printf.sprintf "Anon%i" id) in - let pfm = { - pfm_path = path; - pfm_params = []; - pfm_fields = an.a_fields; - pfm_converted = None; - pfm_arity = count_fields an.a_fields; - } in - self#add_pfm path pfm; - Some pfm - end; - | _ -> - None -end - type field_generation_info = { mutable has_this_before_super : bool; (* This is an ordered list of fields that are targets of super() calls which is determined during @@ -214,7 +53,8 @@ module Info = struct end open Info - +open OverloadResolution +open Tanon_identification class ['a] preprocessor (basic : basic_types) (convert : Type.t -> 'a) = let make_native cf = diff --git a/src/typing/tanon_identification.ml b/src/typing/tanon_identification.ml new file mode 100644 index 00000000000..8af805fe9ee --- /dev/null +++ b/src/typing/tanon_identification.ml @@ -0,0 +1,177 @@ +open Globals +open Type + +let rec replace_mono t = + match t with + | TMono t -> + (match t.tm_type with + | None -> Monomorph.bind t t_dynamic + | Some _ -> ()) + | TEnum (_,p) | TInst (_,p) | TType (_,p) | TAbstract (_,p) -> + List.iter replace_mono p + | TFun (args,ret) -> + List.iter (fun (_,_,t) -> replace_mono t) args; + replace_mono ret + | TAnon _ + | TDynamic _ -> () + | TLazy f -> + replace_mono (lazy_type f) + +type 'a path_field_mapping = { + pfm_path : path; + pfm_params : type_params; + pfm_fields : (string,tclass_field) PMap.t; + mutable pfm_converted : (string * 'a) list option; + pfm_arity : int; +} + +let count_fields pm = + PMap.fold (fun _ i -> i + 1) pm 0 + +let pfm_of_typedef td = match follow td.t_type with + | TAnon an -> { + pfm_path = td.t_path; + pfm_params = td.t_params; + pfm_fields = an.a_fields; + pfm_converted = None; + pfm_arity = count_fields an.a_fields; + } + | _ -> + die "" __LOC__ + +class ['a] tanon_identification (empty_path : string list * string) = + let is_normal_anon an = match !(an.a_status) with + | Closed | Const -> true + | _ -> false + in +object(self) + + val pfms = Hashtbl.create 0 + val pfm_by_arity = DynArray.create () + val mutable num = 0 + + method get_pfms = pfms + + method add_pfm (path : path) (pfm : 'a path_field_mapping) = + while DynArray.length pfm_by_arity <= pfm.pfm_arity do + DynArray.add pfm_by_arity (DynArray.create ()) + done; + DynArray.add (DynArray.get pfm_by_arity pfm.pfm_arity) pfm; + Hashtbl.replace pfms path pfm + + method unify (tc : Type.t) (pfm : 'a path_field_mapping) = + let check () = + let pair_up fields = + PMap.fold (fun cf acc -> + let cf' = PMap.find cf.cf_name fields in + (cf,cf') :: acc + ) pfm.pfm_fields [] + in + let monos = match follow tc with + | TInst(c,tl) -> + let pairs = pair_up c.cl_fields in + let monos = List.map (fun _ -> mk_mono()) pfm.pfm_params in + let map = apply_params pfm.pfm_params monos in + List.iter (fun (cf,cf') -> + if not (unify_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); + Type.unify (apply_params c.cl_params tl (monomorphs cf'.cf_params cf'.cf_type)) (map (monomorphs cf.cf_params cf.cf_type)) + ) pairs; + monos + | TAnon an1 -> + let fields = ref an1.a_fields in + let pairs = pair_up an1.a_fields in + let monos = List.map (fun _ -> mk_mono()) pfm.pfm_params in + let map = apply_params pfm.pfm_params monos in + List.iter (fun (cf,cf') -> + if not (unify_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); + fields := PMap.remove cf.cf_name !fields; + Type.type_eq EqDoNotFollowNull cf'.cf_type (map (monomorphs cf.cf_params cf.cf_type)) + ) pairs; + if not (PMap.is_empty !fields) then raise (Unify_error [Unify_custom "not enough fields"]); + monos + | _ -> + raise (Unify_error [Unify_custom "bad type"]) + in + (* Check if we applied Void to a return type parameter... (#3463) *) + List.iter (fun t -> match follow t with + | TMono r -> + Monomorph.bind r t_dynamic + | t -> + if Type.ExtType.is_void t then raise(Unify_error [Unify_custom "return mono"]) + ) monos + in + try + check() + with Not_found -> + raise (Unify_error []) + + method find_compatible (arity : int) (tc : Type.t) = + if arity >= DynArray.length pfm_by_arity then + raise Not_found; + let d = DynArray.get pfm_by_arity arity in + let l = DynArray.length d in + let rec loop i = + if i >= l then + raise Not_found; + let pfm = DynArray.unsafe_get d i in + try + self#unify tc pfm; + pfm + with Unify_error _ -> + loop (i + 1) + in + loop 0 + + method identify_typedef (td : tdef) = + let rec loop t = match t with + | TAnon an when is_normal_anon an && not (PMap.is_empty an.a_fields) -> + self#add_pfm td.t_path (pfm_of_typedef td) + | TMono {tm_type = Some t} -> + loop t + | TLazy f -> + loop (lazy_type f) + | t -> + () + in + loop td.t_type + + method identify (accept_anons : bool) (t : Type.t) = + match t with + | TType(td,tl) -> + begin try + Some (Hashtbl.find pfms td.t_path) + with Not_found -> + self#identify accept_anons (apply_typedef td tl) + end + | TMono {tm_type = Some t} -> + self#identify accept_anons t + | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> + self#identify accept_anons (Abstract.get_underlying_type a tl) + | TAbstract({a_path=([],"Null")},[t]) -> + self#identify accept_anons t + | TLazy f -> + self#identify accept_anons (lazy_type f) + | TAnon an when accept_anons && not (PMap.is_empty an.a_fields) -> + let arity = PMap.fold (fun cf i -> + replace_mono cf.cf_type; + i + 1 + ) an.a_fields 0 in + begin try + Some (self#find_compatible arity t) + with Not_found -> + let id = num in + num <- num + 1; + let path = (["haxe";"generated"],Printf.sprintf "Anon%i" id) in + let pfm = { + pfm_path = path; + pfm_params = []; + pfm_fields = an.a_fields; + pfm_converted = None; + pfm_arity = count_fields an.a_fields; + } in + self#add_pfm path pfm; + Some pfm + end; + | _ -> + None +end \ No newline at end of file