Skip to content

Fix "is not a trait" when extending a Java annotation #13225

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1579,15 +1579,6 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
}
}

/** Does this symbol actually correspond to an interface that will be emitted?
* In the backend, this should be preferred over `isInterface` because it
* also returns true for the symbols of the fake companion objects we
* create for Java-defined classes as well as for Java annotations
* which we represent as classes.
*/
private def isEmittedInterface(sym: Symbol): Boolean = sym.isInterface ||
sym.is(JavaDefined) && (toDenot(sym).isAnnotation || sym.is(ModuleClass) && (sym.companionClass.is(PureInterface)) || sym.companionClass.is(Trait))

}

object BCodeBodyBuilder {
Expand Down
24 changes: 20 additions & 4 deletions compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = {
val superClassSym: Symbol = {
val t = classSym.asClass.superClass
if (t.exists) t
if (toDenot(classSym).isJavaAnnotation) defn.ObjectClass
else if (t.exists) t
else if (classSym.is(ModuleClass)) {
// workaround #371

Expand All @@ -106,7 +107,7 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
assert(
if (classSym == defn.ObjectClass)
superClassSym == NoSymbol
else if (classSym.isInterface)
else if (isEmittedInterface(classSym))
superClassSym == defn.ObjectClass
else
// A ClassBType for a primitive class (scala.Boolean et al) is only created when compiling these classes.
Expand Down Expand Up @@ -305,7 +306,7 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
0 .addFlagIf(privateFlag, ACC_PRIVATE)
.addFlagIf(!privateFlag, ACC_PUBLIC)
.addFlagIf(sym.is(Deferred) || sym.isOneOf(AbstractOrTrait), ACC_ABSTRACT)
.addFlagIf(sym.isInterface, ACC_INTERFACE)
.addFlagIf(isEmittedInterface(sym), ACC_INTERFACE)
.addFlagIf(finalFlag
// Primitives are "abstract final" to prohibit instantiation
// without having to provide any implementations, but that is an
Expand All @@ -317,12 +318,13 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
.addFlagIf(sym.isStaticMember, ACC_STATIC)
.addFlagIf(sym.is(Bridge), ACC_BRIDGE | ACC_SYNTHETIC)
.addFlagIf(sym.is(Artifact), ACC_SYNTHETIC)
.addFlagIf(sym.isClass && !sym.isInterface, ACC_SUPER)
.addFlagIf(sym.isClass && !isEmittedInterface(sym), ACC_SUPER)
.addFlagIf(sym.isAllOf(JavaEnumTrait), ACC_ENUM)
.addFlagIf(sym.is(JavaVarargs), ACC_VARARGS)
.addFlagIf(sym.is(Synchronized), ACC_SYNCHRONIZED)
.addFlagIf(sym.isDeprecated, ACC_DEPRECATED)
.addFlagIf(sym.is(Enum), ACC_ENUM)
.addFlagIf(sym.isJavaAnnotation, ACC_ANNOTATION)
}

def javaFieldFlags(sym: Symbol) = {
Expand All @@ -333,4 +335,18 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
.addFlagIf(sym.hasAnnotation(VolatileAttr), ACC_VOLATILE)
.addFlagIf(!sym.is(Mutable), ACC_FINAL)
}

/** Does this symbol actually correspond to an interface that will be emitted?
* In the backend, this should be preferred over `isInterface` because it
* also returns true for the symbols of the fake companion objects we
* create for Java-defined classes as well as for Java annotations
* which we represent as classes.
*/
final def isEmittedInterface(sym: Symbol): Boolean =
sym.isInterface ||
sym.is(JavaDefined) && (
toDenot(sym).isAnnotation
|| sym.is(ModuleClass) && sym.companionClass.is(PureInterface)
|| sym.companionClass.is(Trait)
)
}
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ class Definitions {
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")

@tu lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation")
@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")

// A list of meta-annotations that are relevant for fields and accessors
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,10 @@ object SymDenotations {
def isAnnotation(using Context): Boolean =
isClass && derivesFrom(defn.AnnotationClass)

/** Is this a Java annotation ? */
def isJavaAnnotation(using Context): Boolean =
isClass && derivesFrom(defn.JavaAnnotationClass)

/** Is this symbol a class that extends `java.io.Serializable` ? */
def isSerializable(using Context): Boolean =
isClass && derivesFrom(defn.JavaSerializableClass)
Expand Down
12 changes: 3 additions & 9 deletions compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,7 @@ object JavaParsers {

def scalaAnnotationDot(name: Name): Select = Select(scalaDot(nme.annotation), name)

def javaDot(name: Name): Tree =
Select(rootDot(nme.java), name)

def javaLangDot(name: Name): Tree =
Select(javaDot(nme.lang), name)

def javaLangObject(): Tree = javaLangDot(tpnme.Object)
def javaLangObject(): Tree = javaDotLangDot(tpnme.Object)

def arrayOf(tpt: Tree): AppliedTypeTree =
AppliedTypeTree(scalaDot(tpnme.Array), List(tpt))
Expand Down Expand Up @@ -878,7 +872,7 @@ object JavaParsers {
}
def annotationParents: List[Select] = List(
scalaAnnotationDot(tpnme.Annotation),
Select(javaLangDot(nme.annotation), tpnme.Annotation),
Select(javaDotLangDot(nme.annotation), tpnme.Annotation),
scalaAnnotationDot(tpnme.ClassfileAnnotation)
)
def annotationDecl(start: Offset, mods: Modifiers): List[Tree] = {
Expand Down Expand Up @@ -942,7 +936,7 @@ object JavaParsers {
AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType))
*/
val superclazz = Apply(TypeApply(
Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), Nil)
Select(New(javaDotLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), Nil)
val enumclazz = atSpan(start, nameOffset) {
TypeDef(name,
makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.JavaEnumTrait)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1284,8 +1284,8 @@ class Namer { typer: Typer =>
val ptype = parentType(parent)(using completerCtx.superCallContext).dealiasKeepAnnots
if (cls.isRefinementClass) ptype
else {
val pt = checkClassType(ptype, parent.srcPos,
traitReq = parent ne parents.head, stablePrefixReq = true)
val traitReq = (parent ne parents.head) && !(ptype.typeSymbol.isJavaAnnotation)
val pt = checkClassType(ptype, parent.srcPos, traitReq = traitReq, stablePrefixReq = true)
if (pt.derivesFrom(cls)) {
val addendum = parent match {
case Select(qual: Super, _) if Feature.migrateTo3 =>
Expand Down
1 change: 1 addition & 0 deletions tests/run/i12840.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NamedImpl
3 changes: 3 additions & 0 deletions tests/run/i12840/Named.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package example;

public @interface Named {}
11 changes: 11 additions & 0 deletions tests/run/i12840/NamedImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package example;

import java.lang.annotation.Annotation;

public class NamedImpl implements Named {
public Class<? extends Annotation> annotationType() {
return Named.class;
}

public String toString() { return "NamedImpl"; }
}
1 change: 1 addition & 0 deletions tests/run/i12840/Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@main def Test = println(new example.NamedImpl())