summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-07-11 11:05:59 +0200
committerJason Zaugg <jzaugg@gmail.com>2014-07-11 15:43:20 +0200
commitf36e7008937a1672c0e57dbf2eefe7a80e8988d7 (patch)
tree428de605917c244ff68a7b8c84f7a8c2293e0658 /src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
parent47c62158b98d6faa74c7466dd30c28ad421ae8ff (diff)
downloadscala-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.scala6
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