Skip to content

Companions and nested classes are nestmates #10765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: 2.13.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
keepPhaseStack = settings.log.isSetByUser

val isScala3: Boolean = settings.isScala3: @nowarn
val isJDK11: Boolean = settings.targetValue.toInt >= 11

object sourceFeatures {
private val s = settings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,13 +711,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
generatedType = genPrimitiveOp(app, expectedType)
} else { // normal method call
def isTraitSuperAccessorBodyCall = app.hasAttachment[UseInvokeSpecial.type]
def isPrivateSpecial = sym.isPrivate && (claszSymbol.info <:< sym.owner.info)
val invokeStyle =
if (sym.isStaticMember)
InvokeStyle.Static
else if (sym.isPrivate || sym.isClassConstructor) InvokeStyle.Special
else if (isTraitSuperAccessorBodyCall)
else if (isPrivateSpecial || sym.isClassConstructor || isTraitSuperAccessorBodyCall)
InvokeStyle.Special
else InvokeStyle.Virtual
else
InvokeStyle.Virtual

if (invokeStyle.hasInstance) genLoadQualifier(fun)
genLoadArguments(args, paramTKs(app))
Expand Down
16 changes: 13 additions & 3 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
*
* Given that CleanUp delivers trees that produce values on the stack,
* the entry-point to all-things instruction-emit is `genLoad()`.
* There, an operation taking N arguments results in recursively emitting instructions to lead each of them,
* There, an operation taking N arguments results in recursively emitting instructions to load each of them,
* followed by emitting instructions to process those arguments (to be found at run-time on the operand-stack).
*
* In a few cases the above recipe deserves more details, as provided in the documentation for:
Expand Down Expand Up @@ -124,7 +124,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
thisBTypeDescriptor = thisBType.descriptor
cnode = new ClassNode1()

initJClass(cnode)
initJClass()
val cd = if (isCZStaticModule) {
// Move statements from the primary constructor following the superclass constructor call to
// a newly synthesised tree representing the "<clinit>", which also assigns the MODULE$ field.
Expand Down Expand Up @@ -182,7 +182,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
/*
* must-single-thread
*/
private def initJClass(@unused jclass: asm.ClassVisitor): Unit = {
private def initJClass(): Unit = {

val bType = classBTypeFromSymbol(claszSymbol)
val superClass = bType.info.get.superClass.getOrElse(ObjectRef).internalName
Expand All @@ -199,6 +199,16 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
cnode.visitSource(cunit.source.toString, null /* SourceDebugExtension */)
}

if (currentRun.isJDK11)
claszSymbol.attachments.get[NestHost] match {
case Some(NestHost(host)) => cnode.visitNestHost(internalName(host))
case None =>
claszSymbol.attachments.get[NestMembers] match {
case Some(NestMembers(members)) => for (m <- members) cnode.visitNestMember(internalName(m))
case None =>
}
}

enclosingMethodAttribute(claszSymbol, internalName, methodBTypeFromSymbol(_).descriptor) match {
case Some(EnclosingMethodEntry(className, methodName, methodDescriptor)) =>
cnode.visitOuterClass(className, methodName, methodDescriptor)
Expand Down
8 changes: 2 additions & 6 deletions src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,8 @@ abstract class GenBCode extends SubComponent {
generatedClassHandler = GeneratedClassHandler(global)
statistics.stopTimer(statistics.bcodeInitTimer, initStart)
}
def writeOtherFiles(): Unit = {
global.plugins foreach {
plugin =>
plugin.writeAdditionalOutputs(postProcessor.classfileWriter)
}
}

def writeOtherFiles(): Unit = global.plugins.foreach(_.writeAdditionalOutputs(postProcessor.classfileWriter))

private def close(): Unit =
List[AutoCloseable](
Expand Down
9 changes: 8 additions & 1 deletion src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,13 @@ abstract class ExplicitOuter extends InfoTransform
else atPos(tree.pos)(outerPath(outerValue, currentClass.outerClass, sym)) // (5)

case Select(qual, name) =>
// c0 belongs to same "nest" as c1 if c0 is enclosed by c1,
// or more generally their top enclosing classes are either identical or companions
def isNestable(c0: Symbol, c1: Symbol): Boolean = currentRun.isJDK11 && {
val top0 = c0.enclosingTopLevelClass
val top1 = c1.enclosingTopLevelClass
top0 == top1 || top0.linkedClassOfClass == top1
}
// make not private symbol accessed from inner classes, as well as
// symbols accessed from @inline methods
//
Expand All @@ -442,7 +449,7 @@ abstract class ExplicitOuter extends InfoTransform
def enclMethodIsInline = closestEnclMethod(currentOwner) hasAnnotation ScalaInlineClass
// scala/bug#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)
if ((currentClass != sym.owner || enclMethodIsInline) && !sym.isMethodWithExtension && !isNestable(currentClass, sym.owner))
sym.makeNotPrivate(sym.owner)

val qsym = qual.tpe.widen.typeSymbol
Expand Down
35 changes: 35 additions & 0 deletions src/compiler/scala/tools/nsc/transform/Flatten.scala
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,21 @@ abstract class Flatten extends InfoTransform {
if (tree.symbol.sourceModule.isStaticModule)
removeSymbolInCurrentScope(tree.symbol.sourceModule)
EmptyTree
case ClassDef(_, _, _, _) if !tree.symbol.isModuleClass => // !tree.symbol.isNestedClass
val sym = tree.symbol
val module = sym.companion.moduleClass
if (module.exists) {
module.attachments.get[NestHost] match {
case Some(NestHost(host)) => assert(host == sym, s"bad $module host $host != $sym")
case None => module.updateAttachment(NestHost(sym))
}
val members = sym.attachments.get[NestMembers] match {
case Some(NestMembers(members)) => module :: members
case None => module :: Nil
}
sym.updateAttachment(NestMembers(members))
}
tree.transform(this)
case _ =>
tree.transform(this)
}
Expand Down Expand Up @@ -173,6 +188,26 @@ abstract class Flatten extends InfoTransform {
val stats1 = super.transformStats(stats, exprOwner)
if (currentOwner.isPackageClass) {
val lifted = liftedDefs.remove(currentOwner).toList.flatten
for (d <- lifted) {
val sym = d.symbol
val top = {
val tlc = sym.originalEnclosingTopLevelClassOrDummy
if (tlc.isModuleClass) {
val cmp = tlc.linkedClassOfClass
if (cmp.exists) cmp else tlc
}
else tlc
}
sym.attachments.get[NestHost] match {
case Some(NestHost(host)) => assert(host == top, s"bad $sym host $host != $top")
case None => sym.updateAttachment(NestHost(top))
}
val members = top.attachments.get[NestMembers] match {
case Some(NestMembers(members)) => sym :: members
case None => sym :: Nil
}
top.updateAttachment(NestMembers(members))
}
stats1 ::: lifted
}
else stats1
Expand Down
6 changes: 6 additions & 0 deletions src/reflect/scala/reflect/internal/StdAttachments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,10 @@ trait StdAttachments {

/** This Bind tree was derived immediately from the given tree, for unused accounting. */
case class VarAlias(tree: Tree /*Bind | ValDef*/) extends PlainAttachment

/** A top-level class may be a "nest host" with given "nest members" (nested classes or companion). */
case class NestMembers(members: List[Symbol]) extends PlainAttachment

/** A nested class or a top-level companion module may have a "nest host" for which it is a "nest member". */
case class NestHost(host: Symbol) extends PlainAttachment
}
5 changes: 3 additions & 2 deletions src/reflect/scala/reflect/internal/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2335,9 +2335,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
@tailrec
final def originalEnclosingTopLevelClassOrDummy: Symbol =
if (this eq NoSymbol) this
else if (isTopLevel) {
else if (originalOwner.isPackageClass) {
if (isClass) this else moduleClass.orElse(this)
} else originalOwner.originalEnclosingTopLevelClassOrDummy
}
else originalOwner.originalEnclosingTopLevelClassOrDummy

/** Is this symbol defined in the same scope and compilation unit as `that` symbol? */
def isCoDefinedWith(that: Symbol) = {
Expand Down
2 changes: 2 additions & 0 deletions src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.DiscardedExpr
this.BooleanParameterType
this.VarAlias
this.NestMembers
this.NestHost
this.noPrint
this.typeDebug
// inaccessible: this.posAssigner
Expand Down
68 changes: 68 additions & 0 deletions test/files/run/t6882.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//> using options --target:11
//> using jvm 11+

class C private (private val i: Int, private val j: Int) {
private val c = i + C.secret

@inline def f = j * 2
}
object C {
def unwrap(c: C): Int = c.c

def apply(i: Int, j: Int): C = new C(i, j)

private def secret = 5
}

class D(d0: String) {
private def d = d0
def e = new E
class E {
def e = D.this.d
}
}
object D {
}

object Top {
private def i = 42
class Nested {
def f = i
}
def j = new Nested().f
}

class TopHeavy {
private def i = TopHeavy.underlying
}
object TopHeavy {
private def underlying = 42
class Nested {
def f = new TopHeavy().i
}
def j = new Nested().f
}

object Test {
import java.lang.reflect.Modifier.{PRIVATE => Private}
def main(args: Array[String]): Unit = {
assert(C.unwrap(C(42, 27)) == 47)
for (m <- Class.forName("C$").getDeclaredMethods; n = m.getName if n.contains("secret")) {
assert(n == "secret")
assert((m.getModifiers & Private) != 0)
}

val d = new D("mystery")
assert(d.e.e == "mystery")
for (m <- Class.forName("D").getDeclaredMethods; n = m.getName if n.contains("d")) {
assert(n == "d")
assert((m.getModifiers & Private) != 0)
}

assert(Top.j == 42)
for (m <- Class.forName("Top$").getDeclaredMethods; n = m.getName if n.contains("i")) {
assert(n == "i")
assert((m.getModifiers & Private) != 0)
}
}
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy