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

Conversation

nicolasstucki
Copy link
Contributor

@nicolasstucki nicolasstucki commented Oct 17, 2017

Decide or propose keyword that should be used for unused: https://goo.gl/forms/UD44AfLUD3dkfnxs2

TODO list:

  • Add unused keyword (it can be placed where implicit can be placed)
  • Check that unused terms are not used where they should not
  • Remove references to unused parameters, defs and vals
  • Remove unused parameters and arguments
  • Add UnusedFunctionN and ImplcitUnusedFunctionN for N>0
  • Fix tests/run/unused-2.scala with -optimise
  • Fix erasure of path dependent types on unused terms
  • Put UnusedParams in a miniphase group
  • User documentation
  • Update Scala doc
  • Complete frameless example
  • Remove phantoms and migrate tests (Remove scala.Phantom (replaced by unused) #3410)
  • Update GenericSignatures (needs rebase)
  • Make sure we decide a good keyword

@liufengyun
Copy link
Contributor

test performance please

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 0 running.

@dottybot
Copy link
Member

performance test failed:

Error line number: 16

[check /data/workspace/bench/logs/pull-3342-10-19-18.14.out for more information]

@nicolasstucki
Copy link
Contributor Author

test performance please

@dottybot
Copy link
Member

performance test scheduled: 2 job(s) in queue, 1 running.

@dottybot
Copy link
Member

performance test failed:

Error line number: 16

[check /data/workspace/bench/logs/pull-3342-10-20-18.49.out for more information]

@liufengyun
Copy link
Contributor

I just checked the logs, this PR failed scalapb.

@nicolasstucki
Copy link
Contributor Author

Rebased and changed all MiniPhaseTransforms with MegaPhase.MiniPhase.

@odersky
Copy link
Contributor

odersky commented Oct 29, 2017

I would add to the TODO list:

  • remove phantom types, and migrate tests over where they make sense

nicolasstucki added a commit to dotty-staging/dotty that referenced this pull request Oct 31, 2017
@nicolasstucki nicolasstucki force-pushed the implement-unused branch 2 times, most recently from ea541a6 to bd4b6dd Compare October 31, 2017 14:16
Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

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

More to come...

/* Tree transform */

override def transformTypeDef(tree: TypeDef)(implicit ctx: Context): tree.type = {
if (tree.symbol.is(UnusedCommon))
Copy link
Contributor

Choose a reason for hiding this comment

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

is(Unused). UnusedCommon should never be used in an is check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


override def transformTypeDef(tree: TypeDef)(implicit ctx: Context): tree.type = {
if (tree.symbol.is(UnusedCommon))
ctx.error(tree.symbol.showKind + " cannot be unused", tree.pos)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this belongs not in this phase, but where we check the other flags whether they make sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

}

private def isUnusedContext(implicit ctx: Context): Boolean =
ctx.owner.ownersIterator.exists(_.is(Unused)) // TODO make context mode?
Copy link
Contributor

Choose a reason for hiding this comment

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

Or put it in the store, yes.

@@ -0,0 +1,102 @@
package dotty.tools.dotc.transform

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's better to implement these checks as a top-down traversal in PostTyper. It's inherently a top-down algorithm not a bottom-up one.

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 try this in PostTyper. My first approach was in typer and I quickly realized it was a mistake because these failures influenced typing desitions. Hence decided to do it in an isolated miniphase.

@@ -2,17 +2,17 @@ package dotty.tools.dotc.transform

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.NameKinds._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
import dotty.tools.dotc.typer.EtaExpansion

import scala.collection.mutable.ListBuffer

Copy link
Contributor

Choose a reason for hiding this comment

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

We need to have a discussion whether we want to do this. The whole point of unused is to avoid generating code. So if we lift most arguments we miss that point. If we assume a precise effect analysis we could lift arguments if the argument is pure only. But we do not have such an effect analysis.

An alternative is to always drop unused arguments. In a sense unused arguments are then treated similarly to pattern guards - the compiler is allowed to assume they are pure. In the future, if we have a good effect analysis we could also require that unused arguments are pure (and total?).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now I'm convinced that we should not evaluate any of the arguments in an unused method. This implies that unused implicits only check for the existence of such implicit and then disregard its computation. For unused arguments that are explicitly stated we should drop them as well but if they are not pure we will emit a warning.
We should not require them to be pure or total, but rather use those as hints in the warnings to point that proof might be unsafe.

def f(unused x: A): B = ???
def g()(unused implicit x: A): B = ???

f(pureA) // pureA is not evaluated
g() // the implicit A for x is not evaluated
g()(pureA) // pureA is not evaluated

f(impureA) // warn that impureA will not be evaluated
g()(impureA) // the implicit A for x is not evaluated

/** This phase removes all references and calls to unused methods or vals
*
* if `unused def f(x1,...,xn): T = ...`
* then `f(y1,...,yn)` --> `y1; ...; yn; (default value for T)`
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use ??? instead of default value? If something goes wrong later you will fail loudly instead of silently, which is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These default values are intended to stay there, it is the simplest tree the can be both used when f(...) is a statement or an expression. These are trivially eliminated by the optimizer.
This way UnusedRefs does not need to know and special case unused function applications in different positions. The same applies to member selection and identifiers.

@nicolasstucki nicolasstucki force-pushed the implement-unused branch 5 times, most recently from f7247a2 to a06d1a1 Compare November 17, 2017 15:38
@nicolasstucki
Copy link
Contributor Author

@odersky all changes have been made. The PR is ready for review.

@nicolasstucki
Copy link
Contributor Author

Rebase

@Blaisorblade
Copy link
Contributor

Just voted in the poll (is it advertised more widely?).

FWIW, to me unused suggests not that the parameter is erased, but that it is simply not used (but the compiler shouldn't warn me please).

Also, we might want to erase them even though they seem to be used. The current semantics prevent from making =:= erased (as soon as you call its apply method); yesterday I talked with Martin about how that could be fixed by marking as inline methods like apply.

present at runtime to be able to do separate compilation and retain binary compatiblity. Unused parameters are contractually
obligated to not be used at runtime, enforcing the essence of evidences on types and allows them to always be optimized away.

present at runtime to be able to do separate compilation and retain binary compatiblity.
Copy link
Contributor

Choose a reason for hiding this comment

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

present at runtime --> present in some form in the generated code

@odersky odersky assigned nicolasstucki and unassigned odersky Feb 21, 2018
@nicolasstucki
Copy link
Contributor Author

test performance please

@dottybot
Copy link
Member

performance test scheduled: 3 job(s) in queue, 1 running.

@dottybot
Copy link
Member

performance test failed:

Error line number: 24

[check /data/workspace/bench/logs/pull-3342-02-21-14.37.out for more information]

@nicolasstucki nicolasstucki merged commit 8d07271 into scala:master Feb 21, 2018
@allanrenucci allanrenucci deleted the implement-unused branch February 21, 2018 17:31
@joan38
Copy link
Contributor

joan38 commented Mar 1, 2018

Really weired name to call this unused, for me unused == useless == bin it
It also clashes with -Ywarn-unused.
I would have prefered something like erased or phantom.

@nicolasstucki
Copy link
Contributor Author

I also want to change it. erased is not the best name as there is the concept of erasure on types that is quite different. The best contenders are phantom or ghost.

@Blaisorblade
Copy link
Contributor

Any chance to pick another name, maybe even before the release, since apparently nobody likes unused?

@nicolasstucki
Copy link
Contributor Author

I could make the change tomorrow morning.

@nicolasstucki nicolasstucki changed the title Implement unused Implement unused (renamed to ghost terms) Mar 2, 2018
@nicolasstucki
Copy link
Contributor Author

Renamed to ghost in #4061

println(fun(new Bar))
}

def fun(unused foo: Foo): foo.X = {
Copy link
Contributor

Choose a reason for hiding this comment

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

This tests is unsound because it treats ghost values as stable (#4060) /cc @nicolasstucki

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants