Procházet zdrojové kódy

allow inlining of generic abstract calls

Simon Krajewski před 12 roky
rodič
revize
9ed7137a47
6 změnil soubory, kde provedl 65 přidání a 30 odebrání
  1. 46 26
      codegen.ml
  2. 6 2
      tests/unit/MyAbstract.hx
  3. 0 2
      tests/unit/TestBasetypes.hx
  4. 9 0
      tests/unit/TestType.hx
  5. 3 0
      typecore.ml
  6. 1 0
      typer.ml

+ 46 - 26
codegen.ml

@@ -1319,7 +1319,7 @@ let check_local_vars_init e =
 (* ABSTRACT CASTS *)
 
 let handle_abstract_casts ctx e =
-	let make_cast_call c cf a pl args t p =
+	let make_static_call c cf a pl args t p =
 		let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
 		let ethis = mk (TTypeExpr (TClassDecl c)) ta p in
 		let def () =
@@ -1350,14 +1350,14 @@ let handle_abstract_casts ctx e =
 					with Not_found ->
 						c2,snd (find_from a2 pl2 t1 t2),a2,pl2
 					in
-					match cfo with None -> eright | Some cf -> make_cast_call c cf a pl [eright] tleft p
+					match cfo with None -> eright | Some cf -> make_static_call c cf a pl [eright] tleft p
 				end
 			| TDynamic _,_ | _,TDynamic _ ->
 				eright
 			| TAbstract({a_impl = Some c} as a,pl) as t1,t2 ->
-				begin match snd (find_to a pl t1 t2) with None -> eright | Some cf -> make_cast_call c cf a pl [eright] tleft p end
+				begin match snd (find_to a pl t1 t2) with None -> eright | Some cf -> make_static_call c cf a pl [eright] tleft p end
 			| t1,(TAbstract({a_impl = Some c} as a,pl) as t2) ->
-				begin match snd (find_from a pl t1 t2) with None -> eright | Some cf -> make_cast_call c cf a pl [eright] tleft p end
+				begin match snd (find_from a pl t1 t2) with None -> eright | Some cf -> make_static_call c cf a pl [eright] tleft p end
 			| _ ->
 				eright)
 		with Not_found ->
@@ -1366,6 +1366,8 @@ let handle_abstract_casts ctx e =
 		| TBinop(OpAssign,e1,e2) ->
 			let e2 = check_cast e1.etype e2 e.epos in
 			{ e with eexpr = TBinop(OpAssign,loop e1,e2) }
