Skip to content

Commit de10278

Browse files
committed
Merge remote-tracking branch 'origin/2.10.x' into merge/2.10.x-to-master-20140721
Conflicts: src/main/scala/scala/async/internal/AsyncTransform.scala src/main/scala/scala/async/internal/Lifter.scala
2 parents a08a822 + 154a2d7 commit de10278

File tree

7 files changed

+211
-34
lines changed

7 files changed

+211
-34
lines changed

src/main/scala/scala/async/internal/AsyncTransform.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,10 @@ trait AsyncTransform {
157157
case ValDef(_, _, _, rhs) if liftedSyms(tree.symbol) =>
158158
api.atOwner(api.currentOwner) {
159159
val fieldSym = tree.symbol
160-
val set = Assign(gen.mkAttributedStableRef(thisType(fieldSym.owner.asClass), fieldSym), api.recur(rhs))
161-
set.changeOwner(tree.symbol, api.currentOwner)
162-
api.typecheck(atPos(tree.pos)(set))
160+
val lhs = atPos(tree.pos) {
161+
gen.mkAttributedStableRef(thisType(fieldSym.owner.asClass), fieldSym)
162+
}
163+
treeCopy.Assign(tree, lhs, api.recur(rhs)).setType(definitions.UnitTpe).changeOwner(fieldSym, api.currentOwner)
163164
}
164165
case _: DefTree if liftedSyms(tree.symbol) =>
165166
EmptyTree

src/main/scala/scala/async/internal/Lifter.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ trait Lifter {
115115
sym.setFlag(MUTABLE | STABLE | PRIVATE | LOCAL)
116116
sym.setName(name.fresh(sym.name.toTermName))
117117
sym.setInfo(deconst(sym.info))
118-
val zeroRhs = atPos(t.pos)(gen.mkZero(vd.symbol.info))
119-
treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(tpe(sym)).setPos(t.pos), zeroRhs)
118+
treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(tpe(sym)).setPos(t.pos), EmptyTree)
120119
case dd@DefDef(_, _, tparams, vparamss, tpt, rhs) =>
121120
sym.setName(this.name.fresh(sym.name.toTermName))
122121
sym.setFlag(PRIVATE | LOCAL)

