Skip to content

Union of non-value types created during implicit search #2064

Closed
@gsps

Description

@gsps

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.

@smarter @odersky

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions