Skip to content

Commit 653698e

Browse files
authored
Merge pull request #1763 from dotty-staging/fix/annotations
Fix emission of annotations
2 parents 50bcb1a + 9411539 commit 653698e

File tree

9 files changed

+81
-22
lines changed

9 files changed

+81
-22
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,18 +206,15 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
206206
implicit val ConstantClassTag: ClassTag[Constant] = ClassTag[Constant](classOf[Constant])
207207
implicit val ClosureTag: ClassTag[Closure] = ClassTag[Closure](classOf[Closure])
208208

209-
/* dont emit any annotations for now*/
210-
def isRuntimeVisible(annot: Annotation): Boolean = {
211-
annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr) match {
212-
case Some(retentionAnnot) =>
213-
retentionAnnot.tree.find(_.symbol == AnnotationRetentionRuntimeAttr).isDefined
214-
case _ =>
215-
// SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the
216-
// annotation is emitted with visibility `RUNTIME`
217-
// dotty bug: #389
218-
true
209+
def isRuntimeVisible(annot: Annotation): Boolean =
210+
if (toDenot(annot.atp.typeSymbol).hasAnnotation(AnnotationRetentionAttr))
211+
retentionPolicyOf(annot) == AnnotationRetentionRuntimeAttr
212+
else {
213+
// SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the
214+
// annotation is emitted with visibility `RUNTIME`
215+
// dotty bug: #389
216+
true
219217
}
220-
}
221218

222219
def shouldEmitAnnotation(annot: Annotation): Boolean = {
223220
annot.symbol.isJavaDefined &&
@@ -227,7 +224,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
227224

228225
private def retentionPolicyOf(annot: Annotation): Symbol =
229226
annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr).
230-
flatMap(_.argument(0).map(_.symbol)).getOrElse(AnnotationRetentionClassAttr)
227+
flatMap(_.argumentConstant(0).map(_.symbolValue)).getOrElse(AnnotationRetentionClassAttr)
231228

232229
private def emitArgument(av: AnnotationVisitor,
233230
name: String,
@@ -708,7 +705,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
708705
}
709706
else Nil
710707

711-
def annotations: List[Annotation] = Nil
708+
def annotations: List[Annotation] = toDenot(sym).annotations
712709
def companionModuleMembers: List[Symbol] = {
713710
// phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes,
714711
// not local classes of the companion module (E in the exmaple) that were lifted by lambdalift.

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package sbt
33

44
import ast.{Trees, tpd}
55
import core._, core.Decorators._
6-
import Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._
6+
import Annotations._, Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._
77
import Names._, NameOps._, StdNames._
88
import typer.Inliner
99

@@ -333,7 +333,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
333333
// TODO: Never dealias. We currently have to dealias because
334334
// sbt main class discovery relies on the signature of the main
335335
// method being fully dealiased. See https://github.com/sbt/zinc/issues/102
336-
val tp2 = if (!tp.isHK) tp.dealias else tp
336+
val tp2 = if (!tp.isHK) tp.dealiasKeepAnnots else tp
337337
tp2 match {
338338
case NoPrefix | NoType =>
339339
Constants.emptyType
@@ -411,9 +411,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
411411
case ConstantType(constant) =>
412412
new api.Constant(apiType(constant.tpe), constant.stringValue)
413413
case AnnotatedType(tpe, annot) =>
414-
// TODO: Annotation support
415-
ctx.debuglog(i"sbt-api: skipped annotation in $tp2")
416-
apiType(tpe)
414+
new api.Annotated(apiType(tpe), Array(apiAnnotation(annot)))
417415
case tp: ThisType =>
418416
apiThis(tp.cls)
419417
case tp: ParamType =>
@@ -498,7 +496,6 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
498496
sym.is(Implicit), sym.is(Lazy), sym.is(Macro), sym.is(SuperAccessor))
499497
}
500498

