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