Skip to content

Commit b63e940

Browse files
authored
Merge pull request #153 from scala/backport-lts-3.3-22002
Backport "Make sure symbols in annotation trees are fresh before pickling" to 3.3 LTS
2 parents e6033df + 649ea26 commit b63e940

File tree

5 files changed

+72
-11
lines changed

5 files changed

+72
-11
lines changed

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

+32-11
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,7 +132,21 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
132132
case _ =>
133133
case _ =>
134134

135-
private def transformAnnot(annot: Tree)(using Context): Tree = {
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. */
149+
private def transformAnnotTree(annot: Tree)(using Context): Tree = {
136150
val saved = inJavaAnnot
137151
inJavaAnnot = annot.symbol.is(JavaDefined)
138152
if (inJavaAnnot) checkValidJavaAnnotation(annot)
@@ -141,7 +155,19 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
141155
}
142156

143157
private def transformAnnot(annot: Annotation)(using Context): Annotation =
144-
annot.derivedAnnotation(transformAnnot(annot.tree))
158+
val tree1 =
159+
annot match
160+
case _: BodyAnnotation => annot.tree
161+
case _ => copySymbols(annot.tree)
162+
annot.derivedAnnotation(transformAnnotTree(tree1))
163+
164+
/** Transforms all annotations in the given type. */
165+
private def transformAnnotsIn(using Context) =
166+
new TypeMap:
167+
def apply(tp: Type) = tp match
168+
case tp @ AnnotatedType(parent, annot) =>
169+
tp.derivedAnnotatedType(mapOver(parent), transformAnnot(annot))
170+
case _ => mapOver(tp)
145171

146172
private def processMemberDef(tree: Tree)(using Context): tree.type = {
147173
val sym = tree.symbol
@@ -438,7 +464,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
438464
Checking.checkRealizable(tree.tpt.tpe, tree.srcPos, "SAM type")
439465
super.transform(tree)
440466
case tree @ Annotated(annotated, annot) =>
441-
cpy.Annotated(tree)(transform(annotated), transformAnnot(annot))
467+
cpy.Annotated(tree)(transform(annotated), transformAnnotTree(annot))
442468
case tree: AppliedTypeTree =>
443469
if (tree.tpt.symbol == defn.andType)
444470
Checking.checkNonCyclicInherited(tree.tpe, tree.args.tpes, EmptyScope, tree.srcPos)
@@ -460,12 +486,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
460486
report.error(em"type ${alias.tpe} outside bounds $bounds", tree.srcPos)
461487
super.transform(tree)
462488
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-
)
489+
tree.withType(transformAnnotsIn(tree.tpe))
469490
case Typed(Ident(nme.WILDCARD), _) =>
470491
withMode(Mode.Pattern)(super.transform(tree))
471492
// The added mode signals that bounds in a pattern need not

tests/pos/annot-17939.scala

+8
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

+9
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

+8
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-body.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This test checks that symbols in `BodyAnnotation` are not copied in
2+
// `transformAnnot` during `PostTyper`.
3+
4+
package json
5+
6+
trait Reads[A] {
7+
def reads(a: Any): A
8+
}
9+
10+
object JsMacroImpl {
11+
inline def reads[A]: Reads[A] =
12+
new Reads[A] { self =>
13+
def reads(a: Any) = ???
14+
}
15+
}

0 commit comments

Comments
 (0)