Skip to content

Commit 2c5a029

Browse files
committed
Take expected type into account when typing a sequence argument
Given an expected type `*T`, we allow a sequence argument `xs: _*` to be either a `Seq[T]` or an `Array[_ <: T]`, irrespective of whether the method we're calling is a Java or Scala method. So far we typed sequence arguments without an expected type, meaning that adaptation did not take place. But thanks to #8635, we can type them with an expected type of `Seq[T] | Array[_ <: T]` and type inference works out. This is what this commit does.
1 parent 1a9c9eb commit 2c5a029

File tree

7 files changed

+59
-14
lines changed

7 files changed

+59
-14
lines changed

compiler/src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,16 @@ class TypeApplications(val self: Type) extends AnyVal {
389389
}
390390

391391
/** If this is a repeated parameter `*T`, translate it to either `Seq[T]` or
392-
* `Array[? <: T]` depending on the value of `toArray`, keep other types as
393-
* they are.
392+
* `Array[? <: T]` depending on the value of `toArray`.
393+
* Additionally, if `translateWildcard` is true, a wildcard type
394+
* will be translated to `*<?>`.
395+
* Other types are kept as-is.
394396
*/
395-
def translateFromRepeated(toArray: Boolean)(using Context): Type =
396-
if self.isRepeatedParam then
397-
val seqClass = if (toArray) defn.ArrayClass else defn.SeqClass
397+
def translateFromRepeated(toArray: Boolean, translateWildcard: Boolean = false)(using Context): Type =
398+
val seqClass = if (toArray) defn.ArrayClass else defn.SeqClass
399+
if translateWildcard && self.isInstanceOf[WildcardType] then
400+
seqClass.typeRef.appliedTo(WildcardType)
401+
else if self.isRepeatedParam then
398402
// We want `Array[? <: T]` because arrays aren't covariant until after
399403
// erasure. See `tests/pos/i5140`.
400404
translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = toArray)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -728,19 +728,20 @@ class Typer extends Namer
728728

729729
if (untpd.isWildcardStarArg(tree)) {
730730
def typedWildcardStarArgExpr = {
731+
// A sequence argument `xs: _*` can be either a `Seq[T]` or an `Array[_ <: T]`,
732+
// irrespective of whether the method we're calling is a Java or Scala method,
733+
// so the expected type is the union `Seq[T] | Array[_ <: T]`.
731734
val ptArg =
732-
if (ctx.mode.is(Mode.QuotedPattern)) pt.translateFromRepeated(toArray = false)
733-
else WildcardType
735+
// FIXME(#8680): Quoted patterns do not support Array repeated arguments
736+
if (ctx.mode.is(Mode.QuotedPattern)) pt.translateFromRepeated(toArray = false, translateWildcard = true)
737+
else pt.translateFromRepeated(toArray = false, translateWildcard = true) |
738+
pt.translateFromRepeated(toArray = true, translateWildcard = true)
734739
val tpdExpr = typedExpr(tree.expr, ptArg)
735740
tpdExpr.tpe.widenDealias match {
736741
case defn.ArrayOf(_) =>
737-
val starType = defn.ArrayType.appliedTo(WildcardType)
738-
val exprAdapted = adapt(tpdExpr, starType)
739-
arrayToRepeated(exprAdapted)
742+
arrayToRepeated(tpdExpr)
740743
case _ =>
741-
val starType = defn.SeqType.appliedTo(defn.AnyType)
742-
val exprAdapted = adapt(tpdExpr, starType)
743-
seqToRepeated(exprAdapted)
744+
seqToRepeated(tpdExpr)
744745
}
745746
}
746747
cases(

tests/pos/case-signature.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// If `translateFromRepeated` translated wildcards by default, the following
2+
// would break because of the use of wildcards in signatures.
3+
case class Benchmark[A](params: List[A],
4+
sqlInsert: (benchId: Long, params: A, session: Int) => Unit,
5+
fun: List[A])

tests/pos/sequence-argument/B_2.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import scala.reflect.ClassTag
2+
import scala.language.implicitConversions
3+
4+
object B {
5+
def doubleSeq[T](x: T): Seq[T] = Seq(x, x)
6+
def doubleArray[T: ClassTag](x: T): Array[T] = Array(x, x)
7+
8+
def box(args: Integer*): Unit = {}
9+
def widen(args: Long*): Unit = {}
10+
def conv(args: Y*): Unit = {}
11+
12+
box(doubleSeq(1): _*)
13+
box(doubleArray(1): _*)
14+
Java_2.box(doubleSeq(1): _*)
15+
Java_2.box(doubleArray(1): _*)
16+
17+
widen(doubleSeq(1): _*)
18+
widen(doubleArray(1): _*)
19+
Java_2.widen(doubleSeq(1): _*)
20+
Java_2.widen(doubleArray(1): _*)
21+
22+
implicit def xToY(x: X): Y = new Y
23+
val x: X = new X
24+
conv(doubleSeq(x): _*)
25+
conv(doubleArray(x): _*)
26+
Java_2.conv(doubleSeq(x): _*)
27+
Java_2.conv(doubleArray(x): _*)
28+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public class Java_2 {
2+
public static void box(Integer ...args) {}
3+
public static void widen(Long... args) {}
4+
public static void conv(Y... args) {}
5+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class X
2+
class Y

0 commit comments

Comments
 (0)