HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.18k stars 655 forks source link

Issue with inline + multi multitype #6412

Open MSGhero opened 7 years ago

MSGhero commented 7 years ago

A multityped abstract over a multityped abstract over an interface provides unexpected behavior if the outermost abstract has inlined @:to converters. inline or not in the inner abstract's @:to does not appear to matter.

Explanation of code: new A<Int>() should result in a C being created, tracing out "C". new A<String>() should result in D and "D". Because the string path's @:to is inlined, it seems like a B is being created and used instead of a D even though B is abstract.

Note: if A,B,C,D have a parameter in their constructors, new A<String>("param") will cause a runtime error for too many arguments. However, deleting the argument leads to a compile time error for not enough arguments, which is expected.

class Test {
    static function main() {
        // should trace "C"
        trace(new A<Int>()); // "C"
        // should trace "D"
        trace(new A<String>()); // "[object B_Impl_]", issues with double inlined @:to
    }
}

@:multiType(T)
@:forward
abstract A<T>(B<T>) {
    public function new();
    @:to static function aToC<T:Int>(b:B<T>):B<Int> { return new B<Int>(); }
    @:to static inline function aToD<T:String>(b:B<T>):B<String> { return new B<String>(); }
}

@:multiType(T)
@:forward
abstract B<T>(I<T>) {
    public function new();
    @:to static function bToC<T:Int>(c:I<T>):C { return new C(); }
    @:to static function bToD<T:String>(d:I<T>):D { return new D(); }
}

class C implements I<Int> {
    public function new() { }
    public function toString():String { return 'C'; }
}

class D implements I<String> {
    public function new() { }
    public function toString():String { return 'D'; }
}

interface I<T> {
    function toString():String;
}

(sorry if there's a more minimal example)

Relevant js output:

Test.main = function() {
    console.log(_$Test_A_$Impl_$.aToC(null));
    console.log(new _$Test_B_$Impl_$());
};
var _$Test_A_$Impl_$ = {};
_$Test_A_$Impl_$.aToC = function(b) {
    return _$Test_B_$Impl_$.bToC(null);
};
_$Test_A_$Impl_$.aToD = function(b) {
    return _$Test_B_$Impl_$.bToD(null);
};
var _$Test_B_$Impl_$ = {};
_$Test_B_$Impl_$.bToC = function(c) {
    return new C();
};
_$Test_B_$Impl_$.bToD = function(d) {
    return new D();
};

Haxe 3.4.2 master and latest dev if I installed it properly

Simn commented 7 years ago

Inlining + @:multiType is such a big problem even for our Map implementation. I'm considering getting rid of @:multiType entirely in favor of something more robust, but I have yet to come up with something that's actually more robust.