Skip to content

Commit 2e72811

Browse files
committed
Merge pull request #359 from dotty-staging/fix/erasure-lub-alternative
Fix/erasure lub alternative
2 parents c12f213 + 37c2dcc commit 2e72811

File tree

5 files changed

+109
-49
lines changed

5 files changed

+109
-49
lines changed

src/dotty/tools/backend/jvm/LabelDefs.scala

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import java.lang.AssertionError
3232
import dotty.tools.dotc.util.Positions.Position
3333
import Decorators._
3434
import tpd._
35+
import Flags._
3536
import StdNames.nme
3637

3738
/**
@@ -80,54 +81,68 @@ class LabelDefs extends MiniPhaseTransform {
8081

8182
val queue = new ArrayBuffer[Tree]()
8283

83-
84-
85-
override def transformBlock(tree: tpd.Block)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
86-
collectLabelDefs.clear
87-
val newStats = collectLabelDefs.transformStats(tree.stats)
88-
val newExpr = collectLabelDefs.transform(tree.expr)
89-
val labelCalls = collectLabelDefs.labelCalls
90-
val entryPoints = collectLabelDefs.parentLabelCalls
91-
val labelDefs = collectLabelDefs.labelDefs
92-
93-
// make sure that for every label there's a single location it should return and single entry point
94-
// if theres already a location that it returns to that's a failure
95-
val disallowed = new mutable.HashMap[Symbol, Tree]()
96-
queue.sizeHint(labelCalls.size + entryPoints.size)
97-
def moveLabels(entryPoint: Tree): List[Tree] = {
98-
if((entryPoint.symbol is Flags.Label) && labelDefs.contains(entryPoint.symbol)) {
99-
val visitedNow = new mutable.HashMap[Symbol, Tree]()
100-
val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first
101-
queue.clear()
102-
103-
var visited = 0
104-
queue += entryPoint
105-
while (visited < queue.size) {
106-
val owningLabelDefSym = queue(visited).symbol
107-
val owningLabelDef = labelDefs(owningLabelDefSym)
108-
for (call <- labelCalls(owningLabelDefSym))
109-
if (disallowed.contains(call.symbol)) {
110-
val oldCall = disallowed(call.symbol)
111-
ctx.error(s"Multiple return locations for Label $oldCall and $call", call.symbol.pos)
112-
} else {
113-
if ((!visitedNow.contains(call.symbol)) && labelDefs.contains(call.symbol)) {
114-
val df = labelDefs(call.symbol)
115-
visitedNow.put(call.symbol, labelDefs(call.symbol))
116-
queue += call
84+
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
85+
if (tree.symbol is Flags.Label) tree
86+
else {
87+
collectLabelDefs.clear
88+
val newRhs = collectLabelDefs.transform(tree.rhs)
89+
val labelCalls = collectLabelDefs.labelCalls
90+
var entryPoints = collectLabelDefs.parentLabelCalls
91+
var labelDefs = collectLabelDefs.labelDefs
92+
93+
// make sure that for every label there's a single location it should return and single entry point
94+
// if theres already a location that it returns to that's a failure
95+
val disallowed = new mutable.HashMap[Symbol, Tree]()
96+
queue.sizeHint(labelCalls.size + entryPoints.size)
97+
def moveLabels(entryPoint: Tree): List[Tree] = {
98+
if ((entryPoint.symbol is Flags.Label) && labelDefs.contains(entryPoint.symbol)) {
99+
val visitedNow = new mutable.HashMap[Symbol, Tree]()
100+
val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first
101+
queue.clear()
102+
103+
var visited = 0
104+
queue += entryPoint
105+
while (visited < queue.size) {
106+
val owningLabelDefSym = queue(visited).symbol
107+
val owningLabelDef = labelDefs(owningLabelDefSym)
108+
for (call <- labelCalls(owningLabelDefSym))
109+
if (disallowed.contains(call.symbol)) {
110+
val oldCall = disallowed(call.symbol)
111+
ctx.error(s"Multiple return locations for Label $oldCall and $call", call.symbol.pos)
112+
} else {
113+
if ((!visitedNow.contains(call.symbol)) && labelDefs.contains(call.symbol)) {
114+
visitedNow.put(call.symbol, labelDefs(call.symbol))
115+
queue += call
116+
}
117117
}
118+
if (!treesToAppend.contains(owningLabelDef)) {
119+
treesToAppend += owningLabelDef
118120
}
119-
if(!treesToAppend.contains(owningLabelDef))
120-
treesToAppend += owningLabelDef
121-
visited += 1
121+
visited += 1
122+
}
123+
disallowed ++= visitedNow
124+
125+
treesToAppend.toList
126+
} else Nil
127+
}
128+
129+
val putLabelDefsNearCallees = new TreeMap() {
130+
131+
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
132+
tree match {
133+
case t: Apply if (entryPoints.contains(t)) =>
134+
entryPoints = entryPoints - t
135+
Block(moveLabels(t), t)
136+
case _ => if (entryPoints.nonEmpty && labelDefs.nonEmpty) super.transform(tree) else tree
137+
}
122138
}
123-
disallowed ++= visitedNow
139+
}
124140

125-
treesToAppend.toList
126-
} else Nil
127-
}
128141

129-
cpy.Block(tree)(entryPoints.flatMap(moveLabels).toList ++ newStats, newExpr)
142+
val res = cpy.DefDef(tree)(rhs = putLabelDefsNearCallees.transform(newRhs))
130143

144+
res
145+
}
131146
}
132147

133148
val collectLabelDefs = new TreeMap() {
@@ -137,13 +152,12 @@ class LabelDefs extends MiniPhaseTransform {
137152
var isInsideLabel = false
138153
var isInsideBlock = false
139154

140-
def shouldMoveLabel = !isInsideBlock
155+
def shouldMoveLabel = true
141156

142157
// labelSymbol -> Defining tree
143158
val labelDefs = new mutable.HashMap[Symbol, Tree]()
144159
// owner -> all calls by this owner
145160
val labelCalls = new mutable.HashMap[Symbol, mutable.Set[Tree]]()
146-
val labelCallCounts = new mutable.HashMap[Symbol, Int]()
147161

148162
def clear = {
149163
parentLabelCalls.clear()
@@ -175,7 +189,6 @@ class LabelDefs extends MiniPhaseTransform {
175189
} else r
176190
case t: Apply if t.symbol is Flags.Label =>
177191
parentLabelCalls = parentLabelCalls + t
178-
labelCallCounts.get(t.symbol)
179192
super.transform(tree)
180193
case _ =>
181194
super.transform(tree)

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ object Erasure extends TypeTestsCasts{
251251
override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal =
252252
if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt)
253253
else super.typedLiteral(tree)
254-
254+
255255
/** Type check select nodes, applying the following rewritings exhaustively
256256
* on selections `e.m`, where `OT` is the type of the owner of `m` and `ET`
257257
* is the erased type of the selection's original qualifier expression.
@@ -387,9 +387,29 @@ object Erasure extends TypeTestsCasts{
387387
}
388388
}
389389

390+
// The following four methods take as the proto-type the erasure of the pre-existing type,
391+
// if the original proto-type is not a value type.
392+
// This makes all branches be adapted to the correct type.
390393
override def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context) =
391394
super.typedSeqLiteral(tree, erasure(tree.typeOpt))
392-
// proto type of typed seq literal is original type; this makes elements be adapted to correct type.
395+
// proto type of typed seq literal is original type;
396+
397+
override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) =
398+
super.typedIf(tree, adaptProto(tree, pt))
399+
400+
override def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) =
401+
super.typedMatch(tree, adaptProto(tree, pt))
402+
403+
override def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context) =
404+
super.typedTry(tree, adaptProto(tree, pt))
405+
406+
private def adaptProto(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = {
407+
if (pt.isValueType) pt else {
408+
if(tree.typeOpt.derivesFrom(ctx.definitions.UnitClass))
409+
tree.typeOpt
410+
else erasure(tree.typeOpt)
411+
}
412+
}
393413

394414
override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context): ValDef =
395415
super.typedValDef(untpd.cpy.ValDef(vdef)(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
5757
val selector =
5858
ctx.newSymbol(ctx.owner, ctx.freshName("ex").toTermName, Flags.Synthetic, defn.ThrowableType, coord = tree.pos)
5959
val sel = Ident(selector.termRef).withPos(tree.pos)
60-
val rethrow = tpd.CaseDef(sel, EmptyTree, Throw(ref(selector)))
60+
val rethrow = tpd.CaseDef(EmptyTree, EmptyTree, Throw(ref(selector)))
6161
val newCases = tpd.CaseDef(
6262
Bind(selector,untpd.Ident(nme.WILDCARD).withPos(tree.pos).withType(selector.info)),
6363
EmptyTree,

test/dotc/tests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class tests extends CompilerTest {
135135
@Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", failedOther)
136136
// Expected primitive types I - Ljava/lang/Object
137137
// Tried to return an object where expected type was Integer
138-
@Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice)
138+
@Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", failedOther)
139139
@Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice)
140140
@Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", failedOther) // similar to dotc_config
141141
//@Test def dotc_util = compileDir(dotcDir + "tools/dotc/util") //fails inside ExtensionMethods with ClassCastException

tests/pos/erased-lub.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Verify that expressions below perform correct boxings in erasure.
2+
object Test {
3+
def id[T](t: T) = t
4+
5+
val x = true
6+
val one = 1
7+
8+
{ if (x) id(one) else 0 } + 1
9+
10+
{ if (x) new scala.util.Random()}.asInstanceOf[Runnable]
11+
12+
{ x match {
13+
case true => id(one)
14+
case _ => 0
15+
}
16+
} + 1
17+
18+
{ try {
19+
id(one)
20+
} catch {
21+
case ex: Exception => 0
22+
}
23+
}.asInstanceOf[Runnable]
24+
25+
val arr = Array(id(one), 0)
26+
27+
}

0 commit comments

Comments
 (0)