-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add a phase to Dotty that hides Dotty bottom types from JVM. #1002
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
Changes from all commits
1f9fdaf
4da5dfa
4168a25
142268a
15eaa10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package dotty.tools.backend.jvm | ||
|
||
import dotty.tools.dotc.ast.Trees.Thicket | ||
import dotty.tools.dotc.ast.{Trees, tpd} | ||
import dotty.tools.dotc.core.Contexts.Context | ||
import dotty.tools.dotc.core.Types | ||
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, MiniPhase, MiniPhaseTransform} | ||
import dotty.tools.dotc | ||
import dotty.tools.dotc.backend.jvm.DottyPrimitives | ||
import dotty.tools.dotc.core.Flags.FlagSet | ||
import dotty.tools.dotc.transform.Erasure | ||
import dotty.tools.dotc.transform.SymUtils._ | ||
import java.io.{File => JFile} | ||
|
||
import scala.collection.generic.Clearable | ||
import scala.collection.mutable | ||
import scala.collection.mutable.{ListBuffer, ArrayBuffer} | ||
import scala.reflect.ClassTag | ||
import scala.reflect.internal.util.WeakHashSet | ||
import scala.reflect.io.{Directory, PlainDirectory, AbstractFile} | ||
import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor} | ||
import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface} | ||
import dotty.tools.dotc.core._ | ||
import Periods._ | ||
import SymDenotations._ | ||
import Contexts._ | ||
import Types._ | ||
import Symbols._ | ||
import Denotations._ | ||
import Phases._ | ||
import java.lang.AssertionError | ||
import dotty.tools.dotc.util.Positions.Position | ||
import Decorators._ | ||
import tpd._ | ||
import Flags._ | ||
import StdNames.nme | ||
|
||
/** | ||
* Ensures that tree does not contain type subsumptions where subsumed type is bottom type | ||
* of our typesystem, but not the bottom type of JVM typesystem. | ||
*/ | ||
class BottomTypes extends MiniPhaseTransform { | ||
def phaseName: String = "bottomTypes" | ||
|
||
|
||
def adaptBottom(treeOfBottomType: tpd.Tree, expectedType: Type)(implicit ctx: Context) = { | ||
if (defn.isBottomType(treeOfBottomType.tpe) && (treeOfBottomType.tpe ne expectedType)) | ||
Erasure.Boxing.adaptToType(treeOfBottomType, expectedType) | ||
else treeOfBottomType | ||
} | ||
|
||
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { | ||
val returnTp = tree.symbol.info.dealias.finalResultType | ||
cpy.DefDef(tree)(rhs = adaptBottom(tree.rhs, returnTp)) | ||
} | ||
|
||
|
||
override def transformAssign(tree: tpd.Assign)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { | ||
val returnTp = tree.lhs.symbol.info.dealias | ||
cpy.Assign(tree)(tree.lhs, adaptBottom(tree.rhs, returnTp)) | ||
} | ||
|
||
|
||
override def transformTyped(tree: tpd.Typed)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { | ||
cpy.Typed(tree)(adaptBottom(tree.expr, tree.tpt.tpe), tree.tpt) | ||
} | ||
|
||
override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { | ||
val fun = tree.fun | ||
val newArgs: List[tpd.Tree] = tree.args.zip(fun.tpe.dealias.firstParamTypes).map(x => adaptBottom(x._1, x._2)) | ||
val changeNeeded = tree.args != newArgs // cpy.Apply does not check if elements are the same, | ||
// it only does `eq` on lists as whole | ||
if (changeNeeded) cpy.Apply(tree)(fun = fun, args = newArgs) | ||
else tree | ||
} | ||
|
||
override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { | ||
val returnTp = tree.symbol.info.dealias | ||
cpy.ValDef(tree)(rhs = adaptBottom(tree.rhs, returnTp)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -246,15 +246,15 @@ object Erasure extends TypeTestsCasts{ | |
* e -> unbox(e, PT) otherwise, if `PT` is an erased value type | ||
* e -> box(e) if `e` is of primitive type and `PT` is not a primitive type | ||
* e -> unbox(e, PT) if `PT` is a primitive type and `e` is not of primitive type | ||
* e -> cast(e, PT) otherwise | ||
* e -> cast(e, PT) otherwise, including if `PT` is a bottom type. | ||
*/ | ||
def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree = | ||
if (pt.isInstanceOf[FunProto]) tree | ||
else tree.tpe.widen match { | ||
case MethodType(Nil, _) if tree.isTerm => | ||
adaptToType(tree.appliedToNone, pt) | ||
case tpw => | ||
if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt) | ||
if (pt.isInstanceOf[ProtoType] || ((!pt.isValueType || !defn.isBottomType(tree.tpe)) && (tree.tpe <:< pt))) | ||
tree | ||
else if (tpw.isErasedValueType) | ||
adaptToType(box(tree), pt) | ||
|
@@ -267,6 +267,7 @@ object Erasure extends TypeTestsCasts{ | |
else | ||
cast(tree, pt) | ||
} | ||
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, why not use Definitions#isBottomType? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitions#isBottomType(tp) is defined to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I update Definitions#isBottomType to account for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I think it's OK to leave the two separate. Maybe rename the second to On Fri, Dec 18, 2015 at 6:32 PM, Dmitry Petrashko [email protected]
Martin Odersky There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do. |
||
|
||
class Typer extends typer.ReTyper with NoChecking { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
object Test{ | ||
val a: Nothing = ??? | ||
val b: Null = ??? | ||
val c: Null | Nothing = if(a == b) a else b | ||
val d: Nothing | Nothing = if(a == b) a else b | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
object X { | ||
val x: Int = null.asInstanceOf[Nothing] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should have test cases for the various nodes modified by |
||
def d: Int = null.asInstanceOf[Nothing] | ||
var s: Int = 0 | ||
s = null.asInstanceOf[Nothing] | ||
def takeInt(i: Int): Unit | ||
takeInt(null.asInstanceOf[Nothing]) | ||
} | ||
|
||
object Y { | ||
val n: Null = null | ||
val x: Object = n | ||
def d: Object = n | ||
var s: Object = 0 | ||
s = n | ||
def takeInt(i: Object): Unit | ||
takeInt(n) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
fun = fun
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method does not have default arguments.