+		| TLocal v when (match follow v.v_type with TAbstract(a,_) -> Meta.has Meta.Generic a.a_meta | _ -> false) ->
+			{e with etype = v.v_type}
 		| TVars vl ->
 			let vl = List.map (fun (v,eo) -> match eo with
 				| None -> (v,eo)
@@ -1389,32 +1391,50 @@ let handle_abstract_casts ctx e =
 			| None -> assert false
 			| Some cf ->
 				let m = follow m in
-				let e = make_cast_call c cf a pl ((mk (TConst TNull) at e.epos) :: el) m e.epos in
+				let e = make_static_call c cf a pl ((mk (TConst TNull) at e.epos) :: el) m e.epos in
 				{e with etype = m}
 			end
-		| TField({etype = TAbstract({a_impl = Some _} as a,pl)} as e1,fa) when Meta.has Meta.Generic a.a_meta ->
-			let at = apply_params a.a_types pl a.a_this in
-			let m = mk_mono() in
+		| TCall(e1, el) ->
+			let e1 = loop e1 in
 			begin try
-				let _ = find_to a pl at m in
-				(* we could inline this if we had access to Typer.make_call *)
-				{e with eexpr = TField({e1 with etype = m},quick_field m (field_name fa))}
+				begin match e1.eexpr with
+					| TField(e2,fa) ->
+						begin match follow e2.etype with
+							| TAbstract(a,pl) when Meta.has Meta.Generic a.a_meta ->
+								let at = apply_params a.a_types pl a.a_this in
+								let m = mk_mono() in
+								let _ = find_to a pl at m in
+								let fname = field_name fa in
+								begin try
+									let ef = mk (TField({e2 with etype = m},quick_field m fname)) e2.etype e2.epos in
+									make_call ctx ef el e.etype e.epos
+								with Not_found ->
+									(* quick_field raises Not_found if m is an abstract, we have to replicate the 'using' call here *)
+									match follow m with
+									| TAbstract({a_impl = Some c} as a,pl) ->
+										let cf = PMap.find fname c.cl_statics in
+										make_static_call c cf a pl (e2 :: el) e.etype e.epos
+									| _ -> raise Not_found
+								end
+							| _ -> raise Not_found
+						end
+					| _ ->
+						raise Not_found
+				end
 			with Not_found ->
-				e
-			end
-		| TCall(e1, el) ->
-			begin match follow e1.etype with
-				| TFun(args,_) ->
-					let rec loop2 el tl = match el,tl with
-						| [],_ -> []
-						| e :: el, [] -> (loop e) :: loop2 el []
-						| e :: el, (_,_,t) :: tl ->
-							(check_cast t e e.epos) :: loop2 el tl
-					in
-					let el = loop2 el args in
-					{ e with eexpr = TCall(loop e1,el)}
-				| _ ->
-					Type.map_expr loop e
+				begin match follow e1.etype with
+					| TFun(args,_) ->
+						let rec loop2 el tl = match el,tl with
+							| [],_ -> []
+							| e :: el, [] -> (loop e) :: loop2 el []
+							| e :: el, (_,_,t) :: tl ->
+								(check_cast t e e.epos) :: loop2 el tl
+						in
+						let el = loop2 el args in
+						{ e with eexpr = TCall(loop e1,el)}
+					| _ ->
+						Type.map_expr loop e
+				end
 			end
 		| TArrayDecl el ->
 			begin match e.etype with

+ 6 - 2
tests/unit/MyAbstract.hx

@@ -55,7 +55,6 @@ abstract Kilometer(Float) from Float to Float {
 		return new Kilometer(m.get() / 1000.)
 }
 
-#if !cpp
 abstract MyHash(Hash<V>)<V> {
 	private inline function new() {
 		this = new Hash<V>();
@@ -89,7 +88,6 @@ abstract MyHash(Hash<V>)<V> {
 		return hash;
 	}
 }
-#end
 
 class AbstractBase<T> {
 	public var value:T;
@@ -152,4 +150,10 @@ abstract MyInt(Int) from Int to Int {
 			s.add(rhs);
 		return s.toString();
 	}
+}
+
+class ClassWithHashCode {
+	var i:Int;
+	public function new(i) { this.i = i; }
+	public function hashCode() return i
 }

+ 0 - 2
tests/unit/TestBasetypes.hx

@@ -355,7 +355,6 @@ class TestBasetypes extends Test {
 		feq(km, 0.1222);
 	}
 
-	#if !cpp
 	function testAbstractTypeParameters() {
 		var hash1:unit.MyAbstract.MyHash<String> = ["k1", "v1", "k2", "v2"];
 		eq("v1", hash1.get("k1"));
@@ -364,7 +363,6 @@ class TestBasetypes extends Test {
 		eq(2, hash1.get("_s1"));
 		eq(4, hash1.get("_s3"));
 	}
-	#end
 
 	function testAbstractToString() {
 		var km:unit.MyAbstract.Kilometer = 12.5;

+ 9 - 0
tests/unit/TestType.hx

@@ -695,6 +695,15 @@ class TestType extends Test {
 		_mapMe(map); // infer from function call
 		t(Std.is(map, haxe.ds.IntMap));
 
+		var map = new Map();
+		var a = new unit.MyAbstract.ClassWithHashCode(1);
+		var b = new unit.MyAbstract.ClassWithHashCode(2);
+		map.set(a, "foo");
+		map.set(b, "bar");
+		eq(map.get(a), "foo");
+		eq(map.get(b), "bar");
+		t(Std.is(map, haxe.ds.HashMap));
+		
 		//var map = new unit.MyAbstract.MyMap();
 		//map.set(new haxe.Template("foo"), 99);
 		//t(Std.is(map, unit.MyAbstract.PseudoObjectHash));

+ 3 - 0
typecore.ml

@@ -129,6 +129,7 @@ exception Error of error_msg * pos
 
 exception DisplayPosition of Ast.pos list
 
+let make_call_ref : (typer -> texpr -> texpr list -> t -> pos -> texpr) ref = ref (fun _ _ _ _ _ -> assert false)
 let type_expr_ref : (typer -> Ast.expr -> with_type -> texpr) ref = ref (fun _ _ _ -> assert false)
 let unify_min_ref : (typer -> texpr list -> t) ref = ref (fun _ _ -> assert false)
 let match_expr_ref : (typer -> Ast.expr -> (Ast.expr list * Ast.expr option * Ast.expr option) list -> Ast.expr option option -> with_type -> Ast.pos -> texpr) ref = ref (fun _ _ _ _ _ _ -> assert false)
@@ -248,6 +249,8 @@ let display_error ctx msg p = ctx.on_error ctx msg p
 
 let error msg p = raise (Error (Custom msg,p))
 
+let make_call ctx e el t p = (!make_call_ref) ctx e el t p
+
 let type_expr ctx e with_type = (!type_expr_ref) ctx e with_type
 
 let unify_min ctx el = (!unify_min_ref) ctx el

+ 1 - 0
typer.ml

@@ -3706,3 +3706,4 @@ let rec create com =
 
 ;;
 unify_min_ref := unify_min;
+make_call_ref := make_call;