Skip to content

Commit 6e125d2

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 1f05711 commit 6e125d2

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
@@ -3234,9 +3234,10 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32343234
/** Match a single case. */
32353235
def matchCase(cas: MatchTypeCaseSpec): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
32363236
cas match
3237-
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3238-
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3239-
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3237+
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3238+
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3239+
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3240+
case cas: MatchTypeCaseSpec.MissingCaptures => matchMissingCaptures(cas)
32403241
}
32413242

32423243
def matchSubTypeTest(spec: MatchTypeCaseSpec.SubTypeTest): MatchResult =
@@ -3418,6 +3419,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
34183419
MatchResult.Stuck
34193420
end matchLegacyPatMat
34203421

3422+
def matchMissingCaptures(spec: MatchTypeCaseSpec.MissingCaptures): MatchResult =
3423+
MatchResult.Stuck
3424+
34213425
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
34223426
case cas :: remaining1 =>
34233427
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._
@@ -5077,6 +5079,7 @@ object Types {
50775079
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
50785080
case SpeccedPatMat(origMatchCase: HKTypeLambda, captureCount: Int, pattern: MatchTypeCasePattern, body: Type)
50795081
case LegacyPatMat(origMatchCase: HKTypeLambda)
5082+
case MissingCaptures(origMatchCase: HKTypeLambda, missing: ju.BitSet)
50805083

50815084
def origMatchCase: Type
50825085
end MatchTypeCaseSpec
@@ -5086,16 +5089,45 @@ object Types {
50865089
cas match
50875090
case cas: HKTypeLambda =>
50885091
val defn.MatchCase(pat, body) = cas.resultType: @unchecked
5089-
val specPattern = tryConvertToSpecPattern(cas, pat)
5090-
if specPattern != null then
5091-
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5092+
val missing = checkCapturesPresent(cas, pat)
5093+
if !missing.isEmpty then
5094+
MissingCaptures(cas, missing)
50925095
else
5093-
LegacyPatMat(cas)
5096+
val specPattern = tryConvertToSpecPattern(cas, pat)
5097+
if specPattern != null then
5098+
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5099+
else
5100+
LegacyPatMat(cas)
50945101
case _ =>
50955102
val defn.MatchCase(pat, body) = cas: @unchecked
50965103
SubTypeTest(cas, pat, body)
50975104
end analyze
50985105

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

0 commit comments

Comments
 (0)