-
-
Notifications
You must be signed in to change notification settings - Fork 658
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
[jvm] Functional Interfaces not extending proper interface in some cases #11054
Comments
A basic example works for me: package p;
@FunctionalInterface
public interface ModifyEntries {
/**
* Modifies the item group entries.
* @param entries the entries
* @see FabricItemGroupEntries
*/
void modifyEntries(int entries);
} If I build that using function test(m:p.ModifyEntries) {
m.modifyEntries(12);
}
function main() {
test(i -> trace(12));
} I suppose we'll need a reproducible example here. Is there a simple .jar I can test this with or does that have 200 dependencies? |
I don't have a simple JAR to test with yet, sadly. My project will be public soon (I used the workaround of creating a Haxe class that implements the functional interface), so it might be possible to do some more testing with that. What does the generated Java source for the lambda function on line 6 look like? |
public final class Main_Fields_ extends Object {
public static void main(String[] args) {
Init.init();
main();
}
public static void test(ModifyEntries m) {
m.modifyEntries(12);
}
public static void main() {
test((ModifyEntries)Main_Fields_.Closure_main_0.Main_Fields_$Closure_main_0);
}
public static class Closure_main_0 extends Function implements ModifyEntries {
public static final Main_Fields_.Closure_main_0 Main_Fields_$Closure_main_0 = new Main_Fields_.Closure_main_0();
Closure_main_0() {
}
public void invoke(int i) {
((Function)Log.trace).invoke(12, new Anon0("_Main.Main_Fields_", "source/Main.hx", 6, "main"));
}
public void modifyEntries(int arg0) {
this.invoke(arg0);
}
}
} |
Very interesting. In my codebase, the function generates in its own Java file, and does not include the ModifyEntries interface. I'm trying to think of potentially complicating factors. Maybe my build script is accidentally using the wrong version of Haxe, or maybe it's an issue with the fact that ModifyEntries is an inner class like this: public final class ItemGroupEvents {
@FunctionalInterface
public interface ModifyEntries {
/**
* Modifies the item group entries.
* @param entries the entries
* @see FabricItemGroupEntries
*/
void modifyEntries(FabricItemGroupEntries entries);
}
} |
It being an inner class could indeed be related, perhaps the generator doesn't see the interface and thus never processes it. |
I have added a test case which works for me, see linked commit. This is either some setup case on your end or there must be some other complicating factor in play. |
I've reproduced the issue, this time with My test project is public now, here is the branch/sample the issue occurs on: Apologies, the project has some boilerplate to it:
I am attempting to register 3 event listener functions to the bus, and the generated code looks like this: public void forge_registerListeners()
{
//line 78 "E:\\Programming\\Game Modding\\Minecraft Modding\\PickHaxe\\pickhaxe\\src\\net\\pickhaxe\\core\\CommonMod.hx"
net.pickhaxe.core.CommonMod _gthis = this;
//line 85 "E:\\Programming\\Game Modding\\Minecraft Modding\\PickHaxe\\pickhaxe\\src\\net\\pickhaxe\\core\\CommonMod.hx"
this.modEventBus.addListener(((java.util.function.Consumer<net.minecraftforge.registries.NewRegistryEvent>) (((java.lang.Object) (new net.pickhaxe.core.CommonMod_forge_registerListeners_85__Fun(_gthis)) )) ));
//line 85 "E:\\Programming\\Game Modding\\Minecraft Modding\\PickHaxe\\pickhaxe\\src\\net\\pickhaxe\\core\\CommonMod.hx"
java.lang.Object __temp_expr1 = ((java.lang.Object) (null) );
//line 90 "E:\\Programming\\Game Modding\\Minecraft Modding\\PickHaxe\\pickhaxe\\src\\net\\pickhaxe\\core\\CommonMod.hx"
this.modEventBus.addListener(((java.util.function.Consumer<net.minecraftforge.registries.RegisterEvent>) (((java.lang.Object) (new net.pickhaxe.core.CommonMod_forge_registerListeners_90__Fun(_gthis)) )) ));
//line 90 "E:\\Programming\\Game Modding\\Minecraft Modding\\PickHaxe\\pickhaxe\\src\\net\\pickhaxe\\core\\CommonMod.hx"
java.lang.Object __temp_expr2 = ((java.lang.Object) (null) );
//line 95 "E:\\Programming\\Game Modding\\Minecraft Modding\\PickHaxe\\pickhaxe\\src\\net\\pickhaxe\\core\\CommonMod.hx"
this.modEventBus.addListener(((java.util.function.Consumer<net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent>) (((java.lang.Object) (new net.pickhaxe.core.CommonMod_forge_registerListeners_95__Fun(_gthis)) )) ));
//line 95 "E:\\Programming\\Game Modding\\Minecraft Modding\\PickHaxe\\pickhaxe\\src\\net\\pickhaxe\\core\\CommonMod.hx"
java.lang.Object __temp_expr3 = ((java.lang.Object) (null) );
} However, the functions do not implement the
It just occurred to me that my current Haxe version ( |
Can confirm the listeners don't properly implement the interface on |
I can take a look at this (at some point), but it will probably be addressed faster if you reduce this to a minimal example yourself. |
Thanks, I can try to make a minimal example when i get the time |
I just tried following these steps, but I'm having setup issues:
Did you work around #11016 somehow? I'm hoping that these overload errors are just weird follow-up issues. |
These are merely warnings, the final product works fine right now. I ended up finding some kind of workaround for this I think I need to set aside some time to isolate this issue for you, but it'll take messing with Java source as well as Haxe. |
I re-discovered this issue after doing some more experimenting, and it seems #11014 didn't fix it, and I never actually put together a minimal reproduction for this one here, doh. I suspect it has to do with either the use of type parameters or the use of inner functional interfaces. I will experiment with this. |
After bashing my head against a sample repo for an hour, I've reproduced it. https://github.com/EliteMasterEric/Issue11054/
I'm not confident what exactly is causing it, I'm using anonymous classes requesting functional interfaces with type parameters, so it could be any manner of things. |
Ported to pure Haxe: abstract class Robot<T> {
public function new() {}
public function performTask(listener:T) {
trace("Robot.performTask() called!");
}
public function toString() {
return "Robot";
}
}
interface IGreetRobot {
function greet<T>(robot:Robot<T>):Void;
}
interface IMathOperation {
function operate(a:Int, b:Int):Int;
}
class MathRobot extends Robot<IMathOperation> {
override function performTask(listener:IMathOperation) {
super.performTask(listener);
var result = listener.operate(3, 4);
trace("Result: " + result);
}
}
class GreetRobot extends Robot<IGreetRobot> {
var target:Robot<Dynamic>;
public function new(target:Robot<Dynamic>) {
super();
this.target = target;
}
override function performTask(listener:IGreetRobot) {
super.performTask(listener);
listener.greet(target);
}
}
class Main {
public static function main() {
var robot1 = new MathRobot();
var robot2 = new GreetRobot(robot1);
robot1.performTask(add);
robot1.performTask(function(a:Int, b:Int):Int {
return a - b;
});
robot2.performTask(function(target) {
trace('Hello, ${target.toString()}!');
});
}
static function add(a:Int, b:Int):Int {
return a + b;
}
}
My first thought was that it might be about By the way, please remind me: If we have |
It ends up being what's known as a raw type. https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html I believe these act WITHOUT type checking, so at compile time it just assumes the value you pass for T is valid and throws a runtime error if it's not valid. You also get a warning in most IDEs.
|
This works now if So for now, it requires some awareness, but it should be easy to work around. |
Personally, mandating that all method references used with functional interfaces are strongly typed is an acceptable compromise, and the newest commit ( If you want to resolve type inference on method arguments, I would say make a new issue for it, I'd say this one can properly be closed. |
I just recently got around to testing, and although the examples work in pure Haxe, they do not work when trying to interact with an extern which is using functional interfaces.
I got this crash report:
onInitialize()#77
is a Haxe lambda of type(FabricItemGroupEntries) -> Void
, and looks like this:and the generated Java code:
register()
takes aModifyEntries
, which is an inner interface ofFabricItemGroupEntries
, whose full source is:It appears that in the generated Java,
ModItems_onInitialize_77__Fun
is ahaxe.lang.Function
which is attempted to be cast toModifyEntries
, which fails.My expectation is that, since
ModItems_onInitialize_77__Fun
is passed as an argument toregister
and cast to theModifyEntries
type in the generated Java source code, it should be compatible with that class and the lambda function should be able to be properly called. The result is very similar to the pure Java syntax for this example, which looks like this:The lambda function is casted to a ModifyEntries without issue.
Tied to #11014, but I decided this is sufficiently different enough to be its own issue (the previous issue was about functional interfaces not being a thing in general, this one is about their interaction with Java source/extern libraries).
The text was updated successfully, but these errors were encountered: