HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit

Home Page:https://haxe.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Uncorrect resolving Unknown<?> type returned from function

ze0nni opened this issue · comments

commented

Look like haxe resolve type Unknown<?> after first comparisons for all branches of code.

enum abstract GameType<TBody>(String) {
	final Unit: GameType<{ hp: Float }>;
	final Box: GameType<{ size: Float }>;
}

@: forward()
abstract GameObject < TBody = Void > ({
  final type: GameType<TBody>;
  final body: TBody;
}) {
  inline public function new<T>(type: GameType<T>, body: T) {
    this = cast { type: type, body: body };
  }
  inline public function resolve<TResolvedBody>(): GameObject<TResolvedBody> {
    return cast this;
  }
}

class Test {
  static function main() {
    final objects: Array<GameObject> = [
      new GameObject(Unit, { hp: 10 }),
      new GameObject(Box, { size: 5 })
    ];

    for (i in objects) {
      final o = i.resolve();
      switch (o.type) {
        case Unit:
        //o.body.hp -= 1;

        case Box: //<< 36
        //o.body.size += 1;
      }
    }
  }
}

[ERROR] Test.hx:36: characters 14-17

 36 |         case Box:
    |              ^^^
    | error: { hp : Float } has no field size
    | have: GameType<{ size }>
    | want: GameType<{ hp }>

But it's work good here:

static function resolve<TBody>(type: GameType<TBody>, body: TBody) {
    switch(type) {
        case Unit:
                body.hp -= 1;        
        case Box:
                body.size -= 1;
}

Actually I would like to be able to use the Unknown type in my code too. For cases when the type is really unknown is this possible?

I'm not sure what exactly you're reporting here. The first example doesn't look unexpected because TResolvedBody is never unified with anything.

commented

I expect behavior like this

enum abstract GameType<TBody>(String) {
    final Unit: GameType<{ hp: Float }>;
    final Box: GameType<{ size: Float }>;
}

typedef GameObject<TBody> = {
  final type: GameType<TBody>;
  final body: TBody;
}

final objects: object: Array<GameObject<Unknown>> = [];

for (o in objects) {
    switch (o.type) {
       case Unit: o.body.hp -= 1;
       case Box: o.body.size -= 1;
    }
}

You can achieve it with one more step:

class Test {
	static function main() {
		final objects:Array<GameObject<Any>> = [
			{type: Unit, body: {hp: 42}},
			{type: Box, body: {size: 4.2}}
		];

		inline function foo<T>(o:GameObject<T>) {
			switch (o.type) {
				case Unit:
					$type(o); // GameObject<{ hp : Float }>
				case Box:
					$type(o); // GameObject<{ size : Float }>
			}
		}

		for (o in objects)
			foo(o);
	}
}

enum abstract GameType<TBody>(String) {
	final Unit:GameType<{hp:Float}>;
	final Box:GameType<{size:Float}>;
}

typedef GameObject<TBody> = {
	final type:GameType<TBody>;
	final body:TBody;
}

https://try.haxe.org/#A80ca1Da

commented

Right. But why doesn't the same work for return-types?

I don't know if we want to support this kind of typing for monomorphs in general. Type inference is already tricky enough as it is, and changing it to support a corner case like here doesn't have a good risk/reward ratio in my opinion.

commented

Sad to hear. I find it feature powerful when haxe-Enum cannot be used