Skip to content

Fix/erasure lub alternative #359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 58 additions & 45 deletions src/dotty/tools/backend/jvm/LabelDefs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import java.lang.AssertionError
import dotty.tools.dotc.util.Positions.Position
import Decorators._
import tpd._
import Flags._
import StdNames.nme

/**
Expand Down Expand Up @@ -80,54 +81,68 @@ class LabelDefs extends MiniPhaseTransform {

val queue = new ArrayBuffer[Tree]()



override def transformBlock(tree: tpd.Block)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
collectLabelDefs.clear
val newStats = collectLabelDefs.transformStats(tree.stats)
val newExpr = collectLabelDefs.transform(tree.expr)
val labelCalls = collectLabelDefs.labelCalls
val entryPoints = collectLabelDefs.parentLabelCalls
val labelDefs = collectLabelDefs.labelDefs

// make sure that for every label there's a single location it should return and single entry point
// if theres already a location that it returns to that's a failure
val disallowed = new mutable.HashMap[Symbol, Tree]()
queue.sizeHint(labelCalls.size + entryPoints.size)
def moveLabels(entryPoint: Tree): List[Tree] = {
if((entryPoint.symbol is Flags.Label) && labelDefs.contains(entryPoint.symbol)) {
val visitedNow = new mutable.HashMap[Symbol, Tree]()
val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first
queue.clear()

var visited = 0
queue += entryPoint
while (visited < queue.size) {
val owningLabelDefSym = queue(visited).symbol
val owningLabelDef = labelDefs(owningLabelDefSym)
for (call <- labelCalls(owningLabelDefSym))
if (disallowed.contains(call.symbol)) {
val oldCall = disallowed(call.symbol)
ctx.error(s"Multiple return locations for Label $oldCall and $call", call.symbol.pos)
} else {
if ((!visitedNow.contains(call.symbol)) && labelDefs.contains(call.symbol)) {
val df = labelDefs(call.symbol)
visitedNow.put(call.symbol, labelDefs(call.symbol))
queue += call
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
if (tree.symbol is Flags.Label) tree
else {
collectLabelDefs.clear
val newRhs = collectLabelDefs.transform(tree.rhs)
val labelCalls = collectLabelDefs.labelCalls
var entryPoints = collectLabelDefs.parentLabelCalls
var labelDefs = collectLabelDefs.labelDefs

// make sure that for every label there's a single location it should return and single entry point
// if theres already a location that it returns to that's a failure
val disallowed = new mutable.HashMap[Symbol, Tree]()
queue.sizeHint(labelCalls.size + entryPoints.size)
def moveLabels(entryPoint: Tree): List[Tree] = {
if ((entryPoint.symbol is Flags.Label) && labelDefs.contains(entryPoint.symbol)) {
val visitedNow = new mutable.HashMap[Symbol, Tree]()
val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first
queue.clear()

var visited = 0
queue += entryPoint
while (visited < queue.size) {
val owningLabelDefSym = queue(visited).symbol
val owningLabelDef = labelDefs(owningLabelDefSym)
for (call <- labelCalls(owningLabelDefSym))
if (disallowed.contains(call.symbol)) {
val oldCall = disallowed(call.symbol)
ctx.error(s"Multiple return locations for Label $oldCall and $call", call.symbol.pos)
} else {
if ((!visitedNow.contains(call.symbol)) && labelDefs.contains(call.symbol)) {
visitedNow.put(call.symbol, labelDefs(call.symbol))
queue += call
}
}
if (!treesToAppend.contains(owningLabelDef)) {
treesToAppend += owningLabelDef
}
if(!treesToAppend.contains(owningLabelDef))
treesToAppend += owningLabelDef
visited += 1
visited += 1
}
disallowed ++= visitedNow

treesToAppend.toList
} else Nil
}

val putLabelDefsNearCallees = new TreeMap() {

override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
tree match {
case t: Apply if (entryPoints.contains(t)) =>
entryPoints = entryPoints - t
Block(moveLabels(t), t)
case _ => if (entryPoints.nonEmpty && labelDefs.nonEmpty) super.transform(tree) else tree
}
}
disallowed ++= visitedNow
}

treesToAppend.toList
} else Nil
}

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

res
}
}

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

def shouldMoveLabel = !isInsideBlock
def shouldMoveLabel = true

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

def clear = {
parentLabelCalls.clear()
Expand Down Expand Up @@ -175,7 +189,6 @@ class LabelDefs extends MiniPhaseTransform {
} else r
case t: Apply if t.symbol is Flags.Label =>
parentLabelCalls = parentLabelCalls + t
labelCallCounts.get(t.symbol)
super.transform(tree)
case _ =>
super.transform(tree)
Expand Down
24 changes: 22 additions & 2 deletions src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ object Erasure extends TypeTestsCasts{
override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal =
if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt)
else super.typedLiteral(tree)

/** Type check select nodes, applying the following rewritings exhaustively
* on selections `e.m`, where `OT` is the type of the owner of `m` and `ET`
* is the erased type of the selection's original qualifier expression.
Expand Down Expand Up @@ -387,9 +387,29 @@ object Erasure extends TypeTestsCasts{
}
}

// The following four methods take as the proto-type the erasure of the pre-existing type,
// if the original proto-type is not a value type.
// This makes all branches be adapted to the correct type.
override def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context) =
super.typedSeqLiteral(tree, erasure(tree.typeOpt))
// proto type of typed seq literal is original type; this makes elements be adapted to correct type.
// proto type of typed seq literal is original type;

override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) =
super.typedIf(tree, adaptProto(tree, pt))

override def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) =
super.typedMatch(tree, adaptProto(tree, pt))

override def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context) =
super.typedTry(tree, adaptProto(tree, pt))

private def adaptProto(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = {
if (pt.isValueType) pt else {
if(tree.typeOpt.derivesFrom(ctx.definitions.UnitClass))
tree.typeOpt
else erasure(tree.typeOpt)
}
}

override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context): ValDef =
super.typedValDef(untpd.cpy.ValDef(vdef)(
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
val selector =
ctx.newSymbol(ctx.owner, ctx.freshName("ex").toTermName, Flags.Synthetic, defn.ThrowableType, coord = tree.pos)
val sel = Ident(selector.termRef).withPos(tree.pos)
val rethrow = tpd.CaseDef(sel, EmptyTree, Throw(ref(selector)))
val rethrow = tpd.CaseDef(EmptyTree, EmptyTree, Throw(ref(selector)))
val newCases = tpd.CaseDef(
Bind(selector,untpd.Ident(nme.WILDCARD).withPos(tree.pos).withType(selector.info)),
EmptyTree,
Expand Down
2 changes: 1 addition & 1 deletion test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class tests extends CompilerTest {
@Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", failedOther)
// Expected primitive types I - Ljava/lang/Object
// Tried to return an object where expected type was Integer
@Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice)
@Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", failedOther)
@Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice)
@Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", failedOther) // similar to dotc_config
//@Test def dotc_util = compileDir(dotcDir + "tools/dotc/util") //fails inside ExtensionMethods with ClassCastException
Expand Down
27 changes: 27 additions & 0 deletions tests/pos/erased-lub.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Verify that expressions below perform correct boxings in erasure.
object Test {
def id[T](t: T) = t

val x = true
val one = 1

{ if (x) id(one) else 0 } + 1

{ if (x) new scala.util.Random()}.asInstanceOf[Runnable]

{ x match {
case true => id(one)
case _ => 0
}
} + 1

{ try {
id(one)
} catch {
case ex: Exception => 0
}
}.asInstanceOf[Runnable]

val arr = Array(id(one), 0)

}