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