Skip to content

Commit 08e8561

Browse files
committed
Improve mapping and pickling of annotated types
`Annotation.mapWith` maps an `Annotation` with a type map `tm`. As an optimization, this function first checks if `tm` would result in any change (by traversing the annotation’s argument trees with a `TreeAccumulator`) before applying `tm` to the whole annotation tree. This optimization had two problems: 1. it didn’t include type parameters, and 2. it used `frozen_=:=` to compare types, which didn’t work as expected with `NoType`. This commit fixes these issues. Additionally, positions of trees that appear only inside `AnnotatedType` were not pickled. This commit also fixes this.
1 parent 6ee0967 commit 08e8561

13 files changed

+123
-7
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

+9-1
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,17 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
141141
loop(tree, Nil)
142142

143143
/** All term arguments of an application in a single flattened list */
144+
def allTermArguments(tree: Tree): List[Tree] = unsplice(tree) match {
145+
case Apply(fn, args) => allArguments(fn) ::: args
146+
case TypeApply(fn, args) => allArguments(fn)
147+
case Block(_, expr) => allArguments(expr)
148+
case _ => Nil
149+
}
150+
151+
/** All type and term arguments of an application in a single flattened list */
144152
def allArguments(tree: Tree): List[Tree] = unsplice(tree) match {
145153
case Apply(fn, args) => allArguments(fn) ::: args
146-
case TypeApply(fn, _) => allArguments(fn)
154+
case TypeApply(fn, args) => allArguments(fn) ::: args
147155
case Block(_, expr) => allArguments(expr)
148156
case _ => Nil
149157
}

compiler/src/dotty/tools/dotc/core/Annotations.scala

