@@ -5307,10 +5307,25 @@ object Types extends TypeUtils {
53075307 case _ => true
53085308 end MatchTypeCasePattern
53095309
5310+ enum MatchTypeCaseError :
5311+ case Alias (sym : Symbol )
5312+ case RefiningBounds (name : TypeName )
5313+ case StructuralType (name : TypeName )
5314+ case UnaccountedTypeParam (name : TypeName )
5315+
5316+ def explanation (using Context ) = this match
5317+ case Alias (sym) => i " a type alias ` ${sym.name}` "
5318+ case RefiningBounds (name) => i " an abstract type member ` $name` with bounds that need verification "
5319+ case StructuralType (name) => i " an abstract type member ` $name` that does not refine a member in its parent "
5320+ case UnaccountedTypeParam (name) => i " an unaccounted type parameter ` $name` "
5321+ end MatchTypeCaseError
5322+
5323+ type MatchTypeCaseResult = MatchTypeCasePattern | MatchTypeCaseError
5324+
53105325 enum MatchTypeCaseSpec :
53115326 case SubTypeTest (origMatchCase : Type , pattern : Type , body : Type )
53125327 case SpeccedPatMat (origMatchCase : HKTypeLambda , captureCount : Int , pattern : MatchTypeCasePattern , body : Type )
5313- case LegacyPatMat (origMatchCase : HKTypeLambda )
5328+ case LegacyPatMat (origMatchCase : HKTypeLambda , err : MatchTypeCaseError | Null )
53145329 case MissingCaptures (origMatchCase : HKTypeLambda , missing : collection.BitSet )
53155330
53165331 def origMatchCase : Type
@@ -5321,18 +5336,18 @@ object Types extends TypeUtils {
53215336 cas match
53225337 case cas : HKTypeLambda if ! sourceVersion.isAtLeast(SourceVersion .`3.4`) =>
53235338 // Always apply the legacy algorithm under -source:3.3 and below
5324- LegacyPatMat (cas)
5339+ LegacyPatMat (cas, null )
53255340 case cas : HKTypeLambda =>
53265341 val defn .MatchCase (pat, body) = cas.resultType: @ unchecked
53275342 val missing = checkCapturesPresent(cas, pat)
53285343 if ! missing.isEmpty then
53295344 MissingCaptures (cas, missing)
53305345 else
5331- val specPattern = tryConvertToSpecPattern(cas, pat)
5332- if specPattern != null then
5333- SpeccedPatMat (cas, cas.paramNames.size, specPattern, body)
5334- else
5335- LegacyPatMat (cas)
5346+ tryConvertToSpecPattern(cas, pat) match
5347+ case specPattern : MatchTypeCasePattern =>
5348+ SpeccedPatMat (cas, cas.paramNames.size, specPattern, body)
5349+ case err : MatchTypeCaseError =>
5350+ LegacyPatMat (cas, err )
53365351 case _ =>
53375352 val defn .MatchCase (pat, body) = cas : @ unchecked
53385353 SubTypeTest (cas, pat, body)
@@ -5370,15 +5385,15 @@ object Types extends TypeUtils {
53705385 * It must adhere to the specification of legal patterns defined at
53715386 * https://docs.scala-lang.org/sips/match-types-spec.html#legal-patterns
53725387 *
5373- * Returns `null` if the pattern in `caseLambda` is a not a legal pattern.
5388+ * Returns a MatchTypeCaseError if the pattern in `caseLambda` is a not a legal pattern.
53745389 */
5375- private def tryConvertToSpecPattern (caseLambda : HKTypeLambda , pat : Type )(using Context ): MatchTypeCasePattern | Null =
5376- var typeParamRefsAccountedFor : Int = 0
5390+ private def tryConvertToSpecPattern (caseLambda : HKTypeLambda , pat : Type )(using Context ): MatchTypeCaseResult =
5391+ var typeParamRefsUnaccountedFor = ( 0 until caseLambda.paramNames.length).to(mutable. BitSet )
53775392
5378- def rec (pat : Type , variance : Int ): MatchTypeCasePattern | Null =
5393+ def rec (pat : Type , variance : Int ): MatchTypeCaseResult =
53795394 pat match
53805395 case pat @ TypeParamRef (binder, num) if binder eq caseLambda =>
5381- typeParamRefsAccountedFor += 1
5396+ typeParamRefsUnaccountedFor -= num
53825397 MatchTypeCasePattern .Capture (num, isWildcard = pat.paramName.is(WildcardParamName ))
53835398
53845399 case pat @ AppliedType (tycon : TypeRef , args) if variance == 1 =>
@@ -5394,13 +5409,13 @@ object Types extends TypeUtils {
53945409 MatchTypeCasePattern .BaseTypeTest (tycon, argPatterns, needsConcreteScrut)
53955410 }
53965411 else if defn.isCompiletime_S(tyconSym) && args.sizeIs == 1 then
5397- val argPattern = rec(args.head, variance)
5398- if argPattern == null then
5399- null
5400- else if argPattern.isTypeTest then
5401- MatchTypeCasePattern . TypeTest (pat)
5402- else
5403- MatchTypeCasePattern .CompileTimeS (argPattern)
5412+ rec(args.head, variance) match
5413+ case err : MatchTypeCaseError =>
5414+ err
5415+ case argPattern : MatchTypeCasePattern =>
5416+ if argPattern.isTypeTest
5417+ then MatchTypeCasePattern . TypeTest (pat)
5418+ else MatchTypeCasePattern .CompileTimeS (argPattern)
54045419 else
54055420 tycon.info match
54065421 case _ : RealTypeBounds =>
@@ -5416,7 +5431,7 @@ object Types extends TypeUtils {
54165431 */
54175432 rec(pat.superType, variance)
54185433 case _ =>
5419- null
5434+ MatchTypeCaseError . Alias (tyconSym)
54205435
54215436 case pat @ AppliedType (tycon : TypeParamRef , _) if variance == 1 =>
54225437 recAbstractTypeConstructor(pat)
@@ -5437,40 +5452,40 @@ object Types extends TypeUtils {
54375452 MatchTypeCasePattern .TypeMemberExtractor (refinedName, capture)
54385453 else
54395454 // Otherwise, a type-test + capture combo might be necessary, and we are out of spec
5440- null
5455+ MatchTypeCaseError . RefiningBounds (refinedName)
54415456 case _ =>
54425457 // If the member does not refine a member of the `parent`, we are out of spec
5443- null
5458+ MatchTypeCaseError . StructuralType (refinedName)
54445459
54455460 case _ =>
54465461 MatchTypeCasePattern .TypeTest (pat)
54475462 end rec
54485463
5449- def recAbstractTypeConstructor (pat : AppliedType ): MatchTypeCasePattern | Null =
5464+ def recAbstractTypeConstructor (pat : AppliedType ): MatchTypeCaseResult =
54505465 recArgPatterns(pat) { argPatterns =>
54515466 MatchTypeCasePattern .AbstractTypeConstructor (pat.tycon, argPatterns)
54525467 }
54535468 end recAbstractTypeConstructor
54545469
5455- def recArgPatterns (pat : AppliedType )(whenNotTypeTest : List [MatchTypeCasePattern ] => MatchTypeCasePattern | Null ): MatchTypeCasePattern | Null =
5470+ def recArgPatterns (pat : AppliedType )(whenNotTypeTest : List [MatchTypeCasePattern ] => MatchTypeCaseResult ): MatchTypeCaseResult =
54565471 val AppliedType (tycon, args) = pat
54575472 val tparams = tycon.typeParams
54585473 val argPatterns = args.zip(tparams).map { (arg, tparam) =>
54595474 rec(arg, tparam.paramVarianceSign)
54605475 }
5461- if argPatterns.exists(_ == null ) then
5462- null
5463- else
5464- val argPatterns1 = argPatterns.asInstanceOf [List [MatchTypeCasePattern ]] // they are not null
5476+ argPatterns.find(_.isInstanceOf [MatchTypeCaseError ]).getOrElse:
5477+ val argPatterns1 = argPatterns.asInstanceOf [List [MatchTypeCasePattern ]] // they are not errors
54655478 if argPatterns1.forall(_.isTypeTest) then
54665479 MatchTypeCasePattern .TypeTest (pat)
54675480 else
54685481 whenNotTypeTest(argPatterns1)
54695482 end recArgPatterns
54705483
5471- val result = rec(pat, variance = 1 )
5472- if typeParamRefsAccountedFor == caseLambda.paramNames.size then result
5473- else null
5484+ rec(pat, variance = 1 ) match
5485+ case err : MatchTypeCaseError => err
5486+ case ok if typeParamRefsUnaccountedFor.isEmpty => ok
5487+ case _ =>
5488+ MatchTypeCaseError .UnaccountedTypeParam (caseLambda.paramNames(typeParamRefsUnaccountedFor.head))
54745489 end tryConvertToSpecPattern
54755490 end MatchTypeCaseSpec
54765491
0 commit comments