diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index d7e0871dc087..2f46106e1230 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -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) } diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 449e2f6a309d..d82d5bb92854 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -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 @@ -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) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 75a0908abba1..3e0a313ee869 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -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) => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index e0feca8c8d1b..1ebf13b0845f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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 _ => @@ -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 @@ -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) @@ -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 } } @@ -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 = @@ -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 = @@ -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) @@ -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 = @@ -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) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 4bb1a828d731..a3c84d114b95 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 03c5b4dd32cf..61ccd2d9f530 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -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( diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index f288c1ae38fb..ffa2627c20ab 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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) diff --git a/tests/neg/repeatedArgs213.scala b/tests/neg/repeatedArgs213.scala index e20403613d7b..d1b259453e9e 100644 --- a/tests/neg/repeatedArgs213.scala +++ b/tests/neg/repeatedArgs213.scala @@ -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: _*) + } } diff --git a/tests/pos/repeatedArgs213.scala b/tests/pos/repeatedArgs213.scala index 94419d88a950..73a9eb90c496 100644 --- a/tests/pos/repeatedArgs213.scala +++ b/tests/pos/repeatedArgs213.scala @@ -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 + } }