diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 7238756454b3..eab65890c227 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -330,7 +330,7 @@ object SpaceEngine { * The types should be atomic (non-decomposable) and unrelated (neither * should be a subtype of the other). */ - def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug) { + def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug, show) { // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1). if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then // Since projections of types don't include null, intersection with null is empty. @@ -468,17 +468,8 @@ object SpaceEngine { WildcardType case tp @ AppliedType(tycon, args) => - val args2 = - if tycon.isRef(defn.ArrayClass) then - args.map(arg => erase(arg, inArray = true, isValue = false)) - else tycon.typeParams.lazyZip(args).map { (tparam, arg) => - if isValue && tparam.paramVarianceSign == 0 then - // when matching against a value, - // any type argument for an invariant type parameter will be unchecked, - // meaning it won't fail to match against anything; thus the wildcard replacement - WildcardType - else erase(arg, inArray = false, isValue = false) - } + val inArray = tycon.isRef(defn.ArrayClass) + val args2 = args.map(arg => erase(arg, inArray = inArray, isValue = false)) tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2) case tp @ OrType(tp1, tp2) => @@ -642,7 +633,7 @@ object SpaceEngine { // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. parts.map(tp.derivedAppliedType(_, targs)) - case tp if tp.classSymbol.isDecomposableToChildren => + 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... @@ -678,8 +669,8 @@ object SpaceEngine { rec(tp, Nil) } - extension (cls: Symbol) - /** A type is decomposable to children if it's sealed, + extension (tp: Type) + /** A type is decomposable to children if it has a simple kind, it's sealed, * abstract (or a trait) - so its not a sealed concrete class that can be instantiated on its own, * has no anonymous children, which we wouldn't be able to name as counter-examples, * but does have children. @@ -688,7 +679,8 @@ object SpaceEngine { * A sealed trait with subclasses that then get removed after `refineUsingParent`, decomposes to the empty list. * So that's why we consider whether a type has children. */ def isDecomposableToChildren(using Context): Boolean = - cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty + val cls = tp.classSymbol + tp.hasSimpleKind && cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) diff --git a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala index 175096fc6b21..92d86b3307e5 100644 --- a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala +++ b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala @@ -18,10 +18,12 @@ object Test { def err2[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[B] => // spurious // error 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] => // error b.x + case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } } diff --git a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala index 5e3bdef7553d..c7c8a6c4e1fb 100644 --- a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala +++ b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala @@ -4,5 +4,7 @@ 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]) => ??? // error + case Fun(x: Exp[Int => String]) => ??? // error + case _ => } } diff --git a/tests/neg-custom-args/isInstanceOf/i11178.scala b/tests/neg-custom-args/isInstanceOf/i11178.scala index 71bc346e5743..47e8b4c3acab 100644 --- a/tests/neg-custom-args/isInstanceOf/i11178.scala +++ b/tests/neg-custom-args/isInstanceOf/i11178.scala @@ -12,6 +12,7 @@ object Test1 { def test[A](bar: Bar[A]) = bar match { case _: Bar[Boolean] => ??? // error + case _ => ??? } } diff --git a/tests/neg-custom-args/isInstanceOf/i8932.scala b/tests/neg-custom-args/isInstanceOf/i8932.scala index e070fdae518c..84d2f7d4990a 100644 --- a/tests/neg-custom-args/isInstanceOf/i8932.scala +++ b/tests/neg-custom-args/isInstanceOf/i8932.scala @@ -6,6 +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 // error + case dummy: Dummy => ??? } def test = bugReport(new Dummy: Foo[String]) diff --git a/tests/neg/i16451.check b/tests/pending/neg/i16451.check similarity index 100% rename from tests/neg/i16451.check rename to tests/pending/neg/i16451.check diff --git a/tests/neg/i16451.scala b/tests/pending/neg/i16451.scala similarity index 93% rename from tests/neg/i16451.scala rename to tests/pending/neg/i16451.scala index 685b79477bbe..49997d2bcf92 100644 --- a/tests/neg/i16451.scala +++ b/tests/pending/neg/i16451.scala @@ -1,10 +1,6 @@ // scalac: -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) diff --git a/tests/pos/i17230.bootstrap.scala b/tests/pos/i17230.bootstrap.scala new file mode 100644 index 000000000000..ef2d98d8f55b --- /dev/null +++ b/tests/pos/i17230.bootstrap.scala @@ -0,0 +1,16 @@ +type Untyped = Type | Null + +class Type +abstract class SearchFailureType extends Type + +abstract class Tree[+T <: Untyped]: + def tpe: T = null.asInstanceOf[T] + +class SearchFailureIdent[+T <: Untyped] extends Tree[T] + +class Test_i17230_bootstrap: + def t1(arg: Tree[Type]) = arg match + case arg: SearchFailureIdent[?] => arg.tpe match + case x: SearchFailureType => + case _ => + case _ => diff --git a/tests/pos/i17230.min1.scala b/tests/pos/i17230.min1.scala new file mode 100644 index 000000000000..e2df63e168c1 --- /dev/null +++ b/tests/pos/i17230.min1.scala @@ -0,0 +1,15 @@ +// scalac: -Werror +trait Foo: + type Bar[_] + +object Foo: + type Aux[B[_]] = Foo { type Bar[A] = B[A] } + +class Test: + def t1[B[_]](self: Option[Foo.Aux[B]]) = self match + case Some(_) => 1 + case None => 2 + + def t2[B[_]](self: Option[Foo.Aux[B]]) = self match + case Some(f) => 1 + case None => 2 diff --git a/tests/pos/i17230.orig.scala b/tests/pos/i17230.orig.scala new file mode 100644 index 000000000000..d72a0082a116 --- /dev/null +++ b/tests/pos/i17230.orig.scala @@ -0,0 +1,20 @@ +// scalac: -Werror +import scala.util.* + +trait Transaction { + type State[_] +} +object Transaction { + type of[S[_]] = Transaction { type State[A] = S[A] } +} +trait DynamicScope[State[_]] + +case class ScopeSearch[State[_]](self: Either[Transaction.of[State], DynamicScope[State]]) { + + def embedTransaction[T](f: Transaction.of[State] => T): T = + self match { + case Left(integrated) => ??? + case Right(ds) => ??? + } +} +