scala / scala3

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

Sequence arg in interpolation is rejected #14564

Closed som-snytt closed 2 years ago

som-snytt commented 2 years ago

Compiler version

3.1.1

Minimized code

➜  ~ scala
Welcome to Scala 3.1.1 (17.0.2, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> extension (sc: StringContext) def sum(xs: Int*): String = xs.sum.toString
def sum(sc: StringContext)(xs: Int*): String

scala> sum"${ List(42)* }"
-- Error: ---------------------------------------------------------------------------------------------------------------------------------------
1 |sum"${ List(42)* }"
  |       ^^^^^^^^
  |       postfix operator `*` needs to be enabled
  |       by making the implicit value scala.language.postfixOps visible.
  |       ----
  |       This can be achieved by adding the import clause 'import scala.language.postfixOps'
  |       or by setting the compiler option -language:postfixOps.
  |       See the Scaladoc for value scala.language.postfixOps for a discussion
  |       why the feature needs to be explicitly enabled.
1 error found

scala> sum"${ List(42): _* }"
-- [E031] Syntax Error: -------------------------------------------------------------------------------------------------------------------------
1 |sum"${ List(42): _* }"
  |                 ^
  |                 * can be used only for last argument

longer explanation available when compiling with `-explain`

Output

DNC

Expectation

➜  ~ scala
Welcome to Scala 2.13.8 (OpenJDK 64-Bit Server VM, Java 17.0.2).
Type in expressions for evaluation. Or try :help.

scala> implicit class sum(val sc: StringContext) { def sum(xs: Int*) = xs.sum.toString }
class sum

scala> sum"${ List(42): _* }"
val res0: String = 42
nicolasstucki commented 2 years ago

This is not allowed in Scala 2. It fails with the error repeated argument not allowed here.

nicolasstucki commented 2 years ago

Given that we can do

extension (sc: StringContext) def sum(xs: Seq[Int]): String = xs.sum.toString
def test(xs: List[Int]) = sum"${ xs }"

but not

extension (sc: StringContext) def sum(xs: Int*): String = xs.sum.toString
def test = sum"${ 1, 2, 3 }"

it seems natural to not allow the variable arguments in string interpolators.

nicolasstucki commented 2 years ago

We need a better error message

nicolasstucki commented 2 years ago
extension (sc: StringContext) def sum(xs: Int*): String = xs.sum.toString
def test1(xs: List[Int]) = sum"${ xs* }" // needs fix in desugar
def test2(xs: List[Int]) = sum"${ xs: _* }" // needs fix in parser

See

som-snytt commented 2 years ago

The test on Scala 2 is here

I noticed the behavior backporting Scala 3 messages.

I think you mean the macro expansion of s is broken on Scala 2.

scala> implicit class summer(val sc: StringContext) { def sum(xs: Int*) = xs.mkString("+") + "=" + xs.sum.toString }
class summer

scala> sum"${42}${17}"
val res0: String = 42+17=59

scala> sum"${List(42,27): _*}"
val res1: String = 42+27=69

scala> s"${List(42,27): _*}"
                      ^
       error: no `: _*` annotation allowed here
       (such annotations are only allowed in arguments to *-parameters)

// val res1: String = ("".+((scala.`package`.List.apply[Int](42, 27): _*)): String);
nicolasstucki commented 2 years ago

Strange. I tried the same example in Scastie for Scala 2.13.8 and it failed

implicit class StringContextOps(val sc: StringContext) extends AnyVal {
  def sum(xs: Int*): String = xs.sum.toString
}
def test = sum"${List(42,27): _*}" // error: repeated argument not allowed here

See https://scastie.scala-lang.org/KP2jZBi0TkmVEA24VeOUTQ

Could it be a bug in the Scala 2 repl?

nicolasstucki commented 2 years ago

In Scala 3 the repl parser is consistent with the normal parser.

som-snytt commented 2 years ago

@nicolasstucki that is a ScalametaParser message

./scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala:          syntaxError("repeated argument not allowed here", at = arg.tokens.last.prev)