Skip to content

Commit 1f9fdaf

Browse files
committed
Add a phase to Dotty that hides Dotty bottom types from JVM.
It is very convenient to have bottom types in type system. Unfortunately JVM does not have bottom types and we need to insert casts that would make bytecode pass verification.
1 parent 4bca332 commit 1f9fdaf

File tree

3 files changed

+90
-2
lines changed

3 files changed

+90
-2
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package dotty.tools.backend.jvm
2+
3+
import dotty.tools.dotc.ast.Trees.Thicket
4+
import dotty.tools.dotc.ast.{Trees, tpd}
5+
import dotty.tools.dotc.core.Contexts.Context
6+
import dotty.tools.dotc.core.Types
7+
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, MiniPhase, MiniPhaseTransform}
8+
import dotty.tools.dotc
9+
import dotty.tools.dotc.backend.jvm.DottyPrimitives
10+
import dotty.tools.dotc.core.Flags.FlagSet
11+
import dotty.tools.dotc.transform.Erasure
12+
import dotty.tools.dotc.transform.SymUtils._
13+
import java.io.{File => JFile}
14+
15+
import scala.collection.generic.Clearable
16+
import scala.collection.mutable
17+
import scala.collection.mutable.{ListBuffer, ArrayBuffer}
18+
import scala.reflect.ClassTag
19+
import scala.reflect.internal.util.WeakHashSet
20+
import scala.reflect.io.{Directory, PlainDirectory, AbstractFile}
21+
import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor}
22+
import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface}
23+
import dotty.tools.dotc.core._
24+
import Periods._
25+
import SymDenotations._
26+
import Contexts._
27+
import Types._
28+
import Symbols._
29+
import Denotations._
30+
import Phases._
31+
import java.lang.AssertionError
32+
import dotty.tools.dotc.util.Positions.Position
33+
import Decorators._
34+
import tpd._
35+
import Flags._
36+
import StdNames.nme
37+
38+
/**
39+
* Ensures that tree does not contain type subsumptions where subsumed type is bottom type
40+
* of our typesystem, but not the bottom type of JVM typesystem.
41+
*/
42+
class BottomTypes extends MiniPhaseTransform {
43+
def phaseName: String = "bottomTypes"
44+
45+
46+
def adaptBottom(treeOfBottomType: tpd.Tree, expectedType: Type)(implicit ctx: Context) = {
47+
if (Erasure.Boxing.isNonJVMBottomType(treeOfBottomType.tpe) && (treeOfBottomType.tpe ne expectedType))
48+
Erasure.Boxing.adaptToType(treeOfBottomType, expectedType)
49+
else treeOfBottomType
50+
}
51+
52+
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
53+
val returnTp = tree.symbol.info.dealias.finalResultType
54+
cpy.DefDef(tree)(rhs = adaptBottom(tree.rhs, returnTp))
55+
}
56+
57+
58+
override def transformAssign(tree: tpd.Assign)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
59+
val returnTp = tree.lhs.symbol.info.dealias
60+
cpy.Assign(tree)(tree.lhs, adaptBottom(tree.rhs, returnTp))
61+
}
62+
63+
64+
override def transformTyped(tree: tpd.Typed)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
65+
cpy.Typed(tree)(adaptBottom(tree.expr, tree.tpt.tpe), tree.tpt)
66+
}
67+
68+
override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
69+
val fun = tree.fun
70+
val newArgs: List[tpd.Tree] = tree.args.zip(fun.tpe.dealias.firstParamTypes).map(x => adaptBottom(x._1, x._2))
71+
val changeNeeded = tree.args == newArgs // cpy.Apply does not check if elements are the same,
72+
// it only does `eq` on lists as whole
73+
if (changeNeeded) cpy.Apply(tree)(fun = fun, args = newArgs)
74+
else tree
75+
}
76+
77+
override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
78+
val returnTp = tree.symbol.info.dealias
79+
cpy.ValDef(tree)(rhs = adaptBottom(tree.rhs, returnTp))
80+
}
81+
}

src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import transform.TreeTransforms.{TreeTransform, TreeTransformer}
1515
import core.DenotTransformers.DenotTransformer
1616
import core.Denotations.SingleDenotation
1717

18-
import dotty.tools.backend.jvm.{LabelDefs, GenBCode}
18+
import dotty.tools.backend.jvm.{BottomTypes, LabelDefs, GenBCode}
1919

2020
class Compiler {
2121

@@ -82,6 +82,7 @@ class Compiler {
8282
List(/*new PrivateToStatic,*/
8383
new ExpandPrivate,
8484
new CollectEntryPoints,
85+
new BottomTypes,
8586
new LabelDefs),
8687
List(new GenBCode)
8788
)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ object Erasure extends TypeTestsCasts{
254254
case MethodType(Nil, _) if tree.isTerm =>
255255
adaptToType(tree.appliedToNone, pt)
256256
case tpw =>
257-
if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt)
257+
if (pt.isInstanceOf[ProtoType] || (!isNonJVMBottomType(tree.tpe) && (tree.tpe <:< pt)))
258258
tree
259259
else if (tpw.isErasedValueType)
260260
adaptToType(box(tree), pt)
@@ -267,6 +267,12 @@ object Erasure extends TypeTestsCasts{
267267
else
268268
cast(tree, pt)
269269
}
270+
271+
272+
/** Is `tpe` a type which is a bottom type for Dotty, but not a bottom type for JVM */
273+
def isNonJVMBottomType(tpe: Type)(implicit ctx: Context): Boolean = {
274+
tpe.derivesFrom(ctx.definitions.NothingClass) || tpe.derivesFrom(ctx.definitions.NullClass)
275+
}
270276
}
271277

272278
class Typer extends typer.ReTyper with NoChecking {

0 commit comments

Comments
 (0)