소스 검색

refactor HDyn / added HNull

Nicolas Cannasse 9 년 전
부모
커밋
4f12cf168d
2개의 변경된 파일132개의 추가작업 그리고 90개의 파일을 삭제
  1. 131 89
      genhl.ml
  2. 1 1
      std/hl/_std/String.hx

+ 131 - 89
genhl.ml

@@ -37,7 +37,7 @@ type ttype =
 	| HF64
 	| HBool
 	| HBytes
-	| HDyn of ttype option
+	| HDyn
 	| HFun of ttype list * ttype
 	| HObj of class_proto
 	| HArray
@@ -47,6 +47,7 @@ type ttype =
 	| HDynObj
 	| HAbstract of string * string index
 	| HEnum of enum_proto
+	| HNull of ttype
 
 and class_proto = {
 	pname : string;
@@ -290,8 +291,7 @@ let rec tstr ?(stack=[]) ?(detailed=false) t =
 	| HF64 -> "f64"
 	| HBool -> "bool"
 	| HBytes -> "bytes"
-	| HDyn None -> "dyn"
-	| HDyn (Some t) -> "dyn(" ^ tstr t ^ ")"
+	| HDyn  -> "dyn"
 	| HFun (args,ret) -> "(" ^ String.concat "," (List.map (tstr ~detailed) args) ^ "):" ^ tstr ~detailed ret
 	| HObj o when not detailed -> "#" ^ o.pname
 	| HObj o ->
@@ -317,6 +317,7 @@ let rec tstr ?(stack=[]) ?(detailed=false) t =
 		"enum(" ^ String.concat "," (List.map tstr (Array.to_list fl)) ^ ")"
 	| HEnum e ->
 		"enum(" ^ e.ename ^ ")"
+	| HNull t -> "null(" ^ tstr t ^ ")"
 
 let rec tsame t1 t2 =
 	if t1 == t2 then true else
@@ -335,15 +336,31 @@ let rec tsame t1 t2 =
 			if i1 = i2 && tsame t1 t2 then loop (i + 1) else false
 		in
 		loop 0
-	| HDyn None, HDyn None -> true
-	| HDyn (Some t1), HDyn (Some t2) -> tsame t1 t2
+	| HNull t1, HNull t2 -> tsame t1 t2
 	| HRef t1, HRef t2 -> tsame t1 t2
 	| _ -> false
 
+
+(*
+	does the runtime value can be set to null
+*)
+let is_nullable t =
+	match t with
+	| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> true
+	| _ -> false
+
+(*
+	does the runtime value carry its type
+*)
+let is_dynamic t =
+	match t with
+	| HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HNull _ -> true
+	| _ -> false
+
 let rec safe_cast t1 t2 =
 	if t1 == t2 then true else
 	match t1, t2 with
-	| (HDyn _ | HObj _ | HFun _ | HArray | HDynObj), HDyn None -> true
+	| _, HDyn -> is_dynamic t1
 	| HVirtual v1, HVirtual v2 when Array.length v2.vfields < Array.length v1.vfields ->
 		let rec loop i =
 			if i = Array.length v2.vfields then true else
@@ -359,7 +376,7 @@ let rec safe_cast t1 t2 =
 		in
 		loop p1
 	| HFun (args1,t1), HFun (args2,t2) when List.length args1 = List.length args2 ->
-		List.for_all2 (fun t1 t2 -> safe_cast t2 t1 || (t2 = HDyn None && safe_cast t1 t2)) args1 args2 && safe_cast t1 t2
+		List.for_all2 (fun t1 t2 -> safe_cast t2 t1 || (t2 = HDyn && is_dynamic t1)) args1 args2 && safe_cast t1 t2
 	| _ ->
 		tsame t1 t2
 
@@ -488,14 +505,13 @@ let rec to_type ctx t =
 	match t with
 	| TMono r ->
 		(match !r with
-		| None -> HDyn None
+		| None -> HDyn
 		| Some t -> to_type ctx t)
 	| TType (t,tl) ->
 		(match t.t_path with
 		| [], "Null" ->
-			(match to_type ctx (apply_params t.t_params tl t.t_type) with
-			| HI8 | HI16 | HI32 | HF32 | HF64 | HBool as t -> HDyn (Some t)
-			| t -> t)
+			let t = to_type ctx (apply_params t.t_params tl t.t_type) in
+			if is_nullable t then t else HNull t
 		| _ ->
 			to_type ctx (apply_params t.t_params tl t.t_type))
 	| TLazy f ->
@@ -538,14 +554,14 @@ let rec to_type ctx t =
 			t
 		)
 	| TDynamic _ ->
