HaxeFoundation / haxe

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

inline vs. overload #7599

Closed Simn closed 4 years ago

Simn commented 6 years ago

Compiling this to Java:

class Main {
    static public function main() {
        inlineMe();
    }

    static inline function inlineMe() {
        test("foo");
    }

    @:overload static function test(i:Int) { }
    @:overload static function test(s:String) { }
}

We get this AST dump:

    public static function main[Function:Void -> Void]
        [Block:Void]
            [Call:Void]
                [Field:(s : String) -> Void]
                    [TypeExpr Main:Class<Main>]
                    [FStatic:(s : String) -> Void]
                        Main
                        test:(i : Int) -> Void
                [Const:String] "foo"

    static inline function inlineMe[Function:Void -> Void]
        [Block:Void]
            [Call:Void]
                [Field:(s : String) -> Void]
                    [TypeExpr Main:Class<Main>]
                    [FStatic:(s : String) -> Void]
                        Main
                        test:(s : String) -> Void
                [Const:String] "foo"

Notice how test:(s : String) -> Void becomes test:(i : Int) -> Void after being inlined. This means that the AST holds the incorrect tclass_field. Surprisingly, the Java generator doesn't seem to care and generates the correct output.

Not sure if regression or just stupid.

Simn commented 6 years ago

This happens because map_expr_type tries to re-type the field access after mapping the field expression. It does so by looking up the field by-name through quick_field, which obviously ignores overload types.

I think this means we have to review all occurrences of quick_field, and perhaps even all PMap.find lookups into cl_fields and cl_statics...

Simn commented 4 years ago

Extended test case:

class Main {
    @:analyzer(ignore)
    static public function main() {
        var m = new Main();
        inlineMe("foo");
        inlineMe(12);
        inlineMe(m);
        inlineMeMember(m, "foo");
        inlineMeMember(m, 12);
        inlineMeMember(m, m);
    }

    function new() {}

    static inline function inlineMe<T>(t:T) {
        test(t);
    }

    static inline function inlineMeMember<T>(m:Main, t:T) {
        m.testMember(t);
    }

    overload static function test(d:Dynamic) {}

    overload static function test(i:Int) {}

    overload static function test(s:String) {}

    overload function testMember(d:Dynamic) {}

    overload function testMember(i:Int) {}

    overload function testMember(s:String) {}
}

Dump:

    public static function main[Function:() -> Void]
        [Block:Void]
            [Var m(3290):Main] [New:Main] Main
            [Call:Void]
                [Field:(d : Dynamic) -> Void]
                    [TypeExpr Main:Class<Main>]
                    [FStatic:(d : Dynamic) -> Void]
                        Main
                        test:(d : Dynamic) -> Void
                [Const:java.lang.String] "foo"
            [Call:Void]
                [Field:(d : Dynamic) -> Void]
                    [TypeExpr Main:Class<Main>]
                    [FStatic:(d : Dynamic) -> Void]
                        Main
                        test:(d : Dynamic) -> Void
                [Const:Int] 12
            [Call:Void]
                [Field:(d : Dynamic) -> Void]
                    [TypeExpr Main:Class<Main>]
                    [FStatic:(d : Dynamic) -> Void]
                        Main
                        test:(d : Dynamic) -> Void
                [Local m(3290):Main:Main]
            [Call:Void]
                [Field:(d : Dynamic) -> Void]
                    [Local m(3290):Main:Main]
                    [FInstance:(d : Dynamic) -> Void]
                        Main
                        testMember:(d : Dynamic) -> Void
                [Const:java.lang.String] "foo"
            [Call:Void]
                [Field:(d : Dynamic) -> Void]
                    [Local m(3290):Main:Main]
                    [FInstance:(d : Dynamic) -> Void]
                        Main
                        testMember:(d : Dynamic) -> Void
                [Const:Int] 12
            [Call:Void]
                [Field:(d : Dynamic) -> Void]
                    [Local m(3290):Main:Main]
                    [FInstance:(d : Dynamic) -> Void]
                        Main
                        testMember:(d : Dynamic) -> Void
                [Local m(3290):Main:Main]

Also, I think the same problem could be reproduced with @:generic.