@@ -3237,11 +3237,22 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
3237
3237
}
3238
3238
}
3239
3239
3240
+ def instantiateParamsSpec (insts : Array [Type ], caseLambda : HKTypeLambda ) = new TypeMap {
3241
+ variance = 0
3242
+
3243
+ def apply (t : Type ) = t match {
3244
+ case t @ TypeParamRef (b, n) if b `eq` caseLambda => insts(n)
3245
+ case t : LazyRef => apply(t.ref)
3246
+ case _ => mapOver(t)
3247
+ }
3248
+ }
3249
+
3240
3250
/** Match a single case. */
3241
3251
def matchCase (cas : MatchTypeCaseSpec ): MatchResult = trace(i " $scrut match ${MatchTypeTrace .caseText(cas)}" , matchTypes, show = true ) {
3242
3252
cas match
3243
- case cas : MatchTypeCaseSpec .SubTypeTest => matchSubTypeTest(cas)
3244
- case cas : MatchTypeCaseSpec .LegacyPatMat => matchLegacyPatMat(cas)
3253
+ case cas : MatchTypeCaseSpec .SubTypeTest => matchSubTypeTest(cas)
3254
+ case cas : MatchTypeCaseSpec .SpeccedPatMat => matchSpeccedPatMat(cas)
3255
+ case cas : MatchTypeCaseSpec .LegacyPatMat => matchLegacyPatMat(cas)
3245
3256
}
3246
3257
3247
3258
def matchSubTypeTest (spec : MatchTypeCaseSpec .SubTypeTest ): MatchResult =
@@ -3253,6 +3264,128 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
3253
3264
MatchResult .Stuck
3254
3265
end matchSubTypeTest
3255
3266
3267
+ def matchSpeccedPatMat (spec : MatchTypeCaseSpec .SpeccedPatMat ): MatchResult =
3268
+ /* Concreteness checking
3269
+ *
3270
+ * When following a baseType and reaching a non-wildcard, in-variant-pos type capture,
3271
+ * we have to make sure that the scrutinee is concrete enough to uniquely determine
3272
+ * the values of the captures. This comes down to checking that we do not follow any
3273
+ * upper bound of an abstract type.
3274
+ *
3275
+ * See notably neg/wildcard-match.scala for examples of this.
3276
+ */
3277
+
3278
+ def followEverythingConcrete (tp : Type ): Type =
3279
+ val widenedTp = tp.widenDealias
3280
+ val tp1 = widenedTp.normalized
3281
+
3282
+ def followTp1 : Type =
3283
+ // If both widenDealias and normalized did something, start again
3284
+ if (tp1 ne widenedTp) && (widenedTp ne tp) then followEverythingConcrete(tp1)
3285
+ else tp1
3286
+
3287
+ tp1 match
3288
+ case tp1 : TypeRef =>
3289
+ tp1.info match
3290
+ case TypeAlias (tl : HKTypeLambda ) => tl
3291
+ case MatchAlias (tl : HKTypeLambda ) => tl
3292
+ case _ => followTp1
3293
+ case tp1 @ AppliedType (tycon, args) =>
3294
+ val concreteTycon = followEverythingConcrete(tycon)
3295
+ if concreteTycon eq tycon then followTp1
3296
+ else followEverythingConcrete(concreteTycon.applyIfParameterized(args))
3297
+ case _ =>
3298
+ followTp1
3299
+ end followEverythingConcrete
3300
+
3301
+ def isConcrete (tp : Type ): Boolean =
3302
+ followEverythingConcrete(tp) match
3303
+ case tp1 : AndOrType => isConcrete(tp1.tp1) && isConcrete(tp1.tp2)
3304
+ case tp1 => tp1.underlyingClassRef(refinementOK = true ).exists
3305
+
3306
+ // Actual matching logic
3307
+
3308
+ val instances = Array .fill[Type ](spec.captureCount)(NoType )
3309
+
3310
+ def rec (pattern : MatchTypeCasePattern , scrut : Type , variance : Int , scrutIsWidenedAbstract : Boolean ): Boolean =
3311
+ pattern match
3312
+ case MatchTypeCasePattern .Capture (num, isWildcard) =>
3313
+ instances(num) = scrut match
3314
+ case scrut : TypeBounds =>
3315
+ if isWildcard then
3316
+ // anything will do, as long as it conforms to the bounds for the subsequent `scrut <:< instantiatedPat` test
3317
+ scrut.hi
3318
+ else if scrutIsWidenedAbstract then
3319
+ // always keep the TypeBounds so that we can report the correct NoInstances
3320
+ scrut
3321
+ else
3322
+ variance match
3323
+ case 1 => scrut.hi
3324
+ case - 1 => scrut.lo
3325
+ case 0 => scrut
3326
+ case _ =>
3327
+ if ! isWildcard && scrutIsWidenedAbstract && variance != 0 then
3328
+ // force a TypeBounds to report the correct NoInstances
3329
+ // the Nothing and Any bounds are used so that they are not displayed; not for themselves in particular
3330
+ if variance > 0 then TypeBounds (defn.NothingType , scrut)
3331
+ else TypeBounds (scrut, defn.AnyType )
3332
+ else
3333
+ scrut
3334
+ ! instances(num).isError
3335
+
3336
+ case MatchTypeCasePattern .TypeTest (tpe) =>
3337
+ // The actual type test is handled by `scrut <:< instantiatedPat`
3338
+ true
3339
+
3340
+ case MatchTypeCasePattern .BaseTypeTest (classType, argPatterns, needsConcreteScrut) =>
3341
+ val cls = classType.classSymbol.asClass
3342
+ scrut.baseType(cls) match
3343
+ case base @ AppliedType (baseTycon, baseArgs) if baseTycon =:= classType =>
3344
+ val innerScrutIsWidenedAbstract =
3345
+ scrutIsWidenedAbstract
3346
+ || (needsConcreteScrut && ! isConcrete(scrut)) // no point in checking concreteness if it does not need to be concrete
3347
+
3348
+ def matchArgs (argPatterns : List [MatchTypeCasePattern ], baseArgs : List [Type ], tparams : List [TypeParamInfo ]): Boolean =
3349
+ if argPatterns.isEmpty then
3350
+ true
3351
+ else
3352
+ rec(argPatterns.head, baseArgs.head, tparams.head.paramVarianceSign, innerScrutIsWidenedAbstract)
3353
+ && matchArgs(argPatterns.tail, baseArgs.tail, tparams.tail)
3354
+
3355
+ matchArgs(argPatterns, baseArgs, classType.typeParams)
3356
+
3357
+ case _ =>
3358
+ false
3359
+ end rec
3360
+
3361
+ // This might not be needed
3362
+ val constrainedCaseLambda = constrained(spec.origMatchCase, ast.tpd.EmptyTree )._1.asInstanceOf [HKTypeLambda ]
3363
+
3364
+ def tryDisjoint : MatchResult =
3365
+ val defn .MatchCase (origPattern, _) = constrainedCaseLambda.resultType: @ unchecked
3366
+ if provablyDisjoint(scrut, origPattern) then
3367
+ MatchResult .Disjoint
3368
+ else
3369
+ MatchResult .Stuck
3370
+
3371
+ if rec(spec.pattern, scrut, variance = 1 , scrutIsWidenedAbstract = false ) then
3372
+ if instances.exists(_.isInstanceOf [TypeBounds ]) then
3373
+ MatchResult .NoInstance {
3374
+ constrainedCaseLambda.paramNames.zip(instances).collect {
3375
+ case (name, bounds : TypeBounds ) => (name, bounds)
3376
+ }
3377
+ }
3378
+ else
3379
+ val defn .MatchCase (instantiatedPat, reduced) =
3380
+ instantiateParamsSpec(instances, constrainedCaseLambda)(constrainedCaseLambda.resultType): @ unchecked
3381
+ if scrut <:< instantiatedPat then
3382
+ MatchResult .Reduced (reduced)
3383
+ else
3384
+ tryDisjoint
3385
+ else
3386
+ tryDisjoint
3387
+ end matchSpeccedPatMat
3388
+
3256
3389
def matchLegacyPatMat (spec : MatchTypeCaseSpec .LegacyPatMat ): MatchResult =
3257
3390
val caseLambda = constrained(spec.origMatchCase, ast.tpd.EmptyTree )._1.asInstanceOf [HKTypeLambda ]
3258
3391
this .caseLambda = caseLambda
0 commit comments