Skip to content

Commit 3b030cf

Browse files
committed
Erase unused arguments as if they did not exist in the program
* Addapted all tests * Moved unused argument checks to postyper (for top down traversal with Unused ctx mode). * Removed UnusedArgLift and revert to PhantomArgLift * Disallow user written unused defs and non parameter unused val (temporary)
1 parent 679427b commit 3b030cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+133
-474
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,10 @@ class Compiler {
4848
List(new Pickler), // Generate TASTY info
4949
List(new LinkAll), // Reload compilation units from TASTY for library code (if needed)
5050
List(new FirstTransform, // Some transformations to put trees into a canonical form
51-
new UnusedChecks, // Check that unused terms are not used
5251
new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars
5352
new ElimJavaPackages), // Eliminate syntactic references to Java packages
5453
List(new CheckStatic, // Check restrictions that apply to @static members
5554
new UnusedRefs, // Removes all calls and references to unused values
56-
new UnusedArgLift, // Extracts the evaluation of unused arguments placing them before the call.
5755
new ElimRepeated, // Rewrite vararg parameters and arguments
5856
new NormalizeFlags, // Rewrite some definition flags
5957
new ExtensionMethods, // Expand methods of value classes with extension methods
@@ -71,7 +69,8 @@ class Compiler {
7169
new ShortcutImplicits, // Allow implicit functions without creating closures
7270
new CrossCastAnd, // Normalize selections involving intersection types.
7371
new Splitter), // Expand selections involving union types into conditionals
74-
List(new UnusedDecls, // Removes all unused defs and vals decls (except for parameters)
72+
List(new PhantomArgLift, // Extracts the evaluation of phantom arguments placing them before the call
73+
new UnusedDecls, // Removes all unused defs and vals decls (except for parameters)
7574
new VCInlineMethods, // Inlines calls to value class methods
7675
new SeqLiterals, // Express vararg arguments as arrays
7776
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods

compiler/src/dotty/tools/dotc/core/Mode.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,9 @@ object Mode {
9999

100100
/** We are in the IDE */
101101
val Interactive = newMode(20, "Interactive")
102+
103+
/** We are currently in code that will not be used at runtime.
104+
* It can be in an argument to an unused parameter or a type selection.
105+
*/
106+
val Unused = newMode(21, "Unused")
102107
}

compiler/src/dotty/tools/dotc/transform/UnusedArgLift.scala renamed to compiler/src/dotty/tools/dotc/transform/PhantomArgLift.scala

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ package dotty.tools.dotc.transform
22

33
import dotty.tools.dotc.ast.tpd
44
import dotty.tools.dotc.core.Contexts._
5-
import dotty.tools.dotc.core.Symbols._
5+
import dotty.tools.dotc.core.NameKinds._
66
import dotty.tools.dotc.core.Types._
77
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
88
import dotty.tools.dotc.typer.EtaExpansion
99

1010
import scala.collection.mutable.ListBuffer
1111

12-
/** This phase extracts the unused arguments before the application to avoid losing any
13-
* effects in the argument tree. This trivializes the removal of parameter in RemoveUnusedParams and Erasure phases.
12+
/** This phase extracts the arguments of phantom type before the application to avoid losing any
13+
* effects in the argument tree. This trivializes the removal of parameter in the Erasure phase.
1414
*
15-
* `f(x1,...)(y1,...)...(...)` with at least one unused argument list
15+
* `f(x1,...)(y1,...)...(...)` with at least one phantom argument
1616
*
1717
* -->
1818
*
@@ -24,15 +24,10 @@ import scala.collection.mutable.ListBuffer
2424
* `ev$f(ev$x1,...)(ev$y1,...)...(...)`
2525
*
2626
*/
27-
class UnusedArgLift extends MiniPhase {
27+
class PhantomArgLift extends MiniPhase {
2828
import tpd._
2929

30-
override def phaseName: String = "unusedArgLift"
31-
32-
override def runsAfter = Set(
33-
// Not required, avoids creation of unnecessary vals for applications on methods with @unused
34-
classOf[UnusedRefs]
35-
)
30+
override def phaseName: String = "phantomArgLift"
3631

3732
/** Check what the phase achieves, to be called at any point after it is finished. */
3833
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match {
@@ -48,11 +43,12 @@ class UnusedArgLift extends MiniPhase {
4843
override def transformApply(tree: Apply)(implicit ctx: Context): Tree = tree.tpe.widen match {
4944
case _: MethodType => tree // Do the transformation higher in the tree if needed
5045
case _ =>
51-
if (!hasImpurePhantomArgs(tree) && !hasUnusedParams(tree)) tree
46+
if (!hasImpurePhantomArgs(tree)) tree
5247
else {
5348
val buffer = ListBuffer.empty[Tree]
5449
val app = EtaExpansion.liftApp(buffer, tree)
55-
seq(buffer.result(), app)
50+
if (buffer.isEmpty) app
51+
else Block(buffer.result(), app)
5652
}
5753
}
5854

@@ -70,16 +66,4 @@ class UnusedArgLift extends MiniPhase {
7066
}
7167
}
7268

73-
/** Returns true if at least on of the arguments is an unused parameter.
74-
* Inner applies are also checked in case of multiple parameter list.
75-
*/
76-
private def hasUnusedParams(tree: Apply)(implicit ctx: Context): Boolean = {
77-
def hasUnusedParams(tp: Type): Boolean = tp match {
78-
case tp: MethodType if tp.isUnusedMethod => true
79-
case tp: MethodOrPoly => hasUnusedParams(tp.resType)
80-
case _ => false
81-
}
82-
hasUnusedParams(tree.symbol.info)
83-
}
84-
8569
}

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
173173
override def transform(tree: Tree)(implicit ctx: Context): Tree =
174174
try tree match {
175175
case tree: Ident if !tree.isType =>
176+
chekedUnused(tree)
176177
tree.tpe match {
177178
case tpe: ThisType => This(tpe.cls).withPos(tree.pos)
178179
case _ => paramFwd.adaptRef(fixSignature(tree))
179180
}
180181
case tree @ Select(qual, name) =>
182+
chekedUnused(tree)
181183
if (name.isTypeName) {
182184
Checking.checkRealizable(qual.tpe, qual.pos.focus)
183-
super.transform(tree)
185+
super.transform(tree)(ctx.addMode(Mode.Unused))
184186
}
185187
else
186188
transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil)
@@ -189,14 +191,17 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
189191
ctx.error(SuperCallsNotAllowedInline(ctx.owner), tree.pos)
190192
super.transform(tree)
191193
case tree: Apply =>
194+
def transformApply() = {
195+
val ctx1 = if (tree.fun.tpe.widen.isUnusedMethod) ctx.addMode(Mode.Unused) else ctx
196+
cpy.Apply(tree)(transform(tree.fun), transform(tree.args)(ctx1))
197+
}
192198
methPart(tree) match {
193199
case Select(nu: New, nme.CONSTRUCTOR) if isCheckable(nu) =>
194200
// need to check instantiability here, because the type of the New itself
195201
// might be a type constructor.
196202
Checking.checkInstantiable(tree.tpe, nu.pos)
197-
withNoCheckNews(nu :: Nil)(super.transform(tree))
198-
case _ =>
199-
super.transform(tree)
203+
withNoCheckNews(nu :: Nil)(transformApply())
204+
case _ => transformApply()
200205
}
201206
case tree: TypeApply =>
202207
val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
@@ -243,6 +248,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
243248
ctx.compilationUnit.source.exists &&
244249
sym != defn.SourceFileAnnot)
245250
sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path))
251+
if (sym.is(Case)) {
252+
tree.rhs match {
253+
case rhs: Template =>
254+
for (param <- rhs.constr.vparamss.head if param.symbol.is(Unused))
255+
ctx.error("First parameter list of case classes may not contain `unused` parameters", param.pos)
256+
case _ =>
257+
}
258+
}
246259
tree
247260
}
248261
super.transform(tree)
@@ -297,5 +310,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
297310
println(i"error while transforming $tree")
298311
throw ex
299312
}
313+
314+
private def chekedUnused(tree: RefTree)(implicit ctx: Context): Unit = {
315+
if (tree.symbol.is(Unused) && !ctx.mode.is(Mode.Unused))
316+
ctx.error(i"`unused` value $tree can only be used as unused arguments", tree.pos)
317+
}
300318
}
301319
}

