diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 334c47f3511d..2dd0bf48db8d 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -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 { diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 78dfd8e0a869..eb2318edcbcd 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -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 @@ -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. @@ -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 @@ -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) = { @@ -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) + ) } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 7c1c2494d323..d63c3068a552 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index df2c195b9e74..0f0835a3a6f1 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 8322c75e5e2d..3b9f49627013 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -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)) @@ -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] = { @@ -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) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index d2264e367f36..dd83ebed5886 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -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 => diff --git a/tests/run/i12840.check b/tests/run/i12840.check new file mode 100644 index 000000000000..a914077ee9e8 --- /dev/null +++ b/tests/run/i12840.check @@ -0,0 +1 @@ +NamedImpl diff --git a/tests/run/i12840/Named.java b/tests/run/i12840/Named.java new file mode 100644 index 000000000000..66750609afda --- /dev/null +++ b/tests/run/i12840/Named.java @@ -0,0 +1,3 @@ +package example; + +public @interface Named {} diff --git a/tests/run/i12840/NamedImpl.java b/tests/run/i12840/NamedImpl.java new file mode 100644 index 000000000000..178eca212b8b --- /dev/null +++ b/tests/run/i12840/NamedImpl.java @@ -0,0 +1,11 @@ +package example; + +import java.lang.annotation.Annotation; + +public class NamedImpl implements Named { + public Class annotationType() { + return Named.class; + } + + public String toString() { return "NamedImpl"; } +} diff --git a/tests/run/i12840/Test.scala b/tests/run/i12840/Test.scala new file mode 100644 index 000000000000..32106c9ca44d --- /dev/null +++ b/tests/run/i12840/Test.scala @@ -0,0 +1 @@ +@main def Test = println(new example.NamedImpl())