501-
// TODO: Support other annotations
502499
def apiAnnotations(s: Symbol): List[api.Annotation] = {
503500
val annots = new mutable.ListBuffer[api.Annotation]
504501

@@ -513,6 +510,27 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
513510
annots += marker(Inliner.bodyToInline(s).show(printTypesCtx).toString)
514511
}
515512

513+
// In the Scala2 ExtractAPI phase we only extract annotations that extend
514+
// StaticAnnotation, but in Dotty we currently pickle all annotations so we
515+
// extract everything (except inline body annotations which are handled
516+
// above).
517+
s.annotations.filter(_.symbol != defn.BodyAnnot) foreach { annot =>
518+
annots += apiAnnotation(annot)
519+
}
520+
516521
annots.toList
517522
}
523+
524+
def apiAnnotation(annot: Annotation): api.Annotation = {
525+
// FIXME: To faithfully extract an API we should extract the annotation tree,
526+
// sbt instead wants us to extract the annotation type and its arguments,
527+
// to do this properly we would need a way to hash trees and types in dotty itself,
528+
// instead we pretty-print the annotation tree.
529+
// However, we still need to extract the annotation type in the way sbt expect
530+
// because sbt uses this information to find tests to run (for example
531+
// junit tests are annotated @org.junit.Test).
532+
new api.Annotation(
533+
apiType(annot.tree.tpe), // Used by sbt to find tests to run
534+
Array(new api.AnnotationArgument("FULLTREE", annot.tree.show.toString)))
535+
}
518536
}

compiler/src/dotty/tools/dotc/transform/TreeTransform.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package dotc
33
package transform
44

55
import dotty.tools.dotc.ast.tpd
6-
import dotty.tools.dotc.core.Annotations.ConcreteAnnotation
76
import dotty.tools.dotc.core.Contexts.Context
87
import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, DenotTransformer}
98
import dotty.tools.dotc.core.Denotations.SingleDenotation
@@ -181,10 +180,15 @@ object TreeTransforms {
181180
abstract override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation =
182181
super.transform(ref) match {
183182
case ref1: SymDenotation if ref1.symbol.isDefinedInCurrentRun =>
184-
val annotTrees = ref1.annotations.map(_.tree)
183+
val annots = ref1.annotations
184+
val annotTrees = annots.map(_.tree)
185185
val annotTrees1 = annotTrees.mapConserve(annotationTransformer.macroTransform)
186186
if (annotTrees eq annotTrees1) ref1
187-
else ref1.copySymDenotation(annotations = annotTrees1.map(new ConcreteAnnotation(_)))
187+
else {
188+
val derivedAnnots = (annots, annotTrees1).zipped.map((annot, annotTree1) =>
189+
annot.derivedAnnotation(annotTree1))
190+
ref1.copySymDenotation(annotations = derivedAnnots)
191+
}
188192
case ref1 =>
189193
ref1
190194
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A {
2+
def three: Int = 3 // Ah!
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := "0.1-SNAPSHOT",
10+
scalaOrganization := "ch.epfl.lamp",
11+
scalacOptions += "-language:Scala2",
12+
scalaBinaryVersion := "2.11",
13+
autoScalaLibrary := false,
14+
libraryDependencies ++= Seq("org.scala-lang" % "scala-library" % "2.11.5"),
15+
scalaCompilerBridgeSource := ("ch.epfl.lamp" % "dotty-sbt-bridge" % "0.1.1-SNAPSHOT" % "component").sources()
16+
)
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A {
2+
def three: Int = 4 // Hmm
3+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import org.junit.Test
2+
import org.junit.Assert.assertEquals
3+
4+
class TestA {
5+
@Test def testThree = {
6+
assertEquals(A.three, 3)
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
> compile
2+
# Intentionally failing test
3+
-> test
4+
# Fix the bug!
5+
$ copy-file changes/A2.scala src/main/scala/A.scala
6+
> compile
7+
# The test should pass now
8+
> test

0 commit comments

Comments
 (0)