diff --git a/src/codegen/codegen.ml b/src/codegen/codegen.ml index 71967c57544..c4b211a1158 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 @@ -387,7 +387,8 @@ module Dump = struct let dep = Hashtbl.create 0 in List.iter (fun m -> print "%s:\n" (Path.UniqueKey.lazy_path m.m_extra.m_file); - PMap.iter (fun _ m2 -> + PMap.iter (fun _ (sign,mpath) -> + let m2 = (com.cs#get_context sign)#find_module mpath in let file = Path.UniqueKey.lazy_path m2.m_extra.m_file in print "\t%s\n" file; let l = try Hashtbl.find dep file with Not_found -> [] in diff --git a/src/compiler/compiler.ml b/src/compiler/compiler.ml index b2cd674ddc8..3f1ef3ae676 100644 --- a/src/compiler/compiler.ml +++ b/src/compiler/compiler.ml @@ -143,7 +143,14 @@ module Setup = struct "python" | Hl -> add_std "hl"; - if not (Common.defined com Define.HlVer) then Define.define_value com.defines Define.HlVer (try Std.input_file (Common.find_file com "hl/hl_version") with Not_found -> die "" __LOC__); + if not (Common.defined com Define.HlVer) then begin + let hl_ver = try + Std.input_file (Common.find_file com "hl/hl_version") + with Not_found -> + failwith "The file hl_version could not be found. Please make sure HAXE_STD_PATH is set to the standard library corresponding to the used compiler version." + in + Define.define_value com.defines Define.HlVer hl_ver + end; "hl" | Eval -> add_std "eval"; diff --git a/src/compiler/server.ml b/src/compiler/server.ml index 105bf636077..47076beb92b 100644 --- a/src/compiler/server.ml +++ b/src/compiler/server.ml @@ -307,7 +307,9 @@ let check_module sctx ctx m p = end in let check_dependencies () = - PMap.iter (fun _ m2 -> match check m2 with + PMap.iter (fun _ (sign,mpath) -> + let m2 = (com.cs#get_context sign)#find_module mpath in + match check m2 with | None -> () | Some reason -> raise (Dirty (DependencyDirty(m2.m_path,reason))) ) m.m_extra.m_deps; @@ -407,7 +409,10 @@ let add_modules sctx ctx m p = ) m.m_types; TypeloadModule.ModuleLevel.add_module ctx m p; PMap.iter (Hashtbl.replace com.resources) m.m_extra.m_binded_res; - PMap.iter (fun _ m2 -> add_modules (tabs ^ " ") m0 m2) m.m_extra.m_deps + PMap.iter (fun _ (sign,mpath) -> + let m2 = (com.cs#get_context sign)#find_module mpath in + add_modules (tabs ^ " ") m0 m2 + ) m.m_extra.m_deps ) end in 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 *) diff --git a/src/context/display/displayFields.ml b/src/context/display/displayFields.ml index fb587833e4c..346a9368c64 100644 --- a/src/context/display/displayFields.ml +++ b/src/context/display/displayFields.ml @@ -228,21 +228,25 @@ let collect ctx e_ast e dk with_type p = | TAnon an -> (* @:forwardStatics *) let items = match !(an.a_status) with - | Statics { cl_kind = KAbstractImpl { a_meta = meta; a_this = TInst (c,_) }} when Meta.has Meta.ForwardStatics meta -> - let items = List.fold_left (fun acc cf -> - if should_access c cf true && is_new_item acc cf.cf_name then begin - let origin = Self(TClassDecl c) in - let item = make_class_field origin cf in - PMap.add cf.cf_name item acc - end else - acc - ) items c.cl_ordered_statics in - PMap.foldi (fun name item acc -> - if is_new_item acc name then - PMap.add name item acc - else - acc - ) PMap.empty items + | Statics { cl_kind = KAbstractImpl { a_meta = meta; a_this}} when Meta.has Meta.ForwardStatics meta -> + begin match follow a_this with + | TInst (c,_) -> + let items = List.fold_left (fun acc cf -> + if should_access c cf true && is_new_item acc cf.cf_name then begin + let origin = Self(TClassDecl c) in + let item = make_class_field origin cf in + PMap.add cf.cf_name item acc + end else + acc + ) items c.cl_ordered_statics in + PMap.foldi (fun name item acc -> + if is_new_item acc name then + PMap.add name item acc + else + acc + ) PMap.empty items + | _ -> items + end | _ -> items in (* Anon own fields *) diff --git a/src/context/display/displayJson.ml b/src/context/display/displayJson.ml index 06a336c8aac..1ed9e25cd25 100644 --- a/src/context/display/displayJson.ml +++ b/src/context/display/displayJson.ml @@ -182,13 +182,14 @@ let handler = "server/module", (fun hctx -> let sign = Digest.from_hex (hctx.jsonrpc#get_string_param "signature") in let path = Path.parse_path (hctx.jsonrpc#get_string_param "path") in - let cc = hctx.display#get_cs#get_context sign in + let cs = hctx.display#get_cs in + let cc = cs#get_context sign in let m = try cc#find_module path with Not_found -> hctx.send_error [jstring "No such module"] in - hctx.send_result (generate_module cc m) + hctx.send_result (generate_module cs cc m) ); "server/type", (fun hctx -> let sign = Digest.from_hex (hctx.jsonrpc#get_string_param "signature") in diff --git a/src/context/memory.ml b/src/context/memory.ml index 9b692d344d9..8d16ca17ffe 100644 --- a/src/context/memory.ml +++ b/src/context/memory.ml @@ -33,12 +33,14 @@ let update_module_type_deps deps md = ) md.m_types; !deps -let rec scan_module_deps m h = +let rec scan_module_deps cs m h = if Hashtbl.mem h m.m_id then () else begin Hashtbl.add h m.m_id m; - PMap.iter (fun _ m -> scan_module_deps m h) m.m_extra.m_deps + PMap.iter (fun _ (sign,mpath) -> + let m = (cs#get_context sign)#find_module mpath in + scan_module_deps cs m h) m.m_extra.m_deps end let module_sign key md = @@ -61,7 +63,7 @@ let get_out out = let get_module_memory cs all_modules m = let mdeps = Hashtbl.create 0 in - scan_module_deps m mdeps; + scan_module_deps cs m mdeps; let deps = ref [Obj.repr null_module] in let out = ref all_modules in let deps = Hashtbl.fold (fun _ md deps -> @@ -272,8 +274,9 @@ let display_memory com = ()); if verbose then begin print (Printf.sprintf " %d total deps" (List.length deps)); - PMap.iter (fun _ md -> - print (Printf.sprintf " dep %s%s" (s_type_path md.m_path) (module_sign key md)); + PMap.iter (fun _ (sign,mpath) -> + let md = (com.cs#get_context sign)#find_module mpath in + print (Printf.sprintf " dep %s%s" (s_type_path mpath) (module_sign key md)); ) m.m_extra.m_deps; end; flush stdout diff --git a/src/context/typecore.ml b/src/context/typecore.ml index 561bd9e3004..158825e6931 100644 --- a/src/context/typecore.ml +++ b/src/context/typecore.ml @@ -374,7 +374,7 @@ let add_local_with_origin ctx origin n t p = check_local_variable_name ctx n origin p; add_local ctx (VUser origin) n t p -let gen_local_prefix = "_g" +let gen_local_prefix = "`" let gen_local ctx t p = add_local ctx VGenerated gen_local_prefix t p diff --git a/src/core/json/genjson.ml b/src/core/json/genjson.ml index ed85b04a7a1..c3f79615fdf 100644 --- a/src/core/json/genjson.ml +++ b/src/core/json/genjson.ml @@ -707,7 +707,7 @@ let generate_module_type ctx mt = (* module *) -let generate_module cc m = +let generate_module cs cc m = jobject [ "id",jint m.m_id; "path",generate_module_path m.m_path; @@ -718,10 +718,12 @@ let generate_module cc m = | MSGood -> "Good" | MSBad reason -> Printer.s_module_skip_reason reason | MSUnknown -> "Unknown"); - "dependencies",jarray (PMap.fold (fun m acc -> (jobject [ - "path",jstring (s_type_path m.m_path); - "sign",jstring (Digest.to_hex m.m_extra.m_sign); - ]) :: acc) m.m_extra.m_deps []); + "dependencies",jarray (PMap.fold (fun (sign,mpath) acc -> + (jobject [ + "path",jstring (s_type_path mpath); + "sign",jstring (Digest.to_hex ((cs#get_context sign)#find_module mpath).m_extra.m_sign); + ]) :: acc + ) m.m_extra.m_deps []); "dependents",jarray (List.map (fun m -> (jobject [ "path",jstring (s_type_path m.m_path); "sign",jstring (Digest.to_hex m.m_extra.m_sign); diff --git a/src/core/tFunctions.ml b/src/core/tFunctions.ml index 2879e5ff212..ff3540b08ef 100644 --- a/src/core/tFunctions.ml +++ b/src/core/tFunctions.ml @@ -233,8 +233,8 @@ let null_abstract = { } let add_dependency ?(skip_postprocess=false) m mdep = - if m != null_module && m != mdep then begin - m.m_extra.m_deps <- PMap.add mdep.m_id mdep m.m_extra.m_deps; + if m != null_module && (m.m_path != mdep.m_path || m.m_extra.m_sign != mdep.m_extra.m_sign) then begin + m.m_extra.m_deps <- PMap.add mdep.m_id (mdep.m_extra.m_sign, mdep.m_path) m.m_extra.m_deps; (* In case the module is cached, we'll have to run post-processing on it again (issue #10635) *) if not skip_postprocess then m.m_extra.m_processed <- 0 end diff --git a/src/core/tPrinting.ml b/src/core/tPrinting.ml index 74a8949bbb0..fd4351ea0a2 100644 --- a/src/core/tPrinting.ml +++ b/src/core/tPrinting.ml @@ -605,7 +605,7 @@ module Printer = struct "m_cache_state",s_module_cache_state me.m_cache_state; "m_added",string_of_int me.m_added; "m_checked",string_of_int me.m_checked; - "m_deps",s_pmap string_of_int (fun m -> snd m.m_path) me.m_deps; + "m_deps",s_pmap string_of_int (fun (_,m) -> snd m) me.m_deps; "m_processed",string_of_int me.m_processed; "m_kind",s_module_kind me.m_kind; "m_binded_res",""; (* TODO *) diff --git a/src/core/tType.ml b/src/core/tType.ml index dd328432ebb..6ec5f025b21 100644 --- a/src/core/tType.ml +++ b/src/core/tType.ml @@ -390,7 +390,7 @@ and module_def_extra = { mutable m_added : int; mutable m_checked : int; mutable m_processed : int; - mutable m_deps : (int,module_def) PMap.t; + mutable m_deps : (int,(string (* sign *) * path)) PMap.t; mutable m_kind : module_kind; mutable m_binded_res : (string, string) PMap.t; mutable m_if_feature : (string *(tclass * tclass_field * bool)) list; 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 } 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/src/filters/renameVars.ml b/src/filters/renameVars.ml index 33a6fba1a00..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 @@ -358,11 +361,17 @@ and collect_ignore_block ?(in_block=false) rc scope e = (** Rename `v` if needed *) +let trailing_numbers = Str.regexp "[0-9]+$" let maybe_rename_var rc reserved (v,overlaps) = let commit name = v.v_meta <- (Meta.RealPath,[EConst (String(v.v_name,SDoubleQuotes)),null_pos],null_pos) :: v.v_meta; v.v_name <- name in + (* chop escape char for all local variables generated *) + if String.unsafe_get v.v_name 0 = String.unsafe_get Typecore.gen_local_prefix 0 then begin + let name = String.sub v.v_name 1 (String.length v.v_name - 1) in + commit ("_g" ^ (Str.replace_first trailing_numbers "" name)) + end; let rec loop name count = if StringMap.mem name !reserved || Overlaps.has_name name overlaps then begin let count = count + 1 in 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 ;; 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/genhl.ml b/src/generators/genhl.ml index a6de801d3de..dee561b888b 100644 --- a/src/generators/genhl.ml +++ b/src/generators/genhl.ml @@ -130,6 +130,7 @@ type access = | AInstanceProto of texpr * field index | AInstanceField of texpr * field index | AArray of reg * (ttype * ttype) * reg + | ACArray of reg * ttype * reg | AVirtualMethod of texpr * field index | ADynamic of texpr * string index | AEnum of tenum * field index @@ -309,6 +310,12 @@ let unsigned_op e1 e2 = in is_unsigned e1 && is_unsigned e2 +let rec get_const e = + match e.eexpr with + | TConst c -> c + | TParenthesis e | TCast (e,_) -> get_const e + | _ -> abort "Should be a constant" e.epos + let set_curpos ctx p = ctx.m.mcurpos <- p @@ -1376,6 +1383,13 @@ and get_access ctx e = free ctx a; let t = to_type ctx t in AArray (a,(t,t),i) + | TInst ({ cl_path = ["hl"],"Abstract" },[TInst({ cl_kind = KExpr (EConst (String("hl_carray",_)),_) },_)]) -> + let a = eval_null_check ctx a in + hold ctx a; + let i = eval_to ctx i HI32 in + free ctx a; + let t = to_type ctx e.etype in + ACArray (a,t,i) | TAbstract (a,pl) -> loop (Abstract.get_underlying_type a pl) | _ -> @@ -1893,7 +1907,13 @@ and eval_expr ctx e = r | "$asize", [e] -> let r = alloc_tmp ctx HI32 in - op ctx (OArraySize (r, eval_to ctx e HArray)); + (match follow e.etype with + | TInst ({cl_path=["hl"],"Abstract"},[TInst({ cl_kind = KExpr (EConst (String("hl_carray",_)),_) },_)]) -> + let arr = eval_expr ctx e in + op ctx (ONullCheck arr); + op ctx (OArraySize (r, arr)) + | _ -> + op ctx (OArraySize (r, eval_to ctx e HArray))); r | "$aalloc", [esize] -> let et = (match follow e.etype with TAbstract ({ a_path = ["hl"],"NativeArray" },[t]) -> to_type ctx t | _ -> invalid()) in @@ -2053,6 +2073,15 @@ and eval_expr ctx e = free ctx rfile; free ctx min; r + | "$prefetch", [value; mode] -> + let mode = (match get_const mode with + | TInt m -> Int32.to_int m + | _ -> abort "Constant mode required" e.epos + ) in + (match get_access ctx value with + | AInstanceField (f, index) -> op ctx (OPrefetch (eval_expr ctx f, index + 1, mode)) + | _ -> op ctx (OPrefetch (eval_expr ctx value, 0, mode))); + alloc_tmp ctx HVoid | _ -> abort ("Unknown native call " ^ s) e.epos) | TEnumIndex v -> @@ -2207,7 +2236,7 @@ and eval_expr ctx e = ignore(make_fun ctx ("","") fid f None None); end; op ctx (OStaticClosure (r,fid)); - | ANone | ALocal _ | AArray _ | ACaptured _ -> + | ANone | ALocal _ | AArray _ | ACaptured _ | ACArray _ -> abort "Invalid access" e.epos); let to_t = to_type ctx e.etype in (match to_t with @@ -2451,7 +2480,7 @@ and eval_expr ctx e = let r = value() in op ctx (OSetEnumField (ctx.m.mcaptreg,index,r)); r - | AEnum _ | ANone | AInstanceFun _ | AInstanceProto _ | AStaticFun _ | AVirtualMethod _ -> + | AEnum _ | ANone | AInstanceFun _ | AInstanceProto _ | AStaticFun _ | AVirtualMethod _ | ACArray _ -> die "" __LOC__) | OpBoolOr -> let r = alloc_tmp ctx HBool in @@ -2726,6 +2755,10 @@ and eval_expr ctx e = (match get_access ctx e with | AArray (a,at,idx) -> array_read ctx a at idx e.epos + | ACArray (a,t,idx) -> + let tmp = alloc_tmp ctx t in + op ctx (OGetArray (tmp,a,idx)); + tmp | _ -> die "" __LOC__) | TMeta (_,e) -> @@ -3045,7 +3078,7 @@ and gen_assign_op ctx acc e1 f = free ctx robj; op ctx (ODynSet (robj,fid,r)); r - | ANone | ALocal _ | AStaticFun _ | AInstanceFun _ | AInstanceProto _ | AVirtualMethod _ | AEnum _ -> + | ANone | ALocal _ | AStaticFun _ | AInstanceFun _ | AInstanceProto _ | AVirtualMethod _ | AEnum _ | ACArray _ -> die "" __LOC__ and build_capture_vars ctx f = @@ -3329,7 +3362,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 +3373,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/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 95038772dc8..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. *) @@ -2581,7 +2582,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 @@ -3067,7 +3074,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 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/generators/hl2c.ml b/src/generators/hl2c.ml index af03a261786..d5b94f0ddff 100644 --- a/src/generators/hl2c.ml +++ b/src/generators/hl2c.ml @@ -1087,6 +1087,15 @@ let generate_function ctx f = sexpr "%s = %s + %s" (reg r) (reg r2) (reg off) | ONop _ -> () + | OPrefetch (r,fid,mode) -> + let expr = (if fid = 0 then reg r else (match rtype r with + | HObj o | HStruct o -> + let name, t = resolve_field o (fid - 1) in + Printf.sprintf "%s->%s" (reg r) name + | _ -> + Globals.die "" __LOC__ + )) in + sexpr "__hl_prefetch_m%d(%s)" mode expr ) f.code; flush_options (Array.length f.code); unblock(); diff --git a/src/generators/hlcode.ml b/src/generators/hlcode.ml index 733a6b10e05..d40313f9c26 100644 --- a/src/generators/hlcode.ml +++ b/src/generators/hlcode.ml @@ -201,6 +201,7 @@ type opcode = | ORefData of reg * reg | ORefOffset of reg * reg * reg | ONop of string + | OPrefetch of reg * field index * int type fundecl = { fpath : string * string; @@ -572,6 +573,8 @@ let ostr fstr o = | ORefData (r,d) -> Printf.sprintf "refdata %d, %d" r d | ORefOffset (r,r2,off) -> Printf.sprintf "refoffset %d, %d, %d" r r2 off | ONop s -> if s = "" then "nop" else "nop " ^ s + | OPrefetch (r,f,mode) -> Printf.sprintf "prefetch %d[%d] %d" r f mode + let fundecl_name f = if snd f.fpath = "" then "fun$" ^ (string_of_int f.findex) else (fst f.fpath) ^ "." ^ (snd f.fpath) let dump pr code = diff --git a/src/generators/hlinterp.ml b/src/generators/hlinterp.ml index f6fc7f32fd0..e50714f7ee4 100644 --- a/src/generators/hlinterp.ml +++ b/src/generators/hlinterp.ml @@ -1154,7 +1154,7 @@ let interp ctx f args = (match get r2, get off with | VRef (RArray (a,pos),t), VInt i -> set r (VRef (RArray (a,pos + Int32.to_int i),t)) | _ -> Globals.die "" __LOC__) - | ONop _ -> + | ONop _ | OPrefetch _ -> () ); loop() @@ -2436,7 +2436,7 @@ let check code macros = | ORethrow r -> reg r HDyn | OGetArray (v,a,i) -> - reg a HArray; + (match rtype a with HAbstract ("hl_carray",_) -> () | _ -> reg a HArray); reg i HI32; ignore(rtype v); | OGetUI8 (r,b,p) | OGetUI16(r,b,p) -> @@ -2466,7 +2466,7 @@ let check code macros = ignore(rtype a); ignore(rtype b); | OArraySize (r,a) -> - reg a HArray; + (match rtype a with HAbstract ("hl_carray",_) -> () | _ -> reg a HArray); reg r HI32 | OType (r,_) -> reg r HType @@ -2547,6 +2547,8 @@ let check code macros = reg off HI32; | ONop _ -> (); + | OPrefetch (r,f,_) -> + if f = 0 then ignore(rtype r) else ignore(tfield r (f - 1) false) ) f.code (* TODO : check that all path correctly initialize NULL values and reach a return *) in diff --git a/src/generators/hlopt.ml b/src/generators/hlopt.ml index 2e5bd20c4c6..c11db0161aa 100644 --- a/src/generators/hlopt.ml +++ b/src/generators/hlopt.ml @@ -164,6 +164,8 @@ let opcode_fx frw op = write r; | ONop _ -> () + | OPrefetch (r,_,_) -> + read r let opcode_eq a b = match a, b with @@ -432,6 +434,9 @@ let opcode_map read write op = ORefOffset (write r,r2,off); | ONop _ -> op + | OPrefetch (r, fid, mode) -> + let r2 = read r in + OPrefetch (r2, fid, mode) (* build code graph *) @@ -875,6 +880,11 @@ let _optimize (f:fundecl) = | OGetThis (r,fid) when (match f.regs.(r) with HStruct _ -> true | _ -> false) -> do_write r; if is_packed_field 0 fid then state.(r).rnullcheck <- true; + | OGetArray (r,arr,idx) -> + do_read arr; + do_read idx; + do_write r; + (match f.regs.(arr) with HAbstract _ -> state.(r).rnullcheck <- true | _ -> ()); | _ -> opcode_fx (fun r read -> if read then do_read r else do_write r 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/src/optimization/inline.ml b/src/optimization/inline.ml index b2aea6ab167..5157d03e78a 100644 --- a/src/optimization/inline.ml +++ b/src/optimization/inline.ml @@ -656,7 +656,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f ) in (match api_inline ctx cl cf.cf_name params p with | None -> raise Exit - | Some e -> Some e) + | Some e -> e) with Exit -> let has_params,map_type = match config with Some config -> config | None -> inline_default_config cf ethis.etype in let params = inline_rest_params ctx f params map_type p in @@ -831,10 +831,8 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f state#set_side_effect; begin match follow t with | TInst({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,_) -> - begin match type_inline_ctor ctx c cf tf ethis el po with - | Some e -> map term false e - | None -> raise_typing_error "Could not inline super constructor call" po - end + let e = type_inline_ctor ctx c cf tf ethis el po in + map term false e | _ -> raise_typing_error "Cannot inline function containing super" po end | TCall(e1,el) -> @@ -876,7 +874,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f (se "\t" e) ); end; - Some e + e (* Same as type_inline, but modifies the function body to add field inits *) and type_inline_ctor ctx c cf tf ethis el po = diff --git a/src/optimization/inlineConstructors.ml b/src/optimization/inlineConstructors.ml index 3c629c0f948..63cc3f926f5 100644 --- a/src/optimization/inlineConstructors.ml +++ b/src/optimization/inlineConstructors.ml @@ -374,34 +374,29 @@ let inline_constructors ctx original_e = let argvs, pl = analyze_call_args pl in let _, cname = c.cl_path in let v = alloc_var VGenerated ("inl"^cname) e.etype e.epos in - match Inline.type_inline_ctor ctx c cf tf (mk (TLocal v) (TInst (c,tl)) e.epos) pl e.epos with - | Some inlined_expr -> - let inlined_expr = mark_ctors inlined_expr in - let has_untyped = (Meta.has Meta.HasUntyped cf.cf_meta) in - let forced = is_extern_ctor c cf || force_inline in - let io = mk_io (IOKCtor{ioc_class=c; ioc_tparams=tl; ioc_field=cf; ioc_forced=forced}) io_id inlined_expr ~has_untyped:has_untyped in - io.io_dependent_vars <- argvs; - let rec loop (c:tclass) (tl:t list) = - let apply = apply_params c.cl_params tl in - List.iter (fun cf -> - match cf.cf_kind,cf.cf_expr with - | Var _, _ -> - let fieldt = apply cf.cf_type in - ignore(alloc_io_field io cf.cf_name fieldt v.v_pos); - | _ -> () - ) c.cl_ordered_fields; - match c.cl_super with - | Some (c,tl) -> loop c (List.map apply tl) - | None -> () - in loop c tl; - let iv = add v IVKLocal in - set_iv_alias iv io; - ignore(analyze_aliases_in_ctor cf true io.io_expr); - Some iv - | _ -> - List.iter (fun v -> cancel_v v v.v_pos) argvs; - if is_extern_ctor c cf then display_error ctx.com "Extern constructor could not be inlined" e.epos; - None + let inlined_expr = Inline.type_inline_ctor ctx c cf tf (mk (TLocal v) (TInst (c,tl)) e.epos) pl e.epos in + let inlined_expr = mark_ctors inlined_expr in + let has_untyped = (Meta.has Meta.HasUntyped cf.cf_meta) in + let forced = is_extern_ctor c cf || force_inline in + let io = mk_io (IOKCtor{ioc_class=c; ioc_tparams=tl; ioc_field=cf; ioc_forced=forced}) io_id inlined_expr ~has_untyped:has_untyped in + io.io_dependent_vars <- argvs; + let rec loop (c:tclass) (tl:t list) = + let apply = apply_params c.cl_params tl in + List.iter (fun cf -> + match cf.cf_kind,cf.cf_expr with + | Var _, _ -> + let fieldt = apply cf.cf_type in + ignore(alloc_io_field io cf.cf_name fieldt v.v_pos); + | _ -> () + ) c.cl_ordered_fields; + match c.cl_super with + | Some (c,tl) -> loop c (List.map apply tl) + | None -> () + in loop c tl; + let iv = add v IVKLocal in + set_iv_alias iv io; + ignore(analyze_aliases_in_ctor cf true io.io_expr); + Some iv end | TNew({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some _} as cf)} as c,_,pl),_ when is_extern_ctor c cf -> raise_typing_error "Extern constructor could not be inlined" e.epos; @@ -501,29 +496,24 @@ let inline_constructors ctx original_e = let argvs, pl = analyze_call_args call_args in io.io_dependent_vars <- io.io_dependent_vars @ argvs; io.io_has_untyped <- io.io_has_untyped or (Meta.has Meta.HasUntyped cf.cf_meta); - begin match Inline.type_inline ctx cf tf (mk (TLocal io_var.iv_var) (TInst (c,tl)) e.epos) pl e.etype None e.epos true with - | Some e -> - let e = mark_ctors e in - io.io_inline_methods <- io.io_inline_methods @ [e]; - begin match analyze_aliases captured e with - | Some(iv) -> - (* - The parent inline object might have been cancelled while analyzing the inlined method body - If the parent inline object is cancelled the inlining of this method will no longer happen, - so the return value must be cancelled. - *) - if io.io_cancelled then begin - cancel_iv iv e.epos; - None - end else begin - io.io_dependent_vars <- iv.iv_var :: io.io_dependent_vars; - Some(iv) - end - | None -> None - end - | None -> - cancel_io io e.epos; - None + let e = Inline.type_inline ctx cf tf (mk (TLocal io_var.iv_var) (TInst (c,tl)) e.epos) pl e.etype None e.epos true in + let e = mark_ctors e in + io.io_inline_methods <- io.io_inline_methods @ [e]; + begin match analyze_aliases captured e with + | Some(iv) -> + (* + The parent inline object might have been cancelled while analyzing the inlined method body + If the parent inline object is cancelled the inlining of this method will no longer happen, + so the return value must be cancelled. + *) + if io.io_cancelled then begin + cancel_iv iv e.epos; + None + end else begin + io.io_dependent_vars <- iv.iv_var :: io.io_dependent_vars; + Some(iv) + end + | None -> None end | IOFInlineVar(iv) -> cancel_iv iv e.epos; diff --git a/src/optimization/optimizer.ml b/src/optimization/optimizer.ml index f3c6a51b1fd..838611a9bd9 100644 --- a/src/optimization/optimizer.ml +++ b/src/optimization/optimizer.ml @@ -352,20 +352,23 @@ let rec reduce_loop ctx e = let cf = mk_field "" ef.etype e.epos null_pos in let ethis = mk (TConst TThis) t_dynamic e.epos in let rt = (match follow ef.etype with TFun (_,rt) -> rt | _ -> die "" __LOC__) in - let inl = (try type_inline ctx cf func ethis el rt None e.epos ~self_calling_closure:true false with Error { err_message = Custom _ } -> None) in - (match inl with - | None -> reduce_expr ctx e - | Some e -> reduce_loop ctx e) + begin try + let e = type_inline ctx cf func ethis el rt None e.epos ~self_calling_closure:true false in + reduce_loop ctx e + with Error { err_message = Custom _ } -> + reduce_expr ctx e + end; | {eexpr = TField(ef,(FStatic(cl,cf) | FInstance(cl,_,cf)))} when needs_inline ctx (Some cl) cf && not (rec_stack_memq cf inline_stack) -> begin match cf.cf_expr with | Some {eexpr = TFunction tf} -> let config = inline_config (Some cl) cf el e.etype in let rt = (match Abstract.follow_with_abstracts e1.etype with TFun (_,rt) -> rt | _ -> die "" __LOC__) in - let inl = (try type_inline ctx cf tf ef el rt config e.epos false with Error { err_message = Custom _ } -> None) in - (match inl with - | None -> reduce_expr ctx e - | Some e -> - rec_stack_default inline_stack cf (fun cf' -> cf' == cf) (fun () -> reduce_loop ctx e) e) + begin try + let e = type_inline ctx cf tf ef el rt config e.epos false in + rec_stack_default inline_stack cf (fun cf' -> cf' == cf) (fun () -> reduce_loop ctx e) e + with Error { err_message = Custom _ } -> + reduce_expr ctx e + end | _ -> reduce_expr ctx e end 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/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/calls.ml b/src/typing/calls.ml index 4159527e6a4..ef39f73a99c 100644 --- a/src/typing/calls.ml +++ b/src/typing/calls.ml @@ -70,11 +70,7 @@ let make_call ctx e params t ?(force_inline=false) p = (match f.cf_expr_unoptimized,f.cf_expr with | Some {eexpr = TFunction fd},_ | None,Some { eexpr = TFunction fd } -> - (match Inline.type_inline ctx f fd ethis params t config p force_inline with - | None -> - if force_inline then raise_typing_error "Inline could not be done" p; - raise Exit; - | Some e -> e) + Inline.type_inline ctx f fd ethis params t config p force_inline | _ -> (* we can't inline because there is most likely a loop in the typing. diff --git a/src/typing/generic.ml b/src/typing/generic.ml index 58ad1a18dbb..5dacd82cd8c 100644 --- a/src/typing/generic.ml +++ b/src/typing/generic.ml @@ -16,57 +16,64 @@ type generic_context = { mutable mg : module_def option; } -let generic_check_const_expr ctx t = - match follow t with - | TInst({cl_kind = KExpr e},_) -> - let e = type_expr {ctx with locals = PMap.empty} e WithType.value in - e.etype,Some e - | _ -> t,None - let make_generic ctx ps pt p = - let rec loop l1 l2 = - match l1, l2 with - | [] , [] -> [] - | ({ttp_type=TLazy f} as tp) :: l1, _ -> loop ({tp with ttp_type=lazy_type f} :: l1) l2 - | tp1 :: l1 , t2 :: l2 -> - let t,eo = generic_check_const_expr ctx t2 in - (tp1.ttp_type,(t,eo)) :: loop l1 l2 - | _ -> die "" __LOC__ + let subst s = "_" ^ string_of_int (Char.code (String.get (Str.matched_string s) 0)) ^ "_" in + let ident_safe = Str.global_substitute (Str.regexp "[^a-zA-Z0-9_]") subst in + let s_type_path_underscore (p,s) = match p with [] -> s | _ -> String.concat "_" p ^ "_" ^ s in + let process t = + let rec loop top t = match t with + | TInst(c,tl) -> + begin match c.cl_kind with + | KExpr e -> + let name = ident_safe (Ast.Printer.s_expr e) in + let e = type_expr {ctx with locals = PMap.empty} e WithType.value in + name,(e.etype,Some e) + | _ -> + ((ident_safe (s_type_path_underscore c.cl_path)) ^ (loop_tl top tl),(t,None)) + end + | TType (td,tl) -> + (s_type_path_underscore td.t_path) ^ (loop_tl top tl),(t,None) + | TEnum(en,tl) -> + (s_type_path_underscore en.e_path) ^ (loop_tl top tl),(t,None) + | TAnon(a) -> + "anon_" ^ String.concat "_" (PMap.foldi (fun s f acc -> (s ^ "_" ^ (loop_deep (follow f.cf_type))) :: acc) a.a_fields []),(t,None) + | TFun(args, return_type) -> + ("func_" ^ (String.concat "_" (List.map (fun (_, _, t) -> loop_deep t) args)) ^ "_" ^ (loop_deep return_type)),(t,None) + | TAbstract(a,tl) -> + (s_type_path_underscore a.a_path) ^ (loop_tl top tl),(t,None) + | TDynamic _ -> + "Dynamic",(t,None) + | TMono { tm_type = None } -> + if not top then + "_",(t,None) + else + raise Exit + | TMono { tm_type = Some t} -> + loop top t + | TLazy f -> + loop top (lazy_type f) + and loop_tl top tl = match tl with + | [] -> "" + | tl -> "_" ^ String.concat "_" (List.map (fun t -> fst (loop top t)) tl) + and loop_deep t = + fst (loop false t) + in + loop true t in - let name = - String.concat "_" (List.map2 (fun {ttp_name=s} t -> - let subst s = "_" ^ string_of_int (Char.code (String.get (Str.matched_string s) 0)) ^ "_" in - let ident_safe = Str.global_substitute (Str.regexp "[^a-zA-Z0-9_]") subst in - let s_type_path_underscore (p,s) = match p with [] -> s | _ -> String.concat "_" p ^ "_" ^ s in - let rec loop top t = match t with - | TInst(c,tl) -> (match c.cl_kind with - | KExpr e -> ident_safe (Ast.Printer.s_expr e) - | _ -> (ident_safe (s_type_path_underscore c.cl_path)) ^ (loop_tl top tl)) - | TType (td,tl) -> (s_type_path_underscore td.t_path) ^ (loop_tl top tl) - | TEnum(en,tl) -> (s_type_path_underscore en.e_path) ^ (loop_tl top tl) - | TAnon(a) -> "anon_" ^ String.concat "_" (PMap.foldi (fun s f acc -> (s ^ "_" ^ (loop false (follow f.cf_type))) :: acc) a.a_fields []) - | TFun(args, return_type) -> "func_" ^ (String.concat "_" (List.map (fun (_, _, t) -> loop false t) args)) ^ "_" ^ (loop false return_type) - | TAbstract(a,tl) -> (s_type_path_underscore a.a_path) ^ (loop_tl top tl) - | _ when not top -> - follow_or t top (fun() -> "_") (* allow unknown/incompatible types as type parameters to retain old behavior *) - | TMono { tm_type = None } -> raise (Generic_Exception (("Could not determine type for parameter " ^ s), p)) - | TDynamic _ -> "Dynamic" - | t -> - follow_or t top (fun() -> raise (Generic_Exception (("Unsupported type parameter: " ^ (s_type (print_context()) t) ^ ")"), p))) - and loop_tl top tl = match tl with - | [] -> "" - | tl -> "_" ^ String.concat "_" (List.map (loop top) tl) - and follow_or t top or_fn = - let ft = follow_once t in - if ft == t then or_fn() - else loop top ft - in - loop true t - ) ps pt) + let rec loop acc_name acc_subst ttpl tl = match ttpl,tl with + | ttp :: ttpl,t :: tl -> + let name,t = try process t with Exit -> raise (Generic_Exception (("Could not determine type for parameter " ^ ttp.ttp_name), p)) in + loop (name :: acc_name) ((follow ttp.ttp_type,t) :: acc_subst) ttpl tl + | [],[] -> + let name = String.concat "_" (List.rev acc_name) in + name,acc_subst + | _ -> + die "" __LOC__ in + let name,subst = loop [] [] ps pt in { ctx = ctx; - subst = loop ps pt; + subst = subst; name = name; p = p; mg = None; 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/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/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/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 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; diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index 614294287d6..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 @@ -1052,165 +1049,178 @@ let create_variable (ctx,cctx,fctx) c f t eo p = TypeBinding.bind_var ctx cctx fctx cf eo; cf -let check_abstract (ctx,cctx,fctx) c cf fd t ret p = - match cctx.abstract with - | Some a -> - let m = mk_mono() in - let ta = TAbstract(a,List.map (fun _ -> mk_mono()) a.a_params) in - let tthis = if fctx.is_abstract_member || Meta.has Meta.To cf.cf_meta then monomorphs a.a_params a.a_this else a.a_this in - let allows_no_expr = ref (Meta.has Meta.CoreType a.a_meta) in - let allow_no_expr () = if not (has_class_field_flag cf CfExtern) then begin - allows_no_expr := true; - fctx.expr_presence_matters <- true; - end in - let rec loop ml = - (match ml with - | (Meta.From,_,_) :: _ -> - let r = exc_protect ctx (fun r -> - r := lazy_processing (fun () -> t); - (* the return type of a from-function must be the abstract, not the underlying type *) - if not fctx.is_macro then (try type_eq EqStrict ret ta with Unify_error l -> raise_typing_error_ext (make_error (Unify l) p)); - match t with - | TFun([_,_,t],_) -> t - | TFun([(_,_,t1);(_,true,t2)],_) when is_pos_infos t2 -> t1 - | _ -> raise_typing_error ("@:from cast functions must accept exactly one argument") p - ) "@:from" in - a.a_from_field <- (TLazy r,cf) :: a.a_from_field; - | (Meta.To,_,_) :: _ -> - if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "cast function" p; - let are_valid_args args = - match args with - | [_] -> true - | [_; (_,true,t)] when is_pos_infos t -> true - | _ -> false - in - (match cf.cf_kind, cf.cf_type with - | Var _, _ -> - raise_typing_error "Invalid metadata: @:to must be used on method of abstract" p - | Method _, TFun(args, _) when not fctx.is_abstract_member && not (are_valid_args args) -> - if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *) - raise_typing_error "static @:to method should have one argument" p - | Method _, TFun(args, _) when fctx.is_abstract_member && not (are_valid_args args) -> - if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *) - raise_typing_error "@:to method should have no arguments" p - | _ -> () - ); - (* TODO: this doesn't seem quite right... *) - if not (has_class_field_flag cf CfImpl) then add_class_field_flag cf CfImpl; - let resolve_m args = - (try unify_raise t (tfun (tthis :: args) m) cf.cf_pos with Error ({ err_message = Unify l; } as err) -> raise_typing_error_ext err); - match follow m with - | TMono _ when (match t with TFun(_,r) -> r == t_dynamic | _ -> false) -> t_dynamic - | m -> m - in - let is_multitype_cast = Meta.has Meta.MultiType a.a_meta && not fctx.is_abstract_member in - if is_multitype_cast && not (Meta.has Meta.MultiType cf.cf_meta) then - cf.cf_meta <- (Meta.MultiType,[],null_pos) :: cf.cf_meta; - let r = exc_protect ctx (fun r -> - r := lazy_processing (fun () -> t); - let args = if is_multitype_cast then begin - let ctor = try - PMap.find "_new" c.cl_statics - with Not_found -> - raise_typing_error "Constructor of multi-type abstract must be defined before the individual @:to-functions are" cf.cf_pos - in - (* delay ctx PFinal (fun () -> unify ctx m tthis f.cff_pos); *) - let args = match follow (monomorphs a.a_params ctor.cf_type) with - | TFun(args,_) -> List.map (fun (_,_,t) -> t) args - | _ -> die "" __LOC__ - in - args - end else - match cf.cf_type with - | TFun([_;(_,true,t)],_) when is_pos_infos t -> [t] - | _ -> [] - in - let t = resolve_m args in - t - ) "@:to" in - a.a_to_field <- (TLazy r, cf) :: a.a_to_field - | ((Meta.ArrayAccess,_,_) | (Meta.Op,[(EArrayDecl _),_],_)) :: _ -> - if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "array-access function" p; - a.a_array <- cf :: a.a_array; - allow_no_expr(); - | (Meta.Op,[EBinop(OpAssign,_,_),_],_) :: _ -> - raise_typing_error "Assignment overloading is not supported" p; - | (Meta.Op,[EBinop(OpAssignOp OpNullCoal,_,_),_],_) :: _ - | (Meta.Op,[EBinop(OpNullCoal,_,_),_],_) :: _ -> - raise_typing_error "Null coalescing overloading is not supported" p; - | (Meta.Op,[ETernary(_,_,_),_],_) :: _ -> - raise_typing_error "Ternary overloading is not supported" p; - | (Meta.Op,[EBinop(op,_,_),_],_) :: _ -> - if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "operator function" p; - let targ = if fctx.is_abstract_member then tthis else ta in - let left_eq,right_eq = - match follow t with - | TFun([(_,_,t1);(_,_,t2)],_) -> - type_iseq targ t1,type_iseq targ t2 - | TFun([(_,_,t1);(_,_,t2);(_,true,t3)],_) when is_pos_infos t3 -> - type_iseq targ t1,type_iseq targ t2 - | _ -> - if fctx.is_abstract_member then - raise_typing_error ("Member @:op functions must accept exactly one argument") cf.cf_pos - else - raise_typing_error ("Static @:op functions must accept exactly two arguments") cf.cf_pos - in - if not (left_eq || right_eq) then raise_typing_error ("The left or right argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; - if right_eq && Meta.has Meta.Commutative cf.cf_meta then raise_typing_error ("Invalid metadata: @:commutative is only allowed if the right argument is not " ^ (s_type (print_context()) targ)) cf.cf_pos; - a.a_ops <- (op,cf) :: a.a_ops; - allow_no_expr(); - | (Meta.Op,[EUnop(op,flag,_),_],_) :: _ -> - if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "operator function" p; - let targ = if fctx.is_abstract_member then tthis else ta in - (try type_eq EqStrict t (tfun [targ] (mk_mono())) with Unify_error l -> raise_error_msg (Unify l) cf.cf_pos); - a.a_unops <- (op,flag,cf) :: a.a_unops; - allow_no_expr(); - | (Meta.Op,[ECall _,_],_) :: _ -> - begin match a.a_call with - | None -> - a.a_call <- Some cf - | Some cf' -> - cf'.cf_overloads <- cf :: cf'.cf_overloads - end; - allow_no_expr(); - | ((Meta.Resolve,_,_) | (Meta.Op,[EField _,_],_)) :: _ -> - let targ = if fctx.is_abstract_member then tthis else ta in - let check_fun t1 t2 = - if not fctx.is_macro then begin - if not (type_iseq targ t1) then raise_typing_error ("First argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; - if not (type_iseq ctx.t.tstring t2) then raise_typing_error ("Second argument type must be String") cf.cf_pos - end - in - begin match follow t with - | TFun((_,_,t1) :: (_,_,t2) :: args,_) when is_empty_or_pos_infos args -> - if a.a_read <> None then raise_typing_error "Multiple resolve-read methods are not supported" cf.cf_pos; - check_fun t1 t2; - a.a_read <- Some cf; - | TFun((_,_,t1) :: (_,_,t2) :: (_,_,t3) :: args,_) when is_empty_or_pos_infos args -> - if a.a_write <> None then raise_typing_error "Multiple resolve-write methods are not supported" cf.cf_pos; - check_fun t1 t2; - a.a_write <- Some cf; - | _ -> - raise_typing_error ("Field type of resolve must be " ^ (s_type (print_context()) targ) ^ " -> String -> T") cf.cf_pos - end; - | _ -> ()); - match ml with - | _ :: ml -> loop ml - | [] -> () +let check_abstract (ctx,cctx,fctx) a c cf fd t ret p = + let m = mk_mono() in + let ta = TAbstract(a,List.map (fun _ -> mk_mono()) a.a_params) in + let tthis = if fctx.is_abstract_member || Meta.has Meta.To cf.cf_meta then monomorphs a.a_params a.a_this else a.a_this in + let allows_no_expr = ref (Meta.has Meta.CoreType a.a_meta) in + let allow_no_expr () = if not (has_class_field_flag cf CfExtern) then begin + allows_no_expr := true; + fctx.expr_presence_matters <- true; + end in + let handle_from () = + let r = exc_protect ctx (fun r -> + r := lazy_processing (fun () -> t); + (* the return type of a from-function must be the abstract, not the underlying type *) + if not fctx.is_macro then (try type_eq EqStrict ret ta with Unify_error l -> raise_typing_error_ext (make_error (Unify l) p)); + match t with + | TFun([_,_,t],_) -> t + | TFun([(_,_,t1);(_,true,t2)],_) when is_pos_infos t2 -> t1 + | _ -> raise_typing_error ("@:from cast functions must accept exactly one argument") p + ) "@:from" in + a.a_from_field <- (TLazy r,cf) :: a.a_from_field; + in + let handle_to () = + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "cast function" p; + let are_valid_args args = + match args with + | [_] -> true + | [_; (_,true,t)] when is_pos_infos t -> true + | _ -> false + in + (match cf.cf_kind, cf.cf_type with + | Var _, _ -> + raise_typing_error "Invalid metadata: @:to must be used on method of abstract" p + | Method _, TFun(args, _) when not fctx.is_abstract_member && not (are_valid_args args) -> + if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *) + raise_typing_error "static @:to method should have one argument" p + | Method _, TFun(args, _) when fctx.is_abstract_member && not (are_valid_args args) -> + if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *) + raise_typing_error "@:to method should have no arguments" p + | _ -> () + ); + (* TODO: this doesn't seem quite right... *) + if not (has_class_field_flag cf CfImpl) then add_class_field_flag cf CfImpl; + let resolve_m args = + (try unify_raise t (tfun (tthis :: args) m) cf.cf_pos with Error ({ err_message = Unify l; } as err) -> raise_typing_error_ext err); + match follow m with + | TMono _ when (match t with TFun(_,r) -> r == t_dynamic | _ -> false) -> t_dynamic + | m -> m + in + let is_multitype_cast = Meta.has Meta.MultiType a.a_meta && not fctx.is_abstract_member in + if is_multitype_cast && not (Meta.has Meta.MultiType cf.cf_meta) then + cf.cf_meta <- (Meta.MultiType,[],null_pos) :: cf.cf_meta; + let r = exc_protect ctx (fun r -> + r := lazy_processing (fun () -> t); + let args = if is_multitype_cast then begin + let ctor = try + PMap.find "_new" c.cl_statics + with Not_found -> + raise_typing_error "Constructor of multi-type abstract must be defined before the individual @:to-functions are" cf.cf_pos + in + (* delay ctx PFinal (fun () -> unify ctx m tthis f.cff_pos); *) + let args = match follow (monomorphs a.a_params ctor.cf_type) with + | TFun(args,_) -> List.map (fun (_,_,t) -> t) args + | _ -> die "" __LOC__ + in + args + end else + match cf.cf_type with + | TFun([_;(_,true,t)],_) when is_pos_infos t -> [t] + | _ -> [] in - loop cf.cf_meta; - if cf.cf_name = "_new" && Meta.has Meta.MultiType a.a_meta then fctx.do_bind <- false; - if fd.f_expr = None then begin - if fctx.is_inline then missing_expression ctx.com fctx "Inline functions must have an expression" cf.cf_pos; - if fd.f_type = None then raise_typing_error ("Functions without expressions must have an explicit return type") cf.cf_pos; - 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 + let t = resolve_m args in + t + ) "@:to" in + a.a_to_field <- (TLazy r, cf) :: a.a_to_field + in + let handle_array_access () = + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "array-access function" p; + a.a_array <- cf :: a.a_array; + allow_no_expr(); + in + let handle_resolve () = + let targ = if fctx.is_abstract_member then tthis else ta in + let check_fun t1 t2 = + if not fctx.is_macro then begin + if not (type_iseq targ t1) then raise_typing_error ("First argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; + if not (type_iseq ctx.t.tstring t2) then raise_typing_error ("Second argument type must be String") cf.cf_pos end + in + begin match follow t with + | TFun((_,_,t1) :: (_,_,t2) :: args,_) when is_empty_or_pos_infos args -> + if a.a_read <> None then raise_typing_error "Multiple resolve-read methods are not supported" cf.cf_pos; + check_fun t1 t2; + a.a_read <- Some cf; + | TFun((_,_,t1) :: (_,_,t2) :: (_,_,t3) :: args,_) when is_empty_or_pos_infos args -> + if a.a_write <> None then raise_typing_error "Multiple resolve-write methods are not supported" cf.cf_pos; + check_fun t1 t2; + a.a_write <- Some cf; + | _ -> + raise_typing_error ("Field type of resolve must be " ^ (s_type (print_context()) targ) ^ " -> String -> T") cf.cf_pos + end; + in + let handle_op e = match fst e with + | (EArrayDecl _) -> + handle_array_access() + | EBinop(OpAssign,_,_) -> + raise_typing_error "Assignment overloading is not supported" p; + | EBinop(OpAssignOp OpNullCoal,_,_) + | EBinop(OpNullCoal,_,_) -> + raise_typing_error "Null coalescing overloading is not supported" p; + | ETernary(_,_,_) -> + raise_typing_error "Ternary overloading is not supported" p; + | EBinop(op,_,_) -> + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "operator function" p; + let targ = if fctx.is_abstract_member then tthis else ta in + let left_eq,right_eq = + match follow t with + | TFun([(_,_,t1);(_,_,t2)],_) -> + type_iseq targ t1,type_iseq targ t2 + | TFun([(_,_,t1);(_,_,t2);(_,true,t3)],_) when is_pos_infos t3 -> + type_iseq targ t1,type_iseq targ t2 + | _ -> + if fctx.is_abstract_member then + raise_typing_error ("Member @:op functions must accept exactly one argument") cf.cf_pos + else + raise_typing_error ("Static @:op functions must accept exactly two arguments") cf.cf_pos + in + if not (left_eq || right_eq) then raise_typing_error ("The left or right argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; + if right_eq && Meta.has Meta.Commutative cf.cf_meta then raise_typing_error ("Invalid metadata: @:commutative is only allowed if the right argument is not " ^ (s_type (print_context()) targ)) cf.cf_pos; + a.a_ops <- (op,cf) :: a.a_ops; + allow_no_expr(); + | EUnop(op,flag,_) -> + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "operator function" p; + let targ = if fctx.is_abstract_member then tthis else ta in + (try type_eq EqStrict t (tfun [targ] (mk_mono())) with Unify_error l -> raise_error_msg (Unify l) cf.cf_pos); + a.a_unops <- (op,flag,cf) :: a.a_unops; + allow_no_expr(); + | ECall _ -> + begin match a.a_call with + | None -> + a.a_call <- Some cf + | Some cf' -> + cf'.cf_overloads <- cf :: cf'.cf_overloads + end; + allow_no_expr(); + | EField _ -> + handle_resolve() | _ -> - () + raise_typing_error ("Invalid @:op expresssions, should be an operator or a call") (pos e) + in + let check_meta m = match m with + | (Meta.From,_,_) -> + handle_from() + | (Meta.To,_,_) -> + handle_to() + | (Meta.Op,[e],_) -> + handle_op e + | (Meta.ArrayAccess,_,_) -> + handle_array_access() + | (Meta.Resolve,_,_) -> + handle_resolve() + | _ -> (); + in + List.iter check_meta cf.cf_meta; + if cf.cf_name = "_new" && Meta.has Meta.MultiType a.a_meta then fctx.do_bind <- false; + if fd.f_expr = None then begin + if fctx.is_inline then missing_expression ctx.com fctx "Inline functions must have an expression" cf.cf_pos; + if fd.f_type = None then raise_typing_error ("Functions without expressions must have an explicit return type") cf.cf_pos; + if !allows_no_expr then begin + cf.cf_meta <- (Meta.NoExpr,[],null_pos) :: cf.cf_meta; + fctx.do_bind <- false; + end + end let type_opt (ctx,cctx,fctx) p t = let c = cctx.tclass in @@ -1428,7 +1438,12 @@ let create_method (ctx,cctx,fctx) c f fd p = () ) parent; generate_args_meta ctx.com (Some c) (fun meta -> cf.cf_meta <- meta :: cf.cf_meta) fd.f_args; - check_abstract (ctx,cctx,fctx) c cf fd t ret p; + begin match cctx.abstract with + | Some a -> + check_abstract (ctx,cctx,fctx) a c cf fd t ret p; + | _ -> + () + end; init_meta_overloads ctx (Some c) cf; ctx.curfield <- cf; if fctx.do_bind then @@ -1854,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 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 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/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; } 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/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/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: []}); + } } } 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) { diff --git a/std/hl/CArray.hx b/std/hl/CArray.hx index 850d86f5705..81c7329e82b 100644 --- a/std/hl/CArray.hx +++ b/std/hl/CArray.hx @@ -9,9 +9,14 @@ abstract CArray(Abstract<"hl_carray">) { public var length(get,never) : Int; - inline function get_length() return getLen(cast this); + #if (hl_ver >= version("1.14.0")) + inline function get_length() return untyped $asize(this); + @:arrayAccess inline function get( index : Int ) : T return untyped this[index]; + #else + inline function get_length() return getLen(cast this); @:arrayAccess inline function get( index : Int ) : T return getIndex(cast this, index); + #end public static function alloc( cl : Class, size : Int ) : CArray { return cast alloc_carray( (cast cl:BaseType).__type__ , size ); 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/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 diff --git a/tests/misc/projects/Issue11176/Main.hx b/tests/misc/projects/Issue11176/Main.hx new file mode 100644 index 00000000000..cfa04d5a61e --- /dev/null +++ b/tests/misc/projects/Issue11176/Main.hx @@ -0,0 +1,6 @@ +function main() { + var cost = [1, 2, 3]; + var _g = []; + for (x in cost) _g.push("" + x); + _g.join(" "); +} diff --git a/tests/misc/projects/Issue11176/compile.hxml b/tests/misc/projects/Issue11176/compile.hxml new file mode 100644 index 00000000000..42409e72918 --- /dev/null +++ b/tests/misc/projects/Issue11176/compile.hxml @@ -0,0 +1 @@ +-main Main diff --git a/tests/misc/projects/Issue11237/Main.hx b/tests/misc/projects/Issue11237/Main.hx new file mode 100644 index 00000000000..b5015c372cb --- /dev/null +++ b/tests/misc/projects/Issue11237/Main.hx @@ -0,0 +1,5 @@ +abstract A(Int) { + @:op(a) function bar() {} +} + +function main() {} diff --git a/tests/misc/projects/Issue11237/compile-fail.hxml b/tests/misc/projects/Issue11237/compile-fail.hxml new file mode 100644 index 00000000000..fab0aeecc3d --- /dev/null +++ b/tests/misc/projects/Issue11237/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue11237/compile-fail.hxml.stderr b/tests/misc/projects/Issue11237/compile-fail.hxml.stderr new file mode 100644 index 00000000000..dfe2ad519b1 --- /dev/null +++ b/tests/misc/projects/Issue11237/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:2: characters 7-8 : Invalid @:op expresssions, should be an operator or a call \ No newline at end of file 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; + } + } } 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 +} 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)); } } 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")))); + } +} 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]); + } +}