From c794c5f07fb9a285ec2c461ea7713b3eee5eb388 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Wed, 28 Feb 2024 21:58:08 +0100 Subject: [PATCH] Make `isMatch` false for applied `MatchAlias`es It is true only for `MatchType`s and higher-kinded abstraction of them. As a result, code using `isMatch` to choose between a `TypeAlias` and `MatchAlias` will now use a `TypeAlias` when aliasing a `MatchAlias`. Which in turn allows for better de-aliasing, since `dealias` only de-aliases standard type aliases. The logic for this distinction has also been extracted to the common `AliasingBounds` supertype. `tryNormalize` on `AppliedType` should only attempt reduction if there is an underlying match type. This could previously be identified by a `MatchAlias` tycon. We now need a recursive check. --- .../tools/dotc/core/TypeApplications.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 32 +++++++++++++++---- .../tools/dotc/core/tasty/TreeUnpickler.scala | 4 +-- .../dotty/tools/dotc/inlines/Inlines.scala | 5 +-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 4 +-- .../test/dotc/pos-test-pickling.blacklist | 1 + tests/neg-macros/i11795.scala | 10 ------ tests/neg/i17944.check | 8 ----- tests/pos-macros/i11795.scala | 12 ++++++- tests/pos/i19821.scala | 26 +++++++++++++++ 10 files changed, 68 insertions(+), 36 deletions(-) delete mode 100644 tests/neg-macros/i11795.scala create mode 100644 tests/pos/i19821.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 223d3bb11515..7d49c736e7f6 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -451,7 +451,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 e38fbbb4b355..a820a2d26db8 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 @@ -4578,26 +4581,35 @@ 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 } - /** Does this application expand to a match type? */ + /** Does this application expand to a match type? + * Note: tycon can not be a non-aliased (higher-kinded abstraction of a) match type, + * since the args would then have already been substituted by the `TypeAssigner` using `appliedTo`. + */ def isMatchAlias(using Context): Boolean = tycon.stripTypeVar match case tycon: TypeRef => tycon.info match - case _: MatchAlias => true + case AliasingBounds(alias) => alias.underlyingMatchType.exists case _ => false case _ => false @@ -5627,6 +5639,14 @@ object Types extends TypeUtils { def lower(lo: Type)(using Context): TypeBounds = apply(lo, defn.AnyType) } + object AliasingBounds: + /** A MatchAlias if alias is match type and 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 a97917e28771..616d764d18a2 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -446,16 +446,13 @@ object Inlines: evidence } - def unrollTupleTypes(tpe: Type): Option[List[Type]] = tpe.dealias match + def unrollTupleTypes(tpe: Type): Option[List[Type]] = tpe.dealias.normalized match case AppliedType(tycon, args) if defn.isTupleClass(tycon.typeSymbol) => Some(args) case AppliedType(tycon, head :: tail :: Nil) if tycon.isRef(defn.PairClass) => 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 _ => 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 32f8cdef1386..b6b4eb9f3165 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/neg/i17944.check b/tests/neg/i17944.check index 80dfaac8c4c8..a217cae1a0c6 100644 --- a/tests/neg/i17944.check +++ b/tests/neg/i17944.check @@ -29,14 +29,6 @@ | Therefore, reduction cannot advance to the remaining case | | case _ *: t => test.FindField0[t, ("i" : String), scala.compiletime.ops.int.S[(0 : Int)]] - | trying to reduce test.FindField[(("s" : String) ->> String, ("i" : String) ->> Int), ("i" : String)] - | trying to reduce test.FindField0[(("s" : String) ->> String, ("i" : String) ->> Int), ("i" : String), (0 : Int)] - | failed since selector (("s" : String) ->> String, ("i" : String) ->> Int) - | does not match case (("i" : String) ->> f) *: _ => (f, (0 : Int)) - | and cannot be shown to be disjoint from it either. - | Therefore, reduction cannot advance to the remaining case - | - | case _ *: t => test.FindField0[t, ("i" : String), scala.compiletime.ops.int.S[(0 : Int)]] | trying to reduce test.FindField0[(("s" : String) ->> String, ("i" : String) ->> Int), ("i" : String), (0 : Int)] | failed since selector (("s" : String) ->> String, ("i" : String) ->> Int) | does not match case (("i" : String) ->> f) *: _ => (f, (0 : Int)) 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