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