Skip to content

Commit

Permalink
Remove unused params in erasure
Browse files Browse the repository at this point in the history
With path dependent types on parameters it is not possible to remove them
before erasure because there is no type that can be put in their place
without messing with typing (Ychecks will fail).
  • Loading branch information
nicolasstucki committed Oct 18, 2017
1 parent 44a8fe6 commit 28a8554
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 173 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 @@ -65,15 +65,14 @@ class Compiler {
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
new ClassOf), // Expand `Predef.classOf` calls.
List(new UnusedParams), // Removes all unused parameters and arguments
List(new TryCatchPatterns, // Compile cases in try/catch
new PatternMatcher, // Compile pattern matches
new ExplicitOuter, // Add accessors to outer classes from nested ones.
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
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 UnusedDecls, // Removes all unused defs and vals decls
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
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
def paramErasure(tpToErase: Type) =
erasureFn(tp.isJavaMethod, semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
val (names, formals0) =
if (tp.paramInfos.exists(_.isPhantom)) tp.paramNames.zip(tp.paramInfos).filterNot(_._2.isPhantom).unzip
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 formals = formals0.mapConserve(paramErasure)
eraseResult(tp.resultType) match {
Expand Down
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,15 @@ object Erasure {
case _ => Nil
}

// TODO: merge this whit protoArgs if it is actually needed
private def protoArgs2(pt: Type, tp: Type): List[untpd.Tree] = (pt, tp) match {
case (pt: FunProto, tp: MethodType) if tp.isUnusedMethod => protoArgs2(pt.resType, tp.resType)
case (pt: FunProto, tp: MethodType) => pt.args ++ protoArgs2(pt.resType, tp.resType)
case _ =>
assert(!tp.isInstanceOf[MethodOrPoly], tp)
Nil
}

override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context) = {
val ntree = interceptTypeApply(tree.asInstanceOf[TypeApply])(ctx.withPhase(ctx.erasurePhase))

Expand Down Expand Up @@ -486,7 +495,10 @@ object Erasure {
fun1.tpe.widen match {
case mt: MethodType =>
val outers = outer.args(fun.asInstanceOf[tpd.Tree]) // can't use fun1 here because its type is already erased
var args0 = outers ::: args ++ protoArgs(pt)
var args0 = protoArgs2(pt, tree.typeOpt)
if (!mt.isUnusedMethod) args0 = args ::: args0
args0 = outers ::: args0

if (args0.length > MaxImplementedFunctionArity && mt.paramInfos.length == 1) {
val bunchedArgs = untpd.JavaSeqLiteral(args0, TypeTree(defn.ObjectType))
.withType(defn.ArrayOf(defn.ObjectType))
Expand Down Expand Up @@ -554,6 +566,7 @@ object Erasure {
vparamss1 = (tpd.ValDef(bunchedParam) :: Nil) :: Nil
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)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class UnusedDecls extends MiniPhaseTransform with InfoTransformer {

/** Check what the phase achieves, to be called at any point after it is finished. */
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match {
case tree: ValOrDefDef => assert(!tree.symbol.is(Unused))
case tree: ValOrDefDef if !tree.symbol.is(Param) => assert(!tree.symbol.is(Unused, butNot = Param))
case _ =>
}

Expand All @@ -35,7 +35,7 @@ class UnusedDecls extends MiniPhaseTransform with InfoTransformer {
override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = transformValOrDefDef(tree)

private def transformValOrDefDef(tree: ValOrDefDef)(implicit ctx: Context): Tree =
if (tree.symbol is Unused) EmptyTree else tree
if (tree.symbol.is(Unused, butNot = Param)) EmptyTree else tree


/* Info transform */
Expand Down
122 changes: 0 additions & 122 deletions compiler/src/dotty/tools/dotc/transform/UnusedParams.scala

This file was deleted.

43 changes: 4 additions & 39 deletions compiler/src/dotty/tools/dotc/transform/UnusedRefs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, Transforme
* then `x` --> `(default value for T)`
* `x.Y` --> `T#Y`
*/
class UnusedRefs extends MiniPhaseTransform with InfoTransformer {
class UnusedRefs extends MiniPhaseTransform {
import tpd._

override def phaseName: String = "unusedRefs"
Expand All @@ -29,22 +29,9 @@ class UnusedRefs extends MiniPhaseTransform with InfoTransformer {
)

/** Check what the phase achieves, to be called at any point after it is finished. */
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
val checker = new TypeMap {
override def apply(tp: Type): Type = tp match {
case tp: TermParamRef => assert(!tp.binder.isUnusedMethod); tp
case tp: TermRef if !tp.symbol.is(Method) => assert(!tp.symbol.is(Unused)); tp
case _ => mapOver(tp)
}
}
tree match {
case _: ValDef =>
case _: Apply | _: RefTree =>
assert(!tree.symbol.is(Unused))
checker.apply(tree.tpe)
case _ =>
checker.apply(tree.tpe)
}
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match {
case _: Apply | _: RefTree => assert(!tree.symbol.is(Unused))
case _ =>
}

/* Tree transform */
Expand All @@ -70,26 +57,4 @@ class UnusedRefs extends MiniPhaseTransform with InfoTransformer {
}
}
}

override def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = {
val newType = removeUnusedPaths(tree.tpe)
if (tree.tpe == newType) tree
else TypeTree(newType)
}


/* Tree info */

override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = removeUnusedPaths(tp)

private def removeUnusedPaths(tp: Type)(implicit ctx: Context): Type = {
// TODO: handle variance
new TypeMap {
override def apply(tp: Type): Type = tp match {
case tp: TermParamRef if tp.binder.isUnusedMethod => tp.classSymbol.typeRef
case tp: TermRef if tp.symbol.is(Unused) => tp.widen
case _ => mapOver(tp)
}
}.apply(tp)
}
}
4 changes: 2 additions & 2 deletions tests/run/unused-3.check
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
foo
foo
foo1
foo2
fun
13 changes: 9 additions & 4 deletions tests/run/unused-3.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
object Test {

def main(args: Array[String]): Unit = {
fun(foo)(foo)
fun(foo1)(foo2)
}

def foo: Int = {
println("foo")
def foo1: Int = {
println("foo1")
42
}

def fun(unused a: Int)(unused b: Int): Unit = {
def foo2: String = {
println("foo2")
"abc"
}

def fun(unused a: Int)(unused b: String): Unit = {
println("fun")
}

Expand Down

0 comments on commit 28a8554

Please sign in to comment.