scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.84k stars 1.05k forks source link

Value discard warning in one-limbed if #20450

Open som-snytt opened 4 months ago

som-snytt commented 4 months ago

Compiler version

3.5

Minimized code

$ ~/projects/dotty/bin/scala -Wnonunit-statement -Wvalue-discard
Welcome to Scala 3.5.1-RC1-bin-SNAPSHOT-git-4636ce2 (21.0.2, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> import scala.util.chaining.given

scala> val it = Iterator.continually(42)
val it: Iterator[Int] = <iterator>

scala> it.hasNext.tap(if (_) it.next())
1 warning found
-- [E175] Potential Issue Warning: -------------------------------------------------------------------------------------
1 |it.hasNext.tap(if (_) it.next())
  |                      ^^^^^^^^^
  |                      discarded non-Unit value of type Int
val res0: Boolean = true

scala>

Output

It warns on one-legged or one-armed if.

Expectation

Scala 2 decides not to warn, because one-limbed if is intentionally a value-discarding Unit expression.

Gedochao commented 4 months ago

note: the same behaviour can be reproduced outside of REPL.

//> using options -Wnonunit-statement -Wvalue-discard
import scala.util.chaining.given
@main def main = {
  val it = Iterator.continually(42)
  it.hasNext.tap(if (_) it.next())
}
nox213 commented 1 month ago

Scala 2 also still gives a warning in this case, right?

som-snytt commented 1 month ago

@nox213 why would you say that.

➜  ~ scala -Wnonunit-statement -Wvalue-discard
Welcome to Scala 2.13.14 (OpenJDK 64-Bit Server VM, Java 22.0.2).
Type in expressions for evaluation. Or try :help.

scala> import scala.util.chaining._
import scala.util.chaining._

scala> val it = Iterator.continually(42)
val it: Iterator[Int] = <iterator>

scala> it.hasNext.tap(if (_) it.next())
val res0: Boolean = true

scala>
nox213 commented 1 month ago

Thanks for double checking, I was confused

nox213 commented 1 month ago
class Boxed[A](a: A) {
  def isEmpty = false
  def foreach[U](f: A => U): Unit =
    if (!isEmpty) f(a) // warn, check is on
}

In Scala 2, it warns for the code above when compiled with the -Wvalue-discard flag. Is this a different case from the example you provided?

som-snytt commented 1 month ago

@nox213 yes, the expected type matters.

From the previous example, compare:

  def f(): Unit = it.hasNext.tap(if (_) it.next())
  def g() = it.hasNext.tap(if (_) it.next())

In Scala 3, if (b) expr is typed as Unit, i.e., discarding.

In Scala 2, if (b) expr is typed as if (b) expr else (), the LUB of expr and Unit.

nox213 commented 1 month ago

Thanks for the explanation!