Skip to content

Commit 826f14e

Browse files
liufengyunodersky
authored andcommitted
fix #3015: use type inference to type child classes
1 parent 8456b37 commit 826f14e

File tree

1 file changed

+74
-33
lines changed
  • compiler/src/dotty/tools/dotc/transform/patmat

1 file changed

+74
-33
lines changed

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@ import Types._
77
import Contexts._
88
import Flags._
99
import ast.Trees._
10-
import ast.tpd
10+
import ast.{tpd, untpd}
1111
import Decorators._
1212
import Symbols._
1313
import StdNames._
1414
import NameOps._
1515
import Constants._
16-
import typer.Applications._
16+
import typer._
17+
import Applications._
18+
import Inferencing._
19+
import ProtoTypes._
1720
import transform.SymUtils._
1821
import reporting.diagnostic.messages._
19-
import config.Printers.{ exhaustivity => debug }
22+
import config.Printers.{exhaustivity => debug}
2023

2124
/** Space logic for checking exhaustivity and unreachability of pattern matching
2225
*
@@ -524,46 +527,82 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
524527
case tp =>
525528
val parts = children.map { sym =>
526529
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)
532531
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)
541534

542535
debug.println(s"${tp.show} decomposes to [${parts.map(_.show).mkString(", ")}]")
543536

544537
parts.map(Typ(_, true))
545538
}
546539
}
547540

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
549548
*
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:
552551
*
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.
555559
*
556-
* (MO) I don't really understand what this does. Let's try to find a precise
557-
* definition!
558560
*/
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+
}
567606
}
568607

569608
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
@@ -593,20 +632,22 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
593632
*
594633
*/
595634
def showType(tp: Type): String = {
596-
val enclosingCls = ctx.owner.enclosingClass.asClass.classInfo.symbolicTypeRef
635+
val enclosingCls = ctx.owner.enclosingClass
597636

598637
def isOmittable(sym: Symbol) =
599638
sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName ||
600639
ctx.definitions.UnqualifiedOwnerTypes.exists(_.symbol == sym) ||
601640
sym.showFullName.startsWith("scala.") ||
602-
sym == enclosingCls.typeSymbol
641+
sym == enclosingCls || sym == enclosingCls.sourceModule
603642

604643
def refinePrefix(tp: Type): String = tp match {
605644
case NoPrefix => ""
606645
case tp: NamedType if isOmittable(tp.symbol) => ""
607646
case tp: ThisType => refinePrefix(tp.tref)
608647
case tp: RefinedType => refinePrefix(tp.parent)
609648
case tp: NamedType => tp.name.show.stripSuffix("$")
649+
case tp: TypeVar => refinePrefix(tp.instanceOpt)
650+
case _ => tp.show
610651
}
611652

612653
def refine(tp: Type): String = tp match {

0 commit comments

Comments
 (0)