From ac51876de38ea8bba3fcc6e900689e4048a081cd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 20 Jun 2024 16:47:24 +0100 Subject: [PATCH] Fix a bundle of patmat issues Some of the issues are legitimate --- .../tools/dotc/transform/patmat/Space.scala | 31 ++++++++++++------- tests/warn/i20121.scala | 13 ++++++++ tests/warn/i20122.scala | 17 ++++++++++ tests/warn/i20123.scala | 16 ++++++++++ tests/warn/i20128.scala | 9 ++++++ tests/warn/i20129.scala | 14 +++++++++ tests/warn/i20130.scala | 11 +++++++ tests/warn/i20131.scala | 17 ++++++++++ tests/warn/i20132.alt.scala | 8 +++++ tests/warn/i20132.scala | 8 +++++ tests/warn/i20132.wo.scala | 8 +++++ tests/warn/i5422.scala | 9 ++++++ tests/warn/t11620.scala | 9 ++++++ 13 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 tests/warn/i20121.scala create mode 100644 tests/warn/i20122.scala create mode 100644 tests/warn/i20123.scala create mode 100644 tests/warn/i20128.scala create mode 100644 tests/warn/i20129.scala create mode 100644 tests/warn/i20130.scala create mode 100644 tests/warn/i20131.scala create mode 100644 tests/warn/i20132.alt.scala create mode 100644 tests/warn/i20132.scala create mode 100644 tests/warn/i20132.wo.scala create mode 100644 tests/warn/i5422.scala create mode 100644 tests/warn/t11620.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 0d66e3bd2bcf..b9fbb688d1a3 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -528,8 +528,7 @@ object SpaceEngine { // force type inference to infer a narrower type: could be singleton // see tests/patmat/i4227.scala mt.paramInfos(0) <:< scrutineeTp - instantiateSelected(mt, tvars) - isFullyDefined(mt, ForceDegree.all) + maximizeType(mt.paramInfos(0), Spans.NoSpan) mt } @@ -543,7 +542,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 = ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil).finalResultType + val resTp = wildApprox(ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil).finalResultType) val sig = if (resTp.isRef(defn.BooleanClass)) @@ -564,20 +563,14 @@ object SpaceEngine { if (arity > 0) productSelectorTypes(resTp, unappSym.srcPos) else { - 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 + val getTp = extractorMemberType(resTp, nme.get, unappSym.srcPos) if (argLen == 1) getTp :: Nil else productSelectorTypes(getTp, unappSym.srcPos) } } } - sig.map(_.annotatedToRepeated) + sig.map { case tp: WildcardType => tp.bounds.hi case tp => tp } } /** Whether the extractor covers the given type */ @@ -623,7 +616,21 @@ 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.isDecomposableToChildren => + 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. + 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) 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... diff --git a/tests/warn/i20121.scala b/tests/warn/i20121.scala new file mode 100644 index 000000000000..ce8e3e4d74f6 --- /dev/null +++ b/tests/warn/i20121.scala @@ -0,0 +1,13 @@ +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 new file mode 100644 index 000000000000..50da42a5926c --- /dev/null +++ b/tests/warn/i20122.scala @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000000..32de903210b2 --- /dev/null +++ b/tests/warn/i20123.scala @@ -0,0 +1,16 @@ +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 new file mode 100644 index 000000000000..f09b323c6ca0 --- /dev/null +++ b/tests/warn/i20128.scala @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000000..de0f9af76718 --- /dev/null +++ b/tests/warn/i20129.scala @@ -0,0 +1,14 @@ +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 new file mode 100644 index 000000000000..571959c2b388 --- /dev/null +++ b/tests/warn/i20130.scala @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000000..662c2896dc9a --- /dev/null +++ b/tests/warn/i20131.scala @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000000..2d45367c61b8 --- /dev/null +++ b/tests/warn/i20132.alt.scala @@ -0,0 +1,8 @@ +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.scala b/tests/warn/i20132.scala new file mode 100644 index 000000000000..a5f40278234a --- /dev/null +++ b/tests/warn/i20132.scala @@ -0,0 +1,8 @@ +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.wo.scala b/tests/warn/i20132.wo.scala new file mode 100644 index 000000000000..a6945758ae8d --- /dev/null +++ b/tests/warn/i20132.wo.scala @@ -0,0 +1,8 @@ +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/i5422.scala b/tests/warn/i5422.scala new file mode 100644 index 000000000000..bc124382d7d3 --- /dev/null +++ b/tests/warn/i5422.scala @@ -0,0 +1,9 @@ +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/t11620.scala b/tests/warn/t11620.scala new file mode 100644 index 000000000000..2d87d4c1a2c6 --- /dev/null +++ b/tests/warn/t11620.scala @@ -0,0 +1,9 @@ +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