Skip to content

Commit f12a43d

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 031538f commit f12a43d

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
@@ -3238,9 +3238,10 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32383238
/** Match a single case. */
32393239
def matchCase(cas: MatchTypeCaseSpec): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
32403240
cas match
3241-
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3242-
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3243-
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3241+
case cas: MatchTypeCaseSpec.SubTypeTest => matchSubTypeTest(cas)
3242+
case cas: MatchTypeCaseSpec.SpeccedPatMat => matchSpeccedPatMat(cas)
3243+
case cas: MatchTypeCaseSpec.LegacyPatMat => matchLegacyPatMat(cas)
3244+
case cas: MatchTypeCaseSpec.MissingCaptures => matchMissingCaptures(cas)
32443245
}
32453246

32463247
def matchSubTypeTest(spec: MatchTypeCaseSpec.SubTypeTest): MatchResult =
@@ -3422,6 +3423,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
34223423
MatchResult.Stuck
34233424
end matchLegacyPatMat
34243425

3426+
def matchMissingCaptures(spec: MatchTypeCaseSpec.MissingCaptures): MatchResult =
3427+
MatchResult.Stuck
3428+
34253429
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
34263430
case cas :: remaining1 =>
34273431
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._
@@ -5080,6 +5082,7 @@ object Types {
50805082
case SubTypeTest(origMatchCase: Type, pattern: Type, body: Type)
50815083
case SpeccedPatMat(origMatchCase: HKTypeLambda, captureCount: Int, pattern: MatchTypeCasePattern, body: Type)
50825084
case LegacyPatMat(origMatchCase: HKTypeLambda)
5085+
case MissingCaptures(origMatchCase: HKTypeLambda, missing: ju.BitSet)
50835086

50845087
def origMatchCase: Type
50855088
end MatchTypeCaseSpec
@@ -5089,16 +5092,45 @@ object Types {
50895092
cas match
50905093
case cas: HKTypeLambda =>
50915094
val defn.MatchCase(pat, body) = cas.resultType: @unchecked
5092-
val specPattern = tryConvertToSpecPattern(cas, pat)
5093-
if specPattern != null then
5094-
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5095+
val missing = checkCapturesPresent(cas, pat)
5096+
if !missing.isEmpty then
5097+
MissingCaptures(cas, missing)
50955098
else
5096-
LegacyPatMat(cas)
5099+
val specPattern = tryConvertToSpecPattern(cas, pat)
5100+
if specPattern != null then
5101+
SpeccedPatMat(cas, cas.paramNames.size, specPattern, body)
5102+
else
5103+
LegacyPatMat(cas)
50975104
case _ =>
50985105
val defn.MatchCase(pat, body) = cas: @unchecked
50995106
SubTypeTest(cas, pat, body)
51005107
end analyze
51015108

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

0 commit comments

Comments
 (0)