diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index eeb18eaa9cc7..efcad3307937 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -461,7 +461,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def toBounds(using Context): TypeBounds = self match { case self: TypeBounds => self // this can happen for wildcard args - case _ => if (self.isMatch) MatchAlias(self) else TypeAlias(self) + case _ => AliasingBounds(self) } /** Translate a type of the form From[T] to either To[T] or To[? <: T] (if `wildcardArg` is set). Keep other types as they are. diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 62844a54bf48..9158062e10b7 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -458,7 +458,10 @@ object Types extends TypeUtils { /** Is this a match type or a higher-kinded abstraction of one? */ - def isMatch(using Context): Boolean = underlyingMatchType.exists + def isMatch(using Context): Boolean = stripped match + case tp: MatchType => true + case tp: HKTypeLambda => tp.resType.isMatch + case _ => false def underlyingMatchType(using Context): Type = stripped match { case tp: MatchType => tp @@ -4587,16 +4590,22 @@ object Types extends TypeUtils { override def tryNormalize(using Context): Type = tycon.stripTypeVar match { case tycon: TypeRef => - def tryMatchAlias = tycon.info match { - case MatchAlias(alias) => + def tryMatchAlias = tycon.info match + case AliasingBounds(alias) if isMatchAlias => trace(i"normalize $this", typr, show = true) { MatchTypeTrace.recurseWith(this) { alias.applyIfParameterized(args.map(_.normalized)).tryNormalize + /* `applyIfParameterized` may reduce several HKTypeLambda applications + * before the underlying MatchType is reached. + * Even if they do not involve any match type normalizations yet, + * we still want to record these reductions in the MatchTypeTrace. + * They should however only be attempted if they eventually expand + * to a match type, which is ensured by the `isMatchAlias` guard. + */ } } case _ => NoType - } tryCompiletimeConstantFold.orElse(tryMatchAlias) case _ => NoType @@ -4606,7 +4615,12 @@ object Types extends TypeUtils { def isMatchAlias(using Context): Boolean = tycon.stripTypeVar match case tycon: TypeRef => tycon.info match - case _: MatchAlias => true + case AliasingBounds(alias) => + alias.underlyingMatchType.exists + /* This is the only true case since anything other than + * a TypeRef of an alias with an underlying match type + * should have been already reduced by `appliedTo` in the TypeAssigner. + */ case _ => false case _ => false @@ -5636,6 +5650,14 @@ object Types extends TypeUtils { def lower(lo: Type)(using Context): TypeBounds = apply(lo, defn.AnyType) } + object AliasingBounds: + /** A MatchAlias if alias is a match type and a TypeAlias o.w. + * Note that aliasing a MatchAlias returns a normal TypeAlias. + */ + def apply(alias: Type)(using Context): AliasingBounds = + if alias.isMatch then MatchAlias(alias) else TypeAlias(alias) + def unapply(tp: AliasingBounds): Option[Type] = Some(tp.alias) + object TypeAlias { def apply(alias: Type)(using Context): TypeAlias = unique(new TypeAlias(alias)) def unapply(tp: TypeAlias): Option[Type] = Some(tp.alias) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 57c0b2217e9d..a75cc6c666d0 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -412,9 +412,7 @@ class TreeUnpickler(reader: TastyReader, readType().appliedTo(until(end)(readType())) case TYPEBOUNDS => val lo = readType() - if nothingButMods(end) then - if lo.isMatch then MatchAlias(readVariances(lo)) - else TypeAlias(readVariances(lo)) + if nothingButMods(end) then AliasingBounds(readVariances(lo)) else val hi = readVariances(readType()) createNullableTypeBounds(lo, hi) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 65792d09f88c..fffe87c3f57a 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -452,9 +452,8 @@ object Inlines: unrollTupleTypes(tail).map(head :: _) case tpe: TermRef if tpe.symbol == defn.EmptyTupleModule => Some(Nil) - case tpRef: TypeRef => tpRef.info match - case MatchAlias(alias) => unrollTupleTypes(alias.tryNormalize) - case _ => None + case tpe: AppliedType if tpe.isMatchAlias => + unrollTupleTypes(tpe.tryNormalize) case _ => None diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 96c5e57dde0e..c7476f5d9777 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -519,9 +519,7 @@ trait TypeAssigner { def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree, alias: Tree)(using Context): TypeBoundsTree = tree.withType( if !alias.isEmpty then alias.tpe - else if lo eq hi then - if lo.tpe.isMatch then MatchAlias(lo.tpe) - else TypeAlias(lo.tpe) + else if lo eq hi then AliasingBounds(lo.tpe) else TypeBounds(lo.tpe, hi.tpe)) def assignType(tree: untpd.Bind, sym: Symbol)(using Context): Bind = diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 3785f8fa6e06..81661e87b84e 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -64,6 +64,7 @@ i17149.scala tuple-fold.scala mt-redux-norm.perspective.scala i18211.scala +10867.scala # Opaque type i5720.scala diff --git a/tests/neg-macros/i11795.scala b/tests/neg-macros/i11795.scala deleted file mode 100644 index 2a7f89831e0e..000000000000 --- a/tests/neg-macros/i11795.scala +++ /dev/null @@ -1,10 +0,0 @@ -import scala.quoted._ -import scala.deriving._ - -def blah[P <: Product] - (m: Mirror.ProductOf[P]) - (using Quotes, Type[m.MirroredElemLabels], Type[m.MirroredElemTypes]) = { - type z = Tuple.Zip[m.MirroredElemLabels, m.MirroredElemTypes] - Type.of[z] // error - () -} diff --git a/tests/pos-macros/i11795.scala b/tests/pos-macros/i11795.scala index 32eaccf2f4e2..26d1c4da1417 100644 --- a/tests/pos-macros/i11795.scala +++ b/tests/pos-macros/i11795.scala @@ -1,7 +1,17 @@ import scala.quoted._ import scala.deriving._ -def blah2[P <: Product, MEL <: Tuple: Type, MET <: Tuple: Type](m: Mirror.ProductOf[P] { type MirroredElemLabels = MEL; type MirroredElemTypes = MET})(using Quotes) = { +def blah[P <: Product] + (m: Mirror.ProductOf[P]) + (using Quotes, Type[m.MirroredElemLabels], Type[m.MirroredElemTypes]) = { + type z = Tuple.Zip[m.MirroredElemLabels, m.MirroredElemTypes] + Type.of[z] // error + () +} + +def blah2[P <: Product, MEL <: Tuple: Type, MET <: Tuple: Type] + (m: Mirror.ProductOf[P] { type MirroredElemLabels = MEL; type MirroredElemTypes = MET}) + (using Quotes) = { Type.of[Tuple.Zip[MEL, MET]] () } diff --git a/tests/pos/i19821.scala b/tests/pos/i19821.scala new file mode 100644 index 000000000000..0dcad965a38b --- /dev/null +++ b/tests/pos/i19821.scala @@ -0,0 +1,26 @@ + +object Test: + + trait T: + type S + type F = T.F[S] + + def foo: F + def bar: T.F[S] + + object T: + type F[X] = X match + case String => Option[Int] + + type G[X] = X match + case Option[x] => Int + + val t: T {type S = String} = ??? + + val b = t.bar + val m1: T.G[b.type] = ??? + val _: Int = m1 // Ok + + val f = t.foo + val m: T.G[f.type] = ??? + val _: Int = m // Error before changes