Skip to content

Commit

Permalink
Make all phantoms unused
Browse files Browse the repository at this point in the history
This removes the necesity of erasing phantoms. This process
is done by the erasure of `unused`.
  • Loading branch information
nicolasstucki committed Feb 21, 2018
1 parent 8d07271 commit b567b8d
Show file tree
Hide file tree
Showing 85 changed files with 267 additions and 493 deletions.
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ class Compiler {
new ShortcutImplicits, // Allow implicit functions without creating closures
new CrossCastAnd, // Normalize selections involving intersection types.
new Splitter) :: // Expand selections involving union types into conditionals
List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call.
new UnusedDecls, // Removes all unused defs and vals decls (except for parameters)
List(new UnusedDecls, // Removes all unused defs and vals decls (except for parameters)
new VCInlineMethods, // Inlines calls to value class methods
new SeqLiterals, // Express vararg arguments as arrays
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
else if (tpw isRef defn.DoubleClass) Literal(Constant(0d))
else if (tpw isRef defn.ByteClass) Literal(Constant(0.toByte))
else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort))
else if (tpw.isPhantom) Literal(Constant(null)).withType(tpw)
else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe)
}

Expand Down
8 changes: 1 addition & 7 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1198,21 +1198,15 @@ class Definitions {

val any = enterCompleteClassSymbol(cls, tpnme.Any, Protected | Final | NoInitsTrait, Nil)
val nothing = enterCompleteClassSymbol(cls, tpnme.Nothing, Protected | Final | NoInitsTrait, List(any.typeRef))
enterMethod(cls, nme.assume_, ExprType(nothing.typeRef), Protected | Final | Method)
enterMethod(cls, nme.assume_, ExprType(nothing.typeRef), Protected | Final | Method | Unused)

cls
}
lazy val Phantom_AnyClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass
lazy val Phantom_NothingClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass
lazy val Phantom_assume = PhantomClass.unforcedDecls.find(_.name eq nme.assume_)

/** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */
def isPhantomTerminalClass(sym: Symbol) = (sym eq Phantom_AnyClass) || (sym eq Phantom_NothingClass)


lazy val ErasedPhantomType: TypeRef = ctx.requiredClassRef("dotty.runtime.ErasedPhantom")
def ErasedPhantomClass(implicit ctx: Context) = ErasedPhantomType.symbol.asClass

def ErasedPhantom_UNIT(implicit ctx: Context) = ErasedPhantomClass.linkedClass.requiredValue("UNIT")

}
33 changes: 0 additions & 33 deletions compiler/src/dotty/tools/dotc/core/PhantomErasure.scala

This file was deleted.