-		HDyn None
+		HDyn
 	| TEnum (e,_) ->
 		enum_type ctx e
 	| TInst ({ cl_path = ["hl";"types"],"NativeAbstract" },[TInst({ cl_kind = KExpr (EConst (String name),_) },_)]) ->
 		HAbstract (name, alloc_string ctx name)
 	| TInst (c,pl) ->
 		(match c.cl_kind with
-		| KTypeParameter _ -> HDyn None
+		| KTypeParameter _ -> HDyn
 		| _ -> class_type ctx c pl false)
 	| TAbstract (a,pl) ->
 		if Meta.has Meta.CoreType a.a_meta then
@@ -563,7 +579,7 @@ let rec to_type ctx t =
 				) in
 				class_type ctx c pl s
 			| [], "Enum" -> HType
-			| [], "EnumValue" -> HDyn None
+			| [], "EnumValue" -> HDyn
 			| ["hl";"types"], "Ref" -> HRef (to_type ctx (List.hd pl))
 			| ["hl";"types"], "Bytes" -> HBytes
 			| ["hl";"types"], "Type" -> HType
@@ -574,7 +590,7 @@ let rec to_type ctx t =
 
 and array_type ctx t =
 	let et = to_type ctx t in
-	if safe_cast et (HDyn None) then et else HDyn None
+	if is_dynamic et then et else HDyn
 
 and resolve_class ctx c pl =
 	let not_supported() =
@@ -780,12 +796,12 @@ let common_type ctx e1 e2 for_eq p =
 		| HI32, HF32 -> t2 (* possible loss of precision *)
 		| (HI32 | HF32), HF64 -> t2
 		| (HI8|HI16|HI32|HF32|HF64), (HI8|HI16|HI32|HF32|HF64) -> t1
-		| (HI8|HI16|HI32|HF32|HF64), (HDyn (Some t2)) -> loop t1 t2
-		| (HDyn (Some t1)), (HI8|HI16|HI32|HF32|HF64) -> loop t1 t2
-		| (HDyn None), (HI8|HI16|HI32|HF32|HF64) -> HF64
-		| (HI8|HI16|HI32|HF32|HF64), (HDyn None) -> HF64
-		| HDyn None, _ -> HDyn None
-		| _, HDyn None -> HDyn None
+		| (HI8|HI16|HI32|HF32|HF64), (HNull t2) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
+		| (HNull t1), (HI8|HI16|HI32|HF32|HF64) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
+		| HDyn, (HI8|HI16|HI32|HF32|HF64) -> HF64
+		| (HI8|HI16|HI32|HF32|HF64), HDyn -> HF64
+		| HDyn, _ -> HDyn
+		| _, HDyn -> HDyn
 		| _ when for_eq && safe_cast t1 t2 -> t2
 		| _ when for_eq && safe_cast t2 t1 -> t1
 		| _ ->
@@ -818,12 +834,12 @@ and cast_to ctx (r:reg) (t:ttype) p =
 	match rt, t with
 	| _, HVoid ->
 		alloc_tmp ctx HVoid
-	| HVirtual _, HDyn None ->
-		let tmp = alloc_tmp ctx (HDyn None) in
+	| HVirtual _, HDyn ->
+		let tmp = alloc_tmp ctx HDyn in
 		op ctx (OUnVirtual (tmp,r));
 		tmp
 	| HVirtual _, HVirtual _ ->
