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