Procházet zdrojové kódy

[abstracts] change handling of transitive casts

Dmitry Maganov před 5 roky
rodič
revize
75cbcc6a9e

+ 6 - 0
src-json/meta.json

@@ -1196,6 +1196,12 @@
 		"platforms": ["java"],
 		"targets": ["TClassField"]
 	},
+	{
+		"name": "Transitive",
+		"metadata": ":transitive",
+		"doc": "Allows transitive casts with an abstract.",
+		"targets": ["TAbstract"]
+	},
 	{
 		"name": "ValueUsed",
 		"metadata": ":valueUsed",

+ 33 - 6
src/core/tUnification.ml

@@ -32,6 +32,7 @@ type eq_kind =
 
 type unification_context = {
 	allow_transitive_cast : bool;
+	allow_abstract_cast   : bool; (* allows a non-transitive abstract cast (from,to,@:from,@:to) *)
 	equality_kind         : eq_kind;
 }
 
@@ -52,6 +53,7 @@ let unify_min_ref : (unification_context -> t -> t list -> unify_min_result) ref
 
 let default_unification_context = {
 	allow_transitive_cast = true;
+	allow_abstract_cast   = true;
 	equality_kind         = EqStrict;
 }
 
@@ -630,6 +632,8 @@ let rec unify (uctx : unification_context) a b =
 	| TAbstract ({ a_path = ["haxe"],"NotVoid" },[]), _
 	| _, TAbstract ({ a_path = ["haxe"],"NotVoid" },[]) ->
 		()
+	| TAbstract _, TAbstract ({ a_path = ["haxe"],("FlatEnum" | "Function" | "Constructible") },_) ->
+		error [cannot_unify a b]
 	| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
 		unify_abstracts uctx a b a1 tl1 a2 tl2
 	| TInst (c1,tl1) , TInst (c2,tl2) ->
@@ -655,6 +659,7 @@ let rec unify (uctx : unification_context) a b =
 		in
 		if not (loop c1 tl1) then error [cannot_unify a b]
 	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
+		let uctx = get_nested_context uctx in
 		let i = ref 0 in
 		(try
 			(match follow r2 with
@@ -873,6 +878,20 @@ and unify_anons uctx a b a1 a2 =
 	with
 		Unify_error l -> error (cannot_unify a b :: l))
 
+and get_abstract_context uctx a b ab =
+	if (Meta.has Meta.CoreType ab.a_meta) || (Meta.has Meta.Transitive ab.a_meta) then
+		uctx
+	else if uctx.allow_abstract_cast then
+		{uctx with allow_abstract_cast = false}
+	else
+		error [cannot_unify a b]
+
+and get_nested_context uctx =
+	{uctx with allow_abstract_cast = true}
+
+and unifies_with_abstract uctx f =
+	(uctx.allow_transitive_cast && f {uctx with allow_transitive_cast = false}) || f uctx
+
 and get_abstract_unify_func uctx equality_kind =
 	if uctx.allow_transitive_cast then unify uctx else type_eq {uctx with equality_kind = equality_kind}
 
@@ -889,26 +908,31 @@ and unify_to uctx a b ab tl =
 	if not (unifies_to uctx a b ab tl) then error [cannot_unify a b]
 
 and unifies_abstracts uctx a b a1 tl1 a2 tl2 =
-	let uctx_no_transitive_casts = {uctx with allow_transitive_cast = false} in
-	(unifies_to uctx_no_transitive_casts a b a1 tl1) || (unifies_from uctx_no_transitive_casts a b a2 tl2)
-	|| (((Meta.has Meta.CoreType a1.a_meta) || (Meta.has Meta.CoreType a2.a_meta))
-		&& ((unifies_to uctx a b a1 tl1) || (unifies_from uctx a b a2 tl2)))
+	unifies_with_abstract uctx (fun uctx ->
+		unifies_to uctx a b a1 tl1 || unifies_from uctx a b a2 tl2
+	)
 
 and unifies_from uctx a b ab tl =
-	List.exists (unifies_from_direct uctx a b ab tl) ab.a_from
+	unifies_with_abstract uctx (fun uctx ->
+		List.exists (unifies_from_direct uctx a b ab tl) ab.a_from
+	)
 
 and unifies_to uctx a b ab tl =
-	List.exists (unifies_to_direct uctx a b ab tl) ab.a_to
+	unifies_with_abstract uctx (fun uctx ->
+		List.exists (unifies_to_direct uctx a b ab tl) ab.a_to
+	)
 
 and unifies_from_direct uctx a b ab tl t =
 	rec_stack_abstract_unifies a b (fun() ->
 		let t = apply_params ab.a_params tl t in
+		let uctx = get_abstract_context uctx a b ab in
 		let unify_func = get_abstract_unify_func uctx EqRightDynamic in
 		unify_func a t)
 
 and unifies_to_direct uctx a b ab tl t =
 	rec_stack_abstract_unifies a b (fun() ->
 		let t = apply_params ab.a_params tl t in
+		let uctx = get_abstract_context uctx a b ab in
 		let unify_func = get_abstract_unify_func uctx EqStrict in
 		unify_func t b)
 
@@ -919,6 +943,7 @@ and unifies_from_field uctx a b ab tl (t,cf) =
 			let map = apply_params ab.a_params tl in
 			let monos = Monomorph.spawn_constrained_monos map cf.cf_params in
 			let map t = map (apply_params cf.cf_params monos t) in
+			let uctx = get_abstract_context uctx a b ab in
 			let unify_func = get_abstract_unify_func uctx EqStrict in
 			unify_func a (map t);
 			unify_func (map r) b;
@@ -931,6 +956,7 @@ and unifies_to_field uctx a b ab tl (t,cf) =
 			let map = apply_params ab.a_params tl in
 			let monos = Monomorph.spawn_constrained_monos map cf.cf_params in
 			let map t = map (apply_params cf.cf_params monos t) in
+			let uctx = get_abstract_context uctx a b ab in
 			let unify_func = get_abstract_unify_func uctx EqStrict in
 			let athis = map ab.a_this in
 			(* we cannot allow implicit casts when the this type is not completely known yet *)
@@ -989,6 +1015,7 @@ and with_variance uctx f t1 t2 =
 		raise (Unify_error l)
 
 and unify_with_access uctx f1 t1 f2 =
+	let uctx = get_nested_context uctx in
 	match f2.cf_kind with
 	(* write only *)
 	| Var { v_read = AccNo } | Var { v_read = AccNever } -> unify uctx f2.cf_type t1

+ 1 - 0
std/UInt.hx

@@ -130,6 +130,7 @@ abstract UInt to Int from Int {
 
 	@see https://haxe.org/manual/types-basic-types.html
 **/
+@:transitive
 abstract UInt(Int) from Int to Int {
 	@:op(A + B) private static inline function add(a:UInt, b:UInt):UInt {
 		return a.toInt() + b.toInt();

+ 1 - 0
std/cpp/_std/haxe/Int64.hx

@@ -136,6 +136,7 @@ private extern class ___Int64 {
 private typedef __Int64 = ___Int64;
 
 @:coreApi
+@:transitive
 abstract Int64(__Int64) from __Int64 to __Int64 {
 	public #if !cppia inline #end function copy():Int64
 		return this;

+ 1 - 0
std/cs/_std/haxe/Int64.hx

@@ -29,6 +29,7 @@ import haxe.Int64Helper;
 private typedef __Int64 = cs.StdTypes.Int64;
 
 @:coreApi
+@:transitive
 abstract Int64(__Int64) from __Int64 to __Int64 {
 	public static inline function make(high:Int32, low:Int32):Int64
 		return new Int64((cast(high, __Int64) << 32) | (cast(low, __Int64) & (untyped __cs__('0xffffffffL') : Int64)));

+ 1 - 0
std/haxe/Int32.hx

@@ -26,6 +26,7 @@ package haxe;
 	Int32 provides a 32-bit integer with consistent overflow behavior across
 	all platforms.
 **/
+@:transitive
 abstract Int32(Int) from Int to Int {
 	@:op(-A) private inline function negate():Int32
 		return clamp(~this + 1);

+ 1 - 0
std/haxe/Int64.hx

@@ -31,6 +31,7 @@ using haxe.Int64;
 #if flash
 @:notNull
 #end
+@:transitive
 abstract Int64(__Int64) from __Int64 to __Int64 {
 	private inline function new(x:__Int64)
 		this = x;

+ 1 - 0
std/haxe/extern/AsVar.hx

@@ -27,5 +27,6 @@ package haxe.extern;
 	argument expressions are bound to a local variable.
 **/
 @:forward
+@:transitive
 @:semantics(variable)
 abstract AsVar<T>(T) from T to T {}

+ 1 - 0
std/haxe/extern/EitherType.hx

@@ -33,4 +33,5 @@ package haxe.extern;
 
 	@see <https://haxe.org/manual/lf-externs.html>
 **/
+@:transitive
 abstract EitherType<T1, T2>(Dynamic) from T1 to T1 from T2 to T2 {}

+ 1 - 0
std/hl/_std/UInt.hx

@@ -20,6 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 @:coreApi
+@:transitive
 abstract UInt(Int) from Int to Int {
 	@:op(A + B) private static inline function add(a:UInt, b:UInt):UInt {
 		return a.toInt() + b.toInt();

+ 1 - 0
std/java/_std/haxe/Int64.hx

@@ -29,6 +29,7 @@ import haxe.Int64Helper;
 private typedef __Int64 = java.StdTypes.Int64;
 
 @:coreApi
+@:transitive
 abstract Int64(__Int64) from __Int64 to __Int64 {
 	#if jvm
 	extern public static function make(high:Int32, low:Int32):Int64;

+ 3 - 2
std/java/lang/Boolean.hx

@@ -23,8 +23,9 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
-@:forward
-@:forwardStatics abstract Boolean(BooleanClass) from BooleanClass to BooleanClass {
+@:transitive
+@:forwardStatics
+@:forward abstract Boolean(BooleanClass) from BooleanClass to BooleanClass {
 	@:to extern inline public function toBool():Bool
 		return this.booleanValue();
 

+ 1 - 0
std/java/lang/Byte.hx

@@ -23,6 +23,7 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
+@:transitive
 @:forwardStatics
 @:forward abstract Byte(ByteClass) from ByteClass to ByteClass {
 	@:to extern inline public function toByte():java.types.Int8

+ 1 - 0
std/java/lang/Character.hx

@@ -23,6 +23,7 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
+@:transitive
 @:forwardStatics
 @:forward abstract Character(CharacterClass) from CharacterClass to CharacterClass {
 	@:to extern inline public function toCharacter():java.types.Char16

+ 1 - 0
std/java/lang/Double.hx

@@ -23,6 +23,7 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
+@:transitive
 @:forwardStatics
 @:forward abstract Double(DoubleClass) from DoubleClass to DoubleClass {
 	@:to extern inline public function toFloat():Float

+ 1 - 0
std/java/lang/Float.hx

@@ -23,6 +23,7 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
+@:transitive
 @:forwardStatics
 @:forward abstract Float(FloatClass) from FloatClass to FloatClass {
 	@:to extern inline public function toFloat():std.StdTypes.Float

+ 1 - 0
std/java/lang/Integer.hx

@@ -23,6 +23,7 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
+@:transitive
 @:forwardStatics
 @:forward abstract Integer(IntegerClass) from IntegerClass to IntegerClass {
 	@:to extern inline public function toInt():Int

+ 1 - 0
std/java/lang/Long.hx

@@ -23,6 +23,7 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
+@:transitive
 @:forwardStatics
 @:forward abstract Long(LongClass) from LongClass to LongClass {
 	@:to extern inline public function toLong():haxe.Int64

+ 1 - 0
std/java/lang/Short.hx

@@ -23,6 +23,7 @@
 package java.lang;
 
 @:native("") // make sure the generator won't see this
+@:transitive
 @:forwardStatics
 @:forward abstract Short(ShortClass) from ShortClass to ShortClass {
 	@:to extern inline public function toShort():java.types.Int16

+ 1 - 0
std/js/lib/Promise.hx

@@ -109,6 +109,7 @@ abstract PromiseHandler<T, TOut>(T->Dynamic) // T->Dynamic, so the compiler alwa
 	A value with a `then` method.
 **/
 @:forward
+@:transitive
 abstract Thenable<T>(ThenableStruct<T>)
 	from ThenableStruct<T> {} // abstract wrapping prevents compiler hanging, see https://github.com/HaxeFoundation/haxe/issues/5785
 

+ 1 - 0
tests/unit/src/unit/issues/Issue2584.hx

@@ -13,6 +13,7 @@ private abstract XX (String) {
 	}
 }
 
+@:transitive
 private abstract X2(X) to X {}
 
 private abstract XArr(Array<X>) to Array<X> {}

+ 2 - 1
tests/unit/src/unit/issues/Issue5385.hx

@@ -23,4 +23,5 @@ private abstract MyIterator<T>(Iterator<T>) from Iterator<T> to Iterator<T> {
 }
 
 @:forward
-private abstract Refs(Array<String>) from Array<String> to Array<String> {}
+@:transitive
+private abstract Refs(Array<String>) from Array<String> to Array<String> {}