-
-
Notifications
You must be signed in to change notification settings - Fork 194
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
Generic interfaces cannot be soft-implemented #169
Comments
I'll take a look at this on the 17th. This kind of implementatio needs special attention because of the need to handle synthetic bridges. |
Just to provide some explanation as to why this is non-trivial. When implementing a generic interface this way, where a type argument appears in a signature of an interface method, the compiler generates "bridge methods" which allow dynamic binding against both the derived interface and the superinterface. To take your example, if this wasn't a Mixin then the following code: public class Bam implements Baz {
public Baz bar() {
return this;
}
} would actually result in the following in the compiled class bytecode: public class Bam implements Baz {
public Baz bar() {
return this;
}
// Synthetic bridge method with signature matching the interface in
// which the method was declared
public Foo bar() {
return this.bar(); // calls the method above
}
} Of course there's no way to explicitly define this type of bridge method in java code, since methods with signatures which differ only by return type are not allowed in Java, though they are of course perfectly legal in bytecode. There is another type of bridge method which is used when a method parameter is generic: interface Damagable<T extends Number> {
void applyDamage(T amount);
}
interface PartialDamagable extends Damagable<Float> {
}
class Gavin implements PartialDamagable {
public void applyDamage(Float amount) {
// stuff
}
} In this case, the bridge method which takes the supertype will have to cast down the argument to the correct type: // synthetic bridge
public void applyDamage(Number amount) {
this.applyDamage((Float)amount);
} These two scenarios can be combined of course. It also gets more complicated if there are multiple type arguments in the interface signature. Normally when you implement an interface "for real" in a Mixin, the bridges are generated by the compiler and Mixin just has to apply the generated methods. However in this case Mixin itself is going to have to generate the correct bridges, which is a nontrivial task. To achieve this, the following will have to be implemented:
Whilst I would normally mark this as |
Added the |
Not to be necro'ing this issue, but just ran into somewhat of a similar problem with a semi related issue: SpongePowered/Sponge#3647 In short, as I suspected was the reasoning case for what's happening in the issue above: MinecraftServerMixin_API is generically extending public bridge synthetic tell(java.lang.Object arg0) { //(Ljava/lang/Object;)V
<localVar:index=0 , name=this , desc=Lorg/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API;, sig=null, start=L1, end=L2>
L1 {
aload0 // reference to self
aload1
checkcast java/lang/Runnable
invokespecial net/minecraft/util/thread/ReentrantBlockableEventLoop.tell(Ljava/lang/Runnable;)V
return
}
L2 {
}
} And this is the crux of the issue, this method is inherited by the extending class Unless a hack workaround like |
Take the following interfaces:
And with the following Mixin:
Mixin will fail to locate the desired generic method and as a result fail.
I haven't tested whether a workaround of not prefixing the generic method and having
MixinBam
implementBaz
will work (of course it compiles, but I haven't checked if at runtime it applies cleanly).The text was updated successfully, but these errors were encountered: