Skip to content

Commit a377544

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 9f5aa6f commit a377544

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
@@ -3181,7 +3181,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
31813181
super.typeVarInstance(tvar)
31823182
}
31833183

3184-
def matchCases(scrut: Type, cases: List[Type])(using Context): Type = {
3184+
def matchCases(scrut: Type, cases: List[MatchTypeCaseSpec])(using Context): Type = {
31853185
// a reference for the type parameters poisoned during matching
31863186
// for use during the reduction step
31873187
var poisoned: Set[TypeParamRef] = Set.empty
@@ -3222,16 +3222,26 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32223222
}
32233223

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

3234-
val defn.MatchCase(pat, body) = cas1: @unchecked
3240+
def matchLegacyPatMat(spec: MatchTypeCaseSpec.LegacyPatMat): MatchResult =
3241+
val caseLambda = constrained(spec.origMatchCase).asInstanceOf[HKTypeLambda]
3242+
this.caseLambda = caseLambda
3243+
3244+
val defn.MatchCase(pat, body) = caseLambda.resultType: @unchecked
32353245

32363246
def matches(canWidenAbstract: Boolean): Boolean =
32373247
val saved = this.canWidenAbstract
@@ -3245,22 +3255,18 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32453255
this.canWidenAbstract = saved
32463256

32473257
def redux(canApprox: Boolean): MatchResult =
3248-
caseLambda match
3249-
case caseLambda: HKTypeLambda =>
3250-
val instances = paramInstances(canApprox)(Array.fill(caseLambda.paramNames.length)(NoType), pat)
3251-
instantiateParams(instances)(body) match
3252-
case Range(lo, hi) =>
3253-
MatchResult.NoInstance {
3254-
caseLambda.paramNames.zip(instances).collect {
3255-
case (name, Range(lo, hi)) => (name, TypeBounds(lo, hi))
3256-
}
3257-
}
3258-
case redux =>
3259-
MatchResult.Reduced(redux)
3260-
case _ =>
3261-
MatchResult.Reduced(body)
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)
32623268

3263-
if caseLambda.exists && matches(canWidenAbstract = false) then
3269+
if matches(canWidenAbstract = false) then
32643270
redux(canApprox = true)
32653271
else if matches(canWidenAbstract = true) then
32663272
redux(canApprox = false)
@@ -3270,9 +3276,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32703276
MatchResult.Disjoint
32713277
else
32723278
MatchResult.Stuck
3273-
}
3279+
end matchLegacyPatMat
32743280

3275-
def recur(remaining: List[Type]): Type = remaining match
3281+
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
32763282
case cas :: remaining1 =>
32773283
matchCase(cas) match
32783284
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
@@ -5003,7 +5003,7 @@ object Types {
50035003
trace(i"reduce match type $this $hashCode", matchTypes, show = true)(inMode(Mode.Type) {
50045004
def matchCases(cmp: TrackingTypeComparer): Type =
50055005
val saved = ctx.typerState.snapshot()
5006-
try cmp.matchCases(scrutinee.normalized, cases)
5006+
try cmp.matchCases(scrutinee.normalized, cases.map(MatchTypeCaseSpec.analyze(_)))
50075007
catch case ex: Throwable =>
50085008
handleRecursive("reduce type ", i"$scrutinee match ...", ex)
50095009
finally
@@ -5055,6 +5055,24 @@ object Types {
50555055
case _ => None
50565056
}
50575057

5058+
enum MatchTypeCaseSpec:
5059+
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
5060+
case LegacyPatMat(origMatchCase: HKTypeLambda)
5061+
5062+
def origMatchCase: Type
5063+
end MatchTypeCaseSpec
5064+
5065+
object MatchTypeCaseSpec:
5066+
def analyze(cas: Type)(using Context): MatchTypeCaseSpec =
5067+
cas match
5068+
case cas: HKTypeLambda =>
5069+
LegacyPatMat(cas)
5070+
case _ =>
5071+
val defn.MatchCase(pat, body) = cas: @unchecked
5072+
SubTypeTest(cas, pat, body)
5073+
end analyze
5074+
end MatchTypeCaseSpec
5075+
50585076
// ------ ClassInfo, Type Bounds --------------------------------------------------
50595077

50605078
type TypeOrSymbol = Type | Symbol

0 commit comments

Comments
 (0)