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

Defer "pure expression .. in statement position" warnings until refchecks. #8995

Merged
merged 2 commits into from Jun 2, 2020

Conversation

retronym
Copy link
Member

This allows a macro to transform code that used to warn into something that
does not.

Conserve unchanged blocks and templates in the output
and avoid temporary lists.
@scala-jenkins scala-jenkins added this to the 2.12.12 milestone May 19, 2020
@retronym retronym requested review from lrytz and hrhino May 19, 2020 10:48
@retronym
Copy link
Member Author

/rebuild

@retronym retronym force-pushed the topic/pure-expression-defer branch from fa9d891 to 0bb09c6 Compare May 20, 2020 00:00
…ecks.

This allows a macro to transform code that used to warn into something that
does not.
@retronym retronym force-pushed the topic/pure-expression-defer branch from 0bb09c6 to 58407ea Compare May 20, 2020 01:11
@som-snytt
Copy link
Contributor

The macro use case corresponds to the linting mode. It's impossible to know in advance, for a given macro, whether before or after expansion should look "clean."

The "pure expr" check is narrow enough that post-expansion is clearly an error; but if the user writes

m { 42 ; 27 }

it depends on m. Alternatively, it's the responsibility of the macro to issue warnings?

The unused warnings are emitted after typer only because imports are tracked then.

$ scalac -Wmacros:help
Usage: -Wmacros:<mode> where <mode> choices are none, before, after, both (default: before).
  none    Do not inspect expansions or their original trees when generating unused symbol warnings.
  before  Only inspect unexpanded user-written code for unused symbols.
  after   Only inspect expanded trees when generating unused symbol warnings.
  both    Inspect both user-written code and expanded trees when generating unused symbol warnings.

@retronym
Copy link
Member Author

retronym commented May 21, 2020

My bias is removing false warnings (especially ones that users can't suppress), so I'm not overly worried about the subset of macros that transform warnable input into non-warnable output trees.

But, with this warning in refchecks, it would possible to hitch it to the -Wmacros option. However -Wmacro, is coarse grained, the right answer might depend on the type of warning (unused vs pure vs ...) and the macro involved.

An example of such a macro where we'd lose the warning is reify:

➜  scala-ref 2.12.x
Welcome to Scala 2.12.12-20200513-094353-8d19363 (OpenJDK 64-Bit Server VM, Java 1.8.0-adoptopenjdk).
Type in expressions for evaluation. Or try :help.

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> reify { 1; 1 }
<console>:15: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
       reify { 1; 1 }
               ^
res0: reflect.runtime.universe.Expr[Int] =
Expr[Int]({
  1;
  1
})

scala> :quit
➜  scala-pr 8995
Welcome to Scala 2.12.12-20200520-011129-58407ea (OpenJDK 64-Bit Server VM, Java 1.8.0-adoptopenjdk).
Type in expressions for evaluation. Or try :help.

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> reify { 1; 1 }
res0: reflect.runtime.universe.Expr[Int] =
Expr[Int]({
  1;
  1
})

A macro implementation that uses a warning reify, like above, would still result in warnings in client code.

➜  scala-ref 2.12.x
scala> import scala.language.experimental.macros; import scala.reflect.macros.blackbox.Context; def impl(c: Context): c.Tree = c.universe.reify { 1; 1 }.tree; def m: Int = macro impl
<console>:11: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
       import scala.language.experimental.macros; import scala.reflect.macros.blackbox.Context; def impl(c: Context): c.Tree = c.universe.reify { 1; 1 }.tree; def m: Int = macro impl
                                                                                                                                                  ^
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
impl: (c: scala.reflect.macros.blackbox.Context)c.Tree
defined term macro m: Int

scala> m
<console>:15: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
       m
       ^
➜   scala-pr 8995
scala> import scala.language.experimental.macros; import scala.reflect.macros.blackbox.Context; def impl(c: Context): c.Tree = c.universe.reify { 1; 1 }.tree; def m: Int = macro impl
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
impl: (c: scala.reflect.macros.blackbox.Context)c.Tree
defined term macro m: Int

scala> m
<console>:15: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
       m
       ^

@som-snytt
Copy link
Contributor

-Wmacros might work better as a -Wconf condition. Sorry to spam your PR. Under lockdown, I have to conserve post-it notes in case they run out at the stationer's.

@retronym
Copy link
Member Author

I appreciate the discussion! This is an aspect of the macro system that didn't have any up-front design so its hard to figure out the answers that balance strictness of warnings vs false positives and have good defaults that avoid the need for users to tweak compiler options.

Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

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

I think this is likely to be an improvement for most use cases, so let's book it, even if we're lacking a principled approach.

eed3si9n added a commit to eed3si9n/scala that referenced this pull request Aug 11, 2020
For some reason, in the recent releases "a pure expression does nothing in statement position" has gotten more accurate. Maybe because it moved to RefCheck in scala#8995?
Regardless, it's sometimes tricky to write `build.sbt` because often I would need to invoke multiple tasks such as `compile.value` and `copyResources.value` without using the return value, and it would cause "a pure expression does nothing in statement position" error.

As a workaround I propose `Predef.unit(a: Any): Unit = ()`, which forces the evaluation but discards it and returns the unit value, which the compiler seems to be happy with.
eed3si9n added a commit to eed3si9n/scala that referenced this pull request Aug 12, 2020
For some reason, in the recent releases "a pure expression does nothing in statement position" has gotten more accurate. Maybe because it moved to RefCheck in scala#8995?
Regardless, it's sometimes tricky to write `build.sbt` because often I would need to invoke multiple tasks such as `compile.value` and `copyResources.value` without using the return value, and it would cause "a pure expression does nothing in statement position" error.

As a workaround I propose `Predef.unit(a: Any): Unit = ()`, which forces the evaluation but discards it and returns the unit value, which the compiler seems to be happy with.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants