@@ -5307,10 +5307,25 @@ object Types extends TypeUtils {
5307
5307
case _ => true
5308
5308
end MatchTypeCasePattern
5309
5309
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
+
5310
5325
enum MatchTypeCaseSpec :
5311
5326
case SubTypeTest (origMatchCase : Type , pattern : Type , body : Type )
5312
5327
case SpeccedPatMat (origMatchCase : HKTypeLambda , captureCount : Int , pattern : MatchTypeCasePattern , body : Type )
5313
- case LegacyPatMat (origMatchCase : HKTypeLambda )
5328
+ case LegacyPatMat (origMatchCase : HKTypeLambda , err : MatchTypeCaseError | Null )
5314
5329
case MissingCaptures (origMatchCase : HKTypeLambda , missing : collection.BitSet )
5315
5330
5316
5331
def origMatchCase : Type
@@ -5321,18 +5336,18 @@ object Types extends TypeUtils {
5321
5336
cas match
5322
5337
case cas : HKTypeLambda if ! sourceVersion.isAtLeast(SourceVersion .`3.4`) =>
5323
5338
// Always apply the legacy algorithm under -source:3.3 and below
5324
- LegacyPatMat (cas)
5339
+ LegacyPatMat (cas, null )
5325
5340
case cas : HKTypeLambda =>
5326
5341
val defn .MatchCase (pat, body) = cas.resultType: @ unchecked
5327
5342
val missing = checkCapturesPresent(cas, pat)
5328
5343
if ! missing.isEmpty then
5329
5344
MissingCaptures (cas, missing)
5330
5345
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 )
5336
5351
case _ =>
5337
5352
val defn .MatchCase (pat, body) = cas : @ unchecked
5338
5353
SubTypeTest (cas, pat, body)
@@ -5370,15 +5385,15 @@ object Types extends TypeUtils {
5370
5385
* It must adhere to the specification of legal patterns defined at
5371
5386
* https://docs.scala-lang.org/sips/match-types-spec.html#legal-patterns
5372
5387
*
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.
5374
5389
*/
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 )
5377
5392
5378
- def rec (pat : Type , variance : Int ): MatchTypeCasePattern | Null =
5393
+ def rec (pat : Type , variance : Int ): MatchTypeCaseResult =
5379
5394
pat match
5380
5395
case pat @ TypeParamRef (binder, num) if binder eq caseLambda =>
5381
- typeParamRefsAccountedFor += 1
5396
+ typeParamRefsUnaccountedFor -= num
5382
5397
MatchTypeCasePattern .Capture (num, isWildcard = pat.paramName.is(WildcardParamName ))
5383
5398
5384
5399
case pat @ AppliedType (tycon : TypeRef , args) if variance == 1 =>
@@ -5394,13 +5409,13 @@ object Types extends TypeUtils {
5394
5409
MatchTypeCasePattern .BaseTypeTest (tycon, argPatterns, needsConcreteScrut)
5395
5410
}
5396
5411
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)
5404
5419
else
5405
5420
tycon.info match
5406
5421
case _ : RealTypeBounds =>
@@ -5416,7 +5431,7 @@ object Types extends TypeUtils {
5416
5431
*/
5417
5432
rec(pat.superType, variance)
5418
5433
case _ =>
5419
- null
5434
+ MatchTypeCaseError . Alias (tyconSym)
5420
5435
5421
5436
case pat @ AppliedType (tycon : TypeParamRef , _) if variance == 1 =>
5422
5437
recAbstractTypeConstructor(pat)
@@ -5437,40 +5452,40 @@ object Types extends TypeUtils {
5437
5452
MatchTypeCasePattern .TypeMemberExtractor (refinedName, capture)
5438
5453
else
5439
5454
// Otherwise, a type-test + capture combo might be necessary, and we are out of spec
5440
- null
5455
+ MatchTypeCaseError . RefiningBounds (refinedName)
5441
5456
case _ =>
5442
5457
// If the member does not refine a member of the `parent`, we are out of spec
5443
- null
5458
+ MatchTypeCaseError . StructuralType (refinedName)
5444
5459
5445
5460
case _ =>
5446
5461
MatchTypeCasePattern .TypeTest (pat)
5447
5462
end rec
5448
5463
5449
- def recAbstractTypeConstructor (pat : AppliedType ): MatchTypeCasePattern | Null =
5464
+ def recAbstractTypeConstructor (pat : AppliedType ): MatchTypeCaseResult =
5450
5465
recArgPatterns(pat) { argPatterns =>
5451
5466
MatchTypeCasePattern .AbstractTypeConstructor (pat.tycon, argPatterns)
5452
5467
}
5453
5468
end recAbstractTypeConstructor
5454
5469
5455
- def recArgPatterns (pat : AppliedType )(whenNotTypeTest : List [MatchTypeCasePattern ] => MatchTypeCasePattern | Null ): MatchTypeCasePattern | Null =
5470
+ def recArgPatterns (pat : AppliedType )(whenNotTypeTest : List [MatchTypeCasePattern ] => MatchTypeCaseResult ): MatchTypeCaseResult =
5456
5471
val AppliedType (tycon, args) = pat
5457
5472
val tparams = tycon.typeParams
5458
5473
val argPatterns = args.zip(tparams).map { (arg, tparam) =>
5459
5474
rec(arg, tparam.paramVarianceSign)
5460
5475
}
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
5465
5478
if argPatterns1.forall(_.isTypeTest) then
5466
5479
MatchTypeCasePattern .TypeTest (pat)
5467
5480
else
5468
5481
whenNotTypeTest(argPatterns1)
5469
5482
end recArgPatterns
5470
5483
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))
5474
5489
end tryConvertToSpecPattern
5475
5490
end MatchTypeCaseSpec
5476
5491
0 commit comments