Skip to content

Commit 106749e

Browse files
committed
Fix #4785: Change typing rule for wildcard star args
We typecheck them without expected type to avoid relying on the implicit conversion from Array to Seq defined in Predef. ElimRepeated does the necessary adaptations (i.e. seqToArray and arrayToSeq conversions).
1 parent 4121bd6 commit 106749e

File tree

6 files changed

+54
-12
lines changed

6 files changed

+54
-12
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ class TypeApplications(val self: Type) extends AnyVal {
497497
}
498498

499499
/** The element type of a sequence or array */
500-
def elemType(implicit ctx: Context): Type = self match {
500+
def elemType(implicit ctx: Context): Type = self.widenDealias match {
501501
case defn.ArrayOf(elemtp) => elemtp
502502
case JavaArrayType(elemtp) => elemtp
503503
case _ => self.baseType(defn.SeqClass).argInfos.headOption.getOrElse(NoType)

compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,14 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
7878
val args1 = tree.args.zipWithConserve(formals) { (arg, formal) =>
7979
arg match {
8080
case arg: Typed if isWildcardStarArg(arg) =>
81-
if (tree.fun.symbol.is(JavaDefined) && arg.expr.tpe.derivesFrom(defn.SeqClass))
81+
val isJavaDefined = tree.fun.symbol.is(JavaDefined)
82+
val tpe = arg.expr.tpe
83+
if (isJavaDefined && tpe.derivesFrom(defn.SeqClass))
8284
seqToArray(arg.expr, formal.underlyingIfRepeated(isJava = true))
83-
else arg.expr
85+
else if (!isJavaDefined && tpe.derivesFrom(defn.ArrayClass))
86+
arrayToSeq(arg.expr)
87+
else
88+
arg.expr
8489
case arg => arg
8590
}
8691
}
@@ -91,12 +96,10 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
9196
private def seqToArray(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree match {
9297
case SeqLiteral(elems, elemtpt) =>
9398
JavaSeqLiteral(elems, elemtpt)
94-
case app@Apply(fun, args) if defn.WrapArrayMethods().contains(fun.symbol) => // rewrite a call to `wrapXArray(arr)` to `arr`
95-
args.head
9699
case _ =>
97100
val elemType = tree.tpe.elemType
98101
var elemClass = elemType.classSymbol
99-
if (defn.NotRuntimeClasses contains elemClass) elemClass = defn.ObjectClass
102+
if (defn.NotRuntimeClasses.contains(elemClass)) elemClass = defn.ObjectClass
100103
ref(defn.DottyArraysModule)
101104
.select(nme.seqToArray)
102105
.appliedToType(elemType)
@@ -105,6 +108,10 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
105108
// Because of phantomclasses, the Java array's type might not conform to the return type
106109
}
107110

111+
/** Convert Java array argument to Scala Seq */
112+
private def arrayToSeq(tree: Tree)(implicit ctx: Context): Tree =
113+
tpd.wrapArray(tree, tree.tpe.elemType)
114+
108115
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context): Tree =
109116
transformTypeOfTree(tree)
110117

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,12 @@ trait TypeAssigner {
153153
if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) checkNoPrivateLeaks(sym, pos)
154154
else sym.info
155155

156-
def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree =
157-
Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass)))
156+
private def toRepeated(tree: Tree, from: ClassSymbol)(implicit ctx: Context): Tree =
157+
Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(from, defn.RepeatedParamClass)))
158+
159+
def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.SeqClass)
160+
161+
def arrayToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.ArrayClass)
158162

159163
/** A denotation exists really if it exists and does not point to a stale symbol. */
160164
final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ class Typer extends Namer
546546
}
547547
case _ => ifExpr
548548
}
549+
549550
def ascription(tpt: Tree, isWildcard: Boolean) = {
550551
val underlyingTreeTpe =
551552
if (isRepeatedParamType(tpt)) TypeTree(defn.SeqType.appliedTo(pt :: Nil))
@@ -557,11 +558,26 @@ class Typer extends Namer
557558
else typed(tree.expr, tpt.tpe.widenSkolem)
558559
assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe)
559560
}
560-
if (untpd.isWildcardStarArg(tree))
561+
562+
if (untpd.isWildcardStarArg(tree)) {
563+
def typedWildcardStarArgExpr = {
564+
val tpdExpr = typedExpr(tree.expr)
565+
tpdExpr.tpe.widenDealias match {
566+
case defn.ArrayOf(_) =>
567+
val starType = defn.ArrayType.appliedTo(WildcardType)
568+
val exprAdapted = adapt(tpdExpr, starType)
569+
arrayToRepeated(exprAdapted)
570+
case _ =>
571+
val starType = defn.SeqType.appliedTo(defn.AnyType)
572+
val exprAdapted = adapt(tpdExpr, starType)
573+
seqToRepeated(exprAdapted)
574+
}
575+
}
561576
cases(
562577
ifPat = ascription(TypeTree(defn.RepeatedParamType.appliedTo(pt)), isWildcard = true),
563-
ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType.appliedTo(defn.AnyType))),
578+
ifExpr = typedWildcardStarArgExpr,
564579
wildName = nme.WILDCARD_STAR)
580+
}
565581
else {
566582
def typedTpt = checkSimpleKinded(typedType(tree.tpt))
567583
def handlePattern: Tree = {

tests/neg/repeatedArgs213.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ class repeatedArgs {
3333
bar("a", "b", "c")
3434
bar(xs: _*)
3535
bar(ys: _*) // error: immutable.Seq expected, found Seq
36-
bar(zs: _*) // error: immutable.Seq expected, found Array
36+
bar(zs: _*) // old-error: Remove (compiler generated) Array to Seq convertion in 2.13?
3737

3838
Paths.get("Hello", "World")
3939
Paths.get("Hello", xs: _*)
4040
Paths.get("Hello", ys: _*) // error: immutable.Seq expected, found Seq
41-
Paths.get("Hello", zs: _*) // error: immutable.Seq expected, found Array
41+
Paths.get("Hello", zs: _*)
4242
}
4343
}

tests/pos/i4785.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.Predef.Set // unimport Predef.wrapRefArray
2+
3+
import java.nio.file.Paths
4+
5+
class i4785 {
6+
def bar(xs: String*) = xs.length
7+
8+
def test(xs: Seq[String], ys: Array[String]) = {
9+
Paths.get("Hello", xs: _*)
10+
Paths.get("Hello", ys: _*)
11+
12+
bar(xs: _*)
13+
bar(ys: _*)
14+
}
15+
}

0 commit comments

Comments
 (0)