src/main/scala/scala/async/internal/LiveVariables.scala

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,22 @@ trait LiveVariables {
126126

127127
/** Tests if `state1` is a predecessor of `state2`.
128128
*/
129-
def isPred(state1: Int, state2: Int, seen: Set[Int] = Set()): Boolean =
130-
if (seen(state1)) false // breaks cycles in the CFG
131-
else cfg get state1 match {
132-
case Some(nextStates) =>
133-
nextStates.contains(state2) || nextStates.exists(isPred(_, state2, seen + state1))
134-
case None =>
135-
false
136-
}
129+
def isPred(state1: Int, state2: Int): Boolean = {
130+
val seen = scala.collection.mutable.HashSet[Int]()
131+
132+
def isPred0(state1: Int, state2: Int): Boolean =
133+
if(state1 == state2) false
134+
else if (seen(state1)) false // breaks cycles in the CFG
135+
else cfg get state1 match {
136+
case Some(nextStates) =>
137+
seen += state1
138+
nextStates.contains(state2) || nextStates.exists(isPred0(_, state2))
139+
case None =>
140+
false
141+
}
142+
143+
isPred0(state1, state2)
144+
}
137145

138146
val finalState = asyncStates.find(as => !asyncStates.exists(other => isPred(as.state, other.state))).get
139147

@@ -162,12 +170,10 @@ trait LiveVariables {
162170
LVexit = LVexit + (finalState.state -> noNull)
163171

164172
var currStates = List(finalState) // start at final state
165-
var pred = List[AsyncState]() // current predecessor states
166-
var hasChanged = true // if something has changed we need to continue iterating
167173
var captured: Set[Symbol] = Set()
168174

169-
while (hasChanged) {
170-
hasChanged = false
175+
while (!currStates.isEmpty) {
176+
var entryChanged: List[AsyncState] = Nil
171177

172178
for (cs <- currStates) {
173179
val LVentryOld = LVentry(cs.state)
@@ -176,44 +182,53 @@ trait LiveVariables {
176182
val LVentryNew = LVexit(cs.state) ++ referenced.used
177183
if (!LVentryNew.sameElements(LVentryOld)) {
178184
LVentry = LVentry + (cs.state -> LVentryNew)
179-
hasChanged = true
185+
entryChanged ::= cs
180186
}
181187
}
182188

183-
pred = currStates.flatMap(cs => asyncStates.filter(_.nextStates.contains(cs.state)))
189+
val pred = entryChanged.flatMap(cs => asyncStates.filter(_.nextStates.contains(cs.state)))
190+
var exitChanged: List[AsyncState] = Nil
184191

185192
for (p <- pred) {
186193
val LVexitOld = LVexit(p.state)
187194
val LVexitNew = p.nextStates.flatMap(succ => LVentry(succ)).toSet
188195
if (!LVexitNew.sameElements(LVexitOld)) {
189196
LVexit = LVexit + (p.state -> LVexitNew)
190-
hasChanged = true
197+
exitChanged ::= p
191198
}
192199
}
193200

194-
currStates = pred
201+
currStates = exitChanged
195202
}
196203

197204
for (as <- asyncStates) {
198205
AsyncUtils.vprintln(s"LVentry at state #${as.state}: ${LVentry(as.state).mkString(", ")}")
199206
AsyncUtils.vprintln(s"LVexit at state #${as.state}: ${LVexit(as.state).mkString(", ")}")
200207
}
201208

202-
def lastUsagesOf(field: Tree, at: AsyncState, avoid: Set[AsyncState]): Set[Int] =
203-
if (avoid(at)) Set()
204-
else if (captured(field.symbol)) {
205-
Set()
206-
}
207-
else LVentry get at.state match {
208-
case Some(fields) if fields.exists(_ == field.symbol) =>
209-
Set(at.state)
210-
case _ =>
211-
val preds = asyncStates.filter(_.nextStates.contains(at.state)).toSet
212-
preds.flatMap(p => lastUsagesOf(field, p, avoid + at))
209+
def lastUsagesOf(field: Tree, at: AsyncState): Set[Int] = {
210+
val avoid = scala.collection.mutable.HashSet[AsyncState]()
211+
212+
def lastUsagesOf0(field: Tree, at: AsyncState): Set[Int] = {
213+
if (avoid(at)) Set()
214+
else if (captured(field.symbol)) {
215+
Set()
216+
}
217+
else LVentry get at.state match {
218+
case Some(fields) if fields.exists(_ == field.symbol) =>
219+
Set(at.state)
220+
case _ =>
221+
avoid += at
222+
val preds = asyncStates.filter(_.nextStates.contains(at.state)).toSet
223+
preds.flatMap(p => lastUsagesOf0(field, p))
224+
}
213225
}
214226

227+
lastUsagesOf0(field, at)
228+
}
229+
215230
val lastUsages: Map[Tree, Set[Int]] =
216-
liftables.map(fld => (fld -> lastUsagesOf(fld, finalState, Set()))).toMap
231+
liftables.map(fld => (fld -> lastUsagesOf(fld, finalState))).toMap
217232

218233
for ((fld, lastStates) <- lastUsages)
219234
AsyncUtils.vprintln(s"field ${fld.symbol.name} is last used in states ${lastStates.mkString(", ")}")

src/main/scala/scala/async/internal/TransformUtils.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,17 @@ private[async] trait TransformUtils {
164164
case ld: LabelDef => ld.symbol
165165
}.toSet
166166
t.exists {
167-
case rt: RefTree => !(labelDefs contains rt.symbol)
167+
case rt: RefTree => rt.symbol != null && isLabel(rt.symbol) && !(labelDefs contains rt.symbol)
168168
case _ => false
169169
}
170170
}
171171

172+
private def isLabel(sym: Symbol): Boolean = {
173+
val LABEL = 1L << 17 // not in the public reflection API.
174+
(internal.flags(sym).asInstanceOf[Long] & LABEL) != 0L
175+
}
176+
177+
172178
/** Map a list of arguments to:
173179
* - A list of argument Trees
174180
* - A list of auxillary results.

src/test/scala/scala/async/run/ifelse1/IfElse1.scala

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,75 @@ class TestIfElse1Class {
8787
}
8888
z
8989
}
90+
91+
def pred: Future[Boolean] = async(true)
92+
93+
def m5: Future[Boolean] = async {
94+
if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(await(pred))
95+
await(pred)
96+
else
97+
false)
98+
await(pred)
99+
else
100+
false)
101+
await(pred)
102+
else
103+
false)
104+
await(pred)
105+
else
106+
false)
107+
await(pred)
108+
else
109+
false)
110+
await(pred)
111+
else
112+
false)
113+
await(pred)
114+
else
115+
false)
116+
await(pred)
117+
else
118+
false)
119+
await(pred)
120+
else
121+
false)
122+
await(pred)
123+
else
124+
false)
125+
await(pred)
126+
else
127+
false)
128+
await(pred)
129+
else
130+
false)
131+
await(pred)
132+
else
133+
false)
134+
await(pred)
135+
else
136+
false)
137+
await(pred)
138+
else
139+
false)
140+
await(pred)
141+
else
142+
false)
143+
await(pred)
144+
else
145+
false)
146+
await(pred)
147+
else
148+
false)
149+
await(pred)
150+
else
151+
false)
152+
await(pred)
153+
else
154+
false)
155+
await(pred)
156+
else
157+
false
158+
}
90159
}
91160

92161
class IfElse1Spec {
@@ -124,4 +193,12 @@ class IfElse1Spec {
124193
val res = Await.result(fut, 2 seconds)
125194
res mustBe (14)
126195
}
196+
197+
@Test
198+
def `await in deeply-nested if-else conditions`() {
199+
val o = new TestIfElse1Class
200+
val fut = o.m5
201+
val res = Await.result(fut, 2 seconds)
202+
res mustBe true
203+
}
127204
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (C) 2012-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package scala.async
6+
package run
7+
package ifelse4
8+
9+
import language.{reflectiveCalls, postfixOps}
10+
import scala.concurrent.{Future, ExecutionContext, future, Await}
11+
import scala.concurrent.duration._
12+
import scala.async.Async.{async, await}
13+
import org.junit.Test
14+
15+
16+
class TestIfElse4Class {
17+
18+
import ExecutionContext.Implicits.global
19+
20+
class F[A]
21+
class S[A](val id: String)
22+
trait P
23+
24+
case class K(f: F[_])
25+
26+
def result[A](f: F[A]) = async {
27+
new S[A with P]("foo")
28+
}
29+
30+
def run(k: K) = async {
31+
val res = await(result(k.f))
32+
// these triggered a crash with mismatched existential skolems
33+
// found : S#10272[_$1#10308 with String#137] where type _$1#10308
34+
// required: S#10272[_$1#10311 with String#137] forSome { type _$1#10311 }
35+
36+
// This variation of the crash could be avoided by fixing the over-eager
37+
// generation of states in `If` nodes, which was caused by a bug in label
38+
// detection code.
39+
if(true) {
40+
identity(res)
41+
}
42+
43+
// This variation remained after the aforementioned fix, however.
44+
// It was fixed by manually typing the `Assign(liftedField, rhs)` AST,
45+
// which is how we avoid these problems through the rest of the ANF transform.
46+
if(true) {
47+
identity(res)
48+
await(result(k.f))
49+
}
50+
res
51+
}
52+
}
53+
54+
class IfElse4Spec {
55+
56+
@Test
57+
def `await result with complex type containing skolem`() {
58+
val o = new TestIfElse4Class
59+
val fut = o.run(new o.K(null))
60+
val res = Await.result(fut, 2 seconds)
61+
res.id mustBe ("foo")
62+
}
63+
}

src/test/scala/scala/async/run/toughtype/ToughType.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,22 @@ class ToughTypeSpec {
211211
}(SomeExecutionContext)
212212
}
213213
}
214+
215+
}
216+
217+
@Test def ticket66Nothing() {
218+
import scala.concurrent.Future
219+
import scala.concurrent.ExecutionContext.Implicits.global
220+
val e = new Exception()
221+
val f: Future[Nothing] = Future.failed(e)
222+
val f1 = async {
223+
await(f)
224+
}
225+
try {
226+
Await.result(f1, 5.seconds)
227+
} catch {
228+
case `e` =>
229+
}
214230
}
215231
}
216232

0 commit comments

Comments
 (0)