@@ -13,6 +13,11 @@ import collection.mutable
13
13
import ast .tpd ._
14
14
import reporting .trace
15
15
import reporting .diagnostic .Message
16
+ import config .Printers .{gadts , typr }
17
+ import typer .Applications ._
18
+ import typer .ProtoTypes ._
19
+ import typer .ForceDegree
20
+ import typer .Inferencing .isFullyDefined
16
21
17
22
import scala .annotation .internal .sharable
18
23
@@ -334,6 +339,154 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
334
339
* This test is used when we are too early in the pipeline to consider imports.
335
340
*/
336
341
def scala2Setting = ctx.settings.language.value.contains(nme.Scala2 .toString)
342
+
343
+ /** Refine child based on parent
344
+ *
345
+ * In child class definition, we have:
346
+ *
347
+ * class Child[Ts] extends path.Parent[Us] with Es
348
+ * object Child extends path.Parent[Us] with Es
349
+ * val child = new path.Parent[Us] with Es // enum values
350
+ *
351
+ * Given a parent type `parent` and a child symbol `child`, we infer the prefix
352
+ * and type parameters for the child:
353
+ *
354
+ * prefix.child[Vs] <:< parent
355
+ *
356
+ * where `Vs` are fresh type variables and `prefix` is the symbol prefix with all
357
+ * non-module and non-package `ThisType` replaced by fresh type variables.
358
+ *
359
+ * If the subtyping is true, the instantiated type `p.child[Vs]` is
360
+ * returned. Otherwise, `NoType` is returned.
361
+ */
362
+ def refineUsingParent (parent : Type , child : Symbol )(implicit ctx : Context ): Type = {
363
+ if (child.isTerm && child.is(Case , butNot = Module )) return child.termRef // enum vals always match
364
+
365
+ // <local child> is a place holder from Scalac, it is hopeless to instantiate it.
366
+ //
367
+ // Quote from scalac (from nsc/symtab/classfile/Pickler.scala):
368
+ //
369
+ // ...When a sealed class/trait has local subclasses, a single
370
+ // <local child> class symbol is added as pickled child
371
+ // (instead of a reference to the anonymous class; that was done
372
+ // initially, but seems not to work, ...).
373
+ //
374
+ if (child.name == tpnme.LOCAL_CHILD ) return child.typeRef
375
+
376
+ val childTp = if (child.isTerm) child.termRef else child.typeRef
377
+
378
+ instantiate(childTp, parent)(ctx.fresh.setNewTyperState()).dealias
379
+ }
380
+
381
+ /** Instantiate type `tp1` to be a subtype of `tp2`
382
+ *
383
+ * Return the instantiated type if type parameters and this type
384
+ * in `tp1` can be instantiated such that `tp1 <:< tp2`.
385
+ *
386
+ * Otherwise, return NoType.
387
+ */
388
+ private def instantiate (tp1 : NamedType , tp2 : Type )(implicit ctx : Context ): Type = {
389
+ /** expose abstract type references to their bounds or tvars according to variance */
390
+ class AbstractTypeMap (maximize : Boolean )(implicit ctx : Context ) extends TypeMap {
391
+ def expose (lo : Type , hi : Type ): Type =
392
+ if (variance == 0 )
393
+ newTypeVar(TypeBounds (lo, hi))
394
+ else if (variance == 1 )
395
+ if (maximize) hi else lo
396
+ else
397
+ if (maximize) lo else hi
398
+
399
+ def apply (tp : Type ): Type = tp match {
400
+ case tp : TypeRef if isBounds(tp.underlying) =>
401
+ val lo = this (tp.info.loBound)
402
+ val hi = this (tp.info.hiBound)
403
+ // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
404
+ val exposed = expose(lo, hi)
405
+ typr.println(s " $tp exposed to =====> $exposed" )
406
+ exposed
407
+
408
+ case AppliedType (tycon : TypeRef , args) if isBounds(tycon.underlying) =>
409
+ val args2 = args.map(this )
410
+ val lo = this (tycon.info.loBound).applyIfParameterized(args2)
411
+ val hi = this (tycon.info.hiBound).applyIfParameterized(args2)
412
+ val exposed = expose(lo, hi)
413
+ typr.println(s " $tp exposed to =====> $exposed" )
414
+ exposed
415
+
416
+ case _ =>
417
+ mapOver(tp)
418
+ }
419
+ }
420
+
421
+ def minTypeMap (implicit ctx : Context ) = new AbstractTypeMap (maximize = false )
422
+ def maxTypeMap (implicit ctx : Context ) = new AbstractTypeMap (maximize = true )
423
+
424
+ // Fix subtype checking for child instantiation,
425
+ // such that `Foo(Test.this.foo) <:< Foo(Foo.this)`
426
+ // See tests/patmat/i3938.scala
427
+ class RemoveThisMap extends TypeMap {
428
+ var prefixTVar : Type = null
429
+ def apply (tp : Type ): Type = tp match {
430
+ case ThisType (tref : TypeRef ) if ! tref.symbol.isStaticOwner =>
431
+ if (tref.symbol.is(Module ))
432
+ TermRef (this (tref.prefix), tref.symbol.sourceModule)
433
+ else if (prefixTVar != null )
434
+ this (tref)
435
+ else {
436
+ prefixTVar = WildcardType // prevent recursive call from assigning it
437
+ prefixTVar = newTypeVar(TypeBounds .upper(this (tref)))
438
+ prefixTVar
439
+ }
440
+ case tp => mapOver(tp)
441
+ }
442
+ }
443
+
444
+ // replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
445
+ def instUndetMap (implicit ctx : Context ) = new TypeMap {
446
+ def apply (t : Type ): Type = t match {
447
+ case tvar : TypeVar if ! tvar.isInstantiated => WildcardType (tvar.origin.underlying.bounds)
448
+ case _ => mapOver(t)
449
+ }
450
+ }
451
+
452
+ val removeThisType = new RemoveThisMap
453
+ val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
454
+ val protoTp1 = removeThisType.apply(tp1).appliedTo(tvars)
455
+
456
+ val force = new ForceDegree .Value (
457
+ tvar =>
458
+ ! (ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) ||
459
+ (tvar `eq` removeThisType.prefixTVar),
460
+ minimizeAll = false ,
461
+ allowBottom = false
462
+ )
463
+
464
+ // If parent contains a reference to an abstract type, then we should
465
+ // refine subtype checking to eliminate abstract types according to
466
+ // variance. As this logic is only needed in exhaustivity check,
467
+ // we manually patch subtyping check instead of changing TypeComparer.
468
+ // See tests/patmat/i3645b.scala
469
+ def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent =>
470
+ implicit val ictx = ctx.fresh.setNewTyperState()
471
+ parent.argInfos.nonEmpty && minTypeMap.apply(parent) <:< maxTypeMap.apply(tp2)
472
+ }
473
+
474
+ if (protoTp1 <:< tp2) {
475
+ if (isFullyDefined(protoTp1, force)) protoTp1
476
+ else instUndetMap.apply(protoTp1)
477
+ }
478
+ else {
479
+ val protoTp2 = maxTypeMap.apply(tp2)
480
+ if (protoTp1 <:< protoTp2 || parentQualify) {
481
+ if (isFullyDefined(AndType (protoTp1, protoTp2), force)) protoTp1
482
+ else instUndetMap.apply(protoTp1)
483
+ }
484
+ else {
485
+ typr.println(s " $protoTp1 <:< $protoTp2 = false " )
486
+ NoType
487
+ }
488
+ }
489
+ }
337
490
}
338
491
339
492
object TypeOps {
0 commit comments