Skip to content

Commit

Permalink
Make isMatch false for applied MatchAliases
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
EugeneFlesselle committed Mar 6, 2024
1 parent 20d95b0 commit 93c3563
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 36 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 @@ -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.
Expand Down
32 changes: 26 additions & 6 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 @@ -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

Expand Down Expand Up @@ -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 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: 1 addition & 4 deletions compiler/src/dotty/tools/dotc/inlines/Inlines.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

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.

8 changes: 0 additions & 8 deletions tests/neg/i17944.check
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
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 93c3563

Please sign in to comment.