Skip to content

Commit 2c88e40

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 f8b3515 commit 2c88e40

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
@@ -3231,9 +3231,10 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32313231
/** Match a single case. */
32323232
def matchCase(cas: MatchTypeCaseSpec): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
32333233
cas match
3234-
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3235-
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3236-
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3234+
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3235+
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3236+
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3237+
case cas: MatchTypeCaseSpec.MissingCaptures => matchMissingCaptures(cas)
32373238
}
32383239

32393240
def matchSubTypeTest(spec: MatchTypeCaseSpec.SubTypeTest): MatchResult =
@@ -3415,6 +3416,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
34153416
MatchResult.Stuck
34163417
end matchLegacyPatMat
34173418

3419+
def matchMissingCaptures(spec: MatchTypeCaseSpec.MissingCaptures): MatchResult =
3420+
MatchResult.Stuck
3421+
34183422
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
34193423
case cas :: remaining1 =>
34203424
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._
@@ -5074,6 +5076,7 @@ object Types {
50745076
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
50755077
case SpeccedPatMat(origMatchCase: HKTypeLambda, captureCount: Int, pattern: MatchTypeCasePattern, body: Type)
50765078
case LegacyPatMat(origMatchCase: HKTypeLambda)
5079+
case MissingCaptures(origMatchCase: HKTypeLambda, missing: ju.BitSet)
50775080

50785081
def origMatchCase: Type
50795082
end MatchTypeCaseSpec
@@ -5083,16 +5086,45 @@ object Types {
50835086
cas match
50845087
case cas: HKTypeLambda =>
50855088
val defn.MatchCase(pat, body) = cas.resultType: @unchecked
5086-
val specPattern = tryConvertToSpecPattern(cas, pat)
5087-
if specPattern != null then
5088-
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5089+
val missing = checkCapturesPresent(cas, pat)
5090+
if !missing.isEmpty then
5091+
MissingCaptures(cas, missing)
50895092
else
5090-
LegacyPatMat(cas)
5093+
val specPattern = tryConvertToSpecPattern(cas, pat)
5094+
if specPattern != null then
5095+
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5096+
else
5097+
LegacyPatMat(cas)
50915098
case _ =>
50925099
val defn.MatchCase(pat, body) = cas: @unchecked
50935100
SubTypeTest(cas, pat, body)
50945101
end analyze
50955102

5103+
/** Checks that all the captures of the case are present in the case.
5104+
*
5105+
* Sometimes, because of earlier substitutions of an abstract type constructor,
5106+
* we can end up with patterns that do not mention all their captures anymore.
5107+
* This can happen even when the body still refers to these missing captures.
5108+
* In that case, we must always consider the case to be unmatchable, i.e., to
5109+
* become `Stuck`.
5110+
*
5111+
* See pos/i12127.scala for an example.
5112+
*/
5113+
def checkCapturesPresent(cas: HKTypeLambda, pat: Type)(using Context): ju.BitSet =
5114+
val captureCount = cas.paramNames.size
5115+
val missing = new java.util.BitSet(captureCount)
5116+
missing.set(0, captureCount)
5117+
new CheckCapturesPresent(cas).apply(missing, pat)
5118+
5119+
private class CheckCapturesPresent(cas: HKTypeLambda)(using Context) extends TypeAccumulator[ju.BitSet]:
5120+
def apply(missing: ju.BitSet, tp: Type): ju.BitSet = tp match
5121+
case TypeParamRef(binder, num) if binder eq cas =>
5122+
missing.clear(num)
5123+
missing
5124+
case _ =>
5125+
foldOver(missing, tp)
5126+
end CheckCapturesPresent
5127+
50965128
private def tryConvertToSpecPattern(caseLambda: HKTypeLambda, pat: Type)(using Context): MatchTypeCasePattern | Null =
50975129
var typeParamRefsAccountedFor: Int = 0
50985130

0 commit comments

Comments
 (0)