Skip to content

Fix passing Seq or Array as a repeated argument when its type is union type #10014

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 3 commits into from
Oct 16, 2020
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val tree2: Select = tree.tpe match {
case tpe: NamedType =>
val qualType = qualifier.tpe.widenIfUnstable
if qualType.isBottomType then tree1.withTypeUnchecked(tree.tpe)
if qualType.isNothing then tree1.withTypeUnchecked(tree.tpe)
else tree1.withType(tpe.derivedSelect(qualType))
case _ => tree1.withTypeUnchecked(tree.tpe)
}
Expand Down
8 changes: 7 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,11 @@ class TypeApplications(val self: Type) extends AnyVal {
case _ =>
if (self.derivesFrom(from)) {
def elemType(tp: Type): Type = tp.widenDealias match
case tp: AndOrType => tp.derivedAndOrType(elemType(tp.tp1), elemType(tp.tp2))
case tp: OrType =>
if defn.isBottomType(tp.tp1) then elemType(tp.tp2)
else if defn.isBottomType(tp.tp2) then elemType(tp.tp1)
else tp.derivedOrType(elemType(tp.tp1), elemType(tp.tp2))
case tp: AndType => tp.derivedAndType(elemType(tp.tp1), elemType(tp.tp2))
case _ => tp.baseType(from).argInfos.headOption.getOrElse(defn.NothingType)
val arg = elemType(self)
val arg1 = if (wildcardArg) TypeBounds.upper(arg) else arg
Expand Down Expand Up @@ -499,6 +503,8 @@ class TypeApplications(val self: Type) extends AnyVal {
def elemType(using Context): Type = self.widenDealias match {
case defn.ArrayOf(elemtp) => elemtp
case JavaArrayType(elemtp) => elemtp
case tp: OrType if defn.isBottomType(tp.tp1) => tp.tp2.elemType
case tp: OrType if defn.isBottomType(tp.tp2) => tp.tp1.elemType
case _ => self.baseType(defn.SeqClass).argInfos.headOption.getOrElse(NoType)
}
}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2325,7 +2325,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
*/
def provablyEmpty(tp: Type): Boolean =
tp.dealias match {
case tp if tp.isBottomType => true
case tp if tp.isNothing => true
case AndType(tp1, tp2) => provablyDisjoint(tp1, tp2)
case OrType(tp1, tp2) => provablyEmpty(tp1) && provablyEmpty(tp2)
case at @ AppliedType(tycon, args) =>
Expand Down
22 changes: 13 additions & 9 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,11 @@ object Types {
case tp: AndType =>
loop(tp.tp1) || loop(tp.tp2)
case tp: OrType =>
loop(tp.tp1) && loop(tp.tp2)
// If the type is `T | Null` or `T | Nothing`, and `T` derivesFrom the class,
// then the OrType derivesFrom the class. Otherwise, we need to check both sides
// derivesFrom the class.
if defn.isBottomType(tp.tp1) then loop(tp.tp2)
else loop(tp.tp1) && (defn.isBottomType(tp.tp2) || loop(tp.tp2))
case tp: JavaArrayType =>
cls == defn.ObjectClass
case _ =>
Expand All @@ -262,7 +266,7 @@ object Types {
}

/** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */
def isBottomType(using Context): Boolean = this match {
def isNothing(using Context): Boolean = this match {
case tp: TypeRef =>
tp.name == tpnme.Nothing && (tp.symbol eq defn.NothingClass)
case _ => false
Expand Down Expand Up @@ -2122,7 +2126,7 @@ object Types {
case arg: TypeBounds =>
val v = param.paramVarianceSign
val pbounds = param.paramInfo
if (v > 0 && pbounds.loBound.dealiasKeepAnnots.isBottomType) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
if (v > 0 && pbounds.loBound.dealiasKeepAnnots.isNothing) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
else if (v < 0 && pbounds.hiBound.dealiasKeepAnnots.isTopType) TypeAlias(arg.loBound | rebase(pbounds.loBound))
else arg recoverable_& rebase(pbounds)
case arg => TypeAlias(arg)
Expand Down Expand Up @@ -2285,7 +2289,7 @@ object Types {
if (base.isAnd == variance >= 0) tp1 & tp2 else tp1 | tp2
case _ =>
if (pre.termSymbol.is(Package)) argForParam(pre.select(nme.PACKAGE))
else if (pre.isBottomType) pre
else if (pre.isNothing) pre
else NoType
}
}
Expand All @@ -2304,7 +2308,7 @@ object Types {
*/
def derivedSelect(prefix: Type)(using Context): Type =
if (prefix eq this.prefix) this
else if (prefix.isBottomType) prefix
else if (prefix.isNothing) prefix
else {
if (isType) {
val res =
Expand Down Expand Up @@ -4288,7 +4292,7 @@ object Types {

/** For uninstantiated type variables: Is the lower bound different from Nothing? */
def hasLowerBound(using Context): Boolean =
!ctx.typerState.constraint.entry(origin).loBound.isBottomType
!ctx.typerState.constraint.entry(origin).loBound.isNothing

/** For uninstantiated type variables: Is the upper bound different from Any? */
def hasUpperBound(using Context): Boolean =
Expand Down Expand Up @@ -5293,7 +5297,7 @@ object Types {
case _ =>
def propagate(lo: Type, hi: Type) =
range(derivedRefinedType(tp, parent, lo), derivedRefinedType(tp, parent, hi))
if (parent.isBottomType) parent
if (parent.isNothing) parent
else info match {
case Range(infoLo: TypeBounds, infoHi: TypeBounds) =>
assert(variance == 0)
Expand Down Expand Up @@ -5386,7 +5390,7 @@ object Types {
case Range(lo, hi) =>
range(tp.derivedAnnotatedType(lo, annot), tp.derivedAnnotatedType(hi, annot))
case _ =>
if (underlying.isBottomType) underlying
if (underlying.isNothing) underlying
else tp.derivedAnnotatedType(underlying, annot)
}
override protected def derivedWildcardType(tp: WildcardType, bounds: Type): WildcardType =
Expand Down Expand Up @@ -5634,7 +5638,7 @@ object Types {
else {
seen += tp
tp match {
case tp if tp.isTopType || tp.isBottomType =>
case tp if tp.isTopType || tp.isNothing =>
cs
case tp: AppliedType =>
foldOver(cs + tp.typeSymbol, tp)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/interactive/Completion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ object Completion {
* considered.
*/
def addMemberCompletions(qual: Tree)(using Context): Unit =
if (!qual.tpe.widenDealias.isBottomType) {
if (!qual.tpe.widenDealias.isNothing) {
addAccessibleMembers(qual.tpe)
if (!mode.is(Mode.Import) && !qual.tpe.isNullType)
// Implicit conversions do not kick in when importing
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ object ErrorReporting {
|Note that `${tree.name}` is treated as an infix operator in Scala 3.
|If you do not want that, insert a `;` or empty line in front
|or drop any spaces behind the operator."""
else if qualType.isBottomType then
else if qualType.isNothing then
""
else
val add = suggestImports(
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,6 @@ class Typer extends Namer
if (ctx.mode.is(Mode.QuotedPattern)) pt.translateFromRepeated(toArray = false, translateWildcard = true)
else pt.translateFromRepeated(toArray = false, translateWildcard = true) |
pt.translateFromRepeated(toArray = true, translateWildcard = true)
val tpdExpr = typedExpr(tree.expr, ptArg)
val expr1 = typedExpr(tree.expr, ptArg)
val fromCls = if expr1.tpe.derivesFrom(defn.ArrayClass) then defn.ArrayClass else defn.SeqClass
val tpt1 = TypeTree(expr1.tpe.widen.translateToRepeated(fromCls)).withSpan(tree.tpt.span)
Expand Down
12 changes: 12 additions & 0 deletions tests/neg/repeatedArgs213.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,16 @@ class repeatedArgs {
Paths.get("Hello", ys: _*) // error: immutable.Seq expected, found Seq
Paths.get("Hello", zs: _*)
}

def test2(xs: immutable.Seq[String] | Null, ys: collection.Seq[String] | Null, zs: Array[String] | Null): Unit = {
bar("a", "b", "c")
bar(xs: _*)
bar(ys: _*) // error: immutable.Seq expected, found Seq
bar(zs: _*) // old-error: Remove (compiler generated) Array to Seq conversion in 2.13?

Paths.get("Hello", "World")
Paths.get("Hello", xs: _*)
Paths.get("Hello", ys: _*) // error: immutable.Seq expected, found Seq
Paths.get("Hello", zs: _*)
}
}
11 changes: 11 additions & 0 deletions tests/pos/repeatedArgs213.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,15 @@ class repeatedArgs {
val List(_, others: _*) = xs.toList // toList should not be needed, see #4790
val x: immutable.Seq[String] = others
}

def test2(xs: immutable.Seq[String] | Null): Unit = {
bar("a", "b", "c")
bar(xs: _*)

Paths.get("Hello", "World")
Paths.get("Hello", xs: _*)

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