Skip to content

Commit 81a97bb

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 c0f95e2 commit 81a97bb

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-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
@@ -3242,9 +3242,10 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32423242
/** Match a single case. */
32433243
def matchCase(cas: MatchTypeCaseSpec): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
32443244
cas match
3245-
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3246-
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3247-
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3245+
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3246+
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3247+
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3248+
case cas: MatchTypeCaseSpec.MissingCaptures => matchMissingCaptures(cas)
32483249
}
32493250

32503251
def matchSubTypeTest(spec: MatchTypeCaseSpec.SubTypeTest): MatchResult =
@@ -3426,6 +3427,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
34263427
MatchResult.Stuck
34273428
end matchLegacyPatMat
34283429

3430+
def matchMissingCaptures(spec: MatchTypeCaseSpec.MissingCaptures): MatchResult =
3431+
MatchResult.Stuck
3432+
34293433
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
34303434
case cas :: remaining1 =>
34313435
matchCase(cas) match

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

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package dotty.tools
22
package dotc
33
package core
44

5+
import java.util as ju
6+
57
import Symbols.*
68
import Flags.*
79
import Names.*
@@ -5108,6 +5110,7 @@ object Types {
51085110
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
51095111
case SpeccedPatMat(origMatchCase: HKTypeLambda, captureCount: Int, pattern: MatchTypeCasePattern, body: Type)
51105112
case LegacyPatMat(origMatchCase: HKTypeLambda)
5113+
case MissingCaptures(origMatchCase: HKTypeLambda, missing: ju.BitSet)
51115114

51125115
def origMatchCase: Type
51135116
end MatchTypeCaseSpec
@@ -5117,16 +5120,45 @@ object Types {
51175120
cas match
51185121
case cas: HKTypeLambda =>
51195122
val defn.MatchCase(pat, body) = cas.resultType: @unchecked
5120-
val specPattern = tryConvertToSpecPattern(cas, pat)
5121-
if specPattern != null then
5122-
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5123+
val missing = checkCapturesPresent(cas, pat)
5124+
if !missing.isEmpty then
5125+
MissingCaptures(cas, missing)
51235126
else
5124-
LegacyPatMat(cas)
5127+
val specPattern = tryConvertToSpecPattern(cas, pat)
5128+
if specPattern != null then
5129+
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5130+
else
5131+
LegacyPatMat(cas)
51255132
case _ =>
51265133
val defn.MatchCase(pat, body) = cas: @unchecked
51275134
SubTypeTest(cas, pat, body)
51285135
end analyze
51295136

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

0 commit comments

Comments
 (0)