Skip to content

Commit 37fc96a

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 9fa468c commit 37fc96a

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
@@ -3185,7 +3185,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
31853185
super.typeVarInstance(tvar)
31863186
}
31873187

3188-
def matchCases(scrut: Type, cases: List[Type])(using Context): Type = {
3188+
def matchCases(scrut: Type, cases: List[MatchTypeCaseSpec])(using Context): Type = {
31893189
// a reference for the type parameters poisoned during matching
31903190
// for use during the reduction step
31913191
var poisoned: Set[TypeParamRef] = Set.empty
@@ -3226,16 +3226,26 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32263226
}
32273227

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

3238-
val defn.MatchCase(pat, body) = cas1: @unchecked
3244+
def matchLegacyPatMat(spec: MatchTypeCaseSpec.LegacyPatMat): MatchResult =
3245+
val caseLambda = constrained(spec.origMatchCase).asInstanceOf[HKTypeLambda]
3246+
this.caseLambda = caseLambda
3247+
3248+
val defn.MatchCase(pat, body) = caseLambda.resultType: @unchecked
32393249

32403250
def matches(canWidenAbstract: Boolean): Boolean =
32413251
val saved = this.canWidenAbstract
@@ -3249,22 +3259,18 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32493259
this.canWidenAbstract = saved
32503260

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

3267-
if caseLambda.exists && matches(canWidenAbstract = false) then
3273+
if matches(canWidenAbstract = false) then
32683274
redux(canApprox = true)
32693275
else if matches(canWidenAbstract = true) then
32703276
redux(canApprox = false)
@@ -3274,9 +3280,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32743280
MatchResult.Disjoint
32753281
else
32763282
MatchResult.Stuck
3277-
}
3283+
end matchLegacyPatMat
32783284

3279-
def recur(remaining: List[Type]): Type = remaining match
3285+
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
32803286
case cas :: remaining1 =>
32813287
matchCase(cas) match
32823288
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
@@ -5009,7 +5009,7 @@ object Types {
50095009
trace(i"reduce match type $this $hashCode", matchTypes, show = true)(inMode(Mode.Type) {
50105010
def matchCases(cmp: TrackingTypeComparer): Type =
50115011
val saved = ctx.typerState.snapshot()
5012-
try cmp.matchCases(scrutinee.normalized, cases)
5012+
try cmp.matchCases(scrutinee.normalized, cases.map(MatchTypeCaseSpec.analyze(_)))
50135013
catch case ex: Throwable =>
50145014
handleRecursive("reduce type ", i"$scrutinee match ...", ex)
50155015
finally
@@ -5061,6 +5061,24 @@ object Types {
50615061
case _ => None
50625062
}
50635063

5064+
enum MatchTypeCaseSpec:
5065+
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
5066+
case LegacyPatMat(origMatchCase: HKTypeLambda)
5067+
5068+
def origMatchCase: Type
5069+
end MatchTypeCaseSpec
5070+
5071+
object MatchTypeCaseSpec:
5072+
def analyze(cas: Type)(using Context): MatchTypeCaseSpec =
5073+
cas match
5074+
case cas: HKTypeLambda =>
5075+
LegacyPatMat(cas)
5076+
case _ =>
5077+
val defn.MatchCase(pat, body) = cas: @unchecked
5078+
SubTypeTest(cas, pat, body)
5079+
end analyze
5080+
end MatchTypeCaseSpec
5081+
50645082
// ------ ClassInfo, Type Bounds --------------------------------------------------
50655083

50665084
type TypeOrSymbol = Type | Symbol

0 commit comments

Comments
 (0)