Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic {
*/
private def shouldEmitAnnotation(annot: AnnotationInfo) = {
annot.symbol.initialize.isJavaDefined &&
annot.matches(ClassfileAnnotationClass) &&
retentionPolicyOf(annot) != AnnotationRetentionPolicySourceValue &&
annot.args.isEmpty
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,9 @@ abstract class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {

val classParents = {
val parents = classSym.info.parents
// scala/bug#9393: the classfile / java source parsers add Annotation and ClassfileAnnotation to the
// scala/bug#9393: the classfile / java source parsers add Annotation and StaticAnnotation to the
// parents of a java annotations. undo this for the backend (where we need classfile-level information).
if (classSym.hasJavaAnnotationFlag) parents.filterNot(c => c.typeSymbol == ClassfileAnnotationClass || c.typeSymbol == AnnotationClass)
if (classSym.hasJavaAnnotationFlag) parents.filterNot(c => c.typeSymbol == StaticAnnotationClass || c.typeSymbol == AnnotationClass)
else parents
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/javac/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
def annotationParents = List(
gen.scalaAnnotationDot(tpnme.Annotation),
Select(javaLangDot(nme.annotation), tpnme.Annotation),
gen.scalaAnnotationDot(tpnme.ClassfileAnnotation)
gen.scalaAnnotationDot(tpnme.StaticAnnotation)
)
def annotationDecl(mods: Modifiers): List[Tree] = {
accept(AT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ abstract class ClassfileParser {
else pool.getSuperClass(u2).tpe_*
val ifaceCount = u2
var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(u2).tpe_*
if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe
if (jflags.isAnnotation) ifaces ::= StaticAnnotationClass.tpe
superType :: ifaces
}

Expand Down
10 changes: 8 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ trait ContextErrors {
setError(templ)
}

def AuxConstrInConstantAnnotation(constr: Tree, clazz: Symbol) =
issueNormalTypeError(constr, s"$clazz cannot have auxiliary constructors because it extends ConstantAnnotation")

def ConstantAnnotationNeedsSingleArgumentList(constr: Tree, clazz: Symbol) =
issueNormalTypeError(constr, s"$clazz needs to have exactly one argument list because it extends ConstantAnnotation")

// additional parentTypes errors
def ConstrArgsInParentWhichIsTraitError(arg: Tree, parent: Symbol) =
issueNormalTypeError(arg, parent + " is a trait; does not take constructor arguments")
Expand Down Expand Up @@ -490,7 +496,7 @@ trait ContextErrors {
NormalTypeError(tree, "expected annotation of type " + expected + ", found " + found)

def MultipleArgumentListForAnnotationError(tree: Tree) =
NormalTypeError(tree, "multiple argument lists on classfile annotation")
NormalTypeError(tree, "multiple argument lists on Java annotation or subclass of ConstantAnnotation")

def UnknownAnnotationNameError(tree: Tree, name: Name) =
NormalTypeError(tree, "unknown annotation argument name: " + name)
Expand All @@ -499,7 +505,7 @@ trait ContextErrors {
NormalTypeError(tree, "duplicate value for annotation argument " + name)

def ClassfileAnnotationsAsNamedArgsError(tree: Tree) =
NormalTypeError(tree, "classfile annotation arguments have to be supplied as named arguments")
NormalTypeError(tree, "arguments to Java annotations or subclasses of ConstantAnnotation have to be supplied as named arguments")

def AnnotationMissingArgError(tree: Tree, annType: Type, sym: Symbol) =
NormalTypeError(tree, "annotation " + annType.typeSymbol.fullName + " is missing argument " + sym.name)
Expand Down
52 changes: 22 additions & 30 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1820,18 +1820,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.typeSymbol == AnyClass)
checkEphemeral(clazz, impl2.body)

if ((clazz isNonBottomSubClass ClassfileAnnotationClass) && (clazz != ClassfileAnnotationClass)) {
if (!clazz.owner.isPackageClass)
context.error(clazz.pos, "inner classes cannot be classfile annotations")
// Ignore @SerialVersionUID, because it is special-cased and handled completely differently.
// It only extends ClassfileAnnotationClass instead of StaticAnnotation to get the enforcement
// of constant argument values "for free". Related to scala/bug#7041.
else if (clazz != SerialVersionUIDAttr) restrictionWarning(cdef.pos, unit,
"""|subclassing Classfile does not
|make your annotation visible at runtime. If that is what
|you want, you must write the annotation class in Java.""".stripMargin)
}

warnTypeParameterShadow(tparams1, clazz)

if (!isPastTyper) {
Expand Down Expand Up @@ -1910,8 +1898,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (txt eq context) namer enterSym tree
else newNamer(txt) enterSym tree

/** <!-- 2 --> Check that inner classes do not inherit from Annotation
*/
def typedTemplate(templ0: Template, parents1: List[Tree]): Template = {
val templ = templ0
// please FIXME: uncommenting this line breaks everything
Expand Down Expand Up @@ -1972,9 +1958,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (clazz.isTrait && hasSuperArgs(parents1.head))
ConstrArgsInParentOfTraitError(parents1.head, clazz)

if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.isTopLevel)
context.error(clazz.pos, "inner classes cannot be classfile annotations")

if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])

Expand All @@ -1996,6 +1979,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (clazz.info.firstParent.typeSymbol == AnyValClass)
validateDerivedValueClass(clazz, body3)

if (!clazz.isTrait && clazz.isNonBottomSubClass(ConstantAnnotationClass)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the restriction on classfile annotations that they could not be inner classes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

val (pConstrOpt, auxConstrs) = body3.filter(s => s.isInstanceOf[DefDef] && s.symbol.isConstructor).splitAt(1)
for (p <- pConstrOpt) p.symbol.paramss match {
case List(ps) =>
case _ => ConstantAnnotationNeedsSingleArgumentList(p, clazz)
}
for (c <- auxConstrs) AuxConstrInConstantAnnotation(c, clazz)
}


if (clazz.isTrait) {
for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) {
context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.")
Expand Down Expand Up @@ -3767,7 +3760,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper

if (!annType.typeSymbol.isSubClass(pt.typeSymbol))
reportAnnotationError(AnnotationTypeMismatchError(tpt, annType, annType))
else if (!annType.typeSymbol.isSubClass(ClassfileAnnotationClass))
else if (!annType.typeSymbol.isJavaDefined)
reportAnnotationError(NestedAnnotationError(ann, annType))

if (annInfo.atp.isErroneous) { hasError = true; None }
Expand Down Expand Up @@ -3819,17 +3812,17 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
)
val treeInfo.Applied(typedFun @ Select(New(annTpt), _), _, _) = typedFunPart
val annType = annTpt.tpe
val isJava = annType != null && annType.typeSymbol.isJavaDefined

finish(
if (typedFun.isErroneous || annType == null)
ErroneousAnnotation
else if (annType.typeSymbol isNonBottomSubClass ClassfileAnnotationClass) {
// annotation to be saved as java classfile annotation
val isJava = typedFun.symbol.owner.isJavaDefined
else if (isJava || annType.typeSymbol.isNonBottomSubClass(ConstantAnnotationClass)) {
// Arguments of Java annotations and ConstantAnnotations are checked to be constants and
// stored in the `assocs` field of the resulting AnnotationInfo
if (argss.length > 1) {
reportAnnotationError(MultipleArgumentListForAnnotationError(ann))
}
else {
} else {
val annScopeJava =
if (isJava) annType.decls.filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined)
else EmptyScope // annScopeJava is only used if isJava
Expand All @@ -3839,12 +3832,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else typedFun.tpe.params.iterator)

def hasValue = names exists (_.name == nme.value)
val args = argss match {
case (arg :: Nil) :: Nil if !isNamedArg(arg) && hasValue => gen.mkNamedArg(nme.value, arg) :: Nil
case args :: Nil => args
val namedArgs = argss match {
case List(List(arg)) if !isNamedArg(arg) && hasValue => gen.mkNamedArg(nme.value, arg) :: Nil
case List(args) => args
}

val nvPairs = args map {
val nvPairs = namedArgs map {
case arg @ NamedArg(Ident(name), rhs) =>
val sym = if (isJava) annScopeJava.lookup(name)
else findSymbol(typedFun.tpe.params)(_.name == name)
Expand Down Expand Up @@ -3872,10 +3865,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}

if (hasError) ErroneousAnnotation
else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(Apply(typedFun, args).setPos(ann.pos))
else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(Apply(typedFun, namedArgs).setPos(ann.pos))
}
}
else {
} else {
val typedAnn: Tree = {
// local dummy fixes scala/bug#5544
val localTyper = newTyper(context.make(ann, context.owner.newLocalDummy(ann.pos)))
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/SerialVersionUID.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ package scala
* @see [[http://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html `java.io.Serializable`]]
* @see [[Serializable]]
*/
class SerialVersionUID(value: Long) extends scala.annotation.ClassfileAnnotation
class SerialVersionUID(value: Long) extends scala.annotation.ConstantAnnotation
17 changes: 11 additions & 6 deletions src/library/scala/annotation/Annotation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@

package scala.annotation

/** A base class for annotations. Annotations extending this class directly
* are not preserved for the Scala type checker and are also not stored as
* Java annotations in classfiles. To enable either or both of these, one
* needs to inherit from [[scala.annotation.StaticAnnotation]] or/and
* [[scala.annotation.ClassfileAnnotation]].
/**
* A base class for annotations.
*
* Annotations extending this class directly are not preserved in the classfile. To enable storing
* annotations in the classfile's Scala signature and make it available to Scala reflection and
* other tools, the annotation needs to inherit from [[scala.annotation.StaticAnnotation]].
*
* Annotation classes defined in Scala are not stored in classfiles in a Java-compatible manner
* and therefore not visible in Java reflection. In order to achieve this, the annotation has to
* be written in Java.
*
* @author Martin Odersky
* @version 1.1, 2/02/2007
* @since 2.4
*/
abstract class Annotation {}
abstract class Annotation
3 changes: 2 additions & 1 deletion src/library/scala/annotation/ClassfileAnnotation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ package scala.annotation
* @version 1.1, 2/02/2007
* @since 2.4
*/
trait ClassfileAnnotation extends StaticAnnotation
@deprecated("Annotation classes need to be written in Java in order to be stored in classfiles in a Java-compatible manner", "2.13.0")
trait ClassfileAnnotation extends ConstantAnnotation
40 changes: 40 additions & 0 deletions src/library/scala/annotation/ConstantAnnotation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2002-2017, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
*/

package scala.annotation

/**
* Annotation classes extending this trait only accept constant values as arguments.
*
* Note that this trait extends [[StaticAnnotation]], so constant annotations are persisted in the
* classfile.
*
* The implementation requires arguments of constant annotations to be passed as named arguments,
* except if there is a single argument, which then defines the annotation's parameter named
* `value`.
*
* Constant annotations may use default arguments. Note that the internal representation of an
* annotation usage (which is visible for compiler plugins, for example) only contains arguments
* that are explicitly provided.
*
* Constant annotations are not allowed to define auxiliary constructors, and the primary
* constructor is required to have a single parameter list.
*
* Example:
*
* {{{
* class Ann(value: Int, x: Int = 0) extends scala.annotation.ConstantAnnotation
* class Test {
* def someInt = 0
* @Ann(value = 0, x = 1) def g = 0
* @Ann(0) def f = 0 // Internal representation contains `@Ann(value = 0)`
* @Ann(someInt) // error: argument needs to be a compile-time constant
* }
* }}}
*/
trait ConstantAnnotation extends StaticAnnotation
9 changes: 7 additions & 2 deletions src/library/scala/annotation/StaticAnnotation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@

package scala.annotation

/** A base class for static annotations. These are available
* to the Scala type checker, even across different compilation units.
/**
* A base class for static annotations. These are available to the Scala type checker or Scala
* reflection, even across different compilation units.
*
* Annotation classes defined in Scala are not stored in classfiles in a Java-compatible manner
* and therefore not visible in Java reflection. In order to achieve this, the annotation has to
* be written in Java.
*
* @author Martin Odersky
* @version 1.1, 2/02/2007
Expand Down
5 changes: 2 additions & 3 deletions src/reflect/scala/reflect/api/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ import scala.collection.immutable.ListMap
*
* <ul>
* <li>''Java annotations'': annotations on definitions produced by the Java compiler, i.e., subtypes of [[java.lang.annotation.Annotation]]
* attached to program definitions. When read by Scala reflection, the [[scala.annotation.ClassfileAnnotation]] trait
* is automatically added as a superclass to every Java annotation type.</li>
* attached to program definitions.</li>
* <li>''Scala annotations'': annotations on definitions or types produced by the Scala compiler.</li>
* </ul>
*
* When a Scala annotation that inherits from [[scala.annotation.StaticAnnotation]] or [[scala.annotation.ClassfileAnnotation]] is compiled,
* When a Scala annotation that inherits from [[scala.annotation.StaticAnnotation]] is compiled,
* it is stored as special attributes in the corresponding classfile, and not as a Java annotation. Note that subclassing
* just [[scala.annotation.Annotation]] is not enough to have the corresponding metadata persisted for runtime reflection.
*
Expand Down
13 changes: 8 additions & 5 deletions src/reflect/scala/reflect/internal/AnnotationInfos.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
}
}

/** Arguments to classfile annotations (which are written to
* bytecode as java annotations) are either:
*
/**
* Arguments to constant annotations (Annotations defined in Java or extending
* ConstantAnnotation). Arguments are either:
* - constants
* - arrays of constants
* - or nested classfile annotations
* - or nested classfile annotations (only for Java annotation)
*
* TODO: rename to `ConstantAnnotationArg`
*/
sealed abstract class ClassfileAnnotArg extends Product with JavaArgumentApi
implicit val JavaArgumentTag = ClassTag[ClassfileAnnotArg](classOf[ClassfileAnnotArg])
Expand Down Expand Up @@ -349,14 +351,15 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => NestedAnnotArg(treeToAnnotation(arg))
case _ => throw new Exception(s"unexpected java argument shape $arg: literals, arrays and nested annotations are supported")
}
// TODO: Java annotations with a single `value` parameter can be created without a named argument.
def encodeJavaArgs(args: List[Tree]): List[(Name, ClassfileAnnotArg)] = args match {
case NamedArg(Ident(name), arg) :: rest => (name, encodeJavaArg(arg)) :: encodeJavaArgs(rest)
case arg :: rest => throw new Exception(s"unexpected java argument shape $arg: only NamedArg trees are supported")
case Nil => Nil
}
val atp = tpt.tpe
if (atp != null && (atp.typeSymbol isNonBottomSubClass StaticAnnotationClass)) AnnotationInfo(atp, args, Nil)
else if (atp != null && (atp.typeSymbol isNonBottomSubClass ClassfileAnnotationClass)) AnnotationInfo(atp, Nil, encodeJavaArgs(args))
else if (atp != null && (atp.typeSymbol.isJavaDefined || atp.typeSymbol.isNonBottomSubClass(ConstantAnnotationClass))) AnnotationInfo(atp, Nil, encodeJavaArgs(args))
else throw new Exception(s"unexpected annotation type $atp: only subclasses of StaticAnnotation and ClassfileAnnotation are supported")
case _ =>
throw new Exception("""unexpected tree shape: only q"new $annType(..$args)" is supported""")
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,7 @@ trait Definitions extends api.StandardDefinitions {

// Annotation base classes
lazy val AnnotationClass = requiredClass[scala.annotation.Annotation]
lazy val ClassfileAnnotationClass = requiredClass[scala.annotation.ClassfileAnnotation]
lazy val ConstantAnnotationClass = getRequiredClass("scala.annotation.ConstantAnnotation")
lazy val StaticAnnotationClass = requiredClass[scala.annotation.StaticAnnotation]

// Java retention annotations
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -260,14 +260,14 @@ trait StdNames {
final val api: NameType = "api"
final val Annotation: NameType = "Annotation"
final val CaseDef: NameType = "CaseDef"
final val ClassfileAnnotation: NameType = "ClassfileAnnotation"
final val ClassManifest: NameType = "ClassManifest"
final val Enum: NameType = "Enum"
final val Group: NameType = "Group"
final val implicitNotFound: NameType = "implicitNotFound"
final val Liftable: NameType = "Liftable"
final val Unliftable: NameType = "Unliftable"
final val Name: NameType = "Name"
final val StaticAnnotation: NameType = "StaticAnnotation"
final val Tree: NameType = "Tree"
final val Text: NameType = "Text"
final val TermName: NameType = "TermName"
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/runtime/JavaMirrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
val jsuperclazz = jclazz.getGenericSuperclass
val ifaces = jclazz.getGenericInterfaces.toList map typeToScala
val isAnnotation = JavaAccFlags(jclazz).isAnnotation
if (isAnnotation) AnnotationClass.tpe :: ClassfileAnnotationClass.tpe :: ifaces
if (isAnnotation) AnnotationClass.tpe :: StaticAnnotationClass.tpe :: ifaces
else if (jclazz.isInterface) ObjectTpe :: ifaces // interfaces have Object as superclass in the classfile (see jvm spec), but getGenericSuperclass seems to return null
else (if (jsuperclazz == null) AnyTpe else typeToScala(jsuperclazz)) :: ifaces
} finally {
Expand Down
Loading