Skip to content

Fix #3938: adapt prefix to ease prefix inference #3939

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 2 commits into from
Jan 30, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 20 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {

debug.println(s"bases of ${tp1.show}: " + bases1)
debug.println(s"bases of ${tp2.show}: " + bases2)
debug.println(s"${tp1.show} <:< ${tp2.show} : " + (tp1 <:< tp2))
debug.println(s"${tp2.show} <:< ${tp1.show} : " + (tp2 <:< tp1))

val noClassConflict =
bases1.forall(sym1 => sym1.is(Trait) || bases2.forall(sym2 => sym2.is(Trait) || sym1.isSubClass(sym2))) ||
Expand Down Expand Up @@ -638,15 +640,31 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}
}

// Fix subtype checking for child instantiation,
// such that `Foo(Test.this.foo) <:< Foo(Foo.this)`
Copy link
Contributor

@odersky odersky Jan 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not correlate the claim Foo(Test.this.foo) <:< Foo(Foo.this) in i3939.scala with the code here. In i3938, foo is not a module, so how does the removeThisType affect it?

Also, note that if we do have a

 object Foo

then we have already Foo.type <:< Foo.this and Foo.this <:< Foo.type by the rules for ThisType in TypeComparer's firstTry and secondTry methods. So I am not sure why the code would be necessesary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification. The correlation is that in the else branch, it throws away of the ThisType. For the module case, I'll try if I can throw it away.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked, the patch for module cannot be removed, as the subtyping for Foo.type <:< Foo.this only works if it's static owner:

          case tp1: NamedType if cls2.is(Module) && cls2.eq(tp1.widen.typeSymbol) =>
            cls2.isStaticOwner ||
            isSubType(tp1.prefix, cls2.owner.thisType) ||
            secondTry(tp1, tp2)

The test case tests/patmat/3455.scala shows that we need the patch:

trait AxisCompanion {
   sealed trait Format
   object Format {
      case object Decimal extends Format
      case object Integer extends Format
   }
}
object Axis extends AxisCompanion
class Axis {
   import Axis._
   def test( f: Format ) = f match {
      case Format.Integer => "Int"
   }
}

// See tests/patmat/i3938.scala
def removeThisType(implicit ctx: Context) = new TypeMap {
def apply(tp: Type): Type = tp match {
case ThisType(tref: TypeRef) =>
if (tref.symbol.is(Module))
TermRef(tref.prefix, tref.symbol.sourceModule)
else
mapOver(tref)
case _ => mapOver(tp)
}
}

// We are checking the possibility of `tp1 <:< tp2`, thus we should
// minimize `tp1` while maximizing `tp2`. See tests/patmat/3645b.scala
def childTypeMap(implicit ctx: Context) = new AbstractTypeMap(maximize = false) {
def apply(t: Type): Type = t.dealias match {
// map `ThisType` of `tp1` to a type variable
// precondition: `tp1` should have the same shape as `path.Child`, thus `ThisType` is always covariant
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
if (tref.symbol.is(Module)) this(tref)
else newTypeVar(TypeBounds.upper(tp.underlying))
if (tref.symbol.is(Module))
this(TermRef(tref.prefix, tref.symbol.sourceModule))
else
newTypeVar(TypeBounds.upper(removeThisType.apply(tref)))

case tp =>
mapOver(tp)
Expand Down
32 changes: 32 additions & 0 deletions tests/patmat/i3938.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* subtyping logs
==> isSubType Test.this.foo.Bar(Test.this.foo.bar) <:< Foo.this.Bar?
==> isSubType Test.this.foo.Bar <:< Foo.this.Bar?
==> isSubType Foo(Test.this.foo) <:< Foo(Foo.this)?
==> isSubType Foo <:< Foo(Foo.this)?
<== isSubType Foo <:< Foo(Foo.this) = false
<== isSubType Foo(Test.this.foo) <:< Foo(Foo.this) = false
<== isSubType Test.this.foo.Bar <:< Foo.this.Bar = false
<== isSubType Test.this.foo.Bar(Test.this.foo.bar) <:< Foo.this.Bar = false
*/


class Foo {
val bar = new Bar
class Bar {
sealed abstract class A
case class B() extends A
case class C() extends A
}
}

class Test {
val foo = new Foo
import foo.bar._

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