Bladeren bron

[cs] progess on Issue #808 :
- Null<> is now changed to haxe.lang.Null<> at an earlier stage (real_type)
- In order to avoid all casts from haxe.lang.Null<T> to T be considered an unsafe cast, a new variable, gsupported_conversions was created.
- haxe.lang.Null<ReferenceType> is now followed to ReferenceType correctly.

Caue Waneck 13 jaren geleden
bovenliggende
commit
07f2862ecd
3 gewijzigde bestanden met toevoegingen van 71 en 20 verwijderingen
  1. 46 10
      gencommon.ml
  2. 20 8
      gencs.ml
  3. 5 2
      std/cs/_std/haxe/lang/Null.hx

+ 46 - 10
gencommon.ml

@@ -479,6 +479,12 @@ type generator_ctx =
   (* does this 'special type' needs cast to this other type? *)
   (* does this 'special type' needs cast to this other type? *)
   (* this is here so we can implement custom behavior for "opaque" typedefs *)
   (* this is here so we can implement custom behavior for "opaque" typedefs *)
   mutable gspecial_needs_cast : t->t->bool;
   mutable gspecial_needs_cast : t->t->bool;
+  (* sometimes we may want to support unrelated conversions on cast detection *)
+  (* for example, haxe.lang.Null<T> -> T on C# *)
+  (* every time an unrelated conversion is found, each to/from path is searched on this hashtbl *)
+  (* if found, the function will be executed with from_type, to_type. If returns true, it means that *)
+  (* it is a supported conversion, and the unsafe cast routine changes to a simple cast *)
+  gsupported_conversions : (path, t->t->bool) Hashtbl.t;
   
   
   (* API for filters *)
   (* API for filters *)
   (* add type can be called at any time, and will add a new module_def that may or may not be filtered *)
   (* add type can be called at any time, and will add a new module_def that may or may not be filtered *)
@@ -650,6 +656,7 @@ let new_ctx con =
     gon_unsafe_cast = (fun t t2 pos -> (gen.gcon.warning ("Type " ^ (debug_type t2) ^ " is being cast to the unrelated type " ^ (s_type (print_context()) t)) pos));
     gon_unsafe_cast = (fun t t2 pos -> (gen.gcon.warning ("Type " ^ (debug_type t2) ^ " is being cast to the unrelated type " ^ (s_type (print_context()) t)) pos));
     gneeds_box = (fun t -> false);
     gneeds_box = (fun t -> false);
     gspecial_needs_cast = (fun to_t from_t -> true);
     gspecial_needs_cast = (fun to_t from_t -> true);
+    gsupported_conversions = Hashtbl.create 0;
     
     
     gadd_type = (fun md should_filter ->
     gadd_type = (fun md should_filter ->
       if should_filter then begin
       if should_filter then begin
@@ -4295,9 +4302,34 @@ struct
         false
         false
       | _ -> true
       | _ -> true
   
   
-  let do_unsafe_cast gen to_t e  =
-    gen.gon_unsafe_cast to_t e.etype e.epos;
-    mk_cast to_t (mk_cast t_dynamic e)
+  let do_unsafe_cast gen from_t to_t e  =
+    let t_path t =
+      match t with
+        | TInst(cl, _) -> cl.cl_path
+        | TEnum(e, _) -> e.e_path
+        | TType(t, _) -> t.t_path
+        | TDynamic _ -> ([], "Dynamic")
+        | _ -> raise Not_found
+    in
+    let do_default () =
+      gen.gon_unsafe_cast to_t e.etype e.epos;
+      mk_cast to_t (mk_cast t_dynamic e)
+    in
+    (* TODO: there really should be a better way to write that *)
+    try
+      if (Hashtbl.find gen.gsupported_conversions (t_path from_t)) from_t to_t then
+        mk_cast to_t e
+      else
+        do_default()
+    with
+      | Not_found ->
+        try
+          if (Hashtbl.find gen.gsupported_conversions (t_path to_t)) from_t to_t then
+            mk_cast to_t e
+          else
+            do_default()
+        with
+          | Not_found -> do_default()
   
   
   (* ****************************** *)
   (* ****************************** *)
   (* cast handler *)
   (* cast handler *)
@@ -4307,7 +4339,7 @@ struct
     at the backend level, since most probably Anons and TInst will have a different representation there
     at the backend level, since most probably Anons and TInst will have a different representation there
   *)
   *)
   let rec handle_cast gen e real_to_t real_from_t =
   let rec handle_cast gen e real_to_t real_from_t =
-    let do_unsafe_cast () = do_unsafe_cast gen real_to_t { e with etype = real_from_t } in
+    let do_unsafe_cast () = do_unsafe_cast gen real_from_t real_to_t { e with etype = real_from_t } in
     let to_t, from_t = real_to_t, real_from_t in
     let to_t, from_t = real_to_t, real_from_t in
     
     
     let e = { e with etype = real_from_t } in
     let e = { e with etype = real_from_t } in
@@ -8071,6 +8103,7 @@ end;;
   a call to the new Null<T> creation;
   a call to the new Null<T> creation;
   Also casts from Null<T> to T or direct uses of Null<T> (call, field access, array access, closure)
   Also casts from Null<T> to T or direct uses of Null<T> (call, field access, array access, closure)
   will result in the actual value being accessed
   will result in the actual value being accessed
+  For compatibility with the C# target, HardNullable will accept both Null<T> and haxe.lang.Null<T> types
   
   
   dependencies:
   dependencies:
     
     
@@ -8084,23 +8117,25 @@ struct
   
   
   let priority = solve_deps name []
   let priority = solve_deps name []
   
   
-  let rec is_null_t t = match t with
-    | TType( { t_path = ([], "Null") }, [of_t]) ->
+  let rec is_null_t gen t = match gen.greal_type t with
+    | TType( { t_path = ([], "Null") }, [of_t])
+    | TInst( { cl_path = (["haxe";"lang"], "Null") }, [of_t]) ->
       let rec take_off_null t =
       let rec take_off_null t =
-        match is_null_t t with | None -> t | Some s -> take_off_null s
+        match is_null_t gen t with | None -> t | Some s -> take_off_null s
       in
       in
       
       
       Some (take_off_null of_t)
       Some (take_off_null of_t)
-    | TMono r -> (match !r with | Some t -> is_null_t t | None -> None)
-    | TLazy f -> is_null_t (!f())
+    | TMono r -> (match !r with | Some t -> is_null_t gen t | None -> None)
+    | TLazy f -> is_null_t gen (!f())
     | TType (t, tl) ->
     | TType (t, tl) ->
-      is_null_t (apply_params t.t_types tl t.t_type)
+      is_null_t gen (apply_params t.t_types tl t.t_type)
     | _ -> None
     | _ -> None
   
   
   let follow_addon gen t =
   let follow_addon gen t =
     let rec strip_off_nullable t =
     let rec strip_off_nullable t =
       let t = gen.gfollow#run_f t in
       let t = gen.gfollow#run_f t in
       match t with
       match t with
+        (* haxe.lang.Null<haxe.lang.Null<>> wouldn't be a valid construct, so only follow Null<> *)
         | TType ( { t_path = ([], "Null") }, [of_t] ) -> strip_off_nullable of_t
         | TType ( { t_path = ([], "Null") }, [of_t] ) -> strip_off_nullable of_t
         | _ -> t
         | _ -> t
     in
     in
@@ -8127,6 +8162,7 @@ struct
           wrap_val e true
           wrap_val e true
     in
     in
     
     
+    let is_null_t = is_null_t gen in
     let rec run e =
     let rec run e =
       let null_et = is_null_t e.etype in
       let null_et = is_null_t e.etype in
       match e.eexpr with 
       match e.eexpr with 

+ 20 - 8
gencs.ml

@@ -33,6 +33,8 @@ let is_cs_basic_type t =
     | TInst( { cl_path = ([], "Float") }, [] )
     | TInst( { cl_path = ([], "Float") }, [] )
     | TEnum( { e_path = ([], "Bool") }, [] ) -> 
     | TEnum( { e_path = ([], "Bool") }, [] ) -> 
       true
       true
+    | TEnum(e, _) when not (has_meta ":$class" e.e_meta) -> true
+    | TInst(cl, _) when has_meta ":struct" cl.cl_meta -> true
     | _ -> false
     | _ -> false
     
     
 let is_int_float t =
 let is_int_float t =
@@ -450,7 +452,7 @@ let configure gen =
   
   
   let ti64 = match ( get_type gen ([], "Int64") ) with | TTypeDecl t -> TType(t,[]) | _ -> assert false in
   let ti64 = match ( get_type gen ([], "Int64") ) with | TTypeDecl t -> TType(t,[]) | _ -> assert false in
   
   
-  let real_type t =
+  let rec real_type t =
     let t = gen.gfollow#run_f t in
     let t = gen.gfollow#run_f t in
     match t with
     match t with
       | TInst( { cl_path = (["haxe"], "Int32") }, [] ) -> gen.gcon.basic.tint
       | TInst( { cl_path = (["haxe"], "Int32") }, [] ) -> gen.gcon.basic.tint
@@ -467,7 +469,17 @@ let configure gen =
           TInst(Hashtbl.find ifaces e.e_path, [])
           TInst(Hashtbl.find ifaces e.e_path, [])
       | TInst(cl, params) -> TInst(cl, change_param_type (TClassDecl cl) params)
       | TInst(cl, params) -> TInst(cl, change_param_type (TClassDecl cl) params)
       | TEnum(e, params) -> TEnum(e, change_param_type (TEnumDecl e) params)
       | TEnum(e, params) -> TEnum(e, change_param_type (TEnumDecl e) params)
-      (* | TType({ t_path = ([], "Null") }, [t]) -> TInst(null_t, [t]) *)
+      | TType({ t_path = ([], "Null") }, [t]) -> 
+        (* 
+          Null<> handling is a little tricky.
+          It will only change to haxe.lang.Null<> when the actual type is non-nullable or a type parameter
+          It works on cases such as Hash<T> returning Null<T> since cast_detect will invoke real_type at the original type,
+          Null<T>, which will then return the type haxe.lang.Null<>
+        *)
+        (match real_type t with
+          | TInst( { cl_kind = KTypeParameter }, _ ) -> TInst(null_t, [t])
+          | _ when is_cs_basic_type t -> TInst(null_t, [t])
+          | _ -> real_type t)
       | TType _ -> t
       | TType _ -> t
       | TAnon (anon) when (match !(anon.a_status) with | Statics _ | EnumStatics _ -> true | _ -> false) -> t
       | TAnon (anon) when (match !(anon.a_status) with | Statics _ | EnumStatics _ -> true | _ -> false) -> t
       | TAnon _ -> dynamic_anon
       | TAnon _ -> dynamic_anon
@@ -1173,6 +1185,8 @@ let configure gen =
   Hashtbl.add gen.gspecial_vars "__as__" true;
   Hashtbl.add gen.gspecial_vars "__as__" true;
   Hashtbl.add gen.gspecial_vars "__cs__" true;
   Hashtbl.add gen.gspecial_vars "__cs__" true;
   
   
+  Hashtbl.add gen.gsupported_conversions (["haxe"; "lang"], "Null") (fun t1 t2 -> true);
+  
   gen.greal_type <- real_type;
   gen.greal_type <- real_type;
   gen.greal_type_param <- change_param_type;
   gen.greal_type_param <- change_param_type;
   
   
@@ -1190,18 +1204,16 @@ let configure gen =
   
   
   StubClosureImpl.configure gen (StubClosureImpl.default_implementation gen float_cl 10 (fun e _ _ -> e));*)
   StubClosureImpl.configure gen (StubClosureImpl.default_implementation gen float_cl 10 (fun e _ _ -> e));*)
   
   
-  let tnull = match (Hashtbl.find gen.gtypes ([],"Null")) with | TTypeDecl t -> t | _ -> assert false in
-  
   HardNullableSynf.configure gen (HardNullableSynf.traverse gen 
   HardNullableSynf.configure gen (HardNullableSynf.traverse gen 
     (fun e ->
     (fun e ->
-      match gen.gfollow#run_f e.etype with
-        | TType({ t_path = ([], "Null") }, [t]) ->
+      match real_type e.etype with
+        | TInst({ cl_path = (["haxe";"lang"], "Null") }, [t]) ->
           { eexpr = TField(e, "value"); etype = t; epos = e.epos }
           { eexpr = TField(e, "value"); etype = t; epos = e.epos }
         | _ -> 
         | _ -> 
-          gen.gcon.error "This expression is not a Nullable expression" e.epos; assert false
+          trace (debug_type e.etype); gen.gcon.error "This expression is not a Nullable expression" e.epos; assert false
     ) 
     ) 
     (fun v has_value ->
     (fun v has_value ->
-      { eexpr = TNew(null_t, [v.etype], [mk_cast v.etype v; { eexpr = TConst(TBool has_value); etype = gen.gcon.basic.tbool; epos = v.epos } ]); etype = TType(tnull, [v.etype]); epos = v.epos }
+      { eexpr = TNew(null_t, [v.etype], [mk_cast v.etype v; { eexpr = TConst(TBool has_value); etype = gen.gcon.basic.tbool; epos = v.epos } ]); etype = TInst(null_t, [v.etype]); epos = v.epos }
     ) 
     ) 
     (fun e ->
     (fun e ->
       {
       {

+ 5 - 2
std/cs/_std/haxe/lang/Null.hx

@@ -44,10 +44,13 @@ package haxe.lang;
 		}
 		}
 	}
 	}
 	
 	
+	@:functionBody('
+		if (this.hasValue)
+			return value;
+		return null;
+	')
 	public function toDynamic():Dynamic
 	public function toDynamic():Dynamic
 	{
 	{
-		if (hasValue) 
-			return value;
 		return null;
 		return null;
 	}
 	}
 }
 }