haxe icon indicating copy to clipboard operation
haxe copied to clipboard

Catching multiple distinct enums catches with the wrong catch

Open 47rooks opened this issue 7 months ago • 13 comments

If you have a try{} from which multiple different enums might be thrown the first catch for any enum will catch whatever enum is thrown. This happens on cpp. HL gets this right and the correct catch is hit. I believe this is related to Std.isOfType on cpp not correctly distinguishing the different enums. In the attached example changing the order of the three enum catches Error, FirstErrorEnum, and SecondErrorEnum will determine which catches the Error.Custom("EOF").

import haxe.ValueException;
import haxe.io.Eof;
import haxe.io.Error;

enum FirstErrorEnum {
	ERROR1;
}

enum SecondErrorEnum {
	ERROR2;
}

class Main {
	static function handle_error(e:Dynamic) {
		switch (e) {
			// First enum in the list will match Error on CPP.
			case Std.isOfType(e, FirstErrorEnum) => true:
				trace('handle FirstErrorEnum: ${Type.typeof(e)}');
			case Std.isOfType(e, SecondErrorEnum) => true:
				trace('handle SecondErrorEnum: ${Type.typeof(e)}');
			case Std.isOfType(_, Eof) => true:
				trace('handle Eof: ${Type.typeof(e)}');
			case Std.isOfType(e, Error) => true:
				trace('handle Error ${Type.typeof(e)}');
			case Std.isOfType(e, ValueException) => true:
				trace('handle ValueException: $e');
			default:
				trace('handle default: $e');
		}
	}

	static function f() {
		throw Error.Custom("EOF");
	}

	static function main() {
		try {
			f();
		} catch (e:Eof) {
			// Eof is a class and is correctly skipped
			trace('catch Eof ${Type.typeof(e)}');
			handle_error(e);
		} catch (e:ValueException) {
			trace('catch ValueException $e');
			handle_error(e);
		} catch (e:FirstErrorEnum) {
			trace('catch FirstErrorEnum ${Type.typeof(e)}');
			handle_error(e);
		} catch (e:Error) {
			trace('catch Error ${Type.typeof(e)}');
			handle_error(e);
		} catch (e:SecondErrorEnum) {
			// First enum in the list will match Error on CPP.
			trace('catch SecondErrorEnum ${Type.typeof(e)}');
			handle_error(e);
		}
	}
}

build-cpp.hxml

-cp src
--main Main
--cpp export/cpp
-D real-position

build-hl.hxml

-cp src
--main Main
--hl export/hl/Main.hl
-D real-position

47rooks avatar Apr 10 '25 03:04 47rooks

Could you reduce this example to what you actually mean? Locally this gives me

source/Main.hx:50: catch Error TEnum(haxe.io.Error)
source/Main.hx:24: handle Error TEnum(haxe.io.Error)

This seems correct, but I'm confused because you talk about catching enums and then your example doesn't actually throw any enums.

Simn avatar Apr 10 '25 04:04 Simn

Ok I should probably have added: Haxe 4.3.6, Windows 10 is where I'm testing. And Hashlink 1.14 for comparison.

On cpp I get:

src/Main.hx:53: catch FirstErrorEnum TEnum(haxe.io.Error)
src/Main.hx:24: handle FirstErrorEnum: TEnum(haxe.io.Error)```

And on HL I get:

 & 'D:\Program Files\HashLink1.14\hl.exe' .\export\hl\Main.hl
src/Main.hx:56: catch Error TEnum(haxe.io.$Error)
src/Main.hx:30: handle Error TEnum(haxe.io.$Error)

As to not throwing an enum f() throws haxe.io.Error which is an enum.

So the basic problem is that the catch is FirstErrorEnum comes ahead of catch Error and it catches the thrown Error.

47rooks avatar Apr 10 '25 13:04 47rooks

There's a chance that this has been fixed in hxcpp already. You could try haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp to test with the current version, which should be backwards-compatible.

Simn avatar Apr 10 '25 13:04 Simn

Just pulled the latest git hxcpp and it still repros.

47rooks avatar Apr 10 '25 13:04 47rooks

@kLabz Could you check this with 4.3 and bisect it? I seem to remember some related fix but I can't find it.

Simn avatar Apr 11 '25 06:04 Simn

On development:

src/Main.hx:50: catch Error TEnum(haxe.io.Error) 
src/Main.hx:24: handle Error TEnum(haxe.io.Error)

From 4.3.0 to 4.3.7:

src/Main.hx:47: catch FirstErrorEnum TEnum(haxe.io.Error) 
src/Main.hx:18: handle FirstErrorEnum: TEnum(haxe.io.Error)

On <= 4.2.5:

src/Main.hx:44: catch ValueException Custom(EOF) 
src/Main.hx:26: handle ValueException: Custom(EOF)

Is development result ok? Will try to bisect, but depending on which result is the expected result, will have to either bisect dev to find the FirstErrorEnum->Error change or bisect 4.3 to find the ValueException->FirstErrorEnum change.


Edit: results for hl:

Both development and <= 4.2.5:

src/Main.hx:44: catch ValueException Custom(EOF) 
src/Main.hx:26: handle ValueException: Custom(EOF)

4.3.x:

src/Main.hx:50: catch Error TEnum(haxe.io.$Error)
src/Main.hx:24: handle Error TEnum(haxe.io.$Error)

kLabz avatar May 12 '25 07:05 kLabz

It's not very easy to tell with this convoluted example, but I think development output is correct.

Simn avatar May 12 '25 07:05 Simn

It's not very easy to tell with this convoluted example, but I think development output is correct.

The cpp or hl one? :/

kLabz avatar May 12 '25 08:05 kLabz

Both seem acceptable because depending on the target a value may or may not be wrapped in ValueException. I don't see how we could unify this unless we always wrap every value on every target, which I don't think is something we want to do.

Simn avatar May 12 '25 08:05 Simn

Seems to be https://github.com/HaxeFoundation/haxe/pull/10519

Will try to find what has been fixed there since then in development

kLabz avatar May 12 '25 12:05 kLabz

Didn't that PR just introduce the inconsistencies by avoiding the wrapping on some targets?

Simn avatar May 12 '25 13:05 Simn

Yeah but unless there's another way to repro the actual issue, before that this issue could not happen as it was wrapped in ValueException

kLabz avatar May 12 '25 14:05 kLabz

Sorry, missed the pings on this.

The development branch on cpp result is correct and the HL 4.3.x result is correct. The test case throws a haxe.io.Error but it is only caught properly in the catch, and matched properly in the switch in these versions. In other cases it is caught/matched by the first enum type in the list of catches or match cases.

47rooks avatar May 17 '25 04:05 47rooks