Search code examples
scalascala-2.11

compile error with implicit class method named "clone"


I get an unexpected compilation error (in 2.11.8) when attempting to make an implicit class with a method named clone.

The following simplified usage:

class Foo(val bar: String)

object Foo {
  implicit class Enrich(foo: Foo) {
    def clone(x: Int, y: Int): Int = x + y
  }
}

object Main extends App {
  val foo = new Foo("hello")
  println(foo.clone(1, 2))    // <- does not compile
}

generated the following error:

method clone in class Object cannot be accessed in Foo Access to protected method clone not permitted because prefix type Foo does not conform to object Main where the access take place

However, I can manually apply the implicit class and it successfully compiles:

println(Foo.Enrich(foo).clone(1, 2))    // <- OK

If I rename the method to something else (clone2, for example) the code compiles as expected.

I assume this is somehow related to the magic around java.lang.Cloneable, but that method does not expect to parameters.

So what is going on here?


Solution

  • This has to do with the fact Object (or AnyRef in Scala) posses a protected method clone() which takes precedence in overload resolution of Foo.

    SI-6760 partially talks about this problem, although clone there has an identical signature while in this case it is different.

    This feels like a bug (and now open as SI-10206). When we expand the typer tree with -Ytyper-debug, you can see that it finds a suitable candidate for def clone(int, int), but then fails in the immediate subsequent search:

    |-- foo.clone(1, 2) : pt=Unit EXPRmode (site: method main in Main)
    |    |    |    |    |-- foo.clone BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Main)
    |    |    |    |    |    |-- foo EXPRmode-POLYmode-QUALmode (silent: method main in Main)
    |    |    |    |    |    |    \-> foo.type (with underlying type my.awesome.pkg.Foo)
    |    |    |    |    |    [search #1] start `my.awesome.pkg.Foo`, searching for adaptation to pt=foo.type => ?{def clone: ?} (silent: method main in Main) implicits disabled
    |    |    |    |    |    |-- my.awesome.pkg.Foo.Enrich TYPEmode (site: method Enrich in Foo)
    |    |    |    |    |    |    \-> my.awesome.pkg.Foo.Enrich
    |    |    |    |    |    |-- Foo TYPEmode (site: value foo in Foo)
    |    |    |    |    |    |    \-> my.awesome.pkg.Foo
    |    |    |    |    |    |-- Int TYPEmode (site: method clone in Enrich)
    |    |    |    |    |    |    \-> Int
    |    |    |    |    |    |-- Int TYPEmode (site: value x in Enrich)
    |    |    |    |    |    |    \-> Int
    |    |    |    |    |    |-- Int TYPEmode (site: value y in Enrich)
    |    |    |    |    |    |    \-> Int
    |    |    |    |    |    [search #1] considering pkg.this.Foo.Enrich
    |    |    |    |    |    |-- pkg.this.Foo.Enrich BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Main) implicits disabled
    |    |    |    |    |    |    \-> (foo: my.awesome.pkg.Foo)my.awesome.pkg.Foo.Enrich
    |    |    |    |    |    [search #1] success inferred value of type foo.type => ?{def clone: ?} is SearchResult(pkg.this.Foo.Enrich, )
    |    |    |    |    |    [search #2] start `my.awesome.pkg.Foo`, searching for adaptation to pt=(=> foo.type) => ?{def clone: ?} (silent: method main in Main) implicits disabled
    |    |    |    |    |    \-> <error>
    Main.scala:6: error: method clone in class Object cannot be accessed in my.awesome.pkg.Foo
     Access to protected method clone not permitted because
     prefix type my.awesome.pkg.Foo does not conform to
     object Main in package pkg where the access take place
        foo.clone(1, 2) // <- does not compile
    

    Edit

    This does compile under 2.10.6