Skip to content

Commit d392300

Browse files
committed
Introduce MatchTypeCaseSpec to categorize match type cases.
For now, we only have `SubTypeTest` and `LegacyPatMat`. * `SubTypeTest` is used when there is no type capture. * `LegacyPatMat` is used when there are captures. In the match type reduction algorithm, we already have a simpler path for `SubTypeTest`. The `LegacyPatMat` path is basically the same as before, but with static knowledge that we have an `HKTypeLambda`.
1 parent 00e9e6b commit d392300

File tree

3 files changed

+61
-34
lines changed

3 files changed

+61
-34
lines changed

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ object MatchTypeTrace:
1212

1313
private enum TraceEntry:
1414
case TryReduce(scrut: Type)
15-
case Stuck(scrut: Type, stuckCase: Type, otherCases: List[Type])
16-
case NoInstance(scrut: Type, stuckCase: Type, fails: List[(Name, TypeBounds)])
15+
case Stuck(scrut: Type, stuckCase: MatchTypeCaseSpec, otherCases: List[MatchTypeCaseSpec])
16+
case NoInstance(scrut: Type, stuckCase: MatchTypeCaseSpec, fails: List[(Name, TypeBounds)])
1717
case EmptyScrutinee(scrut: Type)
1818
import TraceEntry.*
1919

@@ -54,10 +54,10 @@ object MatchTypeTrace:
5454
* not disjoint from it either, which means that the remaining cases `otherCases`
5555
* cannot be visited. Only the first failure is recorded.
5656
*/
57-
def stuck(scrut: Type, stuckCase: Type, otherCases: List[Type])(using Context) =
57+
def stuck(scrut: Type, stuckCase: MatchTypeCaseSpec, otherCases: List[MatchTypeCaseSpec])(using Context) =
5858
matchTypeFail(Stuck(scrut, stuckCase, otherCases))
5959

60-
def noInstance(scrut: Type, stuckCase: Type, fails: List[(Name, TypeBounds)])(using Context) =
60+
def noInstance(scrut: Type, stuckCase: MatchTypeCaseSpec, fails: List[(Name, TypeBounds)])(using Context) =
6161
matchTypeFail(NoInstance(scrut, stuckCase, fails))
6262

