diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d005cabebcfb..840f49a19cc2 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1574,8 +1574,6 @@ object Types { else NoType case SkolemType(tp) => loop(tp) - case pre: WildcardType => - WildcardType case pre: TypeRef => pre.info match { case TypeAlias(alias) => loop(alias) @@ -2554,10 +2552,7 @@ object Types { case _ => true } - /** (1) Reduce a type-ref `W # X` or `W { ... } # U`, where `W` is a wildcard type - * to an (unbounded) wildcard type. - * - * (2) Reduce a type-ref `T { X = U; ... } # X` to `U` + /** Reduce a type-ref `T { X = U; ... } # X` to `U` * provided `U` does not refer with a RecThis to the * refinement type `T { X = U; ... }` */ @@ -2679,7 +2674,7 @@ object Types { case _ => } } - if (prefix.isInstanceOf[WildcardType]) WildcardType + if (prefix.isInstanceOf[WildcardType]) WildcardType.sameKindAs(this) else withPrefix(prefix) } @@ -5253,6 +5248,10 @@ object Types { else result def emptyPolyKind(using Context): TypeBounds = apply(defn.NothingType, defn.AnyKindType) + /** An interval covering all types of the same kind as `tp`. */ + def emptySameKindAs(tp: Type)(using Context): TypeBounds = + val top = tp.topType + if top.isExactlyAny then empty else apply(defn.NothingType, top) def upper(hi: Type)(using Context): TypeBounds = apply(defn.NothingType, hi) def lower(lo: Type)(using Context): TypeBounds = apply(lo, defn.AnyType) } @@ -5428,6 +5427,9 @@ object Types { else result else unique(CachedWildcardType(bounds)) + /** A wildcard matching any type of the same kind as `tp`. */ + def sameKindAs(tp: Type)(using Context): WildcardType = + apply(TypeBounds.emptySameKindAs(tp)) } /** An extractor for single abstract method types. diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 5f7221ee0ce9..d1dd83f36ff7 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -20,6 +20,7 @@ i13871.scala i15181.scala i15922.scala t5031_2.scala +i16997.scala # Tree is huge and blows stack for printing Text i7034.scala diff --git a/tests/pos/i16997.min.scala b/tests/pos/i16997.min.scala new file mode 100644 index 000000000000..abac648bdfd5 --- /dev/null +++ b/tests/pos/i16997.min.scala @@ -0,0 +1,12 @@ +class Fn: + class R[Y] + +case class Foo[F[_]](nest: Foo[F]): + case class Bar[G[_], R[_]](value: Foo[G]) + + def bar[G[_]](using fn: Fn): Bar[G, fn.R] = ??? + + def part[G[_]](using fn: Fn): Bar[G, fn.R] = + (bar[G], ()) match + case (Bar(value), ()) => + Bar(Foo(value)) diff --git a/tests/pos/i16997.scala b/tests/pos/i16997.scala new file mode 100644 index 000000000000..cd7b1ac8ab91 --- /dev/null +++ b/tests/pos/i16997.scala @@ -0,0 +1,63 @@ +class Funs { + sealed trait ->[A, B] +} + +/** + * Binary tree with leafs holding values of types `F[X]`, `F[Y]`, ... + * The complete structure of the tree is expressed by the type `A`, using the tags for branches and leafs. + * + * @tparam <*> tag for branches + * @tparam T tag for leafs. + * @tparam F value type of leafs. Each leaf holds a value of type `F[T]`, for some type `T`. + * @tparam A captures the complete structure of the tree + */ +enum Tree[<*>[_, _], T[_], F[_], A] { + case Branch[<*>[_, _], T[_], F[_], A, B]( + l: Tree[<*>, T, F, A], + r: Tree[<*>, T, F, B], + ) extends Tree[<*>, T, F, A <*> B] + + case Leaf[<*>[_, _], T[_], F[_], A]( + value: F[A], + ) extends Tree[<*>, T, F, T[A]] + + def <*>[B](that: Tree[<*>, T, F, B]): Tree[<*>, T, F, A <*> B] = + Branch(this, that) + + def partition[G[_], H[_]]( + f: [x] => F[x] => Either[G[x], H[x]], + )(using + funs: Funs, + ): Partitioned[G, H, funs.->] = + this match { + case Leaf(a) => + f(a) match + case Left(a) => Partitioned.Left(Leaf(a)) + case Right(a) => Partitioned.Right(Leaf(a)) + case Branch(l, r) => + import Partitioned.{Both, Left, Right} + import l.Partitioned.{Both => LBoth, Left => LLeft, Right => LRight} + import r.Partitioned.{Both => RBoth, Left => RLeft, Right => RRight} + + (l.partition(f), r.partition(f)) match + case (LLeft(lg), RLeft(rg)) => Left(lg <*> rg) + case (LLeft(lg), RRight(rh)) => Both(lg, rh) + case (LLeft(lg), RBoth(rg, rh)) => Both(lg <*> rg, rh) + case (LRight(lh), RLeft(rg)) => Both(rg, lh) + case (LRight(lh), RRight(rh)) => Right(lh <*> rh) + case (LRight(lh), RBoth(rg, rh)) => Both(rg, lh <*> rh) + case (LBoth(lg, lh), RLeft(rg)) => Both(lg <*> rg, lh) + case (LBoth(lg, lh), RRight(rh)) => Both(lg, lh <*> rh) + case (LBoth(lg, lh), RBoth(rg, rh)) => Both(lg <*> rg, lh <*> rh) + } + + // note that `->` is never even used, to keep this reproduction case small + enum Partitioned[G[_], H[_], ->[_, _]] { + case Left(value: Tree[<*>, T, G, A]) + case Right(value: Tree[<*>, T, H, A]) + case Both[G[_], H[_], X, Y, ->[_, _]]( + l: Tree[<*>, T, G, X], + r: Tree[<*>, T, H, Y], + ) extends Partitioned[G, H, ->] + } +}