-		let tmp = alloc_tmp ctx (HDyn None) in
+		let tmp = alloc_tmp ctx HDyn in
 		op ctx (OUnVirtual (tmp,r));
 		cast_to ctx tmp t p
 	| (HI8 | HI16 | HI32), (HF32 | HF64) ->
@@ -848,20 +864,24 @@ and cast_to ctx (r:reg) (t:ttype) p =
 		op ctx (OCall2 (bytes,alloc_std ctx "ftos" [HF64;HRef HI32] HBytes,cast_to ctx r HF64 p,lref));
 		op ctx (OCall3 (out,alloc_fun_path ctx ([],"String") "__alloc__",bytes,len,len));
 		out
-	| (HObj _ | HDynObj | HDyn None) , HVirtual _ ->
+	| (HObj _ | HDynObj | HDyn) , HVirtual _ ->
 		let out = alloc_tmp ctx t in
 		op ctx (OToVirtual (out,r));
 		out
-	| HDyn None, _ ->
+	| HDyn, _ ->
 		let out = alloc_tmp ctx t in
 		op ctx (OSafeCast (out, r));
 		out
-	| HDyn (Some rt), _ when rt == t ->
+	| HNull rt, _ when rt == t ->
 		let out = alloc_tmp ctx t in
 		op ctx (OSafeCast (out, r));
 		out
-	| _ , HDyn _ ->
-		let tmp = alloc_tmp ctx (HDyn (Some rt)) in
+	| _ , HDyn ->
+		let tmp = alloc_tmp ctx HDyn in
+		op ctx (OToDyn (tmp, r));
+		tmp
+	| _, HNull t when rt == t ->
+		let tmp = alloc_tmp ctx (HNull t) in
 		op ctx (OToDyn (tmp, r));
 		tmp
 	| HFun (args1,ret1), HFun (args2, ret2) when List.length args1 = List.length args2 ->
@@ -881,7 +901,7 @@ and unsafe_cast_to ctx (r:reg) (t:ttype) p =
 	| HFun _ ->
 		cast_to ctx r t p
 	| _ ->
-		if safe_cast (rtype ctx r) (HDyn None) && safe_cast t (HDyn None) then
+		if is_dynamic (rtype ctx r) && is_dynamic t then
 			let r2 = alloc_tmp ctx t in
 			op ctx (OUnsafeCast (r2,r));
 			r2
@@ -908,7 +928,7 @@ and object_access ctx eobj t f =
 				AInstanceField (eobj, fid)
 		with Not_found ->
 			ADynamic (eobj, alloc_string ctx f.cf_name))
-	| HDyn None ->
+	| HDyn ->
 		ADynamic (eobj, alloc_string ctx f.cf_name)
 	| _ ->
 		error ("Unsupported field access " ^ tstr t) eobj.epos
@@ -984,7 +1004,7 @@ and jump_expr ctx e jcond =
 		jump ctx (fun i -> if jcond then OJTrue (r,i) else OJFalse (r,i))
 
 and eval_args ctx el t =
-	List.map2 (fun e t -> eval_to ctx e t) el (match t with HFun (args,_) -> args | HDyn None -> List.map (fun _ -> HDyn None) el | _ -> assert false)
+	List.map2 (fun e t -> eval_to ctx e t) el (match t with HFun (args,_) -> args | HDyn -> List.map (fun _ -> HDyn) el | _ -> assert false)
 
 and eval_null_check ctx e =
 	let r = eval_expr ctx e in
@@ -1175,16 +1195,19 @@ and eval_expr ctx e =
 			op ctx (OCall2 (a,alloc_std ctx "aalloc" [HType;HI32] HArray,rt,size));
 			a
 		| "$aget", [a; pos] ->
+			(*
+				read/write on arrays are unsafe : the type of NativeArray needs to be correcly set.
+			*)
 			let at = (match follow a.etype with TAbstract ({ a_path = ["hl";"types"],"NativeArray" },[t]) -> to_type ctx t | _ -> invalid()) in
 			let arr = eval_to ctx a HArray in
 			let pos = eval_to ctx pos HI32 in
 			let r =