2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Signature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) {
* to the parameter part of this signature.
*/
def prepend(params: List[Type], isJava: Boolean)(implicit ctx: Context) =
Signature(params.collect { case p if !p.isPhantom => sigName(p, isJava) } ++ paramsSig, resSig)
Signature(params.map(p => sigName(p, isJava)) ++ paramsSig, resSig)

/** A signature is under-defined if its paramsSig part contains at least one
* `tpnme.Uninstantiated`. Under-defined signatures arise when taking a signature
Expand Down
9 changes: 2 additions & 7 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type.
else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym)
else if (defn.isPhantomTerminalClass(sym)) PhantomErasure.erasedPhantomType
else if (defn.isPhantomTerminalClass(sym)) defn.ErasedPhantomType
else if (sym eq defn.PhantomClass) defn.ObjectType // To erase the definitions of Phantom.{assume, Any, Nothing}
else eraseNormalClassRef(tp)
case tp: AppliedType =>
Expand All @@ -401,10 +401,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
case tp: MethodType =>
def paramErasure(tpToErase: Type) =
erasureFn(tp.isJavaMethod, semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
val (names, formals0) =
if (tp.isUnusedMethod) (Nil, Nil)
else if (tp.paramInfos.exists(_.isPhantom)) tp.paramNames.zip(tp.paramInfos).filterNot(_._2.isPhantom).unzip
else (tp.paramNames, tp.paramInfos)
val (names, formals0) = if (tp.isUnusedMethod) (Nil, Nil) else (tp.paramNames, tp.paramInfos)
val formals = formals0.mapConserve(paramErasure)
eraseResult(tp.resultType) match {
case rt: MethodType =>
Expand Down Expand Up @@ -527,8 +524,6 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
}
if (defn.isSyntheticFunctionClass(sym))
sigName(defn.erasedFunctionType(sym))
else if (defn.isPhantomTerminalClass(tp.symbol))
sigName(PhantomErasure.erasedPhantomType)
else
normalizeClass(sym.asClass).fullName.asTypeName
case tp: AppliedType =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
// Produce aligned accessors and constructor parameters. We have to adjust
// for any outer parameters, which are last in the sequence of original
// parameter accessors but come first in the constructor parameter list.
val accessors = cls.paramAccessors.filterNot(x => x.isSetter || x.info.resultType.classSymbol == defn.ErasedPhantomClass)
val accessors = cls.paramAccessors.filterNot(x => x.isSetter)
val vparamsWithOuterLast = vparams match {
case vparam :: rest if vparam.name == nme.OUTER => rest ::: vparam :: Nil
case _ => vparams
Expand Down
28 changes: 4 additions & 24 deletions compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import ValueClasses._
import TypeUtils._
import ExplicitOuter._
import core.Mode
import core.PhantomErasure
import reporting.trace

class Erasure extends Phase with DenotTransformer {
Expand Down Expand Up @@ -213,8 +212,6 @@ object Erasure {
val tree1 =
if (tree.tpe isRef defn.NullClass)
adaptToType(tree, underlying)
else if (wasPhantom(underlying))
PhantomErasure.erasedParameterRef
else if (!(tree.tpe <:< tycon)) {
assert(!(tree.tpe.typeSymbol.isPrimitiveValueClass))
val nullTree = Literal(Constant(null))
Expand Down Expand Up @@ -437,16 +434,9 @@ object Erasure {
}
}

if ((origSym eq defn.Phantom_assume) || (origSym.is(Flags.ParamAccessor) && wasPhantom(pt)))
PhantomErasure.erasedAssume
else recur(typed(tree.qualifier, AnySelectionProto))
recur(typed(tree.qualifier, AnySelectionProto))
}

override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): tpd.Tree =
if (tree.symbol eq defn.Phantom_assume) PhantomErasure.erasedAssume
else if (tree.symbol.is(Flags.Param) && wasPhantom(tree.typeOpt)) PhantomErasure.erasedParameterRef
else super.typedIdent(tree, pt)

override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =
if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree)
else {
Expand Down Expand Up @@ -507,11 +497,9 @@ object Erasure {
.withType(defn.ArrayOf(defn.ObjectType))
args0 = bunchedArgs :: Nil
}
// Arguments are phantom if an only if the parameters are phantom, guaranteed by the separation of type lattices
val args1 = args0.filterConserve(arg => !wasPhantom(arg.typeOpt))
assert(args1 hasSameLengthAs mt.paramInfos)
val args2 = args1.zipWithConserve(mt.paramInfos)(typedExpr)
untpd.cpy.Apply(tree)(fun1, args2) withType mt.resultType
assert(args0 hasSameLengthAs mt.paramInfos)
val args1 = args0.zipWithConserve(mt.paramInfos)(typedExpr)
untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType
case _ =>
throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}")
}
Expand Down Expand Up @@ -572,11 +560,6 @@ object Erasure {
rhs1 = untpd.Block(paramDefs, rhs1)
}
vparamss1 = vparamss1.mapConserve(_.filterConserve(!_.symbol.is(Flags.Unused)))
vparamss1 = vparamss1.mapConserve(_.filterConserve(vparam => !wasPhantom(vparam.tpe)))
if (sym.is(Flags.ParamAccessor) && wasPhantom(ddef.tpt.tpe)) {
sym.resetFlag(Flags.ParamAccessor)
rhs1 = PhantomErasure.erasedParameterRef
}
val ddef1 = untpd.cpy.DefDef(ddef)(
tparams = Nil,
vparamss = vparamss1,
Expand Down Expand Up @@ -703,7 +686,4 @@ object Erasure {

def takesBridges(sym: Symbol)(implicit ctx: Context) =
sym.isClass && !sym.is(Flags.Trait | Flags.Package)

private def wasPhantom(tp: Type)(implicit ctx: Context): Boolean =
tp.widenDealias.classSymbol eq defn.ErasedPhantomClass
}
8 changes: 0 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/FirstTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,6 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
} else ddef
}

override def transformValDef(vdef: tpd.ValDef)(implicit ctx: Context): tpd.Tree = {
if (vdef.tpt.tpe.isPhantom) {
if (vdef.symbol.is(Mutable)) ctx.error("var fields cannot have Phantom types", vdef.pos)
else if (vdef.symbol.hasAnnotation(defn.VolatileAnnot)) ctx.error("Phantom fields cannot be @volatile", vdef.pos)
}
vdef
}

override def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] =
ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisPhase.next)))

Expand Down
8 changes: 2 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/Memoize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ import Decorators._
if (sym eq defn.NothingClass) Throw(Literal(Constant(null)))
else if (sym eq defn.NullClass) Literal(Constant(null))
else if (sym eq defn.BoxedUnitClass) ref(defn.BoxedUnit_UNIT)
else if (sym eq defn.ErasedPhantomClass) ref(defn.ErasedPhantom_UNIT)
else {
assert(false, sym + " has no erased bottom tree")
EmptyTree
Expand All @@ -120,11 +119,8 @@ import Decorators._
def adaptToField(tree: Tree): Tree =
if (tree.isEmpty) tree else tree.ensureConforms(field.info.widen)

def isErasableBottomField(cls: Symbol): Boolean = {
// TODO: For Scala.js, return false if this field is in a js.Object unless it is an ErasedPhantomClass.
!field.isVolatile &&
((cls eq defn.NothingClass) || (cls eq defn.NullClass) || (cls eq defn.BoxedUnitClass) || (cls eq defn.ErasedPhantomClass))
}
def isErasableBottomField(cls: Symbol): Boolean =
!field.isVolatile && ((cls eq defn.NothingClass) || (cls eq defn.NullClass) || (cls eq defn.BoxedUnitClass))

if (sym.isGetter) {
var rhs = tree.rhs.changeOwnerAfter(sym, field, thisPhase)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0
def wildcardAscription(tp: Type) = Typed(Underscore(tp), TypeTree(tp))
val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C)
val comparisons = accessors collect { case accessor if !accessor.info.isPhantom =>
val comparisons = accessors map { accessor =>
This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor)) }
val rhs = // this.x == this$0.x && this.y == x$0.y
if (comparisons.isEmpty) Literal(Constant(true)) else comparisons.reduceLeft(_ and _)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import core.NameOps._
import transform.MegaPhase.MiniPhase
import config.Printers.simplify
import ast.tpd
import dotty.tools.dotc.core.PhantomErasure

import scala.annotation.tailrec

Expand Down Expand Up @@ -170,8 +169,6 @@ object Simplify {
sym.owner.is(CaseClass) &&
sym.name.isSelectorName &&
!sym.info.decls.exists(_.is(Mutable | Lazy)) // Conservatively covers case class A(var x: Int)
val isErasedPhantom = PhantomErasure.isErasedPhantom(sym)

isImmutableGetter || isCaseAccessor || isProductAccessor || isErasedPhantom
isImmutableGetter || isCaseAccessor || isProductAccessor
}
}
8 changes: 3 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -502,12 +502,10 @@ object Checking {
case param :: params =>
if (param.is(Mutable))
ctx.error(ValueClassParameterMayNotBeAVar(clazz, param), param.pos)
if (param.info.isPhantom)
ctx.error("First parameter of value class must not be phantom", param.pos)
else if (param.is(Unused))
ctx.error("First parameter of value class cannot be `unused`", param.pos)
if (param.is(Unused))
ctx.error("value class first parameter cannot be `unused`", param.pos)
else {
for (p <- params if !(p.info.isPhantom || p.is(Unused)))
for (p <- params if !p.is(Unused))
ctx.error("value class can only have one non `unused` parameter", p.pos)
}
case Nil =>
Expand Down
29 changes: 29 additions & 0 deletions compiler/test/dotty/tools/dotc/FromTastyTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class FromTastyTests extends ParallelTesting {

// Infinite compilation
"t3612.scala",

"phantom-Eq.scala",
"phantom-Evidence.scala",
)
)
step1.checkCompile() // Compile all files to generate the class files with tasty
Expand Down Expand Up @@ -144,6 +147,32 @@ class FromTastyTests extends ParallelTesting {
"unused-21.scala",
"unused-23.scala",
"unused-value-class.scala",
"phantom-methods-12.scala",
"phantom-methods-8.scala",
"phantom-methods-2.scala",
"phantom-methods-9.scala",
"phantom-methods-13.scala",
"phantom-methods-14.scala",
"phantom-decls-4.scala",
"phantom-self-1.scala",
"phantom-val-2.scala",
"phantom-3.scala",
"phantom-OnHList.scala",
"phantom-1.scala",
"phantom-4.scala",
"phantom-5.scala",
"phantom-methods-1.scala",
"phantom-val.scala",
"phantom-decls-2.scala",
"phantom-assume-1.scala",
"phantom-methods-10.scala",
"phantom-methods-7.scala",
"phantom-methods-6.scala",
"phantom-methods-11.scala",
"phantom-erased-methods.scala",
"phantom-methods-5.scala",
"phantom-2.scala",
"phantom-param-accessor.scala",
)
)
step1.checkCompile() // Compile all files to generate the class files with tasty
Expand Down
Loading

0 comments on commit b567b8d

Please sign in to comment.