Skip to content

Commit e56ecae

Browse files
authored
Merge pull request #10014 from noti0na1/repeatedArgWiden
Fix passing Seq or Array as a repeated argument when its type is union type; Rename isBottomType
2 parents 492d221 + b406c66 commit e56ecae

File tree

9 files changed

+47
-15
lines changed

9 files changed

+47
-15
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
586586
val tree2: Select = tree.tpe match {
587587
case tpe: NamedType =>
588588
val qualType = qualifier.tpe.widenIfUnstable
589-
if qualType.isBottomType then tree1.withTypeUnchecked(tree.tpe)
589+
if qualType.isNothing then tree1.withTypeUnchecked(tree.tpe)
590590
else tree1.withType(tpe.derivedSelect(qualType))
591591
case _ => tree1.withTypeUnchecked(tree.tpe)
592592
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,11 @@ class TypeApplications(val self: Type) extends AnyVal {
406406
case _ =>
407407
if (self.derivesFrom(from)) {
408408
def elemType(tp: Type): Type = tp.widenDealias match
409-
case tp: AndOrType => tp.derivedAndOrType(elemType(tp.tp1), elemType(tp.tp2))
409+
case tp: OrType =>
410+
if defn.isBottomType(tp.tp1) then elemType(tp.tp2)
411+
else if defn.isBottomType(tp.tp2) then elemType(tp.tp1)
412+
else tp.derivedOrType(elemType(tp.tp1), elemType(tp.tp2))
413+
case tp: AndType => tp.derivedAndType(elemType(tp.tp1), elemType(tp.tp2))
410414
case _ => tp.baseType(from).argInfos.headOption.getOrElse(defn.NothingType)
411415
val arg = elemType(self)
412416
val arg1 = if (wildcardArg) TypeBounds.upper(arg) else arg
@@ -499,6 +503,8 @@ class TypeApplications(val self: Type) extends AnyVal {
499503
def elemType(using Context): Type = self.widenDealias match {
500504
case defn.ArrayOf(elemtp) => elemtp
501505
case JavaArrayType(elemtp) => elemtp
506+
case tp: OrType if defn.isBottomType(tp.tp1) => tp.tp2.elemType
507+
case tp: OrType if defn.isBottomType(tp.tp2) => tp.tp1.elemType
502508
case _ => self.baseType(defn.SeqClass).argInfos.headOption.getOrElse(NoType)
503509
}
504510
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2325,7 +2325,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23252325
*/
23262326
def provablyEmpty(tp: Type): Boolean =
23272327
tp.dealias match {
2328-
case tp if tp.isBottomType => true
2328+
case tp if tp.isNothing => true
23292329
case AndType(tp1, tp2) => provablyDisjoint(tp1, tp2)
23302330
case OrType(tp1, tp2) => provablyEmpty(tp1) && provablyEmpty(tp2)
23312331
case at @ AppliedType(tycon, args) =>

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,11 @@ object Types {
239239
case tp: AndType =>
240240
loop(tp.tp1) || loop(tp.tp2)
241241
case tp: OrType =>
242-
loop(tp.tp1) && loop(tp.tp2)
242+
// If the type is `T | Null` or `T | Nothing`, and `T` derivesFrom the class,
243+
// then the OrType derivesFrom the class. Otherwise, we need to check both sides
244+
// derivesFrom the class.
245+
if defn.isBottomType(tp.tp1) then loop(tp.tp2)
246+
else loop(tp.tp1) && (defn.isBottomType(tp.tp2) || loop(tp.tp2))
243247
case tp: JavaArrayType =>
244248
cls == defn.ObjectClass
245249
case _ =>
@@ -262,7 +266,7 @@ object Types {
262266
}
263267

264268
/** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */
265-
def isBottomType(using Context): Boolean = this match {
269+
def isNothing(using Context): Boolean = this match {
266270
case tp: TypeRef =>
267271
tp.name == tpnme.Nothing && (tp.symbol eq defn.NothingClass)
268272
case _ => false
@@ -2122,7 +2126,7 @@ object Types {
21222126
case arg: TypeBounds =>
21232127
val v = param.paramVarianceSign
21242128
val pbounds = param.paramInfo
2125-
if (v > 0 && pbounds.loBound.dealiasKeepAnnots.isBottomType) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
2129+
if (v > 0 && pbounds.loBound.dealiasKeepAnnots.isNothing) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
21262130
else if (v < 0 && pbounds.hiBound.dealiasKeepAnnots.isTopType) TypeAlias(arg.loBound | rebase(pbounds.loBound))
21272131
else arg recoverable_& rebase(pbounds)
21282132
case arg => TypeAlias(arg)
@@ -2285,7 +2289,7 @@ object Types {
22852289
if (base.isAnd == variance >= 0) tp1 & tp2 else tp1 | tp2
22862290
case _ =>
22872291
if (pre.termSymbol.is(Package)) argForParam(pre.select(nme.PACKAGE))
2288-
else if (pre.isBottomType) pre
2292+
else if (pre.isNothing) pre
22892293
else NoType
22902294
}
22912295
}
@@ -2304,7 +2308,7 @@ object Types {
23042308
*/
23052309
def derivedSelect(prefix: Type)(using Context): Type =
23062310
if (prefix eq this.prefix) this
2307-
else if (prefix.isBottomType) prefix
2311+
else if (prefix.isNothing) prefix
23082312
else {
23092313
if (isType) {
23102314
val res =
@@ -4288,7 +4292,7 @@ object Types {
42884292

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

42934297
/** For uninstantiated type variables: Is the upper bound different from Any? */
42944298
def hasUpperBound(using Context): Boolean =
@@ -5293,7 +5297,7 @@ object Types {
52935297
case _ =>
52945298
def propagate(lo: Type, hi: Type) =
52955299
range(derivedRefinedType(tp, parent, lo), derivedRefinedType(tp, parent, hi))
5296-
if (parent.isBottomType) parent
5300+
if (parent.isNothing) parent
52975301
else info match {
52985302
case Range(infoLo: TypeBounds, infoHi: TypeBounds) =>
52995303
assert(variance == 0)
@@ -5386,7 +5390,7 @@ object Types {
53865390
case Range(lo, hi) =>
53875391
range(tp.derivedAnnotatedType(lo, annot), tp.derivedAnnotatedType(hi, annot))
53885392
case _ =>
5389-
if (underlying.isBottomType) underlying
5393+
if (underlying.isNothing) underlying
53905394
else tp.derivedAnnotatedType(underlying, annot)
53915395
}
53925396
override protected def derivedWildcardType(tp: WildcardType, bounds: Type): WildcardType =
@@ -5634,7 +5638,7 @@ object Types {
56345638
else {
56355639
seen += tp
56365640
tp match {
5637-
case tp if tp.isTopType || tp.isBottomType =>
5641+
case tp if tp.isTopType || tp.isNothing =>
56385642
cs
56395643
case tp: AppliedType =>
56405644
foldOver(cs + tp.typeSymbol, tp)

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ object Completion {
205205
* considered.
206206
*/
207207
def addMemberCompletions(qual: Tree)(using Context): Unit =
208-
if (!qual.tpe.widenDealias.isBottomType) {
208+
if (!qual.tpe.widenDealias.isNothing) {
209209
addAccessibleMembers(qual.tpe)
210210
if (!mode.is(Mode.Import) && !qual.tpe.isNullType)
211211
// Implicit conversions do not kick in when importing

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ object ErrorReporting {
169169
|Note that `${tree.name}` is treated as an infix operator in Scala 3.
170170
|If you do not want that, insert a `;` or empty line in front
171171
|or drop any spaces behind the operator."""
172-
else if qualType.isBottomType then
172+
else if qualType.isNothing then
173173
""
174174
else
175175
val add = suggestImports(

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,6 @@ class Typer extends Namer
753753
if (ctx.mode.is(Mode.QuotedPattern)) pt.translateFromRepeated(toArray = false, translateWildcard = true)
754754
else pt.translateFromRepeated(toArray = false, translateWildcard = true) |
755755
pt.translateFromRepeated(toArray = true, translateWildcard = true)
756-
val tpdExpr = typedExpr(tree.expr, ptArg)
757756
val expr1 = typedExpr(tree.expr, ptArg)
758757
val fromCls = if expr1.tpe.derivesFrom(defn.ArrayClass) then defn.ArrayClass else defn.SeqClass
759758
val tpt1 = TypeTree(expr1.tpe.widen.translateToRepeated(fromCls)).withSpan(tree.tpt.span)

tests/neg/repeatedArgs213.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,16 @@ class repeatedArgs {
1515
Paths.get("Hello", ys: _*) // error: immutable.Seq expected, found Seq
1616
Paths.get("Hello", zs: _*)
1717
}
18+
19+
def test2(xs: immutable.Seq[String] | Null, ys: collection.Seq[String] | Null, zs: Array[String] | Null): Unit = {
20+
bar("a", "b", "c")
21+
bar(xs: _*)
22+
bar(ys: _*) // error: immutable.Seq expected, found Seq
23+
bar(zs: _*) // old-error: Remove (compiler generated) Array to Seq conversion in 2.13?
24+
25+
Paths.get("Hello", "World")
26+
Paths.get("Hello", xs: _*)
27+
Paths.get("Hello", ys: _*) // error: immutable.Seq expected, found Seq
28+
Paths.get("Hello", zs: _*)
29+
}
1830
}

tests/pos/repeatedArgs213.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,15 @@ class repeatedArgs {
1515
val List(_, others: _*) = xs.toList // toList should not be needed, see #4790
1616
val x: immutable.Seq[String] = others
1717
}
18+
19+
def test2(xs: immutable.Seq[String] | Null): Unit = {
20+
bar("a", "b", "c")
21+
bar(xs: _*)
22+
23+
Paths.get("Hello", "World")
24+
Paths.get("Hello", xs: _*)
25+
26+
val List(_, others: _*) = xs.toList // toList should not be needed, see #4790
27+
val x: immutable.Seq[String] = others
28+
}
1829
}

0 commit comments

Comments
 (0)