Solution (source code)

= Solution

Replace the overloaded entry family by one forwarding method and introduce the second dispatch explicitly:

``
class X {
  def dispatch(to: M): String = to.visit(this)
}
class Y extends X {
  override def dispatch(to: M): String = to.visit(this)
}
class Z extends Y {
  override def dispatch(to: M): String = to.visit(this)
}

class M {
  def f(x: X): String = x.dispatch(this)
  def visit(x: X): String = "f with X at M"
  def visit(x: Y): String = "f with Y at M"
  def visit(x: Z): String = "f with Z at M"
}
class N extends M {
  override def visit(x: X): String = "f with X at N"
  override def visit(x: Y): String = "f with Y at N"
  override def visit(x: Z): String = "f with Z at N"
}
``

The original three calls still type-check because all their arguments are subtypes of `X`. They all refer to the same runtime `Z` object already; the change is how the operation discovers that runtime type. The first virtual call selects `Z.dispatch`, where `this` has static type `Z`, so overload resolution selects `visit(Z)`. The second virtual call selects `N.visit(Z)` from the receiver's runtime type. Thus <double dispatch> gives \b[`f with Z at N` on all three lines].