compiler/src/dotty/tools/dotc/transform/UnusedChecks.scala

Lines changed: 0 additions & 86 deletions
This file was deleted.

compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import dotty.tools.dotc.core.Phases.Phase
99
import dotty.tools.dotc.core.Types._
1010
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
1111

12-
/** This phase removes unused declarations of `def`s and `val`s (except for parameters).
12+
/** This phase removes unused declarations of val`s (except for parameters).
1313
*
14-
* `unused def f(...) = ...` and `unused val x = ...` are removed
14+
* `unused val x = ...` are removed
1515
*/
1616
class UnusedDecls extends MiniPhase with InfoTransformer {
1717
import tpd._
@@ -31,10 +31,7 @@ class UnusedDecls extends MiniPhase with InfoTransformer {
3131

3232
/* Tree transform */
3333

34-
override def transformDefDef(tree: DefDef)(implicit ctx: Context): Tree = transformValOrDefDef(tree)
35-
override def transformValDef(tree: ValDef)(implicit ctx: Context): Tree = transformValOrDefDef(tree)
36-
37-
private def transformValOrDefDef(tree: ValOrDefDef)(implicit ctx: Context): Tree =
34+
override def transformValDef(tree: ValDef)(implicit ctx: Context): Tree =
3835
if (tree.symbol.is(Unused, butNot = Param)) EmptyTree else tree
3936

4037

compiler/src/dotty/tools/dotc/transform/UnusedRefs.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ class UnusedRefs extends MiniPhase {
2222

2323
override def phaseName: String = "unusedRefs"
2424

25-
override def runsAfterGroupsOf: Set[Class[_ <: Phases.Phase]] = Set(
26-
classOf[UnusedChecks]
27-
)
28-
2925
/** Check what the phase achieves, to be called at any point after it is finished. */
3026
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match {
3127
case _: Apply | _: TypeApply | _: RefTree => assert(!tree.symbol.is(Unused), tree)

compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class VCInlineMethods extends MiniPhase with IdentityDenotTransformer {
4545
override def phaseName: String = "vcInlineMethods"
4646

4747
override def runsAfter: Set[Class[_ <: Phase]] =
48-
Set(classOf[ExtensionMethods], classOf[PatternMatcher], classOf[UnusedArgLift])
48+
Set(classOf[ExtensionMethods], classOf[PatternMatcher])
4949

5050
/** Replace a value class method call by a call to the corresponding extension method.
5151
*

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ object Checking {
379379
}
380380
checkApplicable(UnusedType, !sym.is(UnusedType))
381381
if (sym.is(Unused)) {
382-
checkApplicable(Unused, !sym.is(MutableOrLazy))
382+
checkApplicable(Unused, sym.is(Param) || sym.is(ParamAccessor))
383383
if (sym.info.widen.finalResultType.isBottomType)
384384
fail("unused " + sym.showKind + " cannot have type Nothing")
385385
}

tests/neg/unused-1.scala

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,7 @@ object Test {
77
a // error
88
})
99
foo1(a) // OK
10-
foo2(a) // OK
11-
foo3(a) // OK
12-
a // warn
1310
a // error
14-
}
15-
unused def foo2(a: Int): Int = {
16-
foo0(a) // OK
17-
foo1(a) // OK
18-
foo2(a) // OK
19-
foo3(a) // OK
20-
a // warn
21-
a // OK
22-
}
23-
unused def foo3(unused a: Int): Int = {
24-
foo0(a) // OK
25-
foo1(a) // OK
26-
foo2(a) // OK
27-
foo3(a) // OK
28-
a // warn
29-
a // OK
11+
a // error
3012
}
3113
}

tests/neg/unused-2.scala

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)