Skip to content

Commit c96d847

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 8a3fc7a commit c96d847

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
@@ -3197,7 +3197,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
31973197
super.typeVarInstance(tvar)
31983198
}
31993199

3200-
def matchCases(scrut: Type, cases: List[Type])(using Context): Type = {
3200+
def matchCases(scrut: Type, cases: List[MatchTypeCaseSpec])(using Context): Type = {
32013201
// a reference for the type parameters poisoned during matching
32023202
// for use during the reduction step
32033203
var poisoned: Set[TypeParamRef] = Set.empty
@@ -3238,16 +3238,26 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32383238
}
32393239

32403240
/** Match a single case. */
3241-
def matchCase(cas: Type): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
3242-
val cas1 = cas match {
3243-
case cas: HKTypeLambda =>
3244-
caseLambda = constrained(cas, ast.tpd.EmptyTree)._1
3245-
caseLambda.resultType
3246-
case _ =>
3247-
cas
3248-
}
3241+
def matchCase(cas: MatchTypeCaseSpec): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
3242+
cas match
3243+
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3244+
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3245+
}
3246+
3247+
def matchSubTypeTest(spec: MatchTypeCaseSpec.SubTypeTest): MatchResult =
3248+
if necessarySubType(scrut, spec.pattern) then
3249+
MatchResult.Reduced(spec.body)
3250+
else if provablyDisjoint(scrut, spec.pattern) then
3251+
MatchResult.Disjoint
3252+
else
3253+
MatchResult.Stuck
3254+
end matchSubTypeTest
32493255

3250-
val defn.MatchCase(pat, body) = cas1: @unchecked
3256+
def matchLegacyPatMat(spec: MatchTypeCaseSpec.LegacyPatMat): MatchResult =
3257+
val caseLambda = constrained(spec.origMatchCase, ast.tpd.EmptyTree)._1.asInstanceOf[HKTypeLambda]
3258+
this.caseLambda = caseLambda
3259+
3260+
val defn.MatchCase(pat, body) = caseLambda.resultType: @unchecked
32513261

32523262
def matches(canWidenAbstract: Boolean): Boolean =
32533263
val saved = this.canWidenAbstract
@@ -3261,22 +3271,18 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32613271
this.canWidenAbstract = saved
32623272

32633273
def redux(canApprox: Boolean): MatchResult =
3264-
caseLambda match
3265-
case caseLambda: HKTypeLambda =>
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)
3276-
case _ =>
3277-
MatchResult.Reduced(body)
3274+
val instances = paramInstances(canApprox)(Array.fill(caseLambda.paramNames.length)(NoType), pat)
3275+
instantiateParams(instances)(body) match
3276+
case Range(lo, hi) =>
3277+
MatchResult.NoInstance {
3278+
caseLambda.paramNames.zip(instances).collect {
3279+
case (name, Range(lo, hi)) => (name, TypeBounds(lo, hi))
3280+
}
3281+
}
3282+
case redux =>
3283+
MatchResult.Reduced(redux)
32783284

3279-
if caseLambda.exists && matches(canWidenAbstract = false) then
3285+
if matches(canWidenAbstract = false) then
32803286
redux(canApprox = true)
32813287
else if matches(canWidenAbstract = true) then
32823288
redux(canApprox = false)
@@ -3286,9 +3292,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32863292
MatchResult.Disjoint
32873293
else
32883294
MatchResult.Stuck
3289-
}
3295+
end matchLegacyPatMat
32903296

3291-
def recur(remaining: List[Type]): Type = remaining match
3297+
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
32923298
case cas :: remaining1 =>
32933299
matchCase(cas) match
32943300
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
@@ -5036,7 +5036,7 @@ object Types extends TypeUtils {
50365036
trace(i"reduce match type $this $hashCode", matchTypes, show = true)(inMode(Mode.Type) {
50375037
def matchCases(cmp: TrackingTypeComparer): Type =
50385038
val saved = ctx.typerState.snapshot()
5039-
try cmp.matchCases(scrutinee.normalized, cases)
5039+
try cmp.matchCases(scrutinee.normalized, cases.map(MatchTypeCaseSpec.analyze(_)))
50405040
catch case ex: Throwable =>
50415041
handleRecursive("reduce type ", i"$scrutinee match ...", ex)
50425042
finally
@@ -5088,6 +5088,24 @@ object Types extends TypeUtils {
50885088
case _ => None
50895089
}
50905090

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

50935111
type TypeOrSymbol = Type | Symbol

0 commit comments

Comments
 (0)