Skip to content

Commit eec8191

Browse files
committed
Merge pull request #758 from dotty-staging/fix-liftedTry-capturedVars-interaction
Fix lift try and captured vars interaction
2 parents dbbc5ca + 5a36e2f commit eec8191

File tree

10 files changed

+73
-42
lines changed

10 files changed

+73
-42
lines changed

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
641641
def traverse(tree: Tree)(implicit ctx: Context) = tree match {
642642
case tree: DefTree =>
643643
val sym = tree.symbol
644-
if (sym.denot(ctx.withPhase(trans)).owner == from) {
644+
val prevDenot = sym.denot(ctx.withPhase(trans))
645+
if (prevDenot.owner == from) {
645646
val d = sym.copySymDenotation(owner = to)
646647
d.installAfter(trans)
647648
d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d)
@@ -651,7 +652,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
651652
traverseChildren(tree)
652653
}
653654
}
654-
traverser.traverse(tree)
655+
traverser.traverse(tree)(ctx.withMode(Mode.FutureDefsOK))
655656
tree
656657
}
657658

src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ object Decorators {
135135
*/
136136
implicit class PhaseListDecorator(val names: List[String]) extends AnyVal {
137137
def containsPhase(phase: Phase): Boolean = phase match {
138-
case phase: TreeTransformer => phase.transformations.exists(trans => containsPhase(trans.phase))
138+
case phase: TreeTransformer => phase.miniPhases.exists(containsPhase)
139139
case _ =>
140140
names exists { name =>
141141
name == "all" || {

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,13 +592,16 @@ class Definitions {
592592
}
593593

594594
/** The classes for which a Ref type exists. */
595-
lazy val refClasses: collection.Set[Symbol] = ScalaNumericValueClasses + BooleanClass + ObjectClass
595+
lazy val refClassKeys: collection.Set[Symbol] = ScalaNumericValueClasses + BooleanClass + ObjectClass
596596

597597
lazy val refClass: Map[Symbol, Symbol] =
598-
refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap
598+
refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap
599599

600600
lazy val volatileRefClass: Map[Symbol, Symbol] =
601-
refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap
601+
refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap
602+
603+
lazy val boxedRefClasses: collection.Set[Symbol] =
604+
refClassKeys.flatMap(k => Set(refClass(k), volatileRefClass(k)))
602605

603606
def wrapArrayMethodName(elemtp: Type): TermName = {
604607
val cls = elemtp.classSymbol

src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ object Denotations {
680680
// println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}")
681681
// printPeriods(current)
682682
this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId)
683-
if (current.validFor.firstPhaseId == targetId)
683+
if (current.validFor.firstPhaseId >= targetId)
684684
replaceDenotation(current)
685685
else {
686686
// insert this denotation after current

src/dotty/tools/dotc/core/Phases.scala

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,9 @@ object Phases {
109109
assert(false, s"Only tree transforms can be squashed, ${phase.phaseName} can not be squashed")
110110
}
111111
}
112-
val transforms = filteredPhaseBlock.asInstanceOf[List[MiniPhase]].map(_.treeTransform)
113112
val block = new TreeTransformer {
114-
override def phaseName: String = transformations.map(_.phase.phaseName).mkString("TreeTransform:{", ", ", "}")
115-
116-
override def transformations: Array[TreeTransform] = transforms.toArray
113+
override def phaseName: String = miniPhases.map(_.phaseName).mkString("TreeTransform:{", ", ", "}")
114+
override def miniPhases: Array[MiniPhase] = filteredPhaseBlock.asInstanceOf[List[MiniPhase]].toArray
117115
}
118116
prevPhases ++= filteredPhaseBlock.map(_.getClazz)
119117
block
@@ -145,7 +143,7 @@ object Phases {
145143
val flatPhases = collection.mutable.ListBuffer[Phase]()
146144

147145
phasess.foreach(p => p match {
148-
case t: TreeTransformer => flatPhases ++= t.transformations.map(_.phase)
146+
case t: TreeTransformer => flatPhases ++= t.miniPhases
149147
case _ => flatPhases += p
150148
})
151149

@@ -173,11 +171,11 @@ object Phases {
173171
val phase = phasess(i)
174172
phase match {
175173
case t: TreeTransformer =>
176-
val transforms = t.transformations
177-
transforms.foreach{ x =>
178-
checkRequirements(x.phase)
179-
x.phase.init(this, nextPhaseId)}
180-
t.init(this, transforms.head.phase.id, transforms.last.phase.id)
174+
val miniPhases = t.miniPhases
175+
miniPhases.foreach{ phase =>
176+
checkRequirements(phase)
177+
phase.init(this, nextPhaseId)}
178+
t.init(this, miniPhases.head.id, miniPhases.last.id)
181179
case _ =>
182180
phase.init(this, nextPhaseId)
183181
checkRequirements(phase)

src/dotty/tools/dotc/transform/CapturedVars.scala

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,34 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisTransfo
9292
else id
9393
}
9494

95+
/** If assignment is to a boxed ref type, e.g.
96+
*
97+
* intRef.elem = expr
98+
*
99+
* rewrite using a temporary var to
100+
*
101+
* val ev$n = expr
102+
* intRef.elem = ev$n
103+
*
104+
* That way, we avoid the problem that `expr` might contain a `try` that would
105+
* run on a non-empty stack (which is illegal under JVM rules). Note that LiftTry
106+
* has already run before, so such `try`s would not be eliminated.
107+
*
108+
* Also: If the ref type lhs is followed by a cast (can be an artifact of nested translation),
109+
* drop the cast.
110+
*/
95111
override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = {
96-
val lhs1 = tree.lhs match {
97-
case TypeApply(Select(qual @ Select(qual2, nme.elem), nme.asInstanceOf_), _) =>
98-
assert(captured(qual2.symbol))
99-
qual
100-
case _ => tree.lhs
112+
def recur(lhs: Tree): Tree = lhs match {
113+
case TypeApply(Select(qual, nme.asInstanceOf_), _) =>
114+
val Select(_, nme.elem) = qual
115+
recur(qual)
116+
case Select(_, nme.elem) if defn.boxedRefClasses.contains(lhs.symbol.maybeOwner) =>
117+
val tempDef = transformFollowing(SyntheticValDef(ctx.freshName("ev$").toTermName, tree.rhs))
118+
transformFollowing(Block(tempDef :: Nil, cpy.Assign(tree)(lhs, ref(tempDef.symbol))))
119+
case _ =>
120+
tree
101121
}
102-
cpy.Assign(tree)(lhs1, tree.rhs)
122+
recur(tree.lhs)
103123
}
104124
}
105125
}

src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class TreeChecker extends Phase with SymTransformer {
103103

104104
private def previousPhases(phases: List[Phase])(implicit ctx: Context): List[Phase] = phases match {
105105
case (phase: TreeTransformer) :: phases1 =>
106-
val subPhases = phase.transformations.map(_.phase)
106+
val subPhases = phase.miniPhases
107107
val previousSubPhases = previousPhases(subPhases.toList)
108108
if (previousSubPhases.length == subPhases.length) previousSubPhases ::: previousPhases(phases1)
109109
else previousSubPhases

src/dotty/tools/dotc/transform/TreeTransform.scala

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ object TreeTransforms {
6363

6464
def treeTransformPhase: Phase = phase.next
6565

66-
/** id of this treeTransform in group */
67-
var idx: Int = _
68-
6966
def prepareForIdent(tree: Ident)(implicit ctx: Context) = this
7067
def prepareForSelect(tree: Select)(implicit ctx: Context) = this
7168
def prepareForThis(tree: This)(implicit ctx: Context) = this
@@ -137,10 +134,10 @@ object TreeTransforms {
137134
def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, 0)
138135

139136
/** Transform subtree using all transforms following the current one in this group */
140-
def transformFollowingDeep(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, idx + 1)
137+
def transformFollowingDeep(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, phase.idx + 1)
141138

142139
/** Transform single node using all transforms following the current one in this group */
143-
def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, idx + 1)
140+
def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, phase.idx + 1)
144141

145142
def atGroupEnd[T](action : Context => T)(implicit ctx: Context, info: TransformerInfo) = {
146143
val last = info.transformers(info.transformers.length - 1)
@@ -152,14 +149,17 @@ object TreeTransforms {
152149
trait MiniPhase extends Phase { thisPhase =>
153150
def treeTransform: TreeTransform
154151

152+
/** id of this mini phase in group */
153+
var idx: Int = _
154+
155155
/** List of names of phases that should have finished their processing of all compilation units
156156
* before this phase starts
157157
*/
158158
def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set.empty
159159

160160
protected def mkTreeTransformer = new TreeTransformer {
161161
override def phaseName: String = thisPhase.phaseName
162-
override def transformations = Array(treeTransform)
162+
override def miniPhases = Array(thisPhase)
163163
}
164164

165165
override def run(implicit ctx: Context): Unit = {
@@ -197,7 +197,6 @@ object TreeTransforms {
197197

198198
@sharable val NoTransform = new TreeTransform {
199199
def phase = unsupported("phase")
200-
idx = -1
201200
}
202201

203202
type Mutator[T] = (TreeTransform, T, Context) => TreeTransform
@@ -474,7 +473,7 @@ object TreeTransforms {
474473
/** A group of tree transforms that are applied in sequence during the same phase */
475474
abstract class TreeTransformer extends Phase {
476475

477-
def transformations: Array[TreeTransform]
476+
def miniPhases: Array[MiniPhase]
478477

479478
override def run(implicit ctx: Context): Unit = {
480479
val curTree = ctx.compilationUnit.tpdTree
@@ -549,10 +548,10 @@ object TreeTransforms {
549548
val prepForStats: Mutator[List[Tree]] = (trans, trees, ctx) => trans.prepareForStats(trees)(ctx)
550549
val prepForUnit: Mutator[Tree] = (trans, tree, ctx) => trans.prepareForUnit(tree)(ctx)
551550

552-
val initialTransformationsCache = transformations.zipWithIndex.map {
553-
case (transform, id) =>
554-
transform.idx = id
555-
transform
551+
val initialTransformationsCache = miniPhases.zipWithIndex.map {
552+
case (miniPhase, id) =>
553+
miniPhase.idx = id
554+
miniPhase.treeTransform
556555
}
557556

558557
val initialInfoCache = new TransformerInfo(initialTransformationsCache, new NXTransformations(initialTransformationsCache), this)

test/test/transform/TreeTransformerTest.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class TreeTransformerTest extends DottyTest {
2020
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
2121
}
2222
val transformer = new TreeTransformer {
23-
override def transformations = Array(new EmptyTransform)
23+
override def miniPhases = Array(new EmptyTransform)
2424

2525
override def phaseName: String = "test"
2626
}
@@ -42,7 +42,7 @@ class TreeTransformerTest extends DottyTest {
4242
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
4343
}
4444
val transformer = new TreeTransformer {
45-
override def transformations = Array(new ConstantTransform)
45+
override def miniPhases = Array(new ConstantTransform)
4646

4747
override def phaseName: String = "test"
4848
}
@@ -72,7 +72,7 @@ class TreeTransformerTest extends DottyTest {
7272
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
7373
}
7474
val transformer = new TreeTransformer {
75-
override def transformations = Array(new Transformation)
75+
override def miniPhases = Array(new Transformation)
7676

7777
override def phaseName: String = "test"
7878

@@ -119,7 +119,7 @@ class TreeTransformerTest extends DottyTest {
119119
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
120120
}
121121
val transformer = new TreeTransformer {
122-
override def transformations = Array(new Transformation1, new Transformation2)
122+
override def miniPhases = Array(new Transformation1, new Transformation2)
123123

124124
override def phaseName: String = "test"
125125
}
@@ -187,7 +187,7 @@ class TreeTransformerTest extends DottyTest {
187187
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
188188
}
189189
val transformer = new TreeTransformer {
190-
override def transformations = Array(new Transformation1, new Transformation2)
190+
override def miniPhases = Array(new Transformation1, new Transformation2)
191191

192192
override def phaseName: String = "test"
193193
}

tests/run/liftedTry.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,20 @@ object Test {
1212

1313
foo(try 3 catch handle)
1414

15-
def main(args: Array[String]) = {
15+
def main(args: Array[String]): Unit = {
1616
assert(x == 1)
1717
assert(foo(2) == 2)
1818
assert(foo(try raise(3) catch handle) == 3)
19+
Tr.foo
1920
}
2021
}
2122

23+
24+
object Tr {
25+
def fun(a: Int => Unit) = a(2)
26+
def foo: Int = {
27+
var s = 1
28+
s = try {fun(s = _); 3} catch{ case ex: Throwable => val x = 4; s = x; 5 }
29+
s
30+
}
31+
}

0 commit comments

Comments
 (0)