Skip to content

Commit 639fa95

Browse files
committed
Fix #7843: Move LiftTry back to before CapturedVars
We need to lift tries before going into CapturedVars since a lifted try might cause a var to be captured. This means LazyVals now runs after LiftTry, so we have to account in LiftTry for the fact that the RHS of a lazy val needs its tries to be lifted.
1 parent d45fea0 commit 639fa95

File tree

4 files changed

+45
-8
lines changed

4 files changed

+45
-8
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class Compiler {
8383
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
8484
new Getters, // Replace non-private vals and vars with getter defs (fields are added later)
8585
new ElimByName, // Expand by-name parameter references
86+
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
8687
new CollectNullableFields, // Collect fields that can be nulled out after use in lazy initialization
8788
new ElimOuterSelect, // Expand outer selections
8889
new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition.
@@ -105,8 +106,7 @@ class Compiler {
105106
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
106107
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
107108
new Instrumentation, // Count closure allocations under -Yinstrument-closures
108-
new GetClass, // Rewrites getClass calls on primitive types.
109-
new LiftTry) :: // Put try expressions that might execute on non-empty stacks into their own methods their implementations
109+
new GetClass) :: // Rewrites getClass calls on primitive types.
110110
List(new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to
111111
new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
112112
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here

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

+5-6
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ class LiftTry extends MiniPhase with IdentityDenotTransformer { thisPhase =>
3232

3333
val phaseName: String = "liftTry"
3434

35-
// See tests/run/t2333.scala for an example where running after the group of LazyVals matters
36-
override def runsAfterGroupsOf: Set[String] = Set(LazyVals.name)
37-
3835
private var NeedLift: Store.Location[Boolean] = _
3936
private def needLift(implicit ctx: Context): Boolean = ctx.store(NeedLift)
4037

@@ -48,9 +45,11 @@ class LiftTry extends MiniPhase with IdentityDenotTransformer { thisPhase =>
4845
liftingCtx(true)
4946

5047
override def prepareForValDef(tree: ValDef)(implicit ctx: Context): Context =
51-
if (!tree.symbol.exists ||
52-
tree.symbol.isSelfSym ||
53-
tree.symbol.owner == ctx.owner.enclosingMethod) ctx
48+
if !tree.symbol.exists
49+
|| tree.symbol.isSelfSym
50+
|| tree.symbol.owner == ctx.owner.enclosingMethod
51+
&& !tree.symbol.is(Lazy) // lazy vals compile to defs, so there will be a new enclosing method
52+
then ctx
5453
else liftingCtx(true)
5554

5655
override def prepareForAssign(tree: Assign)(implicit ctx: Context): Context =

tests/run/i7843.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
done outside try: false
2+
setting done to true
3+
done in catch clause: true
4+
done

tests/run/i7843.scala

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import scala.concurrent._, duration._
2+
3+
object Test {
4+
implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global
5+
val thunkFuture: Future[() => String] = Future.successful(() => throw new RuntimeException("Whoops"))
6+
7+
final def shouldTerminate: Future[Unit] =
8+
thunkFuture.flatMap { thunk =>
9+
def fold() = {
10+
var done = false
11+
while (!done) {
12+
println(s"done outside try: $done")
13+
try {
14+
thunk()
15+
} catch {
16+
case _: Throwable =>
17+
println("setting done to true")
18+
done = true
19+
println(s"done in catch clause: $done")
20+
}
21+
}
22+
}
23+
24+
Future {
25+
fold()
26+
}
27+
}
28+
29+
30+
def main(args: Array[String]): Unit = {
31+
Await.result(shouldTerminate, Duration.Inf)
32+
println("done")
33+
}
34+
}

0 commit comments

Comments
 (0)