Skip to content

Commit

Permalink
Make aliases of MatchAliases normal TypeAliases
Browse files Browse the repository at this point in the history
Make `isMatch` false for applied `MatchAlias`es,
i.e. 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`s 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.
  • Loading branch information
EugeneFlesselle committed Apr 8, 2024
1 parent 90c3fbd commit ef7db7a
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 26 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
32 changes: 27 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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)
Expand Down
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/inlines/Inlines.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotc/pos-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ i17149.scala
tuple-fold.scala
mt-redux-norm.perspective.scala
i18211.scala
10867.scala

# Opaque type
i5720.scala
Expand Down
10 changes: 0 additions & 10 deletions tests/neg-macros/i11795.scala

This file was deleted.

12 changes: 11 additions & 1 deletion tests/pos-macros/i11795.scala
Original file line number Diff line number Diff line change
@@ -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]]
()
}
26 changes: 26 additions & 0 deletions tests/pos/i19821.scala
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ef7db7a

Please sign in to comment.