Skip to content
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

Implement unused (renamed to ghost terms) #3342

Merged
merged 27 commits into from
Feb 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4b81aae
Implement unused values
nicolasstucki Oct 3, 2017
ea4c483
Transform unused arguments to dummy values in typer
nicolasstucki Nov 16, 2017
cf52f93
Re-enable unused vals and defs
nicolasstucki Nov 16, 2017
85f9a70
Move NoInit checks to Mixin
nicolasstucki Dec 11, 2017
5e774d7
Add modifier to NonEmptyFunction and replace ImplicitFunction
nicolasstucki Dec 11, 2017
68ea63c
Document UnusedFunctionN and UnusedImplicitFunctionN
nicolasstucki Dec 11, 2017
7474ba3
Reformat code of UnusedFunctions in NameOpts
nicolasstucki Dec 11, 2017
e755dd8
Remove unnecessary early removal of unused prameters
nicolasstucki Dec 12, 2017
2177da5
Move error for unused case accessors
nicolasstucki Dec 12, 2017
724bf9c
Improve error message
nicolasstucki Dec 12, 2017
f1aa9fc
Remove unnecessary removal of unused accessors
nicolasstucki Dec 12, 2017
ae8f80e
Remove Nothing restriction on unused parameters
nicolasstucki Dec 12, 2017
81ec565
Change error message
nicolasstucki Dec 12, 2017
dd661dd
Remove special case for unused closure
nicolasstucki Dec 12, 2017
6a40b84
Keep stable unused arguments
nicolasstucki Dec 12, 2017
aa53182
Add documentation
nicolasstucki Dec 12, 2017
aba82ba
Complete doc of FunArgMods
nicolasstucki Dec 13, 2017
7c4662d
Use set for funArgMods
nicolasstucki Dec 13, 2017
ce2d805
Add missing syntax for FunArgMods
nicolasstucki Dec 13, 2017
544c8d5
Improve variable names
nicolasstucki Dec 13, 2017
f10f2d0
Improve comment
nicolasstucki Dec 13, 2017
ddf5b2c
Fix synthetic methods of value class with unused params
nicolasstucki Dec 14, 2017
7f81bc3
Make Unused flag a term flag only
nicolasstucki Dec 14, 2017
c877473
Add rules
nicolasstucki Jan 12, 2018
ed50958
Fix documentation
nicolasstucki Jan 23, 2018
c040798
Move methods from UnusedUtil to Applications
nicolasstucki Jan 23, 2018
68afbcd
Update unused parameter doc
nicolasstucki Feb 21, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class Compiler {
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)
new VCInlineMethods, // Inlines calls to value class methods
new SeqLiterals, // Express vararg arguments as arrays
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ object desugar {
private def toDefParam(tparam: TypeDef): TypeDef =
tparam.withMods(tparam.rawMods & EmptyFlags | Param)
private def toDefParam(vparam: ValDef): ValDef =
vparam.withMods(vparam.rawMods & Implicit | Param)
vparam.withMods(vparam.rawMods & (Implicit | Unused) | Param)

/** The expansion of a class definition. See inline comments for what is involved */
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
Expand Down Expand Up @@ -825,7 +825,7 @@ object desugar {

def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
val params = makeImplicitParameters(formals.map(TypeTree))
new ImplicitFunction(params, body)
new NonEmptyFunction(params, body, Modifiers(Implicit))
}

/** Add annotation to tree:
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
* flags set.
*/
private def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel =
if (!tree.tpe.widen.isParameterless) SimplyPure
if (!tree.tpe.widen.isParameterless || tree.symbol.is(Unused)) SimplyPure
else if (!tree.symbol.isStable) Impure
else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start.
else SimplyPure
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
case tp: MethodType =>
def valueParam(name: TermName, info: Type): TermSymbol = {
val maybeImplicit = if (tp.isImplicitMethod) Implicit else EmptyFlags
ctx.newSymbol(sym, name, TermParam | maybeImplicit, info, coord = sym.coord)
val maybeUnused = if (tp.isUnusedMethod) Unused else EmptyFlags
ctx.newSymbol(sym, name, TermParam | maybeImplicit | maybeUnused, info, coord = sym.coord)
}
val params = (tp.paramNames, tp.paramInfos).zipped.map(valueParam)
val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef)))
Expand Down
9 changes: 5 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,14 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
*/
case class InterpolatedString(id: TermName, segments: List[Tree]) extends TermTree

/** A function type */
case class Function(args: List[Tree], body: Tree) extends Tree {
override def isTerm = body.isTerm
override def isType = body.isType
}

/** An implicit function type */
class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) {
override def toString = s"ImplicitFunction($args, $body)"
}
/** A function type that should have non empty args */
class NonEmptyFunction(args: List[Tree], body: Tree, val mods: Modifiers) extends Function(args, body)

/** A function created from a wildcard expression
* @param placeHolderParams a list of definitions of synthetic parameters
Expand Down Expand Up @@ -119,6 +118,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Implicit() extends Mod(Flags.ImplicitCommon)

case class Unused() extends Mod(Flags.Unused)

case class Final() extends Mod(Flags.Final)

case class Sealed() extends Mod(Flags.Sealed)
Expand Down
85 changes: 60 additions & 25 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class Definitions {
newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered
}

/** The trait FunctionN or ImplicitFunctionN, for some N
/** The trait FunctionN, ImplicitFunctionN, UnusedFunctionN or UnusedImplicitFunction, for some N
* @param name The name of the trait to be created
*
* FunctionN traits follow this template:
Expand All @@ -106,6 +106,20 @@ class Definitions {
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] {
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
* }
*
* UnusedFunctionN traits follow this template:
*
* trait UnusedFunctionN[T0,...,T{N-1}, R] extends Object {
* def apply(unused $x0: T0, ..., $x{N_1}: T{N-1}): R
* }
*
* UnusedImplicitFunctionN traits follow this template:
*
* trait UnusedImplicitFunctionN[T0,...,T{N-1}, R] extends Object with UnusedFunctionN[T0,...,T{N-1}, R] {
* def apply(unused implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
* }
*
* UnusedFunctionN and UnusedImplicitFunctionN erase to Function0.
*/
def newFunctionNTrait(name: TypeName): ClassSymbol = {
val completer = new LazyType {
Expand All @@ -117,18 +131,12 @@ class Definitions {
val argParamRefs = List.tabulate(arity) { i =>
enterTypeParam(cls, paramNamePrefix ++ "T" ++ (i + 1).toString, Contravariant, decls).typeRef
}
val resParam = enterTypeParam(cls, paramNamePrefix ++ "R", Covariant, decls)
val (methodType, parentTraits) =
if (name.firstPart.startsWith(str.ImplicitFunction)) {
val superTrait =
FunctionType(arity).appliedTo(argParamRefs ::: resParam.typeRef :: Nil)
(ImplicitMethodType, superTrait :: Nil)
}
else (MethodType, Nil)
val applyMeth =
decls.enter(
newMethod(cls, nme.apply,
methodType(argParamRefs, resParam.typeRef), Deferred))
val resParamRef = enterTypeParam(cls, paramNamePrefix ++ "R", Covariant, decls).typeRef
val methodType = MethodType.maker(isJava = false, name.isImplicitFunction, name.isUnusedFunction)
val parentTraits =
if (!name.isImplicitFunction) Nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the logic here (and suspect it might be wrong).

  • what is the parent of an UnusedFunction?
  • what of an ImplicitUnusedFunction?

Would be good to document this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add documentation.

else FunctionType(arity, isUnused = name.isUnusedFunction).appliedTo(argParamRefs ::: resParamRef :: Nil) :: Nil
decls.enter(newMethod(cls, nme.apply, methodType(argParamRefs, resParamRef), Deferred))
denot.info =
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
}
Expand Down Expand Up @@ -748,14 +756,14 @@ class Definitions {
sym.owner.linkedClass.typeRef

object FunctionOf {
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) =
FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil)
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false, isUnused: Boolean = false)(implicit ctx: Context) =
FunctionType(args.length, isImplicit, isUnused).appliedTo(args ::: resultType :: Nil)
def unapply(ft: Type)(implicit ctx: Context) = {
val tsym = ft.typeSymbol
if (isFunctionClass(tsym)) {
val targs = ft.dealias.argInfos
if (targs.isEmpty) None
else Some(targs.init, targs.last, tsym.name.isImplicitFunction)
else Some(targs.init, targs.last, tsym.name.isImplicitFunction, tsym.name.isUnusedFunction)
}
else None
}
Expand Down Expand Up @@ -819,20 +827,29 @@ class Definitions {

lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)

def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context) =
if (isImplicit) {
def FunctionClass(n: Int, isImplicit: Boolean = false, isUnused: Boolean = false)(implicit ctx: Context) = {
if (isImplicit && isUnused) {
require(n > 0)
ctx.requiredClass("scala.UnusedImplicitFunction" + n.toString)
}
else if (isImplicit) {
require(n > 0)
ctx.requiredClass("scala.ImplicitFunction" + n.toString)
}
else if (isUnused) {
require(n > 0)
ctx.requiredClass("scala.UnusedFunction" + n.toString)
}
else if (n <= MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
else ctx.requiredClass("scala.Function" + n.toString)
}

lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol

def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef =
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n)
else FunctionClass(n, isImplicit).typeRef
def FunctionType(n: Int, isImplicit: Boolean = false, isUnused: Boolean = false)(implicit ctx: Context): TypeRef =
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes) && !isUnused) ImplementedFunctionType(n)
else FunctionClass(n, isImplicit, isUnused).typeRef

private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet

Expand All @@ -857,14 +874,23 @@ class Definitions {
/** Is a function class.
* - FunctionN for N >= 0
* - ImplicitFunctionN for N > 0
* - UnusedFunctionN for N > 0
* - UnusedImplicitFunctionN for N > 0
*/
def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction

/** Is an implicit function class.
* - ImplicitFunctionN for N > 0
* - UnusedImplicitFunctionN for N > 0
*/
def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction

/** Is an unused function class.
* - UnusedFunctionN for N > 0
* - UnusedImplicitFunctionN for N > 0
*/
def isUnusedFunctionClass(cls: Symbol) = scalaClassName(cls).isUnusedFunction

/** Is a class that will be erased to FunctionXXL
* - FunctionN for N >= 22
* - ImplicitFunctionN for N >= 22
Expand All @@ -889,11 +915,14 @@ class Definitions {
* - FunctionN for 22 > N >= 0 remains as FunctionN
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
* - UnusedFunctionN becomes Function0
* - ImplicitUnusedFunctionN becomes Function0
* - anything else becomes a NoSymbol
*/
def erasedFunctionClass(cls: Symbol): Symbol = {
val arity = scalaClassName(cls).functionArity
if (arity > 22) FunctionXXLClass
if (cls.name.isUnusedFunction) FunctionClass(0)
else if (arity > 22) FunctionXXLClass
else if (arity >= 0) FunctionClass(arity)
else NoSymbol
}
Expand All @@ -903,12 +932,15 @@ class Definitions {
* - FunctionN for 22 > N >= 0 remains as FunctionN
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
* - UnusedFunctionN becomes Function0
* - ImplicitUnusedFunctionN becomes Function0
* - anything else becomes a NoType
*/
def erasedFunctionType(cls: Symbol): Type = {
val arity = scalaClassName(cls).functionArity
if (arity > 22) defn.FunctionXXLType
else if (arity >= 0) defn.FunctionType(arity)
if (cls.name.isUnusedFunction) FunctionType(0)
else if (arity > 22) FunctionXXLType
else if (arity >= 0) FunctionType(arity)
else NoType
}

Expand Down Expand Up @@ -976,7 +1008,7 @@ class Definitions {
def isNonDepFunctionType(tp: Type)(implicit ctx: Context) = {
val arity = functionArity(tp)
val sym = tp.dealias.typeSymbol
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol)
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction, sym.name.isUnusedFunction).typeSymbol)
}

