Skip to content

Tighten restriction for varargs splices #11423

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

Merged
merged 1 commit into from
Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 22 additions & 9 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -509,14 +509,22 @@ trait Applications extends Compatibility {
if (success) formals match {
case formal :: formals1 =>

def checkNoVarArg(arg: Arg) =
if !ctx.isAfterTyper && isVarArg(arg) then
val addendum =
if formal.isRepeatedParam then
i"it is not the only argument to be passed to the corresponding repeated parameter $formal"
else
i"the corresponding parameter has type $formal which is not a repeated parameter type"
fail(em"Sequence argument type annotation `*` cannot be used here:\n$addendum", arg)

/** Add result of typing argument `arg` against parameter type `formal`.
* @return The remaining formal parameter types. If the method is parameter-dependent
* this means substituting the actual argument type for the current formal parameter
* in the remaining formal parameters.
*/
def addTyped(arg: Arg, formal: Type): List[Type] =
if !ctx.isAfterTyper && isVarArg(arg) && !formal.isRepeatedParam then
fail(i"Sequence argument type annotation `: _*` cannot be used here: the corresponding parameter has type $formal which is not a repeated parameter type", arg)
def addTyped(arg: Arg): List[Type] =
if !formal.isRepeatedParam then checkNoVarArg(arg)
addArg(typedArg(arg, formal), formal)
if methodType.isParamDependent && typeOfArg(arg).exists then
// `typeOfArg(arg)` could be missing because the evaluation of `arg` produced type errors
Expand Down Expand Up @@ -553,31 +561,36 @@ trait Applications extends Compatibility {
def implicitArg = implicitArgTree(formal, appPos.span)

if !defaultArg.isEmpty then
matchArgs(args1, addTyped(treeToArg(defaultArg), formal), n + 1)
matchArgs(args1, addTyped(treeToArg(defaultArg)), n + 1)
else if methodType.isContextualMethod && ctx.mode.is(Mode.ImplicitsEnabled) then
matchArgs(args1, addTyped(treeToArg(implicitArg), formal), n + 1)
matchArgs(args1, addTyped(treeToArg(implicitArg)), n + 1)
else
missingArg(n)
}

if (formal.isRepeatedParam)
args match {
case arg :: Nil if isVarArg(arg) =>
addTyped(arg, formal)
addTyped(arg)
case (arg @ Typed(Literal(Constant(null)), _)) :: Nil if ctx.isAfterTyper =>
addTyped(arg, formal)
addTyped(arg)
case _ =>
val elemFormal = formal.widenExpr.argTypesLo.head
val typedArgs =
harmonic(harmonizeArgs, elemFormal)(args.map(typedArg(_, elemFormal)))
harmonic(harmonizeArgs, elemFormal) {
args.map { arg =>
checkNoVarArg(arg)
typedArg(arg, elemFormal)
}
}
typedArgs.foreach(addArg(_, elemFormal))
makeVarArg(args.length, elemFormal)
}
else args match {
case EmptyTree :: args1 =>
tryDefault(n, args1)
case arg :: args1 =>
matchArgs(args1, addTyped(arg, formal), n + 1)
matchArgs(args1, addTyped(arg), n + 1)
case nil =>
tryDefault(n, args)
}
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/changed-features/vararg-splices.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ title: "Vararg Splices"
The syntax of vararg splices in patterns and function arguments has changed. The new syntax uses a postfix `*`, analogously to how a vararg parameter is declared.

```scala
val arr = Array(1, 2, 3)
val lst = List(0, arr*) // vararg splice argument
val arr = Array(0, 1, 2, 3)
val lst = List(arr*) // vararg splice argument
lst match
case List(0, 1, xs*) => println(xs) // binds xs to Seq(2, 3)
case List(1, _*) => // wildcard pattern
Expand All @@ -16,7 +16,7 @@ lst match
The old syntax for splice arguments will be phased out.

```scala
/*!*/ val lst = List(0, arr: _*) // syntax error
/*!*/ val lst = List(arr: _*) // syntax error
lst match
case List(1, 2, xs @ _*) // ok, equivalent to `xs*`
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext
val message = named.find(_.name.get == "message")
val since = named.find(_.name.get == "since")

val content = Seq(
since.map(s => code("[Since version ", parameter(s), "] ")),
message.map(m => parameter(m)),
m.docs.map(_.deprecated.toSeq.map(renderDocPart)):_*
val content = (
Seq(
since.map(s => code("[Since version ", parameter(s), "] ")),
message.map(m => parameter(m)))
++ m.docs.map(_.deprecated.toSeq.map(renderDocPart))
).flatten
Seq(dt("Deprecated"), dd(content:_*))
}
Expand Down
7 changes: 7 additions & 0 deletions tests/neg/i11419.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Test {
def main(args: Array[String]): Unit = {
val arr: Array[String] = Array("foo")
val lst = List("x", arr: _*) // error
println(lst)
}
}
3 changes: 0 additions & 3 deletions tests/pos/i5039.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@ class I0 {
List(null:_*)
List[Null](null:_*)
List[Nothing](null:_*)
List(1, 2, null:_*)
List(null, null:_*)
List(???, null:_*)
}
6 changes: 6 additions & 0 deletions tests/pos/reference/vararg-patterns.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ object t1 extends App {
println(xs)
val List(1, 2, _ *) = List(1, 2, 3)
}
@main def Test =
val arr = Array(0, 1, 2, 3)
val lst = List(arr*) // vararg splice argument
lst match
case List(0, 1, xs*) => println(xs) // binds xs to Seq(2, 3)
case List(1, _*) => // wildcard pattern