Change to implicit resolution's specificity/weighting process changed in Scala 3?

First I want to say that Scala 3 is turning out to be a very nice language and I’m excited for what we can do with it. Thank you.

I am in the process of trying to get Breeze compiling under Scala 3. There is a common pattern in Breeze in how I organize implicits, where for a class XVector I have a trait XVectorOps with a bunch of implicits in it that object XVector extends. Usually these are stacked a bit. In Scala 2 this all worked fine, but in Scala 3, this leads to problems in the case where there is a subtype relationship between different XVectors.

This is a minimized example that compiles fine in Scala 2 but does not work in Scala 3:

trait Op[T, U]

trait HasOps[+This] {
  def foo[TT>:This, U](u: U)(implicit op: Op[TT, U]) = op
}

trait Vector extends HasOps[Vector]

class DenseVector extends Vector with HasOps[DenseVector]

trait VectorOps {
  implicit def vOp: Op[Vector, Double] = ???
}

object Vector extends VectorOps

trait DenseVectorOps {
  implicit def dvOp: Op[DenseVector, Double] = ???
}

object DenseVector extends DenseVectorOps

object Foo {
  def bar() = {
    val dv = new DenseVector
    dv.foo(3.0)
  }
}

If I inline the Ops into DenseVector etc, this works fine, but otherwise I get an ambiguous implicit error:

26 |    dv.foo(3.0)
   |               ^
   |ambiguous implicit arguments: both method dvOp in trait DenseVectorOps and method vOp in trait VectorOps match type Op[TT, Double] of parameter op of method foo in trait HasOps

Is this intentional? It’s going to make it a lot harder to organize implicits. I’m using RC2

I filed a bug here since I suspect it’s not intentional Another incorrect ambiguous implicit error? · Issue #12125 · lampepfl/dotty · GitHub