@@ -7,16 +7,19 @@ import Types._
7
7
import Contexts ._
8
8
import Flags ._
9
9
import ast .Trees ._
10
- import ast .tpd
10
+ import ast .{ tpd , untpd }
11
11
import Decorators ._
12
12
import Symbols ._
13
13
import StdNames ._
14
14
import NameOps ._
15
15
import Constants ._
16
- import typer .Applications ._
16
+ import typer ._
17
+ import Applications ._
18
+ import Inferencing ._
19
+ import ProtoTypes ._
17
20
import transform .SymUtils ._
18
21
import reporting .diagnostic .messages ._
19
- import config .Printers .{ exhaustivity => debug }
22
+ import config .Printers .{exhaustivity => debug }
20
23
21
24
/** Space logic for checking exhaustivity and unreachability of pattern matching
22
25
*
@@ -524,46 +527,82 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
524
527
case tp =>
525
528
val parts = children.map { sym =>
526
529
if (sym.is(ModuleClass ))
527
- refine(tp, sym.sourceModule.termRef)
528
- else if (sym.isTerm)
529
- refine(tp, sym.termRef)
530
- else if (sym.info.typeParams.length > 0 || tp.isInstanceOf [TypeRef ])
531
- refine(tp, sym.typeRef)
530
+ refine(tp, sym.sourceModule)
532
531
else
533
- sym.typeRef
534
- } filter { tpe =>
535
- // Child class may not always be subtype of parent:
536
- // GADT & path-dependent types
537
- val res = tpe <:< expose(tp)
538
- if (! res) debug.println(s " unqualified child ousted: ${tpe.show} !< ${tp.show}" )
539
- res
540
- }
532
+ refine(tp, sym)
533
+ } filter(_.exists)
541
534
542
535
debug.println(s " ${tp.show} decomposes to [ ${parts.map(_.show).mkString(" , " )}] " )
543
536
544
537
parts.map(Typ (_, true ))
545
538
}
546
539
}
547
540
548
- /** Refine tp2 based on tp1
541
+ /** Refine child based on parent
542
+ *
543
+ * In child class definition, we have:
544
+ *
545
+ * class Child[Ts] extends path.Parent[Us] with Es
546
+ * object Child extends path.Parent[Us] with Es
547
+ * val child = new path.Parent[Us] with Es // enum values
549
548
*
550
- * E.g. if `tp1` is `Option[Int]`, `tp2` is `Some `, then return
551
- * `Some[Int]`.
549
+ * Given a parent type `parent` and a child symbol `child `, we infer the prefix
550
+ * and type parameters for the child:
552
551
*
553
- * If `tp1` is `path1.A`, `tp2` is `path2.B`, and `path1` is subtype of
554
- * `path2`, then return `path1.B`.
552
+ * prefix.child[Vs] <:< parent
553
+ *
554
+ * where `Vs` are fresh type variables and `prefix` is the symbol prefix with all
555
+ * non-module and non-package `ThisType` replaced by fresh type variables.
556
+ *
557
+ * If the subtyping is true, the instantiated type `p.child[Vs]` is
558
+ * returned. Otherwise, `NoType` is returned.
555
559
*
556
- * (MO) I don't really understand what this does. Let's try to find a precise
557
- * definition!
558
560
*/
559
- def refine (tp1 : Type , tp2 : Type ): Type = (tp1, tp2) match {
560
- case (tp1 : RefinedType , _ : TypeRef ) => tp1.wrapIfMember(refine(tp1.parent, tp2))
561
- case (tp1 : AppliedType , _) => refine(tp1.superType, tp2)
562
- case (TypeRef (ref1 : TypeProxy , _), tp2 @ TypeRef (ref2 : TypeProxy , _)) =>
563
- if (ref1.underlying <:< ref2.underlying) tp2.derivedSelect(ref1) else tp2
564
- case (TypeRef (ref1 : TypeProxy , _), tp2 @ TermRef (ref2 : TypeProxy , _)) =>
565
- if (ref1.underlying <:< ref2.underlying) tp2.derivedSelect(ref1) else tp2
566
- case _ => tp2
561
+ def refine (parent : Type , child : Symbol ): Type = {
562
+ if (child.isTerm && child.is(Case , butNot = Module )) return child.termRef // enum vals always match
563
+
564
+ val childTp = if (child.isTerm) child.termRef else child.typeRef
565
+
566
+ val resTp = instantiate(childTp, expose(parent))(ctx.fresh.setNewTyperState)
567
+
568
+ if (! resTp.exists) {
569
+ debug.println(s " [refine] unqualified child ousted: ${childTp.show} !< ${parent.show}" )
570
+ NoType
571
+ }
572
+ else {
573
+ debug.println(s " $child instantiated ------> $resTp" )
574
+ resTp
575
+ }
576
+ }
577
+
578
+ /** Instantiate type `tp1` to be a subtype of `tp2`
579
+ *
580
+ * Return the instantiated type if type parameters and this type
581
+ * in `tp1` can be instantiated such that `tp1 <:< tp2`.
582
+ *
583
+ * Otherwise, return NoType.
584
+ *
585
+ */
586
+ def instantiate (tp1 : Type , tp2 : Type )(implicit ctx : Context ): Type = {
587
+ // map `ThisType` of `tp1` to a type variable
588
+ // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant
589
+ val thisTypeMap = new TypeMap {
590
+ def apply (t : Type ): Type = t match {
591
+ case t @ ThisType (tref) if ! tref.symbol.isStaticOwner && ! tref.symbol.is(Module ) =>
592
+ newTypeVar(TypeBounds .upper(mapOver(tref & tref.givenSelfType)))
593
+ case _ =>
594
+ mapOver(t)
595
+ }
596
+ }
597
+
598
+ val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
599
+ val protoTp1 = thisTypeMap(tp1.appliedTo(tvars))
600
+
601
+ if (protoTp1 <:< tp2 && isFullyDefined(protoTp1, ForceDegree .all)) protoTp1
602
+ else {
603
+ debug.println(s " $protoTp1 <:< $tp2 = false " )
604
+ NoType
605
+ }
567
606
}
568
607
569
608
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
@@ -593,20 +632,22 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
593
632
*
594
633
*/
595
634
def showType (tp : Type ): String = {
596
- val enclosingCls = ctx.owner.enclosingClass.asClass.classInfo.symbolicTypeRef
635
+ val enclosingCls = ctx.owner.enclosingClass
597
636
598
637
def isOmittable (sym : Symbol ) =
599
638
sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName ||
600
639
ctx.definitions.UnqualifiedOwnerTypes .exists(_.symbol == sym) ||
601
640
sym.showFullName.startsWith(" scala." ) ||
602
- sym == enclosingCls.typeSymbol
641
+ sym == enclosingCls || sym == enclosingCls.sourceModule
603
642
604
643
def refinePrefix (tp : Type ): String = tp match {
605
644
case NoPrefix => " "
606
645
case tp : NamedType if isOmittable(tp.symbol) => " "
607
646
case tp : ThisType => refinePrefix(tp.tref)
608
647
case tp : RefinedType => refinePrefix(tp.parent)
609
648
case tp : NamedType => tp.name.show.stripSuffix(" $" )
649
+ case tp : TypeVar => refinePrefix(tp.instanceOpt)
650
+ case _ => tp.show
610
651
}
611
652
612
653
def refine (tp : Type ): String = tp match {
0 commit comments