Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map literal incorrectly casted to abstract on IMap on CS target #6474

Closed
romanmikhailov opened this issue Jul 28, 2017 · 0 comments · Fixed by #11551
Closed

Map literal incorrectly casted to abstract on IMap on CS target #6474

romanmikhailov opened this issue Jul 28, 2017 · 0 comments · Fixed by #11551
Labels
platform-cs Everything related to c#
Milestone

Comments

@romanmikhailov
Copy link

I have an abstract on IMap (AbstractMap in example below), Implementation of IMap interface with custom key constraint from abstract class (FooMap in example below).

In my AbstractMap I have @:from and @:to methods to allow constructing of my map.

And for dynamic targets example below works correctly but I receives runtime exception (running on js and C# targets listings in the end of message) in C#:

Main.hx:

package;

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

typedef GlobalIdPair = {
	id: Int,
	value: Dynamic
}

class GlobalIdStorage {
	private static var idMap:IntMap<GlobalIdPair> = new IntMap<GlobalIdPair>();
	private static var lastId:Int = 0;

	public static function createId(object:Dynamic):Int {
		lastId++;
		idMap.set(lastId, {
			id: lastId,
			value: object
		});
		return lastId;
	}

	public static function getIdForObject(object:Dynamic):Int {
		for (it in idMap) {
			if (it.value == object) {
				return it.id;
			}
		}
		return -1;
	}

	public static function getObjectForId(id:Int):Null<Dynamic> {
		return idMap.exists(id) ? idMap.get(id).value : null;
	}
}

abstract Foo<T>(T) {
	public function new(t:T) {
		this = t;
		GlobalIdStorage.createId(t);
	}

	public function getId():Int {
		return GlobalIdStorage.getIdForObject(this);
	}
}

class FooMap<K:Foo<Dynamic>, V> implements Map.IMap<K, V> {
	var h:IntMap<V>;

	public inline function new() {
	    h = new IntMap<V>();
	}

	public function get(k:K):Null<V> {
	    return h.get(k.getId());
	}

	public function set(k:K, v:V) {
		h.set(k.getId(), v);
	}

	public function exists(k:K):Bool {
		return h.exists(k.getId());
	}

	public function remove(k:K):Bool {
		return h.remove(k.getId());
	}

	public function keys():Iterator<K> {
		var i = h.keys();
		return {
			hasNext: i.hasNext,
			next: function():K {
				return GlobalIdStorage.getObjectForId(i.next());
			}
		}
	}

	public inline function iterator():Iterator<V> {
		return h.iterator();
	}

	public inline function toString():String {
		return h.toString();
	}
}

@:multiType
@:forward(exists, get, set)
abstract AbstractMap<K, V>(IMap<K, V>) {
	public function new();

	@:to 
	public static inline function toFooMap<K:Foo<Dynamic>, V>(map:IMap<K, V>):FooMap<K, V> {
		return new FooMap<K, V>();
	}

	@:from static inline function fromHaxeMap<K, V>(map:Map<K, V>):AbstractMap<K, V> {
		return cast map;
	}
}

class Main {
	public static function main():Void {
		var abstractMap:AbstractMap<Foo<String>, String> = [new Foo<String>("Foo<String>0") => "String0", new Foo<String>("Foo<String>1") => "String1"];

		trace(abstractMap.exists(new Foo<String>("Foo<String>0")));
		trace(abstractMap.get(new Foo<String>("Foo<String>0")));

		trace(abstractMap.exists(new Foo<String>("Foo<String>1")));
		trace(abstractMap.get(new Foo<String>("Foo<String>1")));

		trace(abstractMap.exists(new Foo<String>("Foo<String>2")));
		trace(abstractMap.get(new Foo<String>("Foo<String>2")));
	}
}

build.hxml:

-cs bin/cs

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

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

build_js.hxml:

-js bin/js/main.js

-cp .
-main Main
-dce full
-debug

-cmd node bin/js/main.js

JS output:

$ haxe build_js.hxml 
true
String0
true
String1
false
undefined

CS output:

$ haxe build.hxml 
haxelib run hxcs hxcs_build.txt --haxe-version 3402 --feature-level 1
Note: dmcs is deprecated, please use mcs instead!

Unhandled Exception:
System.InvalidCastException: Specified cast is not valid.
  at Main.main () [0x0005a] in <b6f74a2b4df84d9e9f088683dc02b1a3>:0 
  at EntryPoint__Main.Main () [0x00007] in <b6f74a2b4df84d9e9f088683dc02b1a3>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidCastException: Specified cast is not valid.
  at Main.main () [0x0005a] in <b6f74a2b4df84d9e9f088683dc02b1a3>:0 
  at EntryPoint__Main.Main () [0x00007] in <b6f74a2b4df84d9e9f088683dc02b1a3>:0 
Error: Command failed with error 1

Generated C# main method:

public static void main() {
		unchecked {
			global::haxe.IMap<object, object> _g = ((global::haxe.IMap<object, object>) (global::haxe.IMap__Statics_.__hx_cast<object, object>(((global::haxe.IMap) (new global::haxe.ds.StringMap<object>()) ))) );
			_g.@set(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>0") ))), "String0");
			_g.@set(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>1") ))), "String1");
			global::FooMap<object, object> abstractMap = ((global::FooMap<object, object>) (global::FooMap<object, object>.__hx_cast<object, object>(((global::FooMap) (_g) ))) );
			global::haxe.Log.trace.__hx_invoke2_o(default(double), abstractMap.exists(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>0") )))), default(double), new global::haxe.lang.DynamicObject(new int[]{302979532, 1547539107, 1648581351}, new object[]{"main", "Main", "Main.hx"}, new int[]{1981972957}, new double[]{((double) (111) )}));
			global::haxe.Log.trace.__hx_invoke2_o(default(double), global::haxe.lang.Runtime.toString((abstractMap.@get(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>0") ))))).toDynamic()), default(double), new global::haxe.lang.DynamicObject(new int[]{302979532, 1547539107, 1648581351}, new object[]{"main", "Main", "Main.hx"}, new int[]{1981972957}, new double[]{((double) (112) )}));
			global::haxe.Log.trace.__hx_invoke2_o(default(double), abstractMap.exists(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>1") )))), default(double), new global::haxe.lang.DynamicObject(new int[]{302979532, 1547539107, 1648581351}, new object[]{"main", "Main", "Main.hx"}, new int[]{1981972957}, new double[]{((double) (114) )}));
			global::haxe.Log.trace.__hx_invoke2_o(default(double), global::haxe.lang.Runtime.toString((abstractMap.@get(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>1") ))))).toDynamic()), default(double), new global::haxe.lang.DynamicObject(new int[]{302979532, 1547539107, 1648581351}, new object[]{"main", "Main", "Main.hx"}, new int[]{1981972957}, new double[]{((double) (115) )}));
			global::haxe.Log.trace.__hx_invoke2_o(default(double), abstractMap.exists(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>2") )))), default(double), new global::haxe.lang.DynamicObject(new int[]{302979532, 1547539107, 1648581351}, new object[]{"main", "Main", "Main.hx"}, new int[]{1981972957}, new double[]{((double) (117) )}));
			global::haxe.Log.trace.__hx_invoke2_o(default(double), global::haxe.lang.Runtime.toString((abstractMap.@get(global::haxe.lang.Runtime.toString(global::_Main.Foo_Impl_._new<object>(((object) ("Foo<String>2") ))))).toDynamic()), default(double), new global::haxe.lang.DynamicObject(new int[]{302979532, 1547539107, 1648581351}, new object[]{"main", "Main", "Main.hx"}, new int[]{1981972957}, new double[]{((double) (118) )}));
		}
	}
@Simn Simn added the platform-cs Everything related to c# label Apr 17, 2018
@Simn Simn added this to the Bugs milestone Apr 17, 2018
@Simn Simn modified the milestones: Bugs, Later Mar 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform-cs Everything related to c#
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants