Skip to content

Commit

Permalink
Merge pull request #9302 from retronym/topic/extra-lazy-annotation-info
Browse files Browse the repository at this point in the history
  • Loading branch information
lrytz committed Nov 9, 2020
2 parents 0a1942c + 2408bad commit a7d63b9
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 6 deletions.
23 changes: 18 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1902,11 +1902,24 @@ trait Namers extends MethodSynthesis {
annotations filterNot (_ eq null) map { ann =>
val ctx = typer.context
// need to be lazy, #1782. enteringTyper to allow inferView in annotation args, scala/bug#5892.
AnnotationInfo lazily {
enteringTyper {
val annotSig = newTyper(ctx.makeNonSilent(ann)).typedAnnotation(ann, Some(annotee))
if (pred(annotSig)) annotSig else UnmappableAnnotation // UnmappableAnnotation will be dropped in typedValDef and typedDefDef
}
def computeInfo: AnnotationInfo = enteringTyper {
val annotSig = newTyper(ctx.makeNonSilent(ann)).typedAnnotation(ann, Some(annotee))
if (pred(annotSig)) annotSig else UnmappableAnnotation // UnmappableAnnotation will be dropped in typedValDef and typedDefDef
}
ann match {
case treeInfo.Applied(Select(New(tpt), _), _, _) =>
// We can defer typechecking the arguments of annotations. This is important to avoid cycles in
// checking `hasAnnotation(UncheckedStable)` during typechecking.
def computeSymbol = enteringTyper {
val tptCopy = tpt.duplicate
val silentTyper = newTyper(ctx.makeSilent(newtree = tptCopy))
// Discard errors here, we'll report them in `computeInfo`.
val tpt1 = silentTyper.typedTypeConstructor(tptCopy)
tpt1.tpe.finalResultType.typeSymbol
}
AnnotationInfo.lazily(computeSymbol, computeInfo)
case _ =>
AnnotationInfo.lazily(computeInfo)
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/reflect/scala/reflect/internal/AnnotationInfos.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
def lazily(lazyInfo: => AnnotationInfo) =
new LazyAnnotationInfo(lazyInfo)

def lazily(lazySymbol: => Symbol, lazyInfo: => AnnotationInfo) =
new ExtraLazyAnnotationInfo(lazySymbol, lazyInfo)

def apply(atp: Type, args: List[Tree], assocs: List[(Name, ClassfileAnnotArg)]): AnnotationInfo =
new CompleteAnnotationInfo(atp, args, assocs)

Expand Down Expand Up @@ -154,7 +157,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
/** Symbol annotations parsed in `Namer` (typeCompleter of
* definitions) have to be lazy (#1782)
*/
final class LazyAnnotationInfo(lazyInfo: => AnnotationInfo) extends AnnotationInfo {
class LazyAnnotationInfo(lazyInfo: => AnnotationInfo) extends AnnotationInfo {
private[this] var forced = false
private lazy val forcedInfo = try lazyInfo finally forced = true

Expand All @@ -172,6 +175,11 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
override def completeInfo(): Unit = forcedInfo
}

final class ExtraLazyAnnotationInfo(sym: => Symbol, lazyInfo: => AnnotationInfo) extends LazyAnnotationInfo(lazyInfo) {
private[this] lazy val typeSymbol = sym
override def symbol: Symbol = typeSymbol
}

/** Typed information about an annotation. It can be attached to either
* a symbol or an annotated type.
*
Expand Down
7 changes: 7 additions & 0 deletions test/files/pos/annotation-cycle.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import scala.annotation.StaticAnnotation

class anno(attr: Boolean) extends StaticAnnotation

object Test {
@anno(attr = true) def attr: Int = 1
}
60 changes: 60 additions & 0 deletions test/files/pos/t11870.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class Annot(arg: Any) extends scala.annotation.StaticAnnotation

abstract class Demo1 {
@Annot(x)
def x: String = "foo"
}

abstract class Demo2 {
@Annot(y)
def x: String = "foo"

@Annot(x)
def y: String = "bar"
}

class Annot1(arg: Any) extends scala.annotation.StaticAnnotation
class Annot2(arg: Any) extends scala.annotation.StaticAnnotation

abstract class Demo3 {
@Annot1(y)
def x: String = "foo"

@Annot2(x)
def y: String = "bar"
}

// test annotations without argument
import scala.annotation.unchecked

class C {
class D
}

class Test {
locally {
val c = new C
import c._
new D
}

locally {
import unchecked.uncheckedStable
@uncheckedStable def c = new C
import c._
new D
}

locally {
@unchecked.uncheckedStable def c = new C
import c._
new D
}

locally {
import unchecked.{uncheckedStable => uc}
@uc def c = new C
import c._
new D
}
}
8 changes: 8 additions & 0 deletions test/files/pos/unchecked-stable-cyclic-error.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.annotation.StaticAnnotation

class anno(a: Any) extends StaticAnnotation

object Test {
def foo = attr
@anno(foo) def attr: Int = foo
}

0 comments on commit a7d63b9

Please sign in to comment.