+7-4
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ object Annotations {
3030
def derivedAnnotation(tree: Tree)(using Context): Annotation =
3131
if (tree eq this.tree) this else Annotation(tree)
3232

33-
/** All arguments to this annotation in a single flat list */
34-
def arguments(using Context): List[Tree] = tpd.allArguments(tree)
33+
/** All term arguments of this annotation in a single flat list */
34+
def arguments(using Context): List[Tree] = tpd.allTermArguments(tree)
3535

3636
def argument(i: Int)(using Context): Option[Tree] = {
3737
val args = arguments
@@ -54,15 +54,18 @@ object Annotations {
5454
* type, since ranges cannot be types of trees.
5555
*/
5656
def mapWith(tm: TypeMap)(using Context) =
57-
val args = arguments
57+
val args = tpd.allArguments(tree)
5858
if args.isEmpty then this
5959
else
60+
// Checks if `tm` would result in any change by applying it to types
61+
// inside the annotations' arguments and checking if the resulting types
62+
// are different.
6063
val findDiff = new TreeAccumulator[Type]:
6164
def apply(x: Type, tree: Tree)(using Context): Type =
6265
if tm.isRange(x) then x
6366
else
6467
val tp1 = tm(tree.tpe)
65-
foldOver(if tp1 frozen_=:= tree.tpe then x else tp1, tree)
68+
foldOver(if !tp1.exists || (tp1 frozen_=:= tree.tpe) then x else tp1, tree)
6669
val diff = findDiff(NoType, args)
6770
if tm.isRange(diff) then EmptyAnnotation
6871
else if diff.exists then derivedAnnotation(tm.mapOver(tree))

compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ object PositionPickler:
3333
pickler: TastyPickler,
3434
addrOfTree: TreeToAddr,
3535
treeAnnots: untpd.MemberDef => List[tpd.Tree],
36+
typeAnnots: List[tpd.Tree],
3637
relativePathReference: String,
3738
source: SourceFile,
3839
roots: List[Tree],
@@ -136,6 +137,9 @@ object PositionPickler:
136137
}
137138
for (root <- roots)
138139
traverse(root, NoSource)
140+
141+
for annotTree <- typeAnnots do
142+
traverse(annotTree, NoSource)
139143
end picklePositions
140144
end PositionPickler
141145

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

+7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
4141
*/
4242
private val annotTrees = util.EqHashMap[untpd.MemberDef, mutable.ListBuffer[Tree]]()
4343

44+
/** A set of annotation trees appearing in annotated types.
45+
*/
46+
private val annotatedTypeTrees = mutable.ListBuffer[Tree]()
47+
4448
/** A map from member definitions to their doc comments, so that later
4549
* parallel comment pickling does not need to access symbols of trees (which
4650
* would involve accessing symbols of named types and possibly changing phases
@@ -57,6 +61,8 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
5761
val ts = annotTrees.lookup(tree)
5862
if ts == null then Nil else ts.toList
5963

64+
def typeAnnots: List[Tree] = annotatedTypeTrees.toList
65+
6066
def docString(tree: untpd.MemberDef): Option[Comment] =
6167
Option(docStrings.lookup(tree))
6268

@@ -278,6 +284,7 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
278284
case tpe: AnnotatedType =>
279285
writeByte(ANNOTATEDtype)
280286
withLength { pickleType(tpe.parent, richTypes); pickleTree(tpe.annot.tree) }
287+
annotatedTypeTrees += tpe.annot.tree
281288
case tpe: AndType =>
282289
writeByte(ANDtype)
283290
withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) }

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ object PickledQuotes {
224224
if tree.span.exists then
225225
val positionWarnings = new mutable.ListBuffer[Message]()
226226
val reference = ctx.settings.sourceroot.value
227-
PositionPickler.picklePositions(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, reference,
227+
PositionPickler.picklePositions(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, treePkl.typeAnnots, reference,
228228
ctx.compilationUnit.source, tree :: Nil, positionWarnings)
229229
positionWarnings.foreach(report.warning(_))
230230

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ class Pickler extends Phase {
322322
if tree.span.exists then
323323
val reference = ctx.settings.sourceroot.value
324324
PositionPickler.picklePositions(
325-
pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, reference,
325+
pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, treePkl.typeAnnots, reference,
326326
unit.source, tree :: Nil, positionWarnings,
327327
scratch.positionBuffer, scratch.pickledIndices)
328328

tests/pos/annot-17939b.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.annotation.Annotation
2+
class myRefined(f: ? => Boolean) extends Annotation
3+
4+
def test(axes: Int) = true
5+
6+
trait Tensor:
7+
def mean(axes: Int): Int @myRefined(_ => test(axes))
8+
9+
class TensorImpl() extends Tensor:
10+
def mean(axes: Int) = ???

tests/pos/annot-18064.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//> using options "-Xprint:typer"
2+
3+
class myAnnot[T]() extends annotation.Annotation
4+
5+
trait Tensor[T]:
6+
def add: Tensor[T] @myAnnot[T]()
7+
8+
class TensorImpl[A]() extends Tensor[A]:
9+
def add /* : Tensor[A] @myAnnot[A] */ = this

tests/pos/annot-5789.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Annot[T] extends scala.annotation.Annotation
2+
3+
class D[T](val f: Int@Annot[T])
4+
5+
object A{
6+
def main(a:Array[String]) = {
7+
val c = new D[Int](1)
8+
c.f
9+
}
10+
}

tests/printing/annot-18064.check

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[[syntax trees at end of typer]] // tests/printing/annot-18064.scala
2+
package <empty> {
3+
class myAnnot[T >: Nothing <: Any]() extends annotation.Annotation() {
4+
T
5+
}
6+
trait Tensor[T >: Nothing <: Any]() extends Object {
7+
T
8+
def add: Tensor[Tensor.this.T] @myAnnot[T]
9+
}
10+
class TensorImpl[A >: Nothing <: Any]() extends Object(), Tensor[
11+
TensorImpl.this.A] {
12+
A
13+
def add: Tensor[A] @myAnnot[A] = this
14+
}
15+
}
16+

tests/printing/annot-18064.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//> using options "-Xprint:typer"
2+
3+
class myAnnot[T]() extends annotation.Annotation
4+
5+
trait Tensor[T]:
6+
def add: Tensor[T] @myAnnot[T]()
7+
8+
class TensorImpl[A]() extends Tensor[A]:
9+
def add /* : Tensor[A] @myAnnot[A] */ = this

tests/printing/annot-19846b.check

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[[syntax trees at end of typer]] // tests/printing/annot-19846b.scala
2+
package <empty> {
3+
class lambdaAnnot(g: () => Int) extends scala.annotation.Annotation(),
4+
annotation.StaticAnnotation {
5+
private[this] val g: () => Int
6+
}
7+
final lazy module val Test: Test = new Test()
8+
final module class Test() extends Object() { this: Test.type =>
9+
val y: Int = ???
10+
val z:
11+
Int @lambdaAnnot(
12+
{
13+
def $anonfun(): Int = Test.y
14+
closure($anonfun)
15+
}
16+
)
17+
= f(Test.y)
18+
}
19+
final lazy module val annot-19846b$package: annot-19846b$package =
20+
new annot-19846b$package()
21+
final module class annot-19846b$package() extends Object() {
22+
this: annot-19846b$package.type =>
23+
def f(x: Int):
24+
Int @lambdaAnnot(
25+
{
26+
def $anonfun(): Int = x
27+
closure($anonfun)
28+
}
29+
)
30+
= x
31+
}
32+
}
33+

tests/printing/annot-19846b.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class lambdaAnnot(g: () => Int) extends annotation.StaticAnnotation
2+
3+
def f(x: Int): Int @lambdaAnnot(() => x) = x
4+
5+
object Test:
6+
val y: Int = ???
7+
val z /* : Int @lambdaAnnot(() => y) */ = f(y)

0 commit comments

Comments
 (0)