Difference in overloading between Scala 2 and Scala 3 with implicit class

Consider the following code:

object Main {

  implicit class BytesExt(private val _value: Array[Byte]) extends AnyVal {  
    def drop(when: Boolean): Array[Byte] = if (when) Array.empty else _value }

  val a: Array[Byte] = Array(0)
  val b = a.drop(1) }

This compiles fine under Scala 2.13.8 whereas it fails under Scala 3.1.1 with

[error] -- [E007] Type Mismatch Error: .../src/main/scala/Main.scala:7:17 
[error] 7 |  val b = a.drop(1) }
[error]   |                 ^
[error]   |                 Found:    (1 : Int)
[error]   |                 Required: Boolean

Is this the consequence of some changes in the implicit method resolution or is it a compiler bug?

Remarks:

  • changing to the new syntax with extension makes no difference.
  • adding the option “-source:3.0-migration” makes the behaviour equal to Scala 2 again.
1 Like

Note that the code compiles if you move BytesExt outside of Main. So it looks like point 2 on Changes in Implicit Resolution | Scala 3 Language Reference | Scala Documentation to me:

Nesting is now taken into account for selecting an implicit […]

1 Like

Do you mean like this?

implicit class BytesExt(private val _value: Array[Byte]) extends AnyVal {  
  def drop(when: Boolean): Array[Byte] = if (when) Array.empty else _value }

object Main {
  val a: Array[Byte] = Array(0)
  val b = a.drop(1) }

I still get the error though. Besides that, in Scala2 there also was no ambiguity error, which the explanation you refer to suggests. But, maybe it is still related though.

Here’s a Scala 3.1.1 REPL session showing it compiling:

scala> implicit class BytesExt(private val _value: Array[Byte]) extends AnyVal {  
     |   def drop(when: Boolean): Array[Byte] = if (when) Array.empty else _value }
// defined class BytesExt
def BytesExt(_value: Array[Byte]): BytesExt
                                                                                                    
scala> object Main {
     |   val a: Array[Byte] = Array(0)
     |   val b = a.drop(1) }
// defined object Main

I don’t know (without digging, anyway) why the result is different than in your Scastie.

I confirm that the REPL works, but compiling with SBT 1.6.2 and this build.sbt:

scalaVersion := "3.1.1"
scalacOptions ++= Seq("-feature","-deprecation","-unchecked")

gives the same error. (scalacOptions may also be omitted). So the REPL may somehow overcome this, but it does not seem to be Scastie problem. So the question remains, i guess. Is this expected behaviour or a compiler bug?