6363
/** Record a failure that scrutinee `scrut` is provably empty.
@@ -80,13 +80,16 @@ object MatchTypeTrace:
8080
case _ =>
8181
op
8282

83+
def caseText(spec: MatchTypeCaseSpec)(using Context): String =
84+
caseText(spec.origMatchCase)
85+
8386
def caseText(tp: Type)(using Context): String = tp match
8487
case tp: HKTypeLambda => caseText(tp.resultType)
8588
case defn.MatchCase(any, body) if any eq defn.AnyType => i"case _ => $body"
8689
case defn.MatchCase(pat, body) => i"case $pat => $body"
8790
case _ => i"case $tp"
8891

89-
private def casesText(cases: List[Type])(using Context) =
92+
private def casesText(cases: List[MatchTypeCaseSpec])(using Context) =
9093
i"${cases.map(caseText)}%\n %"
9194

9295
private def explainEntry(entry: TraceEntry)(using Context): String = entry match
@@ -116,7 +119,7 @@ object MatchTypeTrace:
116119
| ${fails.map((name, bounds) => i"$name$bounds")}%\n %"""
117120

118121
/** The failure message when the scrutinee `scrut` does not match any case in `cases`. */
119-
def noMatchesText(scrut: Type, cases: List[Type])(using Context): String =
122+
def noMatchesText(scrut: Type, cases: List[MatchTypeCaseSpec])(using Context): String =
120123
i"""failed since selector $scrut
121124
|matches none of the cases
122125
|

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

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3189,7 +3189,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
31893189
super.typeVarInstance(tvar)
31903190
}
31913191

3192-
def matchCases(scrut: Type, cases: List[Type])(using Context): Type = {
3192+
def matchCases(scrut: Type, cases: List[MatchTypeCaseSpec])(using Context): Type = {
31933193
// a reference for the type parameters poisoned during matching
31943194
// for use during the reduction step
31953195
var poisoned: Set[TypeParamRef] = Set.empty
@@ -3230,16 +3230,26 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32303230
}
32313231

32323232
/** Match a single case. */
3233-
def matchCase(cas: Type): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
3234-
val cas1 = cas match {
3235-
case cas: HKTypeLambda =>
3236-
caseLambda = constrained(cas, ast.tpd.EmptyTree)._1
3237-
caseLambda.resultType
3238-
case _ =>
3239-
cas
3240-
}
3233+
def matchCase(cas: MatchTypeCaseSpec): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
3234+
cas match
3235+
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3236+
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3237+
}
3238+
3239+
def matchSubTypeTest(spec: MatchTypeCaseSpec.SubTypeTest): MatchResult =
3240+
if necessarySubType(scrut, spec.pattern) then
3241+
MatchResult.Reduced(spec.body)
3242+
else if provablyDisjoint(scrut, spec.pattern) then
3243+
MatchResult.Disjoint
3244+
else
3245+
MatchResult.Stuck
3246+
end matchSubTypeTest
32413247

3242-
val defn.MatchCase(pat, body) = cas1: @unchecked
3248+
def matchLegacyPatMat(spec: MatchTypeCaseSpec.LegacyPatMat): MatchResult =
3249+
val caseLambda = constrained(spec.origMatchCase, ast.tpd.EmptyTree)._1.asInstanceOf[HKTypeLambda]
3250+
this.caseLambda = caseLambda
3251+
3252+
val defn.MatchCase(pat, body) = caseLambda.resultType: @unchecked
32433253

32443254
def matches(canWidenAbstract: Boolean): Boolean =
32453255
val saved = this.canWidenAbstract
@@ -3253,22 +3263,18 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32533263
this.canWidenAbstract = saved
32543264

32553265
def redux(canApprox: Boolean): MatchResult =
3256-
caseLambda match
3257-
case caseLambda: HKTypeLambda =>
3258-
val instances = paramInstances(canApprox)(Array.fill(caseLambda.paramNames.length)(NoType), pat)
3259-
instantiateParams(instances)(body) match
3260-
case Range(lo, hi) =>
3261-
MatchResult.NoInstance {
3262-
caseLambda.paramNames.zip(instances).collect {
3263-
case (name, Range(lo, hi)) => (name, TypeBounds(lo, hi))
3264-
}
3265-
}
3266-
case redux =>
3267-
MatchResult.Reduced(redux)
3268-
case _ =>
3269-
MatchResult.Reduced(body)
3266+
val instances = paramInstances(canApprox)(Array.fill(caseLambda.paramNames.length)(NoType), pat)
3267+
instantiateParams(instances)(body) match
3268+
case Range(lo, hi) =>
3269+
MatchResult.NoInstance {
3270+
caseLambda.paramNames.zip(instances).collect {
3271+
case (name, Range(lo, hi)) => (name, TypeBounds(lo, hi))
3272+
}
3273+
}
3274+
case redux =>
3275+
MatchResult.Reduced(redux)
32703276

3271-
if caseLambda.exists && matches(canWidenAbstract = false) then
3277+
if matches(canWidenAbstract = false) then
32723278
redux(canApprox = true)
32733279
else if matches(canWidenAbstract = true) then
32743280
redux(canApprox = false)
@@ -3278,9 +3284,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32783284
MatchResult.Disjoint
32793285
else
32803286
MatchResult.Stuck
3281-
}
3287+
end matchLegacyPatMat
32823288

3283-
def recur(remaining: List[Type]): Type = remaining match
3289+
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
32843290
case cas :: remaining1 =>
32853291
matchCase(cas) match
32863292
case MatchResult.Disjoint =>

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5037,7 +5037,7 @@ object Types {
50375037
trace(i"reduce match type $this $hashCode", matchTypes, show = true)(inMode(Mode.Type) {
50385038
def matchCases(cmp: TrackingTypeComparer): Type =
50395039
val saved = ctx.typerState.snapshot()
5040-
try cmp.matchCases(scrutinee.normalized, cases)
5040+
try cmp.matchCases(scrutinee.normalized, cases.map(MatchTypeCaseSpec.analyze(_)))
50415041
catch case ex: Throwable =>
50425042
handleRecursive("reduce type ", i"$scrutinee match ...", ex)
50435043
finally
@@ -5089,6 +5089,24 @@ object Types {
50895089
case _ => None
50905090
}
50915091

5092+
enum MatchTypeCaseSpec:
5093+
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
5094+
case LegacyPatMat(origMatchCase: HKTypeLambda)
5095+
5096+
def origMatchCase: Type
5097+
end MatchTypeCaseSpec
5098+
5099+
object MatchTypeCaseSpec:
5100+
def analyze(cas: Type)(using Context): MatchTypeCaseSpec =
5101+
cas match
5102+
case cas: HKTypeLambda =>
5103+
LegacyPatMat(cas)
5104+
case _ =>
5105+
val defn.MatchCase(pat, body) = cas: @unchecked
5106+
SubTypeTest(cas, pat, body)
5107+
end analyze
5108+
end MatchTypeCaseSpec
5109+
50925110
// ------ ClassInfo, Type Bounds --------------------------------------------------
50935111

50945112
type TypeOrSymbol = Type | Symbol

0 commit comments

Comments
 (0)