-				if safe_cast at (HDyn None) then
+				if is_dynamic at then
 					let r = alloc_tmp ctx at in
 					op ctx (OGetArray (r, arr, pos));
 					r
 				else
-					let tmp = alloc_tmp ctx (HDyn None) in
+					let tmp = alloc_tmp ctx HDyn in
 					op ctx (OGetArray (tmp,arr,pos));
 					unsafe_cast_to ctx tmp at e.epos
 			in
@@ -1213,7 +1236,7 @@ and eval_expr ctx e =
 		) in
 		let tfun = to_type ctx real_type in
 		let el = eval_args ctx el tfun in
-		let ret = alloc_tmp ctx (match tfun with HFun (_,r) -> r | _ -> HDyn None) in
+		let ret = alloc_tmp ctx (match tfun with HFun (_,r) -> r | _ -> HDyn) in
 		(match get_access ctx ec with
 		| AStaticFun f ->
 			(match el with
@@ -1340,7 +1363,7 @@ and eval_expr ctx e =
 						op ctx (OAdd (r,a,b))
 					| HObj { pname = "String" } ->
 						op ctx (OCall2 (r,alloc_fun_path ctx ([],"String") "__add__",a,b))
-					| HDyn None ->
+					| HDyn ->
 						op ctx (OCall2 (r,alloc_fun_path ctx ([],"Std") "__add__",a,b))
 					| t ->
 						error ("Cannot add " ^ tstr t) e.epos)
@@ -1587,8 +1610,8 @@ and eval_expr ctx e =
 			op ctx (OGetFunction (r, fid));
 		r
 	| TThrow v ->
-		op ctx (OThrow (eval_to ctx v (HDyn None)));
-		alloc_tmp ctx HVoid (* not initialized *)
+		op ctx (OThrow (eval_to ctx v HDyn));
+		alloc_tmp ctx HVoid
 	| TWhile (cond,eloop,NormalWhile) ->
 		let oldb = ctx.m.mbreaks and oldc = ctx.m.mcontinues in
 		ctx.m.mbreaks <- [];
@@ -1646,7 +1669,7 @@ and eval_expr ctx e =
 			) el;
 			op ctx (OCall2 (r, alloc_fun_path ctx (["hl";"types"],"ArrayF64") "alloc", b, reg_int ctx (List.length el)));
 		| _ ->
-			let at = if safe_cast et (HDyn None) then et else HDyn None in
+			let at = if is_dynamic et then et else HDyn in
 			let a = alloc_tmp ctx HArray in
 			let rt = alloc_tmp ctx HType in
 			op ctx (OType (rt,et));
@@ -1720,7 +1743,7 @@ and eval_expr ctx e =
 			op ctx (ONull r);
 			let jend = jump ctx (fun i -> OJAlways i) in
 			j();
-			let tmp = alloc_tmp ctx (HDyn None) in
+			let tmp = alloc_tmp ctx HDyn in
 			op ctx (OGetArray (tmp,harr,ri));
 			let r2 = unsafe_cast_to ctx tmp at e.epos in
 			op ctx (OMov (r,r2));
@@ -1814,7 +1837,7 @@ and eval_expr ctx e =
 		alloc_tmp ctx HVoid
 	| TTry (etry,catches) ->
 		let pos = current_pos ctx in
-		let rtrap = alloc_tmp ctx (HDyn None) in
+		let rtrap = alloc_tmp ctx HDyn in
 		op ctx (OTrap (rtrap,-1)); (* loop *)
 		ctx.m.mtrys <- ctx.m.mtrys + 1;
 		let tret = to_type ctx e.etype in
@@ -1910,7 +1933,7 @@ and gen_method_wrapper ctx rt t p =
 		let old = ctx.m in
 		let targs, tret = (match t with HFun (args, ret) -> args, ret | _ -> assert false) in
 		let iargs, iret = (match rt with HFun (args, ret) -> args, ret | _ -> assert false) in
