diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2014-07-11 11:05:59 +0200 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2014-07-11 15:43:20 +0200 |
commit | f36e7008937a1672c0e57dbf2eefe7a80e8988d7 (patch) | |
tree | 428de605917c244ff68a7b8c84f7a8c2293e0658 /src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala | |
parent | 47c62158b98d6faa74c7466dd30c28ad421ae8ff (diff) | |
download | scala-f36e7008937a1672c0e57dbf2eefe7a80e8988d7.tar.gz scala-f36e7008937a1672c0e57dbf2eefe7a80e8988d7.tar.bz2 scala-f36e7008937a1672c0e57dbf2eefe7a80e8988d7.zip |
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);
}
<synthetic> 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 = (<empty>.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.
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 0447e23e9e..51dd858719 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -446,8 +446,10 @@ abstract class ExplicitOuter extends InfoTransform // // See SI-6552 for an example of why `sym.owner.enclMethod hasAnnotation ScalaInlineClass` // is not suitable; if we make a method-local class non-private, it mangles outer pointer names. - if (currentClass != sym.owner || - (closestEnclMethod(currentOwner) hasAnnotation ScalaInlineClass)) + def enclMethodIsInline = closestEnclMethod(currentOwner) hasAnnotation ScalaInlineClass + // SI-8710 The extension method condition reflects our knowledge that a call to `new Meter(12).privateMethod` + // with later be rewritten (in erasure) to `Meter.privateMethod$extension(12)`. + if ((currentClass != sym.owner || enclMethodIsInline) && !sym.isMethodWithExtension) sym.makeNotPrivate(sym.owner) val qsym = qual.tpe.widen.typeSymbol |