Skip to content

Commit 6d7abfc

Browse files
Backport "Streamline tryNormalize with underlyingMatchType" to 3.5.2 (#21449)
Backports #20268 to the 3.5.2 branch. PR submitted by the release tooling. [skip ci]
2 parents bf2d929 + a1a7b12 commit 6d7abfc

File tree

8 files changed

+96
-69
lines changed

8 files changed

+96
-69
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

+40-68
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,7 @@ object Types extends TypeUtils {
490490
case _ => false
491491

492492
/** Does this application expand to a match type? */
493-
def isMatchAlias(using Context): Boolean = underlyingMatchType.exists
494-
495-
def underlyingMatchType(using Context): Type = stripped match {
496-
case tp: MatchType => tp
497-
case tp: HKTypeLambda => tp.resType.underlyingMatchType
498-
case tp: AppliedType => tp.underlyingMatchType
499-
case _ => NoType
500-
}
493+
def isMatchAlias(using Context): Boolean = underlyingNormalizable.isMatch
501494

502495
/** Is this a higher-kinded type lambda with given parameter variances?
503496
* These lambdas are used as the RHS of higher-kinded abstract types or
@@ -1550,19 +1543,24 @@ object Types extends TypeUtils {
15501543
}
15511544
deskolemizer(this)
15521545

1553-
/** The result of normalization using `tryNormalize`, or the type itself if
1554-
* tryNormlize yields NoType
1546+
/** The result of normalization, or the type itself if none apply. */
1547+
final def normalized(using Context): Type = tryNormalize.orElse(this)
1548+
1549+
/** If this type has an underlying match type or applied compiletime.ops,
1550+
* then the result after applying all toplevel normalizations, otherwise NoType.
15551551
*/
1556-
final def normalized(using Context): Type = {
1557-
val normed = tryNormalize
1558-
if (normed.exists) normed else this
1559-
}
1552+
def tryNormalize(using Context): Type = underlyingNormalizable match
1553+
case mt: MatchType => mt.reduced.normalized
1554+
case tp: AppliedType => tp.tryCompiletimeConstantFold
1555+
case _ => NoType
15601556

1561-
/** If this type can be normalized at the top-level by rewriting match types
1562-
* of S[n] types, the result after applying all toplevel normalizations,
1563-
* otherwise NoType
1557+
/** Perform successive strippings, and beta-reductions of applied types until
1558+
* a match type or applied compiletime.ops is reached, if any, otherwise NoType.
15641559
*/
1565-
def tryNormalize(using Context): Type = NoType
1560+
def underlyingNormalizable(using Context): Type = stripped.stripLazyRef match
1561+
case tp: MatchType => tp
1562+
case tp: AppliedType => tp.underlyingNormalizable
1563+
case _ => NoType
15661564

15671565
private def widenDealias1(keep: AnnotatedType => Context ?=> Boolean)(using Context): Type = {
15681566
val res = this.widen.dealias1(keep, keepOpaques = false)
@@ -3258,8 +3256,6 @@ object Types extends TypeUtils {
32583256
private var myRef: Type | Null = null
32593257
private var computed = false
32603258

3261-
override def tryNormalize(using Context): Type = ref.tryNormalize
3262-
32633259
def ref(using Context): Type =
32643260
if computed then
32653261
if myRef == null then
@@ -4615,8 +4611,8 @@ object Types extends TypeUtils {
46154611
private var myEvalRunId: RunId = NoRunId
46164612
private var myEvalued: Type = uninitialized
46174613

4618-
private var validUnderlyingMatch: Period = Nowhere
4619-
private var cachedUnderlyingMatch: Type = uninitialized
4614+
private var validUnderlyingNormalizable: Period = Nowhere
4615+
private var cachedUnderlyingNormalizable: Type = uninitialized
46204616

46214617
def isGround(acc: TypeAccumulator[Boolean])(using Context): Boolean =
46224618
if myGround == 0 then myGround = if acc.foldOver(true, this) then 1 else -1
@@ -4680,37 +4676,25 @@ object Types extends TypeUtils {
46804676
case nil => x
46814677
foldArgs(op(x, tycon), args)
46824678

4683-
/** Exists if the tycon is a TypeRef of an alias with an underlying match type.
4684-
* Anything else should have already been reduced in `appliedTo` by the TypeAssigner.
4679+
/** Exists if the tycon is a TypeRef of an alias with an underlying match type,
4680+
* or a compiletime applied type. Anything else should have already been
4681+
* reduced in `appliedTo` by the TypeAssigner. This may reduce several
4682+
* HKTypeLambda applications before the underlying normalizable type is reached.
46854683
*/
4686-
override def underlyingMatchType(using Context): Type =
4687-
if ctx.period != validUnderlyingMatch then
4688-
cachedUnderlyingMatch = superType.underlyingMatchType
4689-
validUnderlyingMatch = validSuper
4690-
cachedUnderlyingMatch
4684+
override def underlyingNormalizable(using Context): Type =
4685+
if ctx.period != validUnderlyingNormalizable then tycon match
4686+
case tycon: TypeRef if defn.isCompiletimeAppliedType(tycon.symbol) =>
4687+
cachedUnderlyingNormalizable = this
4688+
validUnderlyingNormalizable = ctx.period
4689+
case _ =>
4690+
cachedUnderlyingNormalizable = superType.underlyingNormalizable
4691+
validUnderlyingNormalizable = validSuper
4692+
cachedUnderlyingNormalizable
46914693

4692-
override def tryNormalize(using Context): Type = tycon.stripTypeVar match {
4693-
case tycon: TypeRef =>
4694-
def tryMatchAlias = tycon.info match
4695-
case AliasingBounds(alias) if isMatchAlias =>
4696-
trace(i"normalize $this", typr, show = true) {
4697-
MatchTypeTrace.recurseWith(this) {
4698-
alias.applyIfParameterized(args.map(_.normalized)).tryNormalize
4699-
/* `applyIfParameterized` may reduce several HKTypeLambda applications
4700-
* before the underlying MatchType is reached.
4701-
* Even if they do not involve any match type normalizations yet,
4702-
* we still want to record these reductions in the MatchTypeTrace.
4703-
* They should however only be attempted if they eventually expand
4704-
* to a match type, which is ensured by the `isMatchAlias` guard.
4705-
*/
4706-
}
4707-
}
4708-
case _ =>
4709-
NoType
4710-
tryCompiletimeConstantFold.orElse(tryMatchAlias)
4711-
case _ =>
4712-
NoType
4713-
}
4694+
override def tryNormalize(using Context): Type =
4695+
if isMatchAlias && MatchTypeTrace.isRecording then
4696+
MatchTypeTrace.recurseWith(this)(superType.tryNormalize)
4697+
else super.tryNormalize
47144698

47154699
/** Is this an unreducible application to wildcard arguments?
47164700
* This is the case if tycon is higher-kinded. This means
@@ -5173,13 +5157,6 @@ object Types extends TypeUtils {
51735157
private var myReduced: Type | Null = null
51745158
private var reductionContext: util.MutableMap[Type, Type] | Null = null
51755159

5176-
override def tryNormalize(using Context): Type =
5177-
try
5178-
reduced.normalized
5179-
catch
5180-
case ex: Throwable =>
5181-
handleRecursive("normalizing", s"${scrutinee.show} match ..." , ex)
5182-
51835160
private def thisMatchType = this
51845161

51855162
def reduced(using Context): Type = atPhaseNoLater(elimOpaquePhase) {
@@ -5282,7 +5259,7 @@ object Types extends TypeUtils {
52825259
def apply(bound: Type, scrutinee: Type, cases: List[Type])(using Context): MatchType =
52835260
unique(new CachedMatchType(bound, scrutinee, cases))
52845261

5285-
def thatReducesUsingGadt(tp: Type)(using Context): Boolean = tp.underlyingMatchType match
5262+
def thatReducesUsingGadt(tp: Type)(using Context): Boolean = tp.underlyingNormalizable match
52865263
case mt: MatchType => mt.reducesUsingGadt
52875264
case _ => false
52885265

@@ -5731,7 +5708,8 @@ object Types extends TypeUtils {
57315708
/** Common supertype of `TypeAlias` and `MatchAlias` */
57325709
abstract class AliasingBounds(val alias: Type) extends TypeBounds(alias, alias) {
57335710

5734-
def derivedAlias(alias: Type)(using Context): AliasingBounds
5711+
def derivedAlias(alias: Type)(using Context): AliasingBounds =
5712+
if alias eq this.alias then this else AliasingBounds(alias)
57355713

57365714
override def computeHash(bs: Binders): Int = doHash(bs, alias)
57375715
override def hashIsStable: Boolean = alias.hashIsStable
@@ -5753,10 +5731,7 @@ object Types extends TypeUtils {
57535731

57545732
/** = T
57555733
*/
5756-
class TypeAlias(alias: Type) extends AliasingBounds(alias) {
5757-
def derivedAlias(alias: Type)(using Context): AliasingBounds =
5758-
if (alias eq this.alias) this else TypeAlias(alias)
5759-
}
5734+
class TypeAlias(alias: Type) extends AliasingBounds(alias)
57605735

57615736
/** = T where `T` is a `MatchType`
57625737
*
@@ -5765,10 +5740,7 @@ object Types extends TypeUtils {
57655740
* If we assumed full substitutivity, we would have to reject all recursive match
57665741
* aliases (or else take the jump and allow full recursive types).
57675742
*/
5768-
class MatchAlias(alias: Type) extends AliasingBounds(alias) {
5769-
def derivedAlias(alias: Type)(using Context): AliasingBounds =
5770-
if (alias eq this.alias) this else MatchAlias(alias)
5771-
}
5743+
class MatchAlias(alias: Type) extends AliasingBounds(alias)
57725744

57735745
object TypeBounds {
57745746
def apply(lo: Type, hi: Type)(using Context): TypeBounds =

compiler/src/dotty/tools/dotc/typer/Typer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2031,7 +2031,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
20312031
case _ => false
20322032
}
20332033

2034-
val result = pt.underlyingMatchType match {
2034+
val result = pt.underlyingNormalizable match {
20352035
case mt: MatchType if isMatchTypeShaped(mt) =>
20362036
typedDependentMatchFinish(tree, sel1, selType, tree.cases, mt)
20372037
case _ =>

compiler/test/dotc/neg-best-effort-pickling.blacklist

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ illegal-match-types.scala
1515
i13780-1.scala
1616
i20317a.scala
1717
i11226.scala
18+
i974.scala
1819

1920
# semantic db generation fails in the first compilation
2021
i1642.scala

tests/neg/i12049d.check

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i12049d.scala:14:52 -----------------------------------------------------------
2+
14 |val x: M[NotRelevant[Nothing], Relevant[Nothing]] = 2 // error
3+
| ^
4+
| Found: (2 : Int)
5+
| Required: M[NotRelevant[Nothing], Relevant[Nothing]]
6+
|
7+
| Note: a match type could not be fully reduced:
8+
|
9+
| trying to reduce M[NotRelevant[Nothing], Relevant[Nothing]]
10+
| trying to reduce Relevant[Nothing]
11+
| failed since selector Nothing
12+
| is uninhabited (there are no values of that type).
13+
|
14+
| longer explanation available when compiling with `-explain`

tests/neg/i12049d.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
trait A
3+
trait B
4+
5+
type M[X, Y] = Y match
6+
case A => Int
7+
case B => String
8+
9+
type Relevant[Z] = Z match
10+
case A => B
11+
type NotRelevant[Z] = Z match
12+
case B => A
13+
14+
val x: M[NotRelevant[Nothing], Relevant[Nothing]] = 2 // error

tests/pos/i20482.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trait WrapperType[A]
2+
3+
case class Foo[A]()
4+
5+
case class Bar[A]()
6+
7+
type FooToBar[D[_]] = [A] =>> D[Unit] match {
8+
case Foo[Unit] => Bar[A]
9+
}
10+
11+
case class Test()
12+
object Test {
13+
implicit val wrapperType: WrapperType[Bar[Test]] = new WrapperType[Bar[Test]] {}
14+
}
15+
16+
val test = summon[WrapperType[FooToBar[Foo][Test]]]
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
type Rec[X] = X match
3+
case Int => Rec[X]
4+
5+
type M[Unused, Y] = Y match
6+
case String => Double
7+
8+
def foo[X](d: M[Rec[X], "hi"]) = ???
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
def Test = foo[Int](3d) // crash before changes

0 commit comments

Comments
 (0)