-		ctx.m <- method_context (HDyn None) null_capture;
+		ctx.m <- method_context HDyn null_capture;
 		let rfun = alloc_tmp ctx rt in
 		let rargs = List.map (alloc_tmp ctx) targs in
 		let rret = alloc_tmp ctx iret in
@@ -2147,7 +2170,7 @@ let check code =
 			Printf.sprintf "%s(%d)" code.debugfiles.(dfile) dline
 		in
 		let error msg =
-			failwith ("Check failure at " ^ string_of_int f.findex ^ "@" ^ string_of_int (!pos) ^ " : " ^ msg ^ "\n" ^ debug())
+			failwith ("Check failure " ^ msg ^ "\nAt " ^ string_of_int f.findex ^ "@" ^ string_of_int (!pos) ^ " " ^ debug())
 		in
 		let targs, tret = (match f.ftype with HFun (args,ret) -> args, ret | _ -> assert false) in
 		let rtype i = f.regs.(i) in
@@ -2198,7 +2221,7 @@ let check code =
 			| _ -> error (reg_inf r ^ " should be enum")
 		in
 		let is_dyn r =
-			if not (safe_cast (rtype r) (HDyn None)) then error (reg_inf r ^ " should be castable to dynamic")
+			if not (is_dynamic (rtype r)) then error (reg_inf r ^ " should be castable to dynamic")
 		in
 		let tfield o fid proto =
 			match rtype o with
@@ -2247,10 +2270,8 @@ let check code =
 				reg r HBytes;
 				if i < 0 || i >= Array.length code.strings then error "string outside range";
 			| ONull r ->
-				(match rtype r with
-				| HBytes | HEnum _ | HVirtual _ | HType -> ()
-				| _ when safe_cast (rtype r) (HDyn None) -> ()
-				| t -> error (tstr t ^ " is not nullable"))
+				let t = rtype r in
+				if not (is_nullable t) then error (tstr t ^ " is not nullable")
 			| OAdd (r,a,b) | OSub (r,a,b) | OMul (r,a,b) | OSDiv (r,a,b) | OUDiv (r,a,b) | OSMod (r,a,b) | OUMod(r,a,b) ->
 				numeric r;
 				reg a (rtype r);
@@ -2321,8 +2342,9 @@ let check code =
 			| OJAlways d ->
 				can_jump d
 			| OToDyn (r,a) ->
-				if safe_cast (rtype a) (HDyn None) then reg a HI32; (* don't wrap as dynamic types that can safely be cast to it *)
-				reg r (HDyn (Some (rtype a)))
+				(* we can still use OToDyn on nullable if we want to turn them into dynamic *)
+				if is_dynamic (rtype a) then reg a HI32; (* don't wrap as dynamic types that can safely be cast to it *)
+				if rtype r <> HDyn then reg r (HNull (rtype a))
 			| OToFloat (a,b) ->
 				int b;
 				float a;
@@ -2362,7 +2384,7 @@ let check code =
 					reg r (HFun (tl,tret));
 				| _ -> assert false);
 			| OThrow r ->
-				reg r (HDyn None)
+				reg r HDyn
 			| OGetArray (v,a,i) ->
 				reg a HArray;
 				reg i HI32;
@@ -2404,8 +2426,8 @@ let check code =
 				reg i HI32;
 				is_dyn v;
 			| OUnsafeCast (a,b) ->
-				if not (safe_cast (rtype a) (HDyn None)) then is_obj a;
-				ignore(rtype b);
+				is_dyn a;
+				is_dyn b;
 			| OSafeCast (a,b) ->
 				ignore(rtype a);
 				ignore(rtype b);
@@ -2429,18 +2451,18 @@ let check code =
 				| HVirtual _ -> ()
 				| _ -> reg r (HVirtual {vfields=[||];vindex=PMap.empty;}));
 				(match rtype v with
-				| HObj _ | HDynObj | HDyn None -> ()
+				| HObj _ | HDynObj | HDyn -> ()
 				| _ -> reg v HDynObj)
 			| OUnVirtual (r,v) ->
 				(match rtype v with
 				| HVirtual _ -> ()
 				| _ -> reg r (HVirtual {vfields=[||];vindex=PMap.empty;}));
