Skip to content

Fix #9067: avoid instantiate child parameters to Wildcard if possible #9071

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 16 additions & 21 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import config.Feature
import typer.Applications._
import typer.ProtoTypes._
import typer.ForceDegree
import typer.Inferencing.isFullyDefined
import typer.Inferencing._
import typer.IfBottom

import scala.annotation.internal.sharable
Expand Down Expand Up @@ -618,8 +618,9 @@ object TypeOps:

val childTp = if (child.isTerm) child.termRef else child.typeRef

instantiateToSubType(childTp, parent)(using ctx.fresh.setNewTyperState())
.dealias
inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) {
instantiateToSubType(childTp, parent).dealias
}
}

/** Instantiate type `tp1` to be a subtype of `tp2`
Expand Down Expand Up @@ -665,10 +666,11 @@ object TypeOps:
def minTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false)
def maxTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = true)

// Fix subtype checking for child instantiation,
// such that `Foo(Test.this.foo) <:< Foo(Foo.this)`
// Prefix inference, replace `p.C.this.Child` with `X.Child` where `X <: p.C`
// Note: we need to strip ThisType in `p` recursively.
//
// See tests/patmat/i3938.scala
class RemoveThisMap extends TypeMap {
class InferPrefixMap extends TypeMap {
var prefixTVar: Type = null
def apply(tp: Type): Type = tp match {
case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner =>
Expand All @@ -685,22 +687,14 @@ object TypeOps:
}
}

// replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
def instUndetMap(implicit ctx: Context) = new TypeMap {
def apply(t: Type): Type = t match {
case tvar: TypeVar if !tvar.isInstantiated => WildcardType(tvar.origin.underlying.bounds)
case _ => mapOver(t)
}
}

val removeThisType = new RemoveThisMap
val inferThisMap = new InferPrefixMap
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
val protoTp1 = removeThisType.apply(tp1).appliedTo(tvars)
val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars)

val force = new ForceDegree.Value(
tvar =>
!(ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) ||
(tvar `eq` removeThisType.prefixTVar),
(tvar `eq` inferThisMap.prefixTVar), // always instantiate prefix
IfBottom.flip
)

Expand All @@ -715,14 +709,15 @@ object TypeOps:
}
}

if (protoTp1 <:< tp2)
if (isFullyDefined(protoTp1, force)) protoTp1
else instUndetMap.apply(protoTp1)
if (protoTp1 <:< tp2) {
maximizeType(protoTp1, NoSpan, fromScala2x = false)
wildApprox(protoTp1)
}
else {
val protoTp2 = maxTypeMap.apply(tp2)
if (protoTp1 <:< protoTp2 || parentQualify)
if (isFullyDefined(AndType(protoTp1, protoTp2), force)) protoTp1
else instUndetMap.apply(protoTp1)
else wildApprox(protoTp1)
else {
typr.println(s"$protoTp1 <:< $protoTp2 = false")
NoType
Expand Down
12 changes: 5 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,20 @@ object TypeTestsCasts {
*/
def checkable(X: Type, P: Type, span: Span)(using Context): Boolean = {
def isAbstract(P: Type) = !P.dealias.typeSymbol.isClass
def isPatternTypeSymbol(sym: Symbol) = !sym.isClass && sym.is(Case)

def replaceP(tp: Type)(implicit ctx: Context) = new TypeMap {
def apply(tp: Type) = tp match {
case tref: TypeRef
if isPatternTypeSymbol(tref.typeSymbol) => WildcardType
case AnnotatedType(_, annot)
if annot.symbol == defn.UncheckedAnnot => WildcardType
case tref: TypeRef if tref.typeSymbol.isPatternBound =>
WildcardType
case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedAnnot =>
WildcardType
case _ => mapOver(tp)
}
}.apply(tp)

def replaceX(tp: Type)(implicit ctx: Context) = new TypeMap {
def apply(tp: Type) = tp match {
case tref: TypeRef
if isPatternTypeSymbol(tref.typeSymbol) =>
case tref: TypeRef if tref.typeSymbol.isPatternBound =>
if (variance == 1) tref.info.hiBound
else if (variance == -1) tref.info.loBound
else OrType(defn.AnyType, defn.NothingType)
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,6 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
* }
*/
private def erase(tp: Type, inArray: Boolean = false): Type = trace(i"$tp erased to", debug) {
def isPatternTypeSymbol(sym: Symbol) = !sym.isClass && sym.is(Case)

tp match {
case tp @ AppliedType(tycon, args) =>
Expand All @@ -476,7 +475,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
AndType(erase(tp1, inArray), erase(tp2, inArray))
case tp @ RefinedType(parent, _, _) =>
erase(parent)
case tref: TypeRef if isPatternTypeSymbol(tref.typeSymbol) =>
case tref: TypeRef if tref.typeSymbol.isPatternBound =>
if (inArray) tref.underlying else WildcardType
case _ => tp
}
Expand Down
1 change: 1 addition & 0 deletions tests/patmat/i3938.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
34: Pattern Match Exhaustivity: bar.C()
7 changes: 6 additions & 1 deletion tests/patmat/i3938.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ class Test {
val foo = new Foo
import foo.bar._

def test(a: A) = {
def h(a: A) = {
a match {
case B() => 1
case _ => 2 // unreachable code
}
}

def f(a: A) =
a match {
case B() => 1
}
}
4 changes: 4 additions & 0 deletions tests/patmat/i9067.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
sealed trait Base[A, +B]
case class Foo[A, +B](f: A => B) extends Base[A, B]

def bar(x: Base[Any, Any]): Unit = x match { case Foo(_) => }