scala/scala — pull request #3873 by retronym (2014-07-11 13:36:49+00:00)
Title: SI-8710 Fix crasher for value classes + private + overloading

The link between methods owned by a value class and the corresponding
extension method in the companion is governed in part by a naming
convention that depends on how many eponymous decls exist. This is
easiest to explain with an example:

% cat sandbox/test.scala && \
qscalac -Xprint:extmethods sandbox/test.scala \
| egrep 'class|object|def (foo|bar)|}$'
class C(val x: Any) extends AnyVal {
def foo(a: Int) = ""
def foo(a: String) = ""
def bar(a: Byte)
}
final class C extends scala.AnyVal {
def foo(a: Int): String = C.foo$extension0(C.this)(a);
def foo(a: String): String = C.foo$extension1(C.this)(a);
def bar(a: Byte): Unit = C.bar$extension(C.this)(a);
}
object C extends AnyRef {
final def foo$extension0($this: C)(a: Int): String = "";
final def foo$extension1($this: C)(a: String): String = "";
final def bar$extension($this: C)(a: Byte): Unit = (.asInstanceOf[Unit]: Unit);
}
}
}

Notice how the extension method names for `foo` are suffixed with
a counter. This logic is contained in `extensionNames`.

However, in the enclosed test cases, when we call `extensionNames`
in a late phase of the compiler, in particular during erasure when
we rewire `new C(x).foo(args)` to `C.foo$extensionN(x)(args)`, we
crash. Why? The private method in the value class has been name
mangled by `ExplicitOuter`'s use of `makeNotPrivate`, and we no longer
appear to have multiple eponymous methods in play.

We could try to fix this by changing `extensionNames` to poke around
through the scopes of the value class and its companion in a manner
insensitive to expanded names. This might look something like:

- info.decl(imeth.name)
+ newOverloadedSymbol(info.decls.filter(sym => unexpandedName(sym) == unexpandedName(imeth.name))

But in fact we never need to expand the name of a private method in
an value class, nor its corresponding companion, as:

* calls to private value class members end up as calls to public
extension methods
* extension methods already have a the `$extension[N]` suffix
and need no further mangling.

This commit:
- Resets `PRIVATE` and `LOCAL` when deriving the extension method
- Adds a special case to `ExplicitOuter` to imbue the special
knowledge that a call to `SomeValueClass#somePrivateMethod`
need not `makeNotPrivate`, as erasure will come along later
and rewire that to a (public) extension method.