/** Is `tp` a representation of a (possibly depenent) function type or an alias of such? */
Expand Down Expand Up @@ -1042,6 +1074,9 @@ class Definitions {
def isImplicitFunctionType(tp: Type)(implicit ctx: Context): Boolean =
asImplicitFunctionType(tp).exists

def isUnusedFunctionType(tp: Type)(implicit ctx: Context) =
isFunctionType(tp) && tp.dealias.typeSymbol.name.isUnusedFunction

// ----- primitive value class machinery ------------------------------------------

/** This class would also be obviated by the implicit function type design */
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ object Flags {
/** Symbol is a Java enum */
final val Enum = commonFlag(40, "<enum>")

/** Labeled with `unused` modifier (unused value) */
final val Unused = termFlag(42, "unused")

// Flags following this one are not pickled

/** Symbol is not a member of its owner */
Expand Down Expand Up @@ -438,7 +441,7 @@ object Flags {
/** Flags representing source modifiers */
final val SourceModifierFlags =
commonFlags(Private, Protected, Abstract, Final, Inline,
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic)
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic, Unused)

/** Flags representing modifiers that can appear in trees */
final val ModifierFlags =
Expand Down Expand Up @@ -512,7 +515,7 @@ object Flags {
/** Flags that can apply to a module val */
final val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
Override | Final | Method | Implicit | Lazy |
Accessor | AbsOverride | Stable | Captured | Synchronized | Inline
Accessor | AbsOverride | Stable | Captured | Synchronized | Inline | Unused

/** Flags that can apply to a module class */
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | ImplClass | Enum
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ object Mode {

/** We are in the IDE */
val Interactive = newMode(20, "Interactive")

}
28 changes: 25 additions & 3 deletions compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -174,31 +174,53 @@ object NameOps {
*/
def functionArity: Int =
functionArityFor(str.Function) max {
val n = functionArityFor(str.ImplicitFunction)
val n =
functionArityFor(str.ImplicitFunction) max
functionArityFor(str.UnusedFunction) max
functionArityFor(str.UnusedImplicitFunction)
if (n == 0) -1 else n
}

/** Is a function name
* - FunctionN for N >= 0
* - ImplicitFunctionN for N >= 1
* - UnusedFunctionN for N >= 1
* - UnusedImplicitFunctionN for N >= 1
* - false otherwise
*/
def isFunction: Boolean = functionArity >= 0

/** Is a implicit function name
* - ImplicitFunctionN for N >= 1
* - UnusedImplicitFunctionN for N >= 1
* - false otherwise
*/
def isImplicitFunction: Boolean = functionArityFor(str.ImplicitFunction) >= 1
def isImplicitFunction: Boolean = {
functionArityFor(str.ImplicitFunction) >= 1 ||
functionArityFor(str.UnusedImplicitFunction) >= 1
}

/** Is a implicit function name
* - UnusedFunctionN for N >= 1
* - UnusedImplicitFunctionN for N >= 1
* - false otherwise
*/
def isUnusedFunction: Boolean = {
functionArityFor(str.UnusedFunction) >= 1 ||
functionArityFor(str.UnusedImplicitFunction) >= 1
}

/** Is a synthetic function name
* - FunctionN for N > 22
* - ImplicitFunctionN for N >= 1
* - UnusedFunctionN for N >= 1
* - UnusedImplicitFunctionN for N >= 1
* - false otherwise
*/
def isSyntheticFunction: Boolean = {
functionArityFor(str.Function) > MaxImplementedFunctionArity ||
functionArityFor(str.ImplicitFunction) >= 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with isUnusedFunction || isImplicitFunction?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better

functionArityFor(str.Function) > MaxImplementedFunctionArity ||
        functionArityFor(str.ImplicitFunction) >= 1 ||
        isUnusedFunction

to avoid checking functionArityFor(str.UnusedImplicitFunction) >= 1 twice

functionArityFor(str.ImplicitFunction) >= 1 ||
isUnusedFunction
}

/** Parsed function arity for function with some specific prefix */
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ object StdNames {
final val MODULE_INSTANCE_FIELD = "MODULE$"

final val Function = "Function"
final val UnusedFunction = "UnusedFunction"
final val ImplicitFunction = "ImplicitFunction"
final val UnusedImplicitFunction = "UnusedImplicitFunction"
final val AbstractFunction = "AbstractFunction"
final val Tuple = "Tuple"
final val Product = "Product"
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 @@ -402,7 +402,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
Loading