diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 1ac5c6ecf407..1481ce685c1f 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -405,7 +405,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => override def canExplain = true /** Override with `true` for messages that should always be shown even if their - * position overlaps another message of a different class. On the other hand + * position overlaps another messsage of a different class. On the other hand * multiple messages of the same class with overlapping positions will lead * to only a single message of that class to be issued. */ diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 9750c41b7252..a5c85d4f9f3a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -35,6 +35,13 @@ class PatternMatcher extends MiniPhase { override def runsAfter: Set[String] = Set(ElimRepeated.name) + private val InInlinedCode = new util.Property.Key[Boolean] + private def inInlinedCode(using Context) = ctx.property(InInlinedCode).getOrElse(false) + + override def prepareForInlined(tree: Inlined)(using Context): Context = + if inInlinedCode then ctx + else ctx.fresh.setProperty(InInlinedCode, true) + override def transformMatch(tree: Match)(using Context): Tree = if (tree.isInstanceOf[InlineMatch]) tree else { @@ -46,10 +53,13 @@ class PatternMatcher extends MiniPhase { case rt => tree.tpe val translated = new Translator(matchType, this).translateMatch(tree) - // Skip analysis on inlined code (eg pos/i19157) - if !tpd.enclosingInlineds.nonEmpty then + if !inInlinedCode then // check exhaustivity and unreachability SpaceEngine.checkMatch(tree) + else + // only check exhaustivity, as inlining may generate unreachable code + // like in i19157.scala + SpaceEngine.checkMatchExhaustivityOnly(tree) translated.ensureConforms(matchType) } diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 082c239c6443..c2f8b98d0236 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -74,7 +74,7 @@ object TypeTestsCasts { }.apply(tp) /** Returns true if the type arguments of `P` can be determined from `X` */ - def typeArgsDeterminable(X: Type, P: AppliedType)(using Context) = inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) { + def typeArgsTrivial(X: Type, P: AppliedType)(using Context) = inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) { val AppliedType(tycon, _) = P def underlyingLambda(tp: Type): TypeLambda = tp.ensureLambdaSub match { @@ -161,7 +161,7 @@ object TypeTestsCasts { val tparams = x.typeParams if tparams.isEmpty then x else x.appliedTo(tparams.map(_ => WildcardType)) TypeComparer.provablyDisjoint(xApplied, tpe.derivedAppliedType(tycon, targs.map(_ => WildcardType))) - || typeArgsDeterminable(X, tpe) + || typeArgsTrivial(X, tpe) ||| i"its type arguments can't be determined from $X" } case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 0aa9420a298f..bb1160d501a1 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -3,9 +3,7 @@ package dotc package transform package patmat -import core.* -import Constants.*, Contexts.*, Decorators.*, Flags.*, NullOpsDecorator.*, Symbols.*, Types.* -import Names.*, NameOps.*, StdNames.* +import core.*, Constants.*, Contexts.*, Decorators.*, Flags.*, Names.*, NameOps.*, StdNames.*, Symbols.*, Types.* import ast.*, tpd.* import config.Printers.* import printing.{ Printer, * }, Texts.* @@ -352,7 +350,7 @@ object SpaceEngine { val funRef = fun1.tpe.asInstanceOf[TermRef] if (fun.symbol.name == nme.unapplySeq) val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos) - if fun.symbol.owner == defn.SeqFactoryClass && pat.tpe.hasClassSymbol(defn.ListClass) then + if (fun.symbol.owner == defn.SeqFactoryClass && defn.ListType.appliedTo(elemTp) <:< pat.tpe) // The exhaustivity and reachability logic already handles decomposing sum types (into its subclasses) // and product types (into its components). To get better counter-examples for patterns that are of type // List (or a super-type of list, like LinearSeq) we project them into spaces that use `::` and Nil. @@ -372,7 +370,7 @@ object SpaceEngine { project(pat) case Typed(_, tpt) => - Typ(erase(tpt.tpe.stripAnnots, isValue = true, isTyped = true), decomposed = false) + Typ(erase(tpt.tpe.stripAnnots, isValue = true), decomposed = false) case This(_) => Typ(pat.tpe.stripAnnots, decomposed = false) @@ -457,13 +455,13 @@ object SpaceEngine { tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2) case tp @ OrType(tp1, tp2) => - OrType(erase(tp1, inArray, isValue, isTyped), erase(tp2, inArray, isValue, isTyped), tp.isSoft) + OrType(erase(tp1, inArray, isValue), erase(tp2, inArray, isValue), tp.isSoft) case AndType(tp1, tp2) => - AndType(erase(tp1, inArray, isValue, isTyped), erase(tp2, inArray, isValue, isTyped)) + AndType(erase(tp1, inArray, isValue), erase(tp2, inArray, isValue)) case tp @ RefinedType(parent, _, _) => - erase(parent, inArray, isValue, isTyped) + erase(parent, inArray, isValue) case tref: TypeRef if tref.symbol.isPatternBound => if inArray then erase(tref.underlying, inArray, isValue, isTyped) @@ -524,26 +522,14 @@ object SpaceEngine { val mt: MethodType = unapp.widen match { case mt: MethodType => mt case pt: PolyType => - val locked = ctx.typerState.ownedVars val tvars = constrained(pt) val mt = pt.instantiate(tvars).asInstanceOf[MethodType] scrutineeTp <:< mt.paramInfos(0) // force type inference to infer a narrower type: could be singleton // see tests/patmat/i4227.scala mt.paramInfos(0) <:< scrutineeTp - maximizeType(mt.paramInfos(0), Spans.NoSpan) - if !(ctx.typerState.ownedVars -- locked).isEmpty then - // constraining can create type vars out of wildcard types - // (in legalBound, by using a LevelAvoidMap) - // maximise will only do one pass at maximising the type vars in the target type - // which means we can maximise to types that include other type vars - // this fails TreeChecker's "non-empty constraint at end of $fusedPhase" check - // e.g. run-macros/string-context-implicits - // I can't prove that a second call won't also create type vars, - // but I'd rather have an unassigned new-new type var, than an infinite loop. - // After all, there's nothing strictly "wrong" with unassigned type vars, - // it just fails TreeChecker's linting. - maximizeType(mt.paramInfos(0), Spans.NoSpan) + instantiateSelected(mt, tvars) + isFullyDefined(mt, ForceDegree.all) mt } @@ -557,7 +543,7 @@ object SpaceEngine { // Case unapplySeq: // 1. return the type `List[T]` where `T` is the element type of the unapplySeq return type `Seq[T]` - val resTp = wildApprox(ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil).finalResultType) + val resTp = ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil).finalResultType val sig = if (resTp.isRef(defn.BooleanClass)) @@ -578,14 +564,20 @@ object SpaceEngine { if (arity > 0) productSelectorTypes(resTp, unappSym.srcPos) else { - val getTp = extractorMemberType(resTp, nme.get, unappSym.srcPos) + val getTp = resTp.select(nme.get).finalResultType match + case tp: TermRef if !tp.isOverloaded => + // Like widenTermRefExpr, except not recursively. + // For example, in i17184 widen Option[foo.type]#get + // to Option[foo.type] instead of Option[Int]. + tp.underlying.widenExpr + case tp => tp if (argLen == 1) getTp :: Nil else productSelectorTypes(getTp, unappSym.srcPos) } } } - sig.map { case tp: WildcardType => tp.bounds.hi case tp => tp } + sig.map(_.annotatedToRepeated) } /** Whether the extractor covers the given type */ @@ -624,36 +616,14 @@ object SpaceEngine { case tp if tp.classSymbol.isAllOf(JavaEnum) => tp.classSymbol.children.map(_.termRef) // the class of a java enum value is the enum class, so this must follow SingletonType to not loop infinitely - case Childless(tp @ AppliedType(Parts(parts), targs)) => + case tp @ AppliedType(Parts(parts), targs) if tp.classSymbol.children.isEmpty => // It might not obvious that it's OK to apply the type arguments of a parent type to child types. // But this is guarded by `tp.classSymbol.children.isEmpty`, // meaning we'll decompose to the same class, just not the same type. // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. parts.map(tp.derivedAppliedType(_, targs)) - case tpOriginal if tpOriginal.isDecomposableToChildren => - // isDecomposableToChildren uses .classSymbol.is(Sealed) - // But that classSymbol could be from an AppliedType - // where the type constructor is a non-class type - // E.g. t11620 where `?1.AA[X]` returns as "sealed" - // but using that we're not going to infer A1[X] and A2[X] - // but end up with A1[] and A2[]. - // So we widen (like AppliedType superType does) away - // non-class type constructors. - // - // Can't use `tpOriginal.baseType(cls)` because it causes - // i15893 to return exhaustivity warnings, because instead of: - // <== refineUsingParent(N, class Succ, []) = Succ[] - // <== isSub(Succ[] <:< Succ[Succ[]]) = true - // we get - // <== refineUsingParent(NatT, class Succ, []) = Succ[NatT] - // <== isSub(Succ[NatT] <:< Succ[Succ[]]) = false - def getAppliedClass(tp: Type): Type = tp match - case tp @ AppliedType(_: HKTypeLambda, _) => tp - case tp @ AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => tp - case tp @ AppliedType(tycon: TypeProxy, _) => getAppliedClass(tycon.superType.applyIfParameterized(tp.args)) - case tp => tp - val tp = getAppliedClass(tpOriginal) + case tp if tp.isDecomposableToChildren => def getChildren(sym: Symbol): List[Symbol] = sym.children.flatMap { child => if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz... @@ -706,12 +676,6 @@ object SpaceEngine { final class PartsExtractor(val get: List[Type]) extends AnyVal: def isEmpty: Boolean = get == ListOfNoType - object Childless: - def unapply(tp: Type)(using Context): Result = - Result(if tp.classSymbol.children.isEmpty then tp else NoType) - class Result(val get: Type) extends AnyVal: - def isEmpty: Boolean = !get.exists - /** Show friendly type name with current scope in mind * * E.g. C.this.B --> B if current owner is C @@ -808,15 +772,12 @@ object SpaceEngine { doShow(s) } - extension (self: Type) private def stripUnsafeNulls()(using Context): Type = - if Nullables.unsafeNullsEnabled then self.stripNull() else self - - private def exhaustivityCheckable(sel: Tree)(using Context): Boolean = trace(i"exhaustivityCheckable($sel ${sel.className})") { + private def exhaustivityCheckable(sel: Tree)(using Context): Boolean = { val seen = collection.mutable.Set.empty[Symbol] // Possible to check everything, but be compatible with scalac by default - def isCheckable(tp: Type): Boolean = trace(i"isCheckable($tp ${tp.className})"): - val tpw = tp.widen.dealias.stripUnsafeNulls() + def isCheckable(tp: Type): Boolean = + val tpw = tp.widen.dealias val classSym = tpw.classSymbol classSym.is(Sealed) && !tpw.isLargeGenericTuple || // exclude large generic tuples from exhaustivity // requires an unknown number of changes to make work @@ -851,7 +812,7 @@ object SpaceEngine { /** Return the underlying type of non-module, non-constant, non-enum case singleton types. * Also widen ExprType to its result type, and rewrap any annotation wrappers. * For example, with `val opt = None`, widen `opt.type` to `None.type`. */ - def toUnderlying(tp: Type)(using Context): Type = trace(i"toUnderlying($tp ${tp.className})")(tp match { + def toUnderlying(tp: Type)(using Context): Type = trace(i"toUnderlying($tp)")(tp match { case _: ConstantType => tp case tp: TermRef if tp.symbol.is(Module) => tp case tp: TermRef if tp.symbol.isAllOf(EnumCase) => tp @@ -862,7 +823,7 @@ object SpaceEngine { }) def checkExhaustivity(m: Match)(using Context): Unit = trace(i"checkExhaustivity($m)") { - val selTyp = toUnderlying(m.selector.tpe.stripUnsafeNulls()).dealias + val selTyp = toUnderlying(m.selector.tpe).dealias val targetSpace = trace(i"targetSpace($selTyp)")(project(selTyp)) val patternSpace = Or(m.cases.foldLeft(List.empty[Space]) { (acc, x) => @@ -940,6 +901,9 @@ object SpaceEngine { } def checkMatch(m: Match)(using Context): Unit = - if exhaustivityCheckable(m.selector) then checkExhaustivity(m) + checkMatchExhaustivityOnly(m) if reachabilityCheckable(m.selector) then checkReachability(m) + + def checkMatchExhaustivityOnly(m: Match)(using Context): Unit = + if exhaustivityCheckable(m.selector) then checkExhaustivity(m) } diff --git a/tests/pending/neg/i16451.check b/tests/pending/neg/i16451.check new file mode 100644 index 000000000000..acd44ca97478 --- /dev/null +++ b/tests/pending/neg/i16451.check @@ -0,0 +1,24 @@ +-- Error: tests/neg/i16451.scala:13:9 ---------------------------------------------------------------------------------- +13 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] +-- Error: tests/neg/i16451.scala:21:9 ---------------------------------------------------------------------------------- +21 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Any +-- Error: tests/neg/i16451.scala:25:9 ---------------------------------------------------------------------------------- +25 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] +-- Error: tests/neg/i16451.scala:29:9 ---------------------------------------------------------------------------------- +29 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from A1 +-- Error: tests/neg/i16451.scala:34:11 --------------------------------------------------------------------------------- +34 | case x: Wrapper[Color.Red.type] => x // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] +-- Error: tests/neg/i16451.scala:39:11 --------------------------------------------------------------------------------- +39 | case x: Wrapper[Color.Red.type] => x // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] \ No newline at end of file diff --git a/tests/warn/i16451.scala b/tests/pending/neg/i16451.scala similarity index 58% rename from tests/warn/i16451.scala rename to tests/pending/neg/i16451.scala index 138af3632772..3a93a97e1f03 100644 --- a/tests/warn/i16451.scala +++ b/tests/pending/neg/i16451.scala @@ -1,42 +1,38 @@ -// +//> using options -Werror enum Color: case Red, Green -//sealed trait Color -//object Color: -// case object Red extends Color -// case object Green extends Color case class Wrapper[A](value: A) object Test: def test_correct(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) + case x: Wrapper[Color.Red.type] => Some(x) // error + case null => None def test_different(x: Wrapper[Color]): Option[Wrapper[Color]] = x match case x @ Wrapper(_: Color.Red.type) => Some(x) case x @ Wrapper(_: Color.Green.type) => None def test_any(x: Any): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) + case x: Wrapper[Color.Red.type] => Some(x) // error + case _ => None def test_wrong(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked + case x: Wrapper[Color.Red.type] => Some(x) // error case null => None def t2[A1 <: Wrapper[Color]](x: A1): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked + case x: Wrapper[Color.Red.type] => Some(x) // error case null => None def test_wrong_seq(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = xs.collect { - case x: Wrapper[Color.Red.type] => x // warn: unchecked + case x: Wrapper[Color.Red.type] => x // error } def test_wrong_seq2(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = xs.collect { x => x match - case x: Wrapper[Color.Red.type] => x // warn: unchecked + case x: Wrapper[Color.Red.type] => x // error } def main(args: Array[String]): Unit = diff --git a/tests/warn/enum-approx2.check b/tests/warn/enum-approx2.check deleted file mode 100644 index a75c15b424ff..000000000000 --- a/tests/warn/enum-approx2.check +++ /dev/null @@ -1,14 +0,0 @@ --- [E030] Match case Unreachable Warning: tests/warn/enum-approx2.scala:7:12 ------------------------------------------- -7 | case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // also: unchecked (hidden) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Unreachable case --- [E121] Pattern Match Warning: tests/warn/enum-approx2.scala:8:9 ----------------------------------------------------- -8 | case _ => // warn: unreachable-only-null - | ^ - | Unreachable case except for null (if this is intentional, consider writing case null => instead). --- [E092] Pattern Match Unchecked Warning: tests/warn/enum-approx2.scala:6:13 ------------------------------------------ -6 | case Fun(x: Fun[Int, Double]) => ??? // warn: unchecked - | ^ - |the type test for Fun[Int, Double] cannot be checked at runtime because its type arguments can't be determined from Exp[Int => Int] - | - | longer explanation available when compiling with `-explain` diff --git a/tests/warn/enum-approx2.scala b/tests/warn/enum-approx2.scala deleted file mode 100644 index 38a78cd6a5e9..000000000000 --- a/tests/warn/enum-approx2.scala +++ /dev/null @@ -1,10 +0,0 @@ -sealed trait Exp[T] -case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] - -class Test { - def eval(e: Fun[Int, Int]) = e match { - case Fun(x: Fun[Int, Double]) => ??? // warn: unchecked - case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // also: unchecked (hidden) - case _ => // warn: unreachable-only-null - } -} diff --git a/tests/warn/i11178.scala b/tests/warn/i11178.scala index a59b899be365..7544e21a3791 100644 --- a/tests/warn/i11178.scala +++ b/tests/warn/i11178.scala @@ -12,6 +12,7 @@ object Test1 { def test[A](bar: Bar[A]) = bar match { case _: Bar[Boolean] => ??? // warn + case _ => ??? } } @@ -22,6 +23,7 @@ object Test2 { def test[A](bar: Bar[A]) = bar match { case _: Bar[Boolean] => ??? // warn + case _ => ??? } } @@ -32,5 +34,6 @@ object Test3 { def test[A](bar: Bar[A]) = bar match { case _: Bar[Boolean] => ??? // warn + case _ => ??? } } diff --git a/tests/warn/i15893.min.scala b/tests/warn/i15893.min.scala deleted file mode 100644 index 755dda5cbcda..000000000000 --- a/tests/warn/i15893.min.scala +++ /dev/null @@ -1,13 +0,0 @@ -sealed trait NatT -case class Zero() extends NatT -case class Succ[+N <: NatT](n: N) extends NatT - -type Mod2[N <: NatT] <: NatT = N match - case Zero => Zero - case Succ[Zero] => Succ[Zero] - case Succ[Succ[predPredN]] => Mod2[predPredN] - -def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match - case Zero(): Zero => Zero() // warn - case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warn - case Succ(Succ(predPredN)): Succ[Succ[?]] => dependentlyTypedMod2(predPredN) // warn diff --git a/tests/warn/i16451.check b/tests/warn/i16451.check deleted file mode 100644 index 09c2a7df8179..000000000000 --- a/tests/warn/i16451.check +++ /dev/null @@ -1,44 +0,0 @@ --- [E030] Match case Unreachable Warning: tests/warn/i16451.scala:14:9 ------------------------------------------------- -14 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Unreachable case --- [E030] Match case Unreachable Warning: tests/warn/i16451.scala:22:9 ------------------------------------------------- -22 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Unreachable case --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:13:9 ------------------------------------------------ -13 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] - | - | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:21:9 ------------------------------------------------ -21 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Any - | - | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:25:9 ------------------------------------------------ -25 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] - | - | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:29:9 ------------------------------------------------ -29 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from A1 - | - | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:34:11 ----------------------------------------------- -34 | case x: Wrapper[Color.Red.type] => x // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] - | - | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:39:11 ----------------------------------------------- -39 | case x: Wrapper[Color.Red.type] => x // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] - | - | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i20121.scala b/tests/warn/i20121.scala deleted file mode 100644 index ce8e3e4d74f6..000000000000 --- a/tests/warn/i20121.scala +++ /dev/null @@ -1,13 +0,0 @@ -sealed trait T_A[A, B] -type X = T_A[Byte, Byte] - -case class CC_B[A](a: A) extends T_A[A, X] - -val v_a: T_A[X, X] = CC_B(null) -val v_b = v_a match - case CC_B(_) => 0 // warn: unreachable - case _ => 1 - // for CC_B[A] to match T_A[X, X] - // A := X - // so require X, aka T_A[Byte, Byte] - // which isn't instantiable, outside of null diff --git a/tests/warn/i20122.scala b/tests/warn/i20122.scala deleted file mode 100644 index 50da42a5926c..000000000000 --- a/tests/warn/i20122.scala +++ /dev/null @@ -1,17 +0,0 @@ -sealed trait T_B[C, D] - -case class CC_A() -case class CC_B[A, C](a: A) extends T_B[C, CC_A] -case class CC_C[C, D](a: T_B[C, D]) extends T_B[Int, CC_A] -case class CC_E(a: CC_C[Char, Byte]) - -val v_a: T_B[Int, CC_A] = CC_B(CC_E(CC_C(null))) -val v_b = v_a match - case CC_B(CC_E(CC_C(_))) => 0 // warn: unreachable - case _ => 1 - // for CC_B[A, C] to match T_B[C, CC_A] - // C <: Int, ok - // A <: CC_E, ok - // but you need a CC_C[Char, Byte] - // which requires a T_B[Char, Byte] - // which isn't instantiable, outside of null diff --git a/tests/warn/i20123.scala b/tests/warn/i20123.scala deleted file mode 100644 index 32de903210b2..000000000000 --- a/tests/warn/i20123.scala +++ /dev/null @@ -1,16 +0,0 @@ -sealed trait T_A[A, B] -sealed trait T_B[C] - -case class CC_D[A, C]() extends T_A[A, C] -case class CC_E() extends T_B[Nothing] -case class CC_G[A, C](c: C) extends T_A[A, C] - -val v_a: T_A[Boolean, T_B[Boolean]] = CC_G(null) -val v_b = v_a match { - case CC_D() => 0 - case CC_G(_) => 1 // warn: unreachable - // for CC_G[A, C] to match T_A[Boolean, T_B[Boolean]] - // A := Boolean, which is ok - // C := T_B[Boolean], - // which isn't instantiable, outside of null -} diff --git a/tests/warn/i20128.scala b/tests/warn/i20128.scala deleted file mode 100644 index f09b323c6ca0..000000000000 --- a/tests/warn/i20128.scala +++ /dev/null @@ -1,9 +0,0 @@ -sealed trait T_A[A] -case class CC_B[A](a: T_A[A]) extends T_A[Byte] -case class CC_E[A](b: T_A[A]) extends T_A[Byte] - -val v_a: T_A[Byte] = CC_E(CC_B(null)) -val v_b: Int = v_a match { // warn: not exhaustive - case CC_E(CC_E(_)) => 0 - case CC_B(_) => 1 -} diff --git a/tests/warn/i20129.scala b/tests/warn/i20129.scala deleted file mode 100644 index de0f9af76718..000000000000 --- a/tests/warn/i20129.scala +++ /dev/null @@ -1,14 +0,0 @@ -sealed trait T_A[A] -case class CC_B[A](a: T_A[A], c: T_A[A]) extends T_A[Char] -case class CC_C[A]() extends T_A[A] -case class CC_G() extends T_A[Char] - -val v_a: T_A[Char] = CC_B(CC_G(), CC_C()) -val v_b: Int = v_a match { // warn: not exhaustive - case CC_C() => 0 - case CC_G() => 1 - case CC_B(CC_B(_, _), CC_C()) => 2 - case CC_B(CC_C(), CC_C()) => 3 - case CC_B(_, CC_G()) => 4 - case CC_B(_, CC_B(_, _)) => 5 -} diff --git a/tests/warn/i20130.scala b/tests/warn/i20130.scala deleted file mode 100644 index 571959c2b388..000000000000 --- a/tests/warn/i20130.scala +++ /dev/null @@ -1,11 +0,0 @@ -sealed trait T_A[B] -sealed trait T_B[C] -case class CC_B[C]() extends T_A[T_B[C]] -case class CC_C[B, C](c: T_A[B], d: T_B[C]) extends T_B[C] -case class CC_E[C]() extends T_B[C] - -val v_a: T_B[Int] = CC_C(null, CC_E()) -val v_b: Int = v_a match { // warn: not exhaustive - case CC_C(_, CC_C(_, _)) => 0 - case CC_E() => 5 -} diff --git a/tests/warn/i20131.scala b/tests/warn/i20131.scala deleted file mode 100644 index 662c2896dc9a..000000000000 --- a/tests/warn/i20131.scala +++ /dev/null @@ -1,17 +0,0 @@ -sealed trait Foo -case class Foo1() extends Foo -case class Foo2[A, B]() extends Foo - -sealed trait Bar[A, B] -case class Bar1[A, C, D](a: Bar[C, D]) extends Bar[A, Bar[C, D]] -case class Bar2[ C, D](b: Bar[C, D], c: Foo) extends Bar[Bar1[Int, Byte, Int], Bar[C, D]] - -class Test: - def m1(bar: Bar[Bar1[Int, Byte, Int], Bar[Char, Char]]): Int = bar match - case Bar1(_) => 0 - case Bar2(_, Foo2()) => 1 - def t1 = m1(Bar2(null, Foo1())) - // for Bar2[C, D] to match the scrutinee - // C := Char and D := Char - // which requires a Bar[Char, Char] - // which isn't instantiable, outside of null diff --git a/tests/warn/i20132.alt.scala b/tests/warn/i20132.alt.scala deleted file mode 100644 index 2d45367c61b8..000000000000 --- a/tests/warn/i20132.alt.scala +++ /dev/null @@ -1,8 +0,0 @@ -sealed trait Foo[A] -case class Bar[C](x: Foo[C]) extends Foo[C] -case class End[B]() extends Foo[B] -class Test: - def m1[M](foo: Foo[M]): Int = foo match // warn: not exhaustive - case End() => 0 - case Bar(End()) => 1 - def t1 = m1[Int](Bar[Int](Bar[Int](End[Int]()))) diff --git a/tests/warn/i20132.future-Left.scala b/tests/warn/i20132.future-Left.scala deleted file mode 100644 index a25718eadb6b..000000000000 --- a/tests/warn/i20132.future-Left.scala +++ /dev/null @@ -1,13 +0,0 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types - -import scala.language.unsafeNulls - -import java.util.concurrent.CompletableFuture -import scala.jdk.CollectionConverters._ - -class Test1: - def m1 = - val fut: CompletableFuture[Either[String, List[String]]] = ??? - fut.thenApply: - case Right(edits: List[String]) => edits.asJava - case Left(error: String) => throw new Exception(error) diff --git a/tests/warn/i20132.list-Seq.scala b/tests/warn/i20132.list-Seq.scala deleted file mode 100644 index 95d6e962d547..000000000000 --- a/tests/warn/i20132.list-Seq.scala +++ /dev/null @@ -1,10 +0,0 @@ -class D1 -class D2 - -class Test1: - type Ds = List[D1] | List[D2] - def m1(dss: List[Ds]) = - dss.flatMap: - case Seq(d) => Some(1) - case Seq(head, tail*) => Some(2) - case Seq() => None diff --git a/tests/warn/i20132.scala b/tests/warn/i20132.scala deleted file mode 100644 index a5f40278234a..000000000000 --- a/tests/warn/i20132.scala +++ /dev/null @@ -1,8 +0,0 @@ -sealed trait Foo[A] -case class Bar[C](x: Foo[C]) extends Foo[Int] -case class End[B]() extends Foo[B] -class Test: - def m1[M](foo: Foo[M]): Int = foo match // warn: not exhaustive - case End() => 0 - case Bar(End()) => 1 - def t1 = m1[Int](Bar[Int](Bar[Int](End[Int]()))) diff --git a/tests/warn/i20132.stream-Tuple2.safeNulls.check b/tests/warn/i20132.stream-Tuple2.safeNulls.check deleted file mode 100644 index e444ef8c3340..000000000000 --- a/tests/warn/i20132.stream-Tuple2.safeNulls.check +++ /dev/null @@ -1,8 +0,0 @@ --- [E029] Pattern Match Exhaustivity Warning: tests/warn/i20132.stream-Tuple2.safeNulls.scala:8:24 --------------------- -8 | xs.asJava.forEach { case (a, b) => // warn - | ^ - | match may not be exhaustive. - | - | It would fail on pattern case: _: Null - | - | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i20132.stream-Tuple2.safeNulls.fixed.scala b/tests/warn/i20132.stream-Tuple2.safeNulls.fixed.scala deleted file mode 100644 index 817d8ce06cee..000000000000 --- a/tests/warn/i20132.stream-Tuple2.safeNulls.fixed.scala +++ /dev/null @@ -1,12 +0,0 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types - -import scala.jdk.CollectionConverters.* - -class Test2: - def t1: Unit = { - val xs = List.empty[(String, String)] - xs.asJava.forEach { - case (a, b) => () - case null => () - } - } diff --git a/tests/warn/i20132.stream-Tuple2.safeNulls.scala b/tests/warn/i20132.stream-Tuple2.safeNulls.scala deleted file mode 100644 index 2d4a2318039e..000000000000 --- a/tests/warn/i20132.stream-Tuple2.safeNulls.scala +++ /dev/null @@ -1,11 +0,0 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types - -import scala.jdk.CollectionConverters.* - -class Test2: - def t1: Unit = { - val xs = List.empty[(String, String)] - xs.asJava.forEach { case (a, b) => // warn - () - } - } diff --git a/tests/warn/i20132.stream-Tuple2.scala b/tests/warn/i20132.stream-Tuple2.scala deleted file mode 100644 index b7cf58f8f930..000000000000 --- a/tests/warn/i20132.stream-Tuple2.scala +++ /dev/null @@ -1,18 +0,0 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types - -// Previously failed because the scrutinee under -// unsafeNulls/explicit-nulls/no-flexible-types -// is (String, String) | Null -// Need to strip the null before considering it exhaustivity checkable - -import scala.language.unsafeNulls - -import scala.jdk.CollectionConverters.* - -class Test2: - def t1: Unit = { - val xs = List.empty[(String, String)] - xs.asJava.forEach { case (a, b) => - () - } - } diff --git a/tests/warn/i20132.wo.scala b/tests/warn/i20132.wo.scala deleted file mode 100644 index a6945758ae8d..000000000000 --- a/tests/warn/i20132.wo.scala +++ /dev/null @@ -1,8 +0,0 @@ -sealed trait Foo[A] -case class Bar[C](x: Foo[C]) extends Foo[Int] -case class End[B]() extends Foo[B] -class Test: - def m1[M](foo: Foo[M]): Int = foo match - case End() => 0 - case Bar(_) => 1 - def t1 = m1[Int](Bar[Int](Bar[Int](End[Int]()))) diff --git a/tests/warn/i21218.scala b/tests/warn/i21218.scala new file mode 100644 index 000000000000..29fa957e2e70 --- /dev/null +++ b/tests/warn/i21218.scala @@ -0,0 +1,10 @@ +def Test[U, A](thisElem: A, thatElem: U) = { + case object passedEnd + val any: Seq[Any] = ??? + any.zip(any) + .map { + case (`passedEnd`, r: U @unchecked) => (thisElem, r) + case (l: A @unchecked, `passedEnd`) => (l, thatElem) + case t: (A, U) @unchecked => t // false-positive warning + } +} diff --git a/tests/warn/i5422.scala b/tests/warn/i5422.scala deleted file mode 100644 index bc124382d7d3..000000000000 --- a/tests/warn/i5422.scala +++ /dev/null @@ -1,9 +0,0 @@ -sealed trait Foo[A[_]] - -case class Bar[C[_], X](x: C[X]) extends Foo[C] -case class End[B[_]]() extends Foo[B] - -class Test: - def foo[M[_]](foo: Foo[M]): Int = foo match - case End() => 0 - case Bar(_) => 1 diff --git a/tests/warn/i5826.check b/tests/warn/i5826.check index 18ff50a933cb..b108aef8010a 100644 --- a/tests/warn/i5826.check +++ b/tests/warn/i5826.check @@ -10,20 +10,20 @@ |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from List[String] | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:16:9 ------------------------------------------------- -16 | case ls: A[X] => 4 // warn +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:17:9 ------------------------------------------------- +17 | case ls: A[X] => 4 // warn | ^ |the type test for Foo.this.A[X] cannot be checked at runtime because its type arguments can't be determined from Foo.this.B[X] | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:21:9 ------------------------------------------------- -21 | case ls: List[Int] => ls.head // warn, List extends Int => T +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:22:9 ------------------------------------------------- +22 | case ls: List[Int] => ls.head // warn, List extends Int => T | ^ |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from A => Int | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:27:54 ------------------------------------------------ -27 | def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // warn +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:28:54 ------------------------------------------------ +28 | def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // warn | ^ |the type test for Foo.this.C[String] cannot be checked at runtime because its type arguments can't be determined from Foo.this.A[T] | diff --git a/tests/warn/i5826.scala b/tests/warn/i5826.scala index f54d6e58d033..69c157f89c27 100644 --- a/tests/warn/i5826.scala +++ b/tests/warn/i5826.scala @@ -6,6 +6,7 @@ class Foo { def test2: List[Int] | List[String] => Int = { case ls: List[Int] => ls.head // warn: unchecked + case _ => 0 } trait A[T] diff --git a/tests/warn/i8932.scala b/tests/warn/i8932.scala index 95a4e86e9791..c52d2659614d 100644 --- a/tests/warn/i8932.scala +++ b/tests/warn/i8932.scala @@ -6,7 +6,7 @@ class Dummy extends Bar[Nothing] with Foo[String] def bugReport[A](foo: Foo[A]): Foo[A] = foo match { case bar: Bar[A] => bar // warn: unchecked - case dummy: Dummy => ??? // warn: unreachable + case dummy: Dummy => ??? } def test = bugReport(new Dummy: Foo[String]) diff --git a/tests/warn/suppressed-type-test-warnings.scala b/tests/warn/suppressed-type-test-warnings.scala index c78e8e263153..99fef5e97fac 100644 --- a/tests/warn/suppressed-type-test-warnings.scala +++ b/tests/warn/suppressed-type-test-warnings.scala @@ -1,11 +1,9 @@ object Test { sealed trait Foo[A, B] final case class Bar[X](x: X) extends Foo[X, X] - def foo[A, B](value: Foo[A, B], a: A => Int): B = value match { case Bar(x) => a(x); x } - def bar[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[a] => b.x } @@ -18,10 +16,12 @@ object Test { def err2[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[B] => // spurious // warn b.x + case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } def fail[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[Int] => // warn b.x + case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } -} +} \ No newline at end of file diff --git a/tests/warn/t11620.scala b/tests/warn/t11620.scala deleted file mode 100644 index 2d87d4c1a2c6..000000000000 --- a/tests/warn/t11620.scala +++ /dev/null @@ -1,9 +0,0 @@ -sealed trait A[+T0] -case class A1[+T1](t1: T1) extends A[T1] -case class A2[+T2](t2: T2) extends A[T2] -sealed trait B[+T3] { type AA[+U] <: A[U] ; def a: AA[T3] } -object B { def unapply[T4](b: B[T4]): Some[b.AA[T4]] = Some(b.a) } -class Test: - def m1[X](b: B[X]): X = b match - case B(A1(v1)) => v1 - case B(A2(v2)) => v2