-				reg r (HDyn None)
+				reg r HDyn
 			| ODynGet (v,r,f) | ODynSet (r,f,v) ->
 				ignore(code.strings.(f));
 				ignore(rtype v);
 				(match rtype r with
-				| HObj _ | HDyn None | HDynObj | HVirtual _ -> ()
+				| HObj _ | HDyn | HDynObj | HVirtual _ -> ()
 				| _ -> reg r HDynObj)
 			| OMakeEnum (r,index,pl) ->
 				(match rtype r with
@@ -2477,7 +2499,7 @@ let check code =
 			| ONullCheck r ->
 				ignore(rtype r)
 			| OTrap (r, idx) ->
-				reg r (HDyn None);
+				reg r HDyn;
 				can_jump idx
 			| OEndTrap _ ->
 				()
@@ -2525,7 +2547,7 @@ and vabstract =
 
 and vfunction =
 	| FFun of fundecl
-	| FNativeFun of string * (value list -> value)
+	| FNativeFun of string * (value list -> value) * ttype
 
 and vobject = {
 	oproto : vproto;
@@ -2559,21 +2581,36 @@ exception Return of value
 
 let default t =
 	match t with
-	| HVoid | HFun _ | HDyn _ | HObj _ | HBytes | HArray | HType | HRef _ | HVirtual _ | HDynObj | HAbstract _ | HEnum _ -> VNull
 	| HI8 | HI16 | HI32 -> VInt Int32.zero
 	| HF32 | HF64 -> VFloat 0.
 	| HBool -> VBool false
+	| _ -> if is_nullable t then VNull else VUndef
+
+let get_type = function
+	| VDyn (_,t) -> Some t
+	| VObj o -> Some (HObj o.oproto.pclass)
+	| VDynObj _ -> Some HDynObj
+	| VVirtual v -> Some (HVirtual v.vtype)
+	| VArray _ -> Some HArray
+	| VClosure (f,None) -> Some (match f with FFun f -> f.ftype | FNativeFun (_,_,t) -> t)
+	| VClosure (f,Some _) -> Some (match f with FFun { ftype = HFun(_::args,ret) } | FNativeFun (_,_,HFun(_::args,ret)) -> HFun (args,ret) | _ -> assert false)
+	| _ -> None
+
+let v_dynamic = function
+	| VNull	| VDyn _ | VObj _ | VClosure _ | VArray _ | VVirtual _ | VDynObj _ -> true
+	| _ -> false
 
-let is_compatible v t =
+let rec is_compatible v t =
 	match v, t with
 	| VInt _, HI32 -> true
 	| VBool _, HBool -> true
-	| VNull, (HObj _ | HFun _ | HBytes | HArray | HType | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HDyn _) -> true
+	| VNull, t -> is_nullable t
 	| VObj _, HObj _ -> true
 	| VClosure _, HFun _ -> true
 	| VBytes _, HBytes -> true
-	| VDyn (_,t1), HDyn (Some t2) -> tsame t1 t2
-	| (VDyn _ | VObj _ | VClosure _ | VArray _), HDyn None -> true
+	| VDyn (_,t1), HNull t2 -> tsame t1 t2
+	| v, HNull t -> is_compatible v t
+	| v, HDyn -> v_dynamic v
 	| VUndef, HVoid -> true
 	| VType _, HType -> true
 	| VArray _, HArray -> true
@@ -2588,7 +2625,7 @@ exception InterpThrow of value
 let interp code =
 
 	let globals = Array.map default code.globals in
-	let functions = Array.create (Array.length code.functions + Array.length code.natives) (FNativeFun ("",(fun _ -> assert false))) in
+	let functions = Array.create (Array.length code.functions + Array.length code.natives) (FNativeFun ("",(fun _ -> assert false),HDyn)) in
 	let cached_protos = Hashtbl.create 0 in
 	let func f = Array.unsafe_get functions f in
 
@@ -2658,7 +2695,7 @@ let interp code =
 		| VUndef -> "undef"
 		| VType t -> tstr t
 		| VRef (regs,i,t) -> "*" ^ (vstr regs.(i) t)
-		| VVirtual v -> vstr v.vvalue (HDyn None)
+		| VVirtual v -> vstr v.vvalue HDyn
 		| VDynObj d -> "{" ^ String.concat ", " (Hashtbl.fold (fun f i acc -> (f^":"^vstr d.dvalues.(i) d.dtypes.(i)) :: acc) d.dfields []) ^ "}"
 		| VAbstract _ -> "abstract"
 		| VEnum (i,vals) ->
@@ -2674,12 +2711,12 @@ let interp code =
 
 	and fstr = function
 		| FFun f -> "function@" ^ string_of_int f.findex
-		| FNativeFun (s,_) -> "native[" ^ s ^ "]"
+		| FNativeFun (s,_,_) -> "native[" ^ s ^ "]"
 
 	and fcall f args =
 		match f with
 		| FFun f -> call f args
-		| FNativeFun (_,f) -> f args
+		| FNativeFun (_,f,_) -> f args
 
 	and call f args =
 		let regs = Array.create (Array.length f.regs) VUndef in
@@ -2780,6 +2817,12 @@ let interp code =
 			let l = int_of_char (String.get b (p + 3)) in
 			Int32.logor (Int32.of_int (i lor (j lsl 8) lor (k lsl 16))) (Int32.shift_left (Int32.of_int l) 24);
 		in
+		let make_dyn v t =
+			if v = VNull || is_dynamic t then
+				v
+			else
+				VDyn (v,t)
+		in
 		let rec loop() =
 			let op = f.code.(!pos) in
 			incr pos;
@@ -2836,7 +2879,7 @@ let interp code =
 			| OJEq (a,b,i) -> if vcompare a b = 0 then pos := !pos + i
 			| OJNeq (a,b,i) -> if vcompare a b <> 0 then pos := !pos + i
 			| OJAlways i -> pos := !pos + i
-			| OToDyn (r,a) -> set r (VDyn (get a, f.regs.(a)))
+			| OToDyn (r,a) -> set r (make_dyn (get a) f.regs.(a))
 			| OToFloat (r,a) -> set r (match get a with VInt v -> VFloat (Int32.to_float v) | _ -> assert false)
 			| OToInt (r,a) -> set r (match get a with VFloat v -> VInt (Int32.of_float v) | _ -> assert false)
 			| OLabel _ -> ()
@@ -3045,8 +3088,8 @@ let interp code =
 					else match t, rtype r with
 					| (HI8|HI16|HI32), (HF32|HF64) ->
 						set r (match v with VInt i -> VFloat (Int32.to_float i) | _ -> assert false)
-					| _, HDyn None ->
-						set r (if safe_cast t (HDyn None) then v else VDyn (v,t))
+					| _, HDyn ->
+						set r (make_dyn v t)
 					| _ ->
 						error ("Can't cast " ^ tstr t ^ " to " ^ tstr (rtype r))
 				in
@@ -3075,13 +3118,10 @@ let interp code =
 						if d.dvirtuals <> [] then assert false (* TODO : update virtuals table *)
 					in
 					let v, vt = (match rtype vr with
-						| HDyn _ ->
-							(match v with
-							| VDyn (v,t) -> v,t
-							| VObj o -> v, HObj o.oproto.pclass
-							| VDynObj _ -> v, HDynObj
-							| VVirtual vp -> v, HVirtual vp.vtype
-							| _ -> assert false)
+						| HDyn ->
+							(match get_type v with
+							| None -> assert false
+							| Some t -> (match v with VDyn (v,_) -> v | _ -> v), t)
 						| t -> v, t
 					) in
 					(try
@@ -3170,8 +3210,8 @@ let interp code =
 		exec()
 	in
 	let int = Int32.to_int in
-	let load_native lib name =
-		FNativeFun (lib ^ "@" ^ name, (match lib with
+	let load_native lib name t =
+		let f = (match lib with
 		| "std" ->
 			(match name with
 			| "balloc" ->
@@ -3211,7 +3251,7 @@ let interp code =
 			| "value_to_string" ->
 				(function
 				| [v; VRef (regs,i,_)] ->
-					let str = vstr v (HDyn None) in
+					let str = vstr v HDyn in
 					regs.(i) <- VInt (Int32.of_int (String.length str));
 					VBytes (str ^ "\x00")
 				| _ -> assert false);
@@ -3291,14 +3331,16 @@ let interp code =
 								Array.map (fun f -> VDyn (VBytes (f.fname ^ "\000"),HBytes)) o.pproto
 							]
 						in
-						VArray (fields o,HDyn None)
+						VArray (fields o,HDyn)
 					| _ -> VNull)
 				| _ -> assert false)
 			| _ -> (fun args -> error ("Unresolved native " ^ name)))
 		| _ ->
-			(fun args -> error ("Unresolved native " ^ name))))
+			(fun args -> error ("Unresolved native " ^ name))
+		) in
+		FNativeFun (lib ^ "@" ^ name, f, t)
 	in
