Description
Here's a rather innocuous seeming snippet:
object p {
class Foo[T] {
// Crashes:
def f(): Foo[T] = (if (true) this else this).bar
// Works:
// def f(): Foo[T] = new Bar(if (true) this else this).bar
}
implicit class Bar[A](val self: A) {
def bar(): A = self
}
}
It causes TypeComparer to create an OrType made up of
TypeBounds( TypeRef(ThisType(TypeRef(NoPrefix,scala)),Nothing), TypeRef(ThisType(TypeRef(NoPrefix,scala)),Any))
and TypeVar(PolyParam(A))
. OrType requires both of its constituents to be ValueTypes, however, and TypeBounds isn't, so we crash:
assertion failure for (p.Bar[A] | p.Bar[A'])#p$Bar$$A <:< p.Foo[T], frozen = false
where: A is a type variable with constraint >: p.Foo[T](Foo.this) and <: p.Foo
A' is a type variable with constraint >: p.Foo[T](Foo.this) and <: p.Foo
typeref (p.Bar[A] | p.Bar[A])#p$Bar$$A found in class Bar
p.Foo[T] is a class dotty.tools.dotc.core.Types$CachedRefinedType
exception occurred while typechecking ../dotty-staging/try/bugs/ortype/OrTypeEx.scala
exception occurred while compiling ../dotty-staging/try/bugs/ortype/OrTypeEx.scala
Exception in thread "main" java.lang.AssertionError: assertion failed
at scala.Predef$.assert(Predef.scala:151)
at dotty.tools.dotc.core.Types$OrType.<init>(Types.scala:2237)
at dotty.tools.dotc.core.Types$CachedOrType.<init>(Types.scala:2264)
at dotty.tools.dotc.core.Types$OrType$.apply(Types.scala:2269)
at dotty.tools.dotc.core.TypeComparer$$anonfun$orType$1.apply(TypeComparer.scala:1263)
at dotty.tools.dotc.core.TypeComparer$$anonfun$orType$1.apply(TypeComparer.scala:1263)
at dotty.tools.dotc.core.TypeComparer.dotty$tools$dotc$core$TypeComparer$$liftIfHK(TypeComparer.scala:1276)
at dotty.tools.dotc.core.TypeComparer.orType(TypeComparer.scala:1263)
at dotty.tools.dotc.core.TypeComparer$$anonfun$lub$2.apply(TypeComparer.scala:1164)
at dotty.tools.dotc.core.TypeComparer$$anonfun$lub$2.apply(TypeComparer.scala:1149)
at dotty.tools.dotc.reporting.Reporting$class.traceIndented(Reporter.scala:136)
at dotty.tools.dotc.core.Contexts$Context.traceIndented(Contexts.scala:57)
at dotty.tools.dotc.core.TypeComparer.lub(TypeComparer.scala:1148)
at dotty.tools.dotc.core.Types$Type$$anonfun$$bar$1.apply(Types.scala:801)
at dotty.tools.dotc.core.Types$Type$$anonfun$$bar$1.apply(Types.scala:801)
at dotty.tools.dotc.util.Stats$.track(Stats.scala:35)
at dotty.tools.dotc.core.Types$Type.$bar(Types.scala:800)
at dotty.tools.dotc.core.Types$TypeBounds.$bar(Types.scala:3153)
at dotty.tools.dotc.core.TypeOps$class.approximateOr$1(TypeOps.scala:242)
at dotty.tools.dotc.core.TypeOps$class.orDominator(TypeOps.scala:265)
at dotty.tools.dotc.core.Contexts$Context.orDominator(Contexts.scala:57)
at dotty.tools.dotc.core.TypeOps$class.approximateOr$1(TypeOps.scala:242)
at dotty.tools.dotc.core.TypeOps$class.orDominator(TypeOps.scala:265)
at dotty.tools.dotc.core.Contexts$Context.orDominator(Contexts.scala:57)
at dotty.tools.dotc.core.Types$OrType.join(Types.scala:2246)
at dotty.tools.dotc.core.Types$Type.go$1(Types.scala:465)
at dotty.tools.dotc.core.Types$Type.findMember(Types.scala:590)
at dotty.tools.dotc.core.Denotations$Denotation.findMember(Denotations.scala:178)
at dotty.tools.dotc.core.Types$Type.go$1(Types.scala:441)
at dotty.tools.dotc.core.Types$Type.findMember(Types.scala:590)
at dotty.tools.dotc.core.Types$Type.memberExcluding(Types.scala:426)
at dotty.tools.dotc.core.Types$Type$$anonfun$member$1.apply(Types.scala:410)
at dotty.tools.dotc.core.Types$Type$$anonfun$member$1.apply(Types.scala:410)
at dotty.tools.dotc.util.Stats$.track(Stats.scala:35)
at dotty.tools.dotc.core.Types$Type.member(Types.scala:409)
at dotty.tools.dotc.core.TypeComparer.hasMatchingMember(TypeComparer.scala:880)
at dotty.tools.dotc.core.TypeComparer.compareRefinedSlow$1(TypeComparer.scala:399)
at dotty.tools.dotc.core.TypeComparer.compareRefined$1(TypeComparer.scala:412)
at dotty.tools.dotc.core.TypeComparer.thirdTry(TypeComparer.scala:418)
at dotty.tools.dotc.core.TypeComparer.secondTry(TypeComparer.scala:282)
at dotty.tools.dotc.core.TypeComparer.dotty$tools$dotc$core$TypeComparer$$firstTry(TypeComparer.scala:271)
at dotty.tools.dotc.core.TypeComparer$$anonfun$isSubType$1.apply$mcZ$sp(TypeComparer.scala:112)
at dotty.tools.dotc.core.TypeComparer$$anonfun$isSubType$1.apply(TypeComparer.scala:104)
at dotty.tools.dotc.core.TypeComparer$$anonfun$isSubType$1.apply(TypeComparer.scala:104)
at dotty.tools.dotc.reporting.Reporting$class.traceIndented(Reporter.scala:136)
at dotty.tools.dotc.core.Contexts$Context.traceIndented(Contexts.scala:57)
at dotty.tools.dotc.core.TypeComparer.isSubType(TypeComparer.scala:103)
at dotty.tools.dotc.core.TypeComparer.topLevelSubType(TypeComparer.scala:97)
at dotty.tools.dotc.core.Types$Type$$anonfun$$less$colon$less$1.apply$mcZ$sp(Types.scala:696)
at dotty.tools.dotc.core.Types$Type$$anonfun$$less$colon$less$1.apply(Types.scala:696)
at dotty.tools.dotc.core.Types$Type$$anonfun$$less$colon$less$1.apply(Types.scala:696)
at dotty.tools.dotc.util.Stats$.track(Stats.scala:35)
at dotty.tools.dotc.core.Types$Type.$less$colon$less(Types.scala:695)
at dotty.tools.dotc.typer.Typer.adaptNoArgs$1(Typer.scala:1959)
at dotty.tools.dotc.typer.Typer.adaptInterpolated(Typer.scala:2087)
at dotty.tools.dotc.typer.Typer.adaptNoArgs$1(Typer.scala:1935)
at dotty.tools.dotc.typer.Typer.adaptInterpolated(Typer.scala:2087)
at dotty.tools.dotc.typer.Typer$$anonfun$adapt$1$$anonfun$apply$34.apply(Typer.scala:1732)
at dotty.tools.dotc.typer.Typer$$anonfun$adapt$1$$anonfun$apply$34.apply(Typer.scala:1728)
at dotty.tools.dotc.reporting.Reporting$class.traceIndented(Reporter.scala:136)
at dotty.tools.dotc.core.Contexts$Context.traceIndented(Contexts.scala:57)
at dotty.tools.dotc.typer.Typer$$anonfun$adapt$1.apply(Typer.scala:1728)
at dotty.tools.dotc.typer.Typer$$anonfun$adapt$1.apply(Typer.scala:1728)
at dotty.tools.dotc.util.Stats$.track(Stats.scala:35)
at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:1727)
Note that both the if
expression and the implicit class are essential for the problem to manifest. As noted in the log above, the crash occurs in the context of subtyping check
(p.Bar[A] | p.Bar[A'])#p$Bar$$A <:< p.Foo[T]
,
i.e., after the implicit conversion has been chosen, and, if I'm not mistaken, while method bar
's result type is being checked against f
's result type.
I spent a while digging through the call trace and tried to familiarize myself a bit with the code. Since tp2 = p.Foo[T]
is a RefinedType contributing T
, during the subtyping check we look up member p$Foo$$T
in TypeRef tp1 = (p.Bar[A] | p.Bar[A'])#p$Bar$$A
.
// TypeComparer.scala:396
def compareRefinedSlow: Boolean = {
val name2 = tp2.refinedName
isSubType(tp1, tp2.parent) &&
(name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2))
}
...
// TypeComparer.scala:880
protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = {
val rinfo2 = tp2.refinedInfo
val mbr = tp1.member(name) // AssertionError in here
...
}
So we dive into tp1.findMember(...)
, which first tries to get rid of the TypeRef by computing its denotation (Types.scala:440) -- the rather silly >: A & A <: A | A
(i.e., <SingleDenotation of type TypeBounds(AndType(TypeVar(PolyParam(A)),TypeVar(PolyParam(A))), OrType(TypeVar(PolyParam(A)),TypeVar(PolyParam(A))))>
).
Moving on, findMember
gets rid of the TypeBounds by overapproximating (:455, case tp: TypeProxy => go(tp.underlying)
, missing from the stack trace because go(...)
is tail-recursive!), yielding A | A
, and finally tries to join
that OrType (:460).
As far as I can tell, at least from here things must be going wrong. We are trying to join TypeVar(PolyParam(A))
with itself, landing us in the following case of approximateOr
(TypeOps.scala:242):
case tp1: TypeProxy if !isClassRef(tp1) =>
orDominator(tp1.superType | tp2)
The call to .superType
successively reduces TypeVar(PolyParam(A))
to PolyParam(A)
and finally to TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix,scala)),Nothing), TypeRef(ThisType(TypeRef(NoPrefix,scala)),Any))
, at which point the OrType is created (and its assertion violated).
More importantly, though, the information about A
was lost in the last .superType
call. I suspect it should have been instantiated. I don't know, however, whether instantiating TypeVars would be consistent with .superType
's meaning otherwise.