HaxeFoundation / haxe

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

[cs] DCE seems to strip interfaces fields when using theirs in abstract inline functions #6473

Closed romanmikhailov closed 3 months ago

romanmikhailov commented 7 years ago

I met compilation error (error CS1061: Type `haxe.IMap<object,int>' does not contain a definition for `keys' and no extension method `keys' of type `haxe.IMap<object,int>' could be found. Are you missing an assembly reference?) when try to compile next code:

This can be workarounded one of next ways:

  1. Removing inline from EquatableMap#tryGetKey
  2. Adding --macro keep("haxe.Constraints.IMap") in build.hxml
  3. Move declaration of Main#fooMap to main

Main.hx

package;

import haxe.Constraints.IMap;
import haxe.ds.ObjectMap;

interface IEquatable<T> {
    function equals(other:T):Bool;
}

@:multiType(K)
abstract EquatableMap<K, V>(IMap<K, V>) {
    public function new();

    public inline function containsKey(key:K):Bool {
        key = tryGetKey(key);
        return this.exists(key);
    }

    inline function tryGetKey(key:K):K {
        var result = key;
        if(Std.is(key, IEquatable)) {
            var keyHolder:IEquatable<Dynamic> = cast key;
            for(it in this.keys()) {
                var currentKeyHolder:IEquatable<Dynamic> = cast it;
                if(currentKeyHolder.equals(keyHolder)) {
                    result = it;
                    break;
                }
            }
        }
        return result;
    }

    @:to static inline function toObjectMap<K:{}, V>(t:IMap<K, V>):ObjectMap<K,V> {
        return new ObjectMap<K, V>();
    }
}

class Foo implements IEquatable<Foo> {
    public function new() {
    }

    public function equals(other:Foo):Bool {
        return this == other;
    }
}

class Main {
    private static var fooMap:EquatableMap<Foo, Int> = new EquatableMap<Foo, Int>();

    public static function main():Void {
        var foo:Foo = new Foo();
        var exists:Bool = fooMap.containsKey(foo);
        trace(exists);
    }
}

build.hxml

-cs output/

-cp .
-main Main
-dce full
-debug
-D real-position

-cmd mono output/bin/Main-Debug.exe

Console output

$ haxe build.hxml 
haxelib run hxcs hxcs_build.txt --haxe-version 3402 --feature-level 1
Note: dmcs is deprecated, please use mcs instead!
src/Main.cs(145,24): error CS1061: Type `haxe.IMap<object,int>' does not contain a definition for `keys' and no extension method `keys' of type `haxe.IMap<object,int>' could be found. Are you missing an assembly reference?
src/haxe/Constraints.cs(5,19): (Location of the symbol related to previous error)
Compilation failed: 1 error(s), 0 warnings
Compilation error
Native compilation failed
Error: Build failed
romanmikhailov commented 7 years ago

Minimal reproducible example without maps and generics (using the same build.hxml): Main.hx

package;

interface IInterface {
    function interfaceMethod1():Void;
    function interfaceMethod2():Void;
}

class InterfaceImplementation implements IInterface {
    public function new() {}

    public function interfaceMethod1():Void {
        trace('interfaceMethod1');
    }

    public function interfaceMethod2():Void {
        trace('interfaceMethod2');
    }
}

@:multiType
abstract AbstractOnInterface(IInterface) {
    public function new();

    public inline function abstractMethod1():Void {
        abstractMethod2();
        this.interfaceMethod1();
    }

    public inline function abstractMethod2():Void {
        this.interfaceMethod2();
    }

    @:to static inline function toObject(t:IInterface):InterfaceImplementation {
        return new InterfaceImplementation();
    }
}

class Main {
    private static var abstractOnInterface:AbstractOnInterface = new AbstractOnInterface();

    public static function main():Void {
        abstractOnInterface.abstractMethod1();
    }
}

Console output:

$ haxe build.hxml 
haxelib run hxcs hxcs_build.txt --haxe-version 3402 --feature-level 1
Note: dmcs is deprecated, please use mcs instead!
src/Main.cs(133,9): error CS1061: Type `IInterface' does not contain a definition for `interfaceMethod2' and no extension method `interfaceMethod2' of type `IInterface' could be found. Are you missing an assembly reference?
src/Main.cs(4,18): (Location of the symbol related to previous error)
Compilation failed: 1 error(s), 0 warnings
Compilation error
Native compilation failed
Error: Build failed
fourst4r commented 8 months ago

I also encountered this when doing my Reflaxe/Dart target (although tbf it could be me implementing it wrong) having to add @:keep to IMap to stop it from DCEing the fields.

kLabz commented 3 months ago

Hmm, seems like we missed some C# issues. See #11551 (or provide a way to reproduce this on other targets and reopen)