-	Array.iter (fun (lib,name,_,idx) -> functions.(idx) <- load_native code.strings.(lib) code.strings.(name)) code.natives;
+	Array.iter (fun (lib,name,t,idx) -> functions.(idx) <- load_native code.strings.(lib) code.strings.(name) t) code.natives;
 	Array.iter (fun fd -> functions.(fd.findex) <- FFun fd) code.functions;
 	let get_stack st =
 		String.concat "\n" (List.map (fun (f,pos) ->
@@ -3450,7 +3492,7 @@ let write_code ch code =
 			| HObj p ->
 				(match p.psuper with None -> () | Some p -> get_type (HObj p));
 				Array.iter (fun (_,n,t) -> get_type t) p.pfields
-			| HDyn (Some t) | HRef t ->
+			| HNull t | HRef t ->
 				get_type t
 			| HVirtual v ->
 				Array.iter (fun (_,_,t) -> get_type t) v.vfields
@@ -3461,7 +3503,7 @@ let write_code ch code =
 			t
 		));
 	in
-	List.iter (fun t -> get_type t) [HVoid; HI8; HI16; HI32; HF32; HF64; HBool; HType; HDyn None]; (* make sure all basic types get lower indexes *)
+	List.iter (fun t -> get_type t) [HVoid; HI8; HI16; HI32; HF32; HF64; HBool; HType; HDyn]; (* make sure all basic types get lower indexes *)
 	Array.iter (fun g -> get_type g) code.globals;
 	Array.iter (fun (_,_,t,_) -> get_type t) code.natives;
 	Array.iter (fun f ->
@@ -3501,10 +3543,7 @@ let write_code ch code =
 		| HF64 -> byte 5
 		| HBool -> byte 6
 		| HBytes -> byte 7
-		| HDyn None -> byte 8
-		| HDyn (Some t) ->
-			byte 0x88;
-			write_type t
+		| HDyn -> byte 8
 		| HFun (args,ret) ->
 			let n = List.length args in
 			if n > 0xFF then assert false;
@@ -3546,6 +3585,9 @@ let write_code ch code =
 				write_index (Array.length tl);
 				Array.iter write_type tl;
 			) e.efields
+		| HNull t ->
+			byte 0x18;
+			write_type t
 	) types.arr;
 
 	Array.iter write_type code.globals;

+ 1 - 1
std/hl/_std/String.hx

@@ -51,7 +51,7 @@ class String {
 
 	public function substr( pos : Int, ?len : Int ) : String @:privateAccess {
 		var sl = length;
-		var len = if( len == null ) sl else len;
+		var len : Int = if( len == null ) sl else len;
 		if( len == 0 ) return "";
 
 		if( pos != 0 && len < 0 )