Skip to content

Fix #8933: Fix eta expansion when comparing hk types #8944

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
May 11, 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
52 changes: 42 additions & 10 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,13 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
if ((sym1 ne NoSymbol) && (sym1 eq sym2))
ctx.erasedTypes ||
sym1.isStaticOwner ||
isSubType(tp1.prefix, tp2.prefix) ||
isSubPrefix(tp1.prefix, tp2.prefix) ||
thirdTryNamed(tp2)
else
( (tp1.name eq tp2.name)
&& tp1.isMemberRef
&& tp2.isMemberRef
&& isSubType(tp1.prefix, tp2.prefix)
&& isSubPrefix(tp1.prefix, tp2.prefix)
&& tp1.signature == tp2.signature
&& !(sym1.isClass && sym2.isClass) // class types don't subtype each other
) ||
Expand Down Expand Up @@ -296,12 +296,6 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
def compareThis = {
val cls2 = tp2.cls
tp1 match {
case tp1: ThisType =>
// We treat two prefixes A.this, B.this as equivalent if
// A's selftype derives from B and B's selftype derives from A.
val cls1 = tp1.cls
cls1.classInfo.selfType.derivesFrom(cls2) &&
cls2.classInfo.selfType.derivesFrom(cls1)
case tp1: NamedType if cls2.is(Module) && cls2.eq(tp1.widen.typeSymbol) =>
cls2.isStaticOwner ||
recur(tp1.prefix, cls2.owner.thisType) ||
Expand Down Expand Up @@ -497,7 +491,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
if (base.typeSymbol == cls2) return true
}
else if tp1.isLambdaSub && !tp1.isAnyKind then
return recur(tp1, EtaExpansion(cls2.typeRef))
return recur(tp1, EtaExpansion(tp2))
fourthTry
}

Expand Down Expand Up @@ -819,6 +813,44 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
false
}

/** When called from `pre1.A <:< pre2.A` does `pre1` relate to `pre2` so that
* the subtype test is true? This is the case if `pre1 <:< pre2`, or
* `pre1` and `pre2` are both this-types of related classes. Here, two classes
* are related if each of them has a self type that derives from the other.
*
* This criterion is a bit dubious. I.e. in the test
*
* A.this.T <:< B.this.T
*
* where `T` is the same type, what relationship must exist between A and B
* for the test to be guaranteed true? The problem is we can't tell without additional
* info. One could be an outer this at the point where we do the test, but that
* location is unknown to us.
*
* The conservative choice would be to require A == B, but then some tests involving
* self types fail. Specifically, t360, t361 and pat_iuli fail the pickling test, and
* Namer fails to compile. At line 203, we get
*
* val Deriver : Property.Key[typer.Deriver] = new Property.Key
* ^
* value Deriver in class Namer is not a legal implementation of `Deriver` in class Namer.
* its type dotty.tools.dotc.util.Property.Key[Namer.this.Deriver]
|* does not conform to dotty.tools.dotc.util.Property.Key[Typer.this.Deriver & Namer.this.Deriver]
*/
def isSubPrefix(pre1: Type, pre2: Type): Boolean =
pre1 match
case pre1: ThisType =>
pre2 match
case pre2: ThisType =>
if pre1.cls.classInfo.selfType.derivesFrom(pre2.cls)
&& pre2.cls.classInfo.selfType.derivesFrom(pre1.cls)
then
subtyping.println(i"assume equal prefixes $pre1 $pre2")
return true
case _ =>
case _ =>
isSubType(pre1, pre2)

/** Subtype test for the hk application `tp2 = tycon2[args2]`.
*/
def compareAppliedType2(tp2: AppliedType, tycon2: Type, args2: List[Type]): Boolean = {
Expand Down Expand Up @@ -857,7 +889,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w

val res = (
tycon1sym == tycon2sym
&& isSubType(tycon1.prefix, tycon2.prefix)
&& isSubPrefix(tycon1.prefix, tycon2.prefix)
|| byGadtBounds(tycon1sym, tycon2, fromAbove = true)
|| byGadtBounds(tycon2sym, tycon1, fromAbove = false)
) && {
Expand Down
15 changes: 15 additions & 0 deletions tests/pos/i8933.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Foo[F[_]]

trait Top {
type F[X]
def foo: Foo[F]
}

trait Left extends Top

trait Right extends Top {
trait F[X]
def foo: Foo[F] = new Foo[F] {}
}

class Bottom extends Left with Right