Skip to content

3.3 impl of a 2.13 trait ignores field annotation target #19205

Open
@akshaal

Description

@akshaal

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;
    }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions