Browse Source

[typer] do not apply abstract field casts on static extensions

closes #5924
Simon Krajewski 7 years ago
parent
commit
109048eb30

+ 1 - 0
extra/CHANGES.txt

@@ -41,6 +41,7 @@ XXXX-XX-XX:
 	all : reworked CLI usage/help output (#6862)
 	all : implemented `for` loop unrolling (#3784)
 	all : metadata can now use `.`, e.g. `@:a.b`. This is represented as a string (#3959)
+	all : [breaking] disallow static extensions through abstract field casts (#5924)
 	all : [breaking] disallow static extensions on implicit `this` (#6036)
 	all : allow true and false expressions as type parameters (#6958)
 	all : improved display support in many areas

+ 2 - 2
src/context/display/displayFields.ml

@@ -60,7 +60,7 @@ let collect_static_extensions ctx items e p =
 					| TFun((_,_,TType({t_path=["haxe";"macro"], "ExprOf"}, [t])) :: args, ret)
 					| TFun((_,_,t) :: args, ret) ->
 						begin try
-							unify_raise ctx (dup e.etype) t e.epos;
+							let e = TyperBase.unify_static_extension ctx {e with etype = dup e.etype} t p in
 							List.iter2 (fun m (name,t) -> match follow t with
 								| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] ->
 									List.iter (fun tc -> unify_raise ctx m (map tc) e.epos) constr
@@ -80,7 +80,7 @@ let collect_static_extensions ctx items e p =
 								let item = make_ci_class_field (CompletionClassField.make f CFSMember origin true) (f.cf_type,ct) in
 								PMap.add f.cf_name item acc
 							end
-						with Error (Unify _,_) ->
+						with Error (Unify _,_) | Unify_error _ ->
 							acc
 						end
 					| _ ->

+ 1 - 1
src/typing/fields.ml

@@ -254,7 +254,7 @@ let rec using_field ctx mode e i p =
 			begin match follow t with
 				| TFun((_,_,(TType({t_path = ["haxe";"macro"],"ExprOf"},[t0]) | t0)) :: args,r) ->
 					if is_dynamic && follow t0 != t_dynamic then raise Not_found;
-					let e = AbstractCast.cast_or_unify_raise ctx t0 e p in
+					let e = unify_static_extension ctx e t0 p in
 					(* early constraints check is possible because e.etype has no monomorphs *)
 					List.iter2 (fun m (name,t) -> match follow t with
 						| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] && not (has_mono m) ->

+ 16 - 1
src/typing/typerBase.ml

@@ -171,4 +171,19 @@ let has_constructible_constraint ctx tl el p =
 		| TInst({cl_kind = KTypeParameter tl},_) -> List.exists loop tl
 		| _ -> false
 	in
-	List.exists loop tl
+	List.exists loop tl
+
+let unify_static_extension ctx e t p =
+	let multitype_involed t1 t2 =
+		let check t = match follow t with
+			| TAbstract(a,_) when Meta.has Meta.MultiType a.a_meta -> true
+			| _ -> false
+		in
+		check t1 || check t2
+	in
+	if multitype_involed e.etype t then
+		AbstractCast.cast_or_unify_raise ctx t e p
+	else begin
+		Type.unify e.etype t;
+		e
+	end

+ 47 - 0
tests/display/src/cases/StaticExtension.hx

@@ -0,0 +1,47 @@
+package cases;
+
+class StaticExtension extends DisplayTestCase {
+	/**
+
+	using cases.StaticExtension.MyStaticExtension;
+	class Something {
+		static function test() {
+			var map = ["a" => 1];
+			map.{-1-}
+		}
+	}
+
+	class MyStaticExtension {
+		static public function doSomething(sm:haxe.ds.StringMap<Int>):Void { }
+		static public function doSomethingElse(sm:Map<String, Int>):Void { }
+	}
+
+	**/
+	function test1() {
+		var fields = fields(pos(1));
+		eq(true, hasField(fields, "doSomething", "Void -> Void"));
+		eq(true, hasField(fields, "doSomethingElse", "Void -> Void"));
+	}
+
+	/**
+
+	using cases.StaticExtension.MyStaticExtension;
+	class Something {
+		static function test() {
+			var map = new haxe.ds.StringMap();
+			map.{-1-}
+		}
+	}
+
+	class MyStaticExtension {
+		static public function doSomething(sm:haxe.ds.StringMap<Int>):Void { }
+		static public function doSomethingElse(sm:Map<String, Int>):Void { }
+	}
+
+	**/
+	function test2() {
+		var fields = fields(pos(1));
+		eq(true, hasField(fields, "doSomething", "Void -> Void"));
+		eq(true, hasField(fields, "doSomethingElse", "Void -> Void"));
+	}
+}

+ 21 - 0
tests/misc/projects/Issue5924/Main.hx

@@ -0,0 +1,21 @@
+using Main;
+
+class Main {
+    static function main() {
+        0.bar();
+    }
+
+    static function bar(f:Foo)
+    	f.bar();
+}
+
+abstract Foo(String) {
+
+    inline function new(v) this = v;
+
+    @:from static function ofInt(i:Int)
+      return new Foo('$i');
+
+    public function bar() {};
+    public function baz() {};
+}

+ 2 - 0
tests/misc/projects/Issue5924/compile-fail.hxml

@@ -0,0 +1,2 @@
+-main Main
+--interp

+ 1 - 0
tests/misc/projects/Issue5924/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:5: characters 9-14 : Int has no field bar

+ 0 - 15
tests/unit/src/unit/issues/Issue2152.hx

@@ -1,15 +0,0 @@
-package unit.issues;
-
-using unit.issues.misc.Issue2152Class;
-
-private abstract MyInt(Int) {
-	public inline function new (x:Int) this = x;
-	@:to inline function toString ():String return "asString: " + this;
-}
-
-class Issue2152 extends Test {
-	function test() {
-		var z = new MyInt(1);
-		eq("asString: 1", z.passString());
-	}
-}

+ 24 - 0
tests/unit/src/unit/issues/Issue7142.hx

@@ -0,0 +1,24 @@
+package unit.issues;
+
+using unit.issues.Issue7142;
+
+class Issue7142 extends unit.Test {
+	function test() {
+		var map = ["test" => 1];
+		var smap = new haxe.ds.StringMap();
+		smap.set("test", 1);
+
+		eq(1, map.doSomething());
+		eq(1, smap.doSomething());
+		eq(1, map.doSomethingElse());
+		eq(1, smap.doSomethingElse());
+	}
+
+	static function doSomething(sm:haxe.ds.StringMap<Int>) {
+		return sm.get("test");
+	}
+
+	static function doSomethingElse(sm:Map<String, Int>) {
+		return sm.get("test");
+	}
+}