Skip to content

Commit ce4633d

Browse files
authored
Merge pull request #4597 from dotty-staging/change-protected-accessors
Change protected accessors
2 parents cc7e221 + bf62402 commit ce4633d

11 files changed

+203
-324
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class Compiler {
6060
protected def transformPhases: List[List[Phase]] =
6161
List(new FirstTransform, // Some transformations to put trees into a canonical form
6262
new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars
63+
new ProtectedAccessors, // Add accessors for protected members
6364
new ElimPackagePrefixes) :: // Eliminate references to package prefixes in Select nodes
6465
List(new CheckStatic, // Check restrictions that apply to @static members
6566
new ElimRepeated, // Rewrite vararg parameters and arguments

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,8 @@ object NameKinds {
354354

355355
val SuperAccessorName = new PrefixNameKind(SUPERACCESSOR, "super$")
356356
val InitializerName = new PrefixNameKind(INITIALIZER, "initial$")
357-
val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
358-
val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility
357+
val ProtectedGetterName = new PrefixNameKind(PROTECTEDGETTER, "protected_get$")
358+
val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected_set$")
359359
val InlineGetterName = new PrefixNameKind(INLINEGETTER, "inline_get$")
360360
val InlineSetterName = new PrefixNameKind(INLINESETTER, "inline_set$")
361361

@@ -389,9 +389,12 @@ object NameKinds {
389389
def infoString: String = "Signed"
390390
}
391391

392-
/** Possible name kinds of a method that comes from Scala2 pickling info. */
392+
/** Possible name kinds of a method that comes from Scala2 pickling info.
393+
* and that need to be unmangled. Note: Scala2 protected accessors and setters
394+
* can be left mangled, so they are not included in thus list.
395+
*/
393396
val Scala2MethodNameKinds: List[NameKind] =
394-
List(DefaultGetterName, ExtMethName, UniqueExtMethName, ProtectedAccessorName, ProtectedSetterName)
397+
List(DefaultGetterName, ExtMethName, UniqueExtMethName)
395398

396399
def simpleNameKindOfTag : collection.Map[Int, ClassifiedNameKind] = simpleNameKinds
397400
def qualifiedNameKindOfTag : collection.Map[Int, QualifiedNameKind] = qualifiedNameKinds

compiler/src/dotty/tools/dotc/core/NameTags.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ object NameTags extends TastyFormat.NameTags {
1515
// outer accessor that will be filled in by ExplicitOuter.
1616
// <num> indicates the number of hops needed to select the outer field.
1717

18+
final val PROTECTEDGETTER = 24 // The name of a protected getter `protected_get$<name>` created by ProtectedAccessors.
19+
20+
final val PROTECTEDSETTER = 25 // The name of a protected setter `protected_set$<name>` created by ProtectedAccessors.
21+
1822
final val INITIALIZER = 26 // A mixin initializer method
1923

2024
final val AVOIDCLASH = 27 // Adds a suffix to avoid a name clash;
@@ -47,7 +51,9 @@ object NameTags extends TastyFormat.NameTags {
4751
case OUTERSELECT => "OUTERSELECT"
4852

4953
case SUPERACCESSOR => "SUPERACCESSOR"
50-
case PROTECTEDACCESSOR => "PROTECTEDACCESSOR"
54+
case INLINEGETTER => "INLINEGETTER"
55+
case INLINESETTER => "INLINESETTER"
56+
case PROTECTEDGETTER => "PROTECTEDGETTER"
5157
case PROTECTEDSETTER => "PROTECTEDSETTER"
5258
case INITIALIZER => "INITIALIZER"
5359
case AVOIDCLASH => "AVOIDCLASH"

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ Macro-format:
3939
VARIANT Length underlying_NameRef variance_Nat // 0: Contravariant, 1: Covariant
4040
4141
SUPERACCESSOR Length underlying_NameRef
42-
PROTECTEDACCESSOR Length underlying_NameRef
43-
PROTECTEDSETTER Length underlying_NameRef
42+
INLINEGETTER Length underlying_NameRef
43+
INLINESETTER Length underlying_NameRef
4444
OBJECTCLASS Length underlying_NameRef
4545
4646
SIGNED Length original_NameRef resultSig_NameRef paramSig_NameRef*
@@ -226,8 +226,8 @@ Standard Section: "Positions" Assoc*
226226
object TastyFormat {
227227

228228
final val header = Array(0x5C, 0xA1, 0xAB, 0x1F)
229-
val MajorVersion = 7
230-
val MinorVersion = 1
229+
val MajorVersion = 8
230+
val MinorVersion = 0
231231

232232
/** Tags used to serialize names */
233233
class NameTags {
@@ -252,18 +252,12 @@ object TastyFormat {
252252

253253
final val SUPERACCESSOR = 20 // The name of a super accessor `super$name` created by SuperAccesors.
254254

255-
final val PROTECTEDACCESSOR = 21 // The name of a protected accessor `protected$<name>` created by SuperAccesors.
255+
final val INLINEGETTER = 21 // The name of an inline getter `inline_get$name`
256256

257-
final val PROTECTEDSETTER = 22 // The name of a protected setter `protected$set<name>` created by SuperAccesors.
258-
// This is a dubious encoding for its risk for ambiguity.
259-
// It is kept for Scala-2 compatibility.
257+
final val INLINESETTER = 22 // The name of an inline setter `inline_set$name`
260258

261259
final val OBJECTCLASS = 23 // The name of an object class (or: module class) `<name>$`.
262260

263-
final val INLINEGETTER = 24 // The name of an inline getter `inline_get$name`
264-
265-
final val INLINESETTER = 25 // The name of an inline setter `inline_set$name`
266-
267261
final val SIGNED = 63 // A pair of a name and a signature, used to idenitfy
268262
// possibly overloaded methods.
269263
}

compiler/src/dotty/tools/dotc/transform/AccessProxies.scala

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import NameKinds.ClassifiedNameKind
1414
import ast.Trees._
1515
import util.Property
1616
import util.Positions.Position
17+
import config.Printers.transforms
1718

1819
/** A utility class for generating access proxies. Currently used for
1920
* inline accessors and protected accessors.
2021
*/
2122
abstract class AccessProxies {
2223
import ast.tpd._
24+
import AccessProxies._
2325

2426
def getterName: ClassifiedNameKind
2527
def setterName: ClassifiedNameKind
@@ -56,9 +58,11 @@ abstract class AccessProxies {
5658
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean
5759

5860
/** A fresh accessor symbol */
59-
def newAccessorSymbol(accessed: Symbol, name: TermName, info: Type)(implicit ctx: Context): TermSymbol =
60-
ctx.newSymbol(accessed.owner.enclosingSubClass, name, Synthetic | Method,
61-
info, coord = accessed.pos).entered
61+
def newAccessorSymbol(owner: Symbol, name: TermName, info: Type, pos: Position)(implicit ctx: Context): TermSymbol = {
62+
val sym = ctx.newSymbol(owner, name, Synthetic | Method, info, coord = pos).entered
63+
if (sym.allOverriddenSymbols.exists(!_.is(Deferred))) sym.setFlag(Override)
64+
sym
65+
}
6266

6367
/** Create an accessor unless one exists already, and replace the original
6468
* access with a reference to the accessor.
@@ -73,14 +77,26 @@ abstract class AccessProxies {
7377

7478
def refersToAccessed(sym: Symbol) = accessedBy.get(sym) == Some(accessed)
7579

76-
val accessorInfo =
80+
var accessorClass = hostForAccessorOf(accessed: Symbol)
81+
if (!accessorClass.exists) {
82+
val curCls = ctx.owner.enclosingClass
83+
transforms.println(i"${curCls.ownersIterator.toList}%, %")
84+
ctx.error(i"illegal access to protected ${accessed.showLocated} from $curCls",
85+
reference.pos)
86+
accessorClass = curCls
87+
}
88+
89+
val accessorRawInfo =
7790
if (onLHS) MethodType(accessed.info :: Nil, defn.UnitType)
7891
else accessed.info.ensureMethodic
92+
val accessorInfo =
93+
accessorRawInfo.asSeenFrom(accessorClass.thisType, accessed.owner)
7994
val accessorName = nameKind(accessed.name)
95+
8096
val accessorSymbol =
81-
accessed.owner.info.decl(accessorName).suchThat(refersToAccessed).symbol
97+
accessorClass.info.decl(accessorName).suchThat(refersToAccessed).symbol
8298
.orElse {
83-
val acc = newAccessorSymbol(accessed, accessorName, accessorInfo)
99+
val acc = newAccessorSymbol(accessorClass, accessorName, accessorInfo, accessed.pos)
84100
accessedBy(acc) = accessed
85101
acc
86102
}
@@ -106,4 +122,8 @@ abstract class AccessProxies {
106122
tree
107123
}
108124
}
125+
}
126+
object AccessProxies {
127+
def hostForAccessorOf(accessed: Symbol)(implicit ctx: Context): Symbol =
128+
ctx.owner.ownersIterator.findSymbol(_.derivesFrom(accessed.owner))
109129
}

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import typer.ProtoTypes._
2020
import typer.ErrorReporting._
2121
import core.TypeErasure._
2222
import core.Decorators._
23-
import core.NameKinds._
2423
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
2524
import ast.Trees._
2625
import scala.collection.mutable.ListBuffer
@@ -315,10 +314,6 @@ object Erasure {
315314
}
316315
}
317316

318-
/** The erasure typer.
319-
* Also inserts protected accessors where needed. This logic is placed here
320-
* since it is most naturally done in a macro transform.
321-
*/
322317
class Typer extends typer.ReTyper with NoChecking {
323318
import Boxing._
324319

@@ -327,23 +322,6 @@ object Erasure {
327322
if (tree.isTerm) erasedRef(tp) else valueErasure(tp)
328323
}
329324

330-
object ProtectedAccessors extends AccessProxies {
331-
def getterName = ProtectedAccessorName
332-
def setterName = ProtectedSetterName
333-
334-
val insert = new Insert {
335-
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean =
336-
false &&
337-
sym.isTerm && sym.is(Flags.Protected) &&
338-
ctx.owner.enclosingPackageClass != sym.enclosingPackageClass &&
339-
!ctx.owner.enclosingClass.derivesFrom(sym.owner) &&
340-
{ println(i"need protected acc $sym accessed from ${ctx.owner}"); assert(false); false }
341-
}
342-
}
343-
344-
override def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] =
345-
ProtectedAccessors.addAccessorDefs(cls, body)
346-
347325
override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
348326
assert(tree.hasType)
349327
val erasedTp = erasedType(tree)
@@ -378,9 +356,6 @@ object Erasure {
378356
else
379357
super.typedLiteral(tree)
380358

381-
override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree =
382-
ProtectedAccessors.insert.accessorIfNeeded(super.typedIdent(tree, pt))
383-
384359
/** Type check select nodes, applying the following rewritings exhaustively
385360
* on selections `e.m`, where `OT` is the type of the owner of `m` and `ET`
386361
* is the erased type of the selection's original qualifier expression.
@@ -461,12 +436,9 @@ object Erasure {
461436
}
462437
}
463438

464-
ProtectedAccessors.insert.accessorIfNeeded(recur(typed(tree.qualifier, AnySelectionProto)))
439+
recur(typed(tree.qualifier, AnySelectionProto))
465440
}
466441

467-
override def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree =
468-
ProtectedAccessors.insert.accessorIfNeeded(super.typedAssign(tree, pt))
469-
470442
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =
471443
if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree)
472444
else {

compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ class ExtensionMethods extends MiniPhase with DenotTransformer with FullParamete
4545
/** the following two members override abstract members in Transform */
4646
override def phaseName: String = ExtensionMethods.name
4747

48-
override def runsAfter = Set(ElimRepeated.name)
48+
override def runsAfter = Set(
49+
ElimRepeated.name,
50+
ProtectedAccessors.name // protected accessors cannot handle code that is moved from class to companion object
51+
)
4952

5053
override def runsAfterGroupsOf = Set(FirstTransform.name) // need companion objects to exist
5154

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
216216
case sel: Select =>
217217
val args1 = transform(args)
218218
val sel1 = transformSelect(sel, args1)
219-
if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree1)(sel1, args1)
219+
cpy.TypeApply(tree1)(sel1, args1)
220220
case _ =>
221221
super.transform(tree1)
222222
}
223-
case tree @ Assign(sel: Select, _) =>
224-
super.transform(superAcc.transformAssign(tree))
225223
case Inlined(call, bindings, expansion) =>
226224
// Leave only a call trace consisting of
227225
// - a reference to the top-level class from which the call was inlined,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core.Contexts.Context
5+
import core.NameKinds._
6+
import core.Symbols._
7+
import core.Flags._
8+
import core.Decorators._
9+
import MegaPhase.MiniPhase
10+
import ast.Trees._
11+
import util.Property
12+
13+
/** Add accessors for all protected accesses. An accessor is needed if
14+
* according to the rules of the JVM a protected class member is not accesissible
15+
* from the point of access, but is accessible if the access is from an enclosing
16+
* class. In this point a public access method is placed in that enclosing class.
17+
*/
18+
object ProtectedAccessors {
19+
val name = "protectedAccessors"
20+
21+
private val LHS = new Property.StickyKey[Unit]
22+
23+
/** Is the current context's owner inside the access boundary established by `sym`? */
24+
def insideBoundaryOf(sym: Symbol)(implicit ctx: Context): Boolean = {
25+
if (sym.is(JavaDefined)) {
26+
sym.is(JavaStatic) || // Java's static protected definitions are treated as public
27+
ctx.owner.enclosingPackageClass == sym.enclosingPackageClass
28+
}
29+
else {
30+
// For Scala-defined symbols we currently allow private and protected accesses
31+
// from inner packages, and compensate by widening accessibility of such symbols to public.
32+
// It would be good if we could revisit this at some point.
33+
val boundary = sym.accessBoundary(sym.enclosingPackageClass)
34+
ctx.owner.isContainedIn(boundary) || ctx.owner.isContainedIn(boundary.linkedClass)
35+
}
36+
}
37+
38+
/** Do we need a protected accessor if the current context's owner
39+
* is not in a subclass or subtrait of `sym`?
40+
*/
41+
def needsAccessorIfNotInSubclass(sym: Symbol)(implicit ctx: Context): Boolean =
42+
sym.isTerm && sym.is(Protected) &&
43+
!sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public
44+
!insideBoundaryOf(sym)
45+
46+
/** Do we need a protected accessor for accessing sym from the current context's owner? */
47+
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean =
48+
needsAccessorIfNotInSubclass(sym) &&
49+
!ctx.owner.enclosingClass.derivesFrom(sym.owner)
50+
}
51+
52+
class ProtectedAccessors extends MiniPhase {
53+
import ast.tpd._
54+
import ProtectedAccessors._
55+
56+
override def phaseName = ProtectedAccessors.name
57+
58+
object Accessors extends AccessProxies {
59+
def getterName = ProtectedGetterName
60+
def setterName = ProtectedSetterName
61+
62+
val insert = new Insert {
63+
def needsAccessor(sym: Symbol)(implicit ctx: Context) = ProtectedAccessors.needsAccessor(sym)
64+
}
65+
}
66+
67+
override def prepareForAssign(tree: Assign)(implicit ctx: Context) = {
68+
tree.lhs match {
69+
case lhs: RefTree if needsAccessor(lhs.symbol) => lhs.putAttachment(LHS, ())
70+
case _ =>
71+
}
72+
ctx
73+
}
74+
75+
private def isLHS(tree: RefTree) = tree.removeAttachment(LHS).isDefined
76+
77+
override def transformIdent(tree: Ident)(implicit ctx: Context): Tree =
78+
if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree)
79+
80+
override def transformSelect(tree: Select)(implicit ctx: Context): Tree =
81+
if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree)
82+
83+
override def transformAssign(tree: Assign)(implicit ctx: Context): Tree =
84+
Accessors.insert.accessorIfNeeded(tree)
85+
86+
override def transformTemplate(tree: Template)(implicit ctx: Context): Tree =
87+
cpy.Template(tree)(body = Accessors.addAccessorDefs(tree.symbol.owner, tree.body))
88+
}

0 commit comments

Comments
 (0)