Skip to content

Commit 067b828

Browse files
committed
Short-circuit match type cases with missing captures in their patterns.
So that they do not fall into the legacy fallback.
1 parent cc41d48 commit 067b828

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3250,9 +3250,10 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32503250
/** Match a single case. */
32513251
def matchCase(cas: MatchTypeCaseSpec): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
32523252
cas match
3253-
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3254-
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3255-
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3253+
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3254+
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3255+
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3256+
case cas: MatchTypeCaseSpec.MissingCaptures => matchMissingCaptures(cas)
32563257
}
32573258

32583259
def matchSubTypeTest(spec: MatchTypeCaseSpec.SubTypeTest): MatchResult =
@@ -3434,6 +3435,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
34343435
MatchResult.Stuck
34353436
end matchLegacyPatMat
34363437

3438+
def matchMissingCaptures(spec: MatchTypeCaseSpec.MissingCaptures): MatchResult =
3439+
MatchResult.Stuck
3440+
34373441
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
34383442
case cas :: remaining1 =>
34393443
matchCase(cas) match

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5107,6 +5107,7 @@ object Types extends TypeUtils {
51075107
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
51085108
case SpeccedPatMat(origMatchCase: HKTypeLambda, captureCount: Int, pattern: MatchTypeCasePattern, body: Type)
51095109
case LegacyPatMat(origMatchCase: HKTypeLambda)
5110+
case MissingCaptures(origMatchCase: HKTypeLambda, missing: collection.BitSet)
51105111

51115112
def origMatchCase: Type
51125113
end MatchTypeCaseSpec
@@ -5116,16 +5117,44 @@ object Types extends TypeUtils {
51165117
cas match
51175118
case cas: HKTypeLambda =>
51185119
val defn.MatchCase(pat, body) = cas.resultType: @unchecked
5119-
val specPattern = tryConvertToSpecPattern(cas, pat)
5120-
if specPattern != null then
5121-
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5120+
val missing = checkCapturesPresent(cas, pat)
5121+
if !missing.isEmpty then
5122+
MissingCaptures(cas, missing)
51225123
else
5123-
LegacyPatMat(cas)
5124+
val specPattern = tryConvertToSpecPattern(cas, pat)
5125+
if specPattern != null then
5126+
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5127+
else
5128+
LegacyPatMat(cas)
51245129
case _ =>
51255130
val defn.MatchCase(pat, body) = cas: @unchecked
51265131
SubTypeTest(cas, pat, body)
51275132
end analyze
51285133

5134+
/** Checks that all the captures of the case are present in the case.
5135+
*
5136+
* Sometimes, because of earlier substitutions of an abstract type constructor,
5137+
* we can end up with patterns that do not mention all their captures anymore.
5138+
* This can happen even when the body still refers to these missing captures.
5139+
* In that case, we must always consider the case to be unmatchable, i.e., to
5140+
* become `Stuck`.
5141+
*
5142+
* See pos/i12127.scala for an example.
5143+
*/
5144+
def checkCapturesPresent(cas: HKTypeLambda, pat: Type)(using Context): collection.BitSet =
5145+
val captureCount = cas.paramNames.size
5146+
val missing = new mutable.BitSet(captureCount)
5147+
missing ++= (0 until captureCount)
5148+
new CheckCapturesPresent(cas).apply(missing, pat)
5149+
5150+
private class CheckCapturesPresent(cas: HKTypeLambda)(using Context) extends TypeAccumulator[mutable.BitSet]:
5151+
def apply(missing: mutable.BitSet, tp: Type): mutable.BitSet = tp match
5152+
case TypeParamRef(binder, num) if binder eq cas =>
5153+
missing -= num
5154+
case _ =>
5155+
foldOver(missing, tp)
5156+
end CheckCapturesPresent
5157+
51295158
private def tryConvertToSpecPattern(caseLambda: HKTypeLambda, pat: Type)(using Context): MatchTypeCasePattern | Null =
51305159
var typeParamRefsAccountedFor: Int = 0
51315160

0 commit comments

Comments
 (0)