Skip to content

Commit 0898c31

Browse files
mboveltgodzik
authored andcommitted
Make sure symbols in annotation trees are fresh before pickling
1 parent e6033df commit 0898c31

File tree

5 files changed

+71
-9
lines changed

5 files changed

+71
-9
lines changed

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dotty.tools
22
package dotc
33
package transform
44

5-
import dotty.tools.dotc.ast.{Trees, tpd, untpd, desugar}
5+
import dotty.tools.dotc.ast.{Trees, tpd, untpd, desugar, TreeTypeMap}
66
import scala.collection.mutable
77
import core.*
88
import dotty.tools.dotc.typer.Checking
@@ -16,7 +16,7 @@ import Symbols.*, NameOps.*
1616
import ContextFunctionResults.annotateContextResults
1717
import config.Printers.typr
1818
import config.Feature
19-
import util.SrcPos
19+
import util.{SrcPos, Stats}
2020
import reporting.*
2121
import NameKinds.WildcardParamName
2222

@@ -132,17 +132,39 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
132132
case _ =>
133133
case _ =>
134134

135+
/** Returns a copy of the given tree with all symbols fresh.
136+
*
137+
* Used to guarantee that no symbols are shared between trees in different
138+
* annotations.
139+
*/
140+
private def copySymbols(tree: Tree)(using Context) =
141+
Stats.trackTime("Annotations copySymbols"):
142+
val ttm =
143+
new TreeTypeMap:
144+
override def withMappedSyms(syms: List[Symbol]) =
145+
withMappedSyms(syms, mapSymbols(syms, this, true))
146+
ttm(tree)
147+
148+
/** Transforms the given annotation tree. */
135149
private def transformAnnot(annot: Tree)(using Context): Tree = {
136150
val saved = inJavaAnnot
137151
inJavaAnnot = annot.symbol.is(JavaDefined)
138152
if (inJavaAnnot) checkValidJavaAnnotation(annot)
139-
try transform(annot)
153+
try transform(copySymbols(annot))
140154
finally inJavaAnnot = saved
141155
}
142156

143157
private def transformAnnot(annot: Annotation)(using Context): Annotation =
144158
annot.derivedAnnotation(transformAnnot(annot.tree))
145159

160+
/** Transforms all annotations in the given type. */
161+
private def transformAnnots(using Context) =
162+
new TypeMap:
163+
def apply(tp: Type) = tp match
164+
case tp @ AnnotatedType(parent, annot) =>
165+
tp.derivedAnnotatedType(mapOver(parent), transformAnnot(annot))
166+
case _ => mapOver(tp)
167+
146168
private def processMemberDef(tree: Tree)(using Context): tree.type = {
147169
val sym = tree.symbol
148170
Checking.checkValidOperator(sym)
@@ -460,12 +482,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
460482
report.error(em"type ${alias.tpe} outside bounds $bounds", tree.srcPos)
461483
super.transform(tree)
462484
case tree: TypeTree =>
463-
tree.withType(
464-
tree.tpe match {
465-
case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot))
466-
case tpe => tpe
467-
}
468-
)
485+
tree.withType(transformAnnotsIn(tpe))
469486
case Typed(Ident(nme.WILDCARD), _) =>
470487
withMode(Mode.Pattern)(super.transform(tree))
471488
// The added mode signals that bounds in a pattern need not

tests/pos/annot-17939.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.annotation.Annotation
2+
class myRefined[T](f: T => Boolean) extends Annotation
3+
4+
class Box[T](val x: T)
5+
class Box2(val x: Int)
6+
7+
class A(a: String @myRefined((x: Int) => Box(3).x == 3)) // crash
8+
class A2(a2: String @myRefined((x: Int) => Box2(3).x == 3)) // works

tests/pos/annot-19846.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dependentAnnotation
2+
3+
class lambdaAnnot(g: () => Int) extends annotation.StaticAnnotation
4+
5+
def f(x: Int): Int @lambdaAnnot(() => x + 1) = x
6+
7+
@main def main =
8+
val y: Int = 5
9+
val z = f(y)

tests/pos/annot-19846b.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class qualified[T](predicate: T => Boolean) extends annotation.StaticAnnotation
2+
3+
class EqualPair(val x: Int, val y: Int @qualified[Int](it => it == x))
4+
5+
@main def main =
6+
val p = EqualPair(42, 42)
7+
val y = p.y
8+
println(42)

tests/pos/annot-i20272a.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import language.experimental.captureChecking
2+
3+
trait Iterable[T] { self: Iterable[T]^ =>
4+
def map[U](f: T => U): Iterable[U]^{this, f}
5+
}
6+
7+
object Test {
8+
def assertEquals[A, B](a: A, b: B): Boolean = ???
9+
10+
def foo[T](level: Int, lines: Iterable[T]) =
11+
lines.map(x => x)
12+
13+
def bar(messages: Iterable[String]) =
14+
foo(1, messages)
15+
16+
val it: Iterable[String] = ???
17+
val msgs = bar(it)
18+
19+
assertEquals(msgs, msgs)
20+
}

0 commit comments

Comments
 (0)