Description
When Dotty generates an implementation for a field defined in a trait, it loses annotation target information when the trait itself is compiled with Scala 2.13.
Source code
This is a minimized example to demonstrate the problem. Originally it is a case in which A is actually Rule annotation from the junit library. Trait provides an implementation of a field containing @org.junit.Rule for junit tests. Impl is a test class consuming Trait.
Junit framework requires @rule definitions to be public, therefore the definition is requested to be placed on the getter.
x/A.java
package x;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface A {
}
x/Trait.scala
package x
import scala.annotation.meta.getter;
trait Trait {
@(A @getter)
val x = new Object
}
x/Impl.scala
package x
class Impl extends Trait
Mix of scala 2.13 and 3.3.1
Lets say x/Trait.class is located in a library compiled with scala 2.13 and x/Impl.scala consumes the library and compiles the Impl.scala with scala 3.3.1. This produce a wrong x/Impl.class. The annotation ends up on the private field instead of the public getter. Below are resulting classes decompiled with CFR to java:
Originally compiled with scala 2.13, Trait.class decompiles to:
public interface Trait {
public void x$Trait$_setter_$x_$eq(Object var1);
@A
public Object x();
public static void $init$(Trait $this) {
$this.x$Trait$_setter_$x_$eq(new Object());
}
}
Originally compiled with scala 3.3, Impl.class decompiles to:
public class Impl implements Trait {
@A // <---------------------- THE BUG -----------
private Object x;
public Impl() {
Trait.$init$(this);
Statics.releaseFence();
}
// No annotation here
@Override
public Object x() {
return this.x;
}
@Override
public void x$Trait$_setter_$x_$eq(Object x$0) {
this.x = x$0;
}
}
Only scala 2.13
If everything is compiled with just scala 2.13, then the decompiled Impl.class looks like this:
public class Impl implements Trait {
private Object x;
// The annotation is here.
@A
@Override
public Object x() {
return this.x;
}
@Override
public void x$Trait$_setter_$x_$eq(Object x$1) {
this.x = x$1;
}
public Impl() {
Trait.$init$(this);
Statics.releaseFence();
}
}
Only scala 3.3
Everything is fine here as well.
public interface Trait {
public static void $init$(Trait $this) {
$this.x$Trait$_setter_$x_$eq(new Object());
}
@A
public Object x();
public void x$Trait$_setter_$x_$eq(Object var1);
}
public class Impl implements Trait {
private Object x;
public Impl() {
Trait.$init$(this);
Statics.releaseFence();
}
@A
@Override
public Object x() {
return this.x;
}
@Override
public void x$Trait$_setter_$x_$eq(Object x$0) {
this.x = x$0;
}
}