Skip to content

Commit e101f5a

Browse files
committed
Check protected accessors after pickling
New phase to check protected accessors. We now create accessors also for Scala symbols. Previously this was not done as it was assumed that such symbols will always be mapped to public. We create the accessors anyway in order to better test the functionality, so that we can at some point avoid the access widening from protected to public.
1 parent 4258d8e commit e101f5a

File tree

8 files changed

+148
-33
lines changed

8 files changed

+148
-33
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/SymDenotations.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,11 @@ object SymDenotations {
711711
i"""
712712
| Access to protected $this not permitted because enclosing ${ctx.owner.enclosingClass.showLocated}
713713
| is not a subclass of ${owner.showLocated} where target is defined""")
714+
else if (cls.is(Trait) && !this.owner.is(Trait))
715+
fail(
716+
i"""
717+
| Access to protected $this not permitted from $cls,
718+
| since traits cannot access protected members of superclasses""")
714719
else if (
715720
!( isType // allow accesses to types from arbitrary subclasses fixes #4737
716721
|| pre.derivesFrom(cls)

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

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ 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.
@@ -56,9 +57,11 @@ abstract class AccessProxies {
5657
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean
5758

5859
/** 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
60+
def newAccessorSymbol(owner: Symbol, name: TermName, info: Type, pos: Position)(implicit ctx: Context): TermSymbol = {
61+
val sym = ctx.newSymbol(owner, name, Synthetic | Method, info, coord = pos).entered
62+
if (sym.allOverriddenSymbols.exists(!_.is(Deferred))) sym.setFlag(Override)
63+
sym
64+
}
6265

6366
/** Create an accessor unless one exists already, and replace the original
6467
* access with a reference to the accessor.
@@ -73,14 +76,30 @@ abstract class AccessProxies {
7376

7477
def refersToAccessed(sym: Symbol) = accessedBy.get(sym) == Some(accessed)
7578

76-
val accessorInfo =
79+
val accessorClass = {
80+
def owningClass(start: Symbol) =
81+
start.ownersIterator.findSymbol(_.derivesFrom(accessed.owner))
82+
val curCls = ctx.owner.enclosingClass
83+
var owner = owningClass(curCls) //`orElse` owningClass(curCls.linkedClass)
84+
if (!owner.exists) {
85+
transforms.println(i"${curCls.ownersIterator.toList}%, %")
86+
ctx.error(i"illegal access to protected ${accessed.showLocated} from $curCls", reference.pos)
87+
owner = curCls
88+
}
89+
owner
90+
}
91+
92+
val accessorRawInfo =
7793
if (onLHS) MethodType(accessed.info :: Nil, defn.UnitType)
7894
else accessed.info.ensureMethodic
95+
val accessorInfo =
96+
accessorRawInfo.asSeenFrom(accessorClass.thisType, accessed.owner)
7997
val accessorName = nameKind(accessed.name)
98+
8099
val accessorSymbol =
81-
accessed.owner.info.decl(accessorName).suchThat(refersToAccessed).symbol
100+
accessorClass.info.decl(accessorName).suchThat(refersToAccessed).symbol
82101
.orElse {
83-
val acc = newAccessorSymbol(accessed, accessorName, accessorInfo)
102+
val acc = newAccessorSymbol(accessorClass, accessorName, accessorInfo, accessed.pos)
84103
accessedBy(acc) = accessed
85104
acc
86105
}

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

Lines changed: 1 addition & 25 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
@@ -327,23 +326,6 @@ object Erasure {
327326
if (tree.isTerm) erasedRef(tp) else valueErasure(tp)
328327
}
329328

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-
347329
override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
348330
assert(tree.hasType)
349331
val erasedTp = erasedType(tree)
@@ -378,9 +360,6 @@ object Erasure {
378360
else
379361
super.typedLiteral(tree)
380362

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

464-
ProtectedAccessors.insert.accessorIfNeeded(recur(typed(tree.qualifier, AnySelectionProto)))
443+
recur(typed(tree.qualifier, AnySelectionProto))
465444
}
466445

467-
override def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree =
468-
ProtectedAccessors.insert.accessorIfNeeded(super.typedAssign(tree, pt))
469-
470446
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =
471447
if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree)
472448
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

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
12+
/** Add accessors for all protected accesses. An accessor is needed if
13+
* according to the rules of the JVM a protected class member is not accesissible
14+
* from the point of access, but is accessible if the access is from an enclosing
15+
* class. In this point a public access method is placed in that enclosing class.
16+
*/
17+
class ProtectedAccessors extends MiniPhase {
18+
import ast.tpd._
19+
20+
override def phaseName = ProtectedAccessors.name
21+
22+
object Accessors extends AccessProxies {
23+
def getterName = ProtectedAccessorName
24+
def setterName = ProtectedSetterName
25+
26+
val insert = new Insert {
27+
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean = {
28+
def insideBoundary = {
29+
if (sym.is(JavaDefined)) {
30+
sym.is(JavaStatic) || // Java's static protected definitions are treated as public
31+
ctx.owner.enclosingPackageClass == sym.enclosingPackageClass
32+
}
33+
else {
34+
// For Scala-defined symbols we currently allow private and protected accesses
35+
// from inner packages, and compensate by widening accessibility of such symbols to public.
36+
// It would be good if we could revisit this at some point.
37+
val boundary = sym.accessBoundary(sym.enclosingPackageClass)
38+
ctx.owner.isContainedIn(boundary) || ctx.owner.isContainedIn(boundary.linkedClass)
39+
}
40+
}
41+
sym.isTerm && sym.is(Protected) &&
42+
!sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public
43+
!insideBoundary &&
44+
!ctx.owner.enclosingClass.derivesFrom(sym.owner)
45+
}
46+
}
47+
}
48+
49+
override def prepareForAssign(tree: Assign)(implicit ctx: Context) = tree.lhs match {
50+
case tree: RefTree if Accessors.insert.needsAccessor(tree.symbol) =>
51+
ctx.fresh.setTree(tree)
52+
case _ =>
53+
ctx
54+
}
55+
56+
def isLHS(tree: RefTree)(implicit ctx: Context) = ctx.tree match {
57+
case Assign(lhs, _) => tree.symbol == lhs.symbol
58+
case _ => false
59+
}
60+
61+
override def transformIdent(tree: Ident)(implicit ctx: Context): Tree =
62+
if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree)
63+
64+
override def transformSelect(tree: Select)(implicit ctx: Context): Tree =
65+
if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree)
66+
67+
override def transformAssign(tree: Assign)(implicit ctx: Context): Tree =
68+
Accessors.insert.accessorIfNeeded(tree)
69+
70+
override def transformTemplate(tree: Template)(implicit ctx: Context): Tree =
71+
cpy.Template(tree)(body = Accessors.addAccessorDefs(tree.symbol.owner, tree.body))
72+
}
73+
object ProtectedAccessors { val name = "protectedAccessors" }
74+

tests/neg/protectedacc.scala

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package p {
2+
package a {
3+
4+
/** Test type parameters */
5+
abstract class PolyA[a] {
6+
protected def m(x: a): Unit;
7+
8+
class B {
9+
10+
trait Node {
11+
def s: String = "";
12+
}
13+
protected def tie(x: Node): Unit = { x.s; () }
14+
}
15+
}
16+
}
17+
18+
package b {
19+
import a._;
20+
21+
abstract class X[T] extends PolyA[T] {
22+
23+
trait Inner extends B {
24+
def self: T;
25+
def self2: Node;
26+
def getB: Inner;
27+
28+
m(self)
29+
30+
trait InnerInner {
31+
val g = getB
32+
g.tie(self2.asInstanceOf[g.Node]) // error: acess not permitted
33+
}
34+
}
35+
}
36+
}
37+
}

tests/run/protectedacc.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ package p {
143143

144144
trait InnerInner {
145145
val g = getB
146-
g.tie(self2.asInstanceOf[g.Node])
146+
//g.tie(self2.asInstanceOf[g.Node]) -- this would be a static error
147147
}
148148
}
149149
}

0 commit comments

Comments
 (0)