Skip to content

Fix #4785: change typing rule for _* args #4787

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

Closed
wants to merge 2 commits into from
Closed
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
5 changes: 0 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -345,11 +345,6 @@ class Definitions {
def Predef_classOf(implicit ctx: Context) = Predef_classOfR.symbol
lazy val Predef_undefinedR = ScalaPredefModule.requiredMethodRef("???")
def Predef_undefined(implicit ctx: Context) = Predef_undefinedR.symbol
// The set of all wrap{X, Ref}Array methods, where X is a value type
val Predef_wrapArray = new PerRun[collection.Set[Symbol]]({ implicit ctx =>
val methodNames = ScalaValueTypes.map(TreeGen.wrapArrayMethodName) + nme.wrapRefArray
methodNames.map(ScalaPredefModule.requiredMethodRef(_).symbol)
})

lazy val ScalaRuntimeModuleRef = ctx.requiredModuleRef("scala.runtime.ScalaRunTime")
def ScalaRuntimeModule(implicit ctx: Context) = ScalaRuntimeModuleRef.symbol
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ class TypeApplications(val self: Type) extends AnyVal {
}

/** The element type of a sequence or array */
def elemType(implicit ctx: Context): Type = self match {
def elemType(implicit ctx: Context): Type = self.widenDealias match {
case defn.ArrayOf(elemtp) => elemtp
case JavaArrayType(elemtp) => elemtp
case _ => self.baseType(defn.SeqClass).argInfos.headOption.getOrElse(NoType)
Expand Down
19 changes: 13 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,14 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
val args1 = tree.args.zipWithConserve(formals) { (arg, formal) =>
arg match {
case arg: Typed if isWildcardStarArg(arg) =>
if (tree.fun.symbol.is(JavaDefined) && arg.expr.tpe.derivesFrom(defn.SeqClass))
seqToArray(arg.expr, formal.underlyingIfRepeated(isJava = true))
else arg.expr
val isJavaDefined = tree.fun.symbol.is(JavaDefined)
val expr = arg.expr
if (isJavaDefined && expr.tpe.derivesFrom(defn.SeqClass))
seqToArray(expr, formal.underlyingIfRepeated(isJava = true))
else if (!isJavaDefined && expr.tpe.derivesFrom(defn.ArrayClass))
arrayToSeq(expr)
else
expr
case arg => arg
}
}
Expand All @@ -98,12 +103,10 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
private def seqToArray(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree match {
case SeqLiteral(elems, elemtpt) =>
JavaSeqLiteral(elems, elemtpt)
case app@Apply(fun, args) if defn.Predef_wrapArray().contains(fun.symbol) => // rewrite a call to `wrapXArray(arr)` to `arr`
args.head
case _ =>
val elemType = tree.tpe.elemType
var elemClass = elemType.classSymbol
if (defn.NotRuntimeClasses contains elemClass) elemClass = defn.ObjectClass
if (defn.NotRuntimeClasses.contains(elemClass)) elemClass = defn.ObjectClass
ref(defn.DottyArraysModule)
.select(nme.seqToArray)
.appliedToType(elemType)
Expand All @@ -112,6 +115,10 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
// Because of phantomclasses, the Java array's type might not conform to the return type
}

/** Convert Java array argument to Scala Seq of type `pt` */
private def arrayToSeq(tree: Tree)(implicit ctx: Context): Tree =
TreeGen.wrapArray(tree, tree.tpe.elemType)

override def transformTypeApply(tree: TypeApply)(implicit ctx: Context): Tree =
transformTypeOfTree(tree)

Expand Down
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,12 @@ trait TypeAssigner {
if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) checkNoPrivateLeaks(sym, pos)
else sym.info

def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree =
Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass)))
private def toRepeated(tree: Tree, from: ClassSymbol)(implicit ctx: Context): Tree =
Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(from, defn.RepeatedParamClass)))

def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.SeqClass)

def arrayToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.ArrayClass)

/** A denotation exists really if it exists and does not point to a stale symbol. */
final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try
Expand Down
18 changes: 16 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -557,11 +557,25 @@ class Typer extends Namer
else typed(tree.expr, tpt.tpe.widenSkolem)
assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe)
}
if (untpd.isWildcardStarArg(tree))
if (untpd.isWildcardStarArg(tree)) {
/** If tree is of the form `expr: _*`, try to type `expr` as an `Array`
* and as a `Seq` if there are errors.
*/
def typedWildcardStarArgExpr = {
def asSeq(implicit ctx: Context) =
seqToRepeated(typedExpr(tree.expr, defn.SeqType.appliedTo(defn.AnyType)))
def asArray(implicit ctx: Context) =
arrayToRepeated(typedExpr(tree.expr, defn.ArrayType.appliedTo(WildcardType)))

// we try asArray first because asSeq will succeed if the former succeeds due
// to the implicit conversion from Array to WrappedArray defined in Predef
tryAlternatively[Tree](asArray(_))(asSeq(_))
}
cases(
ifPat = ascription(TypeTree(defn.RepeatedParamType.appliedTo(pt)), isWildcard = true),
ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType.appliedTo(defn.AnyType))),
ifExpr = typedWildcardStarArgExpr,
wildName = nme.WILDCARD_STAR)
}
else {
def typedTpt = checkSimpleKinded(typedType(tree.tpt))
def handlePattern: Tree = {
Expand Down
23 changes: 21 additions & 2 deletions tests/pos/repeatedArgs.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
object testRepeated {
def foo = java.lang.System.out.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d")
import scala.collection.{immutable, mutable}
import java.nio.file.Paths

class repeatedArgs {
def bar(xs: String*): Int = xs.length

def test(xs: immutable.Seq[String], ys: collection.Seq[String], zs: Array[String]): Unit = {
bar("a", "b", "c")
bar(xs: _*)
bar(ys: _*) // error in 2.13
bar(zs: _*)

Paths.get("Hello", "World")
Paths.get("Hello", xs: _*)
Paths.get("Hello", ys: _*) // error in 2.13
Paths.get("Hello", zs: _*)

val List(_, others: _*) = xs.toList // toList should not be needed, see #4790
val x: collection.Seq[String] = others
// val y: immutable.Seq[String] = others // ok in 2.13
}
}