Skip to content

Commit d44273b

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 a7409cc commit d44273b

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
@@ -3178,7 +3178,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
31783178
super.typeVarInstance(tvar)
31793179
}
31803180

3181-
def matchCases(scrut: Type, cases: List[Type])(using Context): Type = {
3181+
def matchCases(scrut: Type, cases: List[MatchTypeCaseSpec])(using Context): Type = {
31823182
// a reference for the type parameters poisoned during matching
31833183
// for use during the reduction step
31843184
var poisoned: Set[TypeParamRef] = Set.empty
@@ -3219,16 +3219,26 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32193219
}
32203220

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

3231-
val defn.MatchCase(pat, body) = cas1: @unchecked
3237+
def matchLegacyPatMat(spec: MatchTypeCaseSpec.LegacyPatMat): MatchResult =
3238+
val caseLambda = constrained(spec.origMatchCase).asInstanceOf[HKTypeLambda]
3239+
this.caseLambda = caseLambda
3240+
3241+
val defn.MatchCase(pat, body) = caseLambda.resultType: @unchecked
32323242

32333243
def matches(canWidenAbstract: Boolean): Boolean =
32343244
val saved = this.canWidenAbstract
@@ -3242,22 +3252,18 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32423252
this.canWidenAbstract = saved
32433253

32443254
def redux(canApprox: Boolean): MatchResult =
3245-
caseLambda match
3246-
case caseLambda: HKTypeLambda =>
3247-
val instances = paramInstances(canApprox)(Array.fill(caseLambda.paramNames.length)(NoType), pat)
3248-
instantiateParams(instances)(body) match
3249-
case Range(lo, hi) =>
3250-
MatchResult.NoInstance {
3251-
caseLambda.paramNames.zip(instances).collect {
3252-
case (name, Range(lo, hi)) => (name, TypeBounds(lo, hi))
3253-
}
3254-
}
3255-
case redux =>
3256-
MatchResult.Reduced(redux)
3257-
case _ =>
3258-
MatchResult.Reduced(body)
3255+
val instances = paramInstances(canApprox)(Array.fill(caseLambda.paramNames.length)(NoType), pat)
3256+
instantiateParams(instances)(body) match
3257+
case Range(lo, hi) =>
3258+
MatchResult.NoInstance {
3259+
caseLambda.paramNames.zip(instances).collect {
3260+
case (name, Range(lo, hi)) => (name, TypeBounds(lo, hi))
3261+
}
3262+
}
3263+
case redux =>
3264+
MatchResult.Reduced(redux)
32593265

3260-
if caseLambda.exists && matches(canWidenAbstract = false) then
3266+
if matches(canWidenAbstract = false) then
32613267
redux(canApprox = true)
32623268
else if matches(canWidenAbstract = true) then
32633269
redux(canApprox = false)
@@ -3267,9 +3273,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32673273
MatchResult.Disjoint
32683274
else
32693275
MatchResult.Stuck
3270-
}
3276+
end matchLegacyPatMat
32713277

3272-
def recur(remaining: List[Type]): Type = remaining match
3278+
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
32733279
case cas :: remaining1 =>
32743280
matchCase(cas) match
32753281
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)