Skip to content

Commit 8206e26

Browse files
committed
Fix typing idempotency bug with Annotated trees
typedAnnotated transforms an Annotated tree into a Typed tree. The original field of the result is set to the Annotated tree. The bug was that typedAnnotated was using the untyped Annotated tree as original, but also set its type. When re-typing later on the same Annotated tree, the typer would consider it as alreadyTyped. This is incorrect, the typer needs to convert Annotated trees to Typed. Also, the Annotated tree only had its type field set, but its children were still untyped. This crashed the compiler lateron, non-typed trees would get out of the typing phase.
1 parent f51ed74 commit 8206e26

File tree

3 files changed

+73
-9
lines changed

3 files changed

+73
-9
lines changed

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4153,15 +4153,11 @@ trait Typers extends Modes with Adaptations with Tags {
41534153
def resultingTypeTree(tpe: Type) = {
41544154
// we need symbol-ful originals for reification
41554155
// hence we go the extra mile to hand-craft tis guy
4156-
val original =
4157-
if (arg1.isType)
4158-
arg1 match {
4159-
case tt @ TypeTree() => Annotated(ann, tt.original)
4160-
// this clause is needed to correctly compile stuff like "new C @D" or "@(inline @getter)"
4161-
case _ => Annotated(ann, arg1)
4162-
}
4163-
else
4164-
tree
4156+
val original = arg1 match {
4157+
case tt @ TypeTree() => Annotated(ann, tt.original)
4158+
// this clause is needed to correctly compile stuff like "new C @D" or "@(inline @getter)"
4159+
case _ => Annotated(ann, arg1)
4160+
}
41654161
original setType ann.tpe
41664162
TypeTree(tpe) setOriginal original setPos tree.pos.focus
41674163
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
typing List(1, 2).map(((x) => {
2+
val another = scala.Tuple2(t.nt, t.tr): @testAnn match {
3+
case scala.Tuple2(_, _) => 1
4+
};
5+
x
6+
}))
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import scala.tools.partest._
2+
import scala.tools.nsc._
3+
4+
object Test extends DirectTest {
5+
6+
override def extraSettings: String = "-usejavacp"
7+
8+
def code = """
9+
class testAnn extends annotation.Annotation
10+
11+
object t {
12+
def nt = 1
13+
def tr = "a"
14+
}
15+
16+
class Test {
17+
List(1,2).map(x => {
18+
val another = ((t.nt, t.tr): @testAnn) match { case (_, _) => 1 }
19+
x
20+
})
21+
}
22+
""".trim
23+
24+
25+
// point of this test: type-check the "Annotated" tree twice. first time the analyzer plugin types it,
26+
// second time the typer.
27+
28+
// bug was that typedAnnotated assigned a type to the Annotated tree. The second type check would consider
29+
// the tree as alreadyTyped, which is not cool, the Annotated needs to be transformed into a Typed tree.
30+
31+
def show() {
32+
val global = newCompiler()
33+
import global._
34+
import analyzer._
35+
import collection.{mutable => m}
36+
37+
object analyzerPlugin extends AnalyzerPlugin {
38+
val templates: m.Map[Symbol, (Template, Typer)] = m.Map()
39+
override def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = {
40+
defTree match {
41+
case impl: Template =>
42+
templates += typer.context.owner -> (impl, typer)
43+
44+
case dd: DefDef if dd.symbol.isPrimaryConstructor && templates.contains(dd.symbol.owner) =>
45+
val (impl, templTyper) = templates(dd.symbol.owner)
46+
for (stat <- impl.body.filterNot(_.isDef)) {
47+
println("typing "+ stat)
48+
val statsOwner = impl.symbol orElse templTyper.context.owner.newLocalDummy(impl.pos)
49+
val tpr = analyzer.newTyper(templTyper.context.make(stat, statsOwner))
50+
tpr.typed(stat)
51+
}
52+
53+
case _ =>
54+
}
55+
tpe
56+
}
57+
}
58+
59+
addAnalyzerPlugin(analyzerPlugin)
60+
compileString(global)(code)
61+
}
62+
}

0 commit comments

Comments
 (0)