Skip to content

More changes to protected accessors #4603

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

Merged
merged 4 commits into from
Jun 3, 2018
Merged
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
14 changes: 9 additions & 5 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -836,19 +836,23 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
tree.select(defn.Boolean_||).appliedTo(that)

/** The translation of `tree = rhs`.
* This is either the tree as an assignment, to a setter call.
* This is either the tree as an assignment, or a setter call.
*/
def becomes(rhs: Tree)(implicit ctx: Context): Tree =
if (tree.symbol is Method) {
val setter = tree.symbol.setter
assert(setter.exists, tree.symbol.showLocated)
def becomes(rhs: Tree)(implicit ctx: Context): Tree = {
val sym = tree.symbol
if (sym is Method) {
val setter = sym.setter.orElse {
assert(sym.name.isSetterName && sym.info.firstParamTypes.nonEmpty)
sym
}
val qual = tree match {
case id: Ident => desugarIdentPrefix(id)
case Select(qual, _) => qual
}
qual.select(setter).appliedTo(rhs)
}
else Assign(tree, rhs)
}

/** A synthetic select with that will be turned into an outer path by ExplicitOuter.
* @param levels How many outer levels to select
Expand Down
6 changes: 2 additions & 4 deletions compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,8 @@ object NameKinds {

val SuperAccessorName = new PrefixNameKind(SUPERACCESSOR, "super$")
val InitializerName = new PrefixNameKind(INITIALIZER, "initial$")
val ProtectedGetterName = new PrefixNameKind(PROTECTEDGETTER, "protected_get$")
val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected_set$")
val InlineGetterName = new PrefixNameKind(INLINEGETTER, "inline_get$")
val InlineSetterName = new PrefixNameKind(INLINESETTER, "inline_set$")
val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
val InlineAccessorName = new PrefixNameKind(INLINEACCESSOR, "inline$")

val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
val DirectMethodName = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true }
Expand Down
10 changes: 3 additions & 7 deletions compiler/src/dotty/tools/dotc/core/NameTags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ object NameTags extends TastyFormat.NameTags {
// outer accessor that will be filled in by ExplicitOuter.
// <num> indicates the number of hops needed to select the outer field.

final val PROTECTEDGETTER = 24 // The name of a protected getter `protected_get$<name>` created by ProtectedAccessors.

final val PROTECTEDSETTER = 25 // The name of a protected setter `protected_set$<name>` created by ProtectedAccessors.
final val PROTECTEDACCESSOR = 24 // The name of a protected accesor `protected$<name>` created by ProtectedAccessors.

final val INITIALIZER = 26 // A mixin initializer method

Expand Down Expand Up @@ -51,10 +49,8 @@ object NameTags extends TastyFormat.NameTags {
case OUTERSELECT => "OUTERSELECT"

case SUPERACCESSOR => "SUPERACCESSOR"
case INLINEGETTER => "INLINEGETTER"
case INLINESETTER => "INLINESETTER"
case PROTECTEDGETTER => "PROTECTEDGETTER"
case PROTECTEDSETTER => "PROTECTEDSETTER"
case INLINEACCESSOR => "INLINEACCESSOR"
case PROTECTEDACCESSOR => "PROTECTEDACCESSOR"
case INITIALIZER => "INITIALIZER"
case AVOIDCLASH => "AVOIDCLASH"
case DIRECT => "DIRECT"
Expand Down
7 changes: 2 additions & 5 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ Macro-format:
VARIANT Length underlying_NameRef variance_Nat // 0: Contravariant, 1: Covariant

SUPERACCESSOR Length underlying_NameRef
INLINEGETTER Length underlying_NameRef
INLINESETTER Length underlying_NameRef
INLINEACCESSOR Length underlying_NameRef
OBJECTCLASS Length underlying_NameRef

SIGNED Length original_NameRef resultSig_NameRef paramSig_NameRef*
Expand Down Expand Up @@ -252,9 +251,7 @@ object TastyFormat {

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

final val INLINEGETTER = 21 // The name of an inline getter `inline_get$name`

final val INLINESETTER = 22 // The name of an inline setter `inline_set$name`
final val INLINEACCESSOR = 21 // The name of an inline accessor `inline$name`

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

Expand Down
79 changes: 44 additions & 35 deletions compiler/src/dotty/tools/dotc/transform/AccessProxies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Contexts.Context
import Symbols._
import Flags._
import Names._
import NameOps._
import Decorators._
import TypeUtils._
import Annotations.Annotation
Expand All @@ -23,9 +24,6 @@ abstract class AccessProxies {
import ast.tpd._
import AccessProxies._

def getterName: ClassifiedNameKind
def setterName: ClassifiedNameKind

/** accessor -> accessed */
private val accessedBy = newMutableSymbolMap[Symbol]

Expand All @@ -38,7 +36,7 @@ abstract class AccessProxies {
polyDefDef(accessor.asTerm, tps => argss => {
val accessRef = ref(TermRef(cls.thisType, accessed))
val rhs =
if (accessor.name.is(setterName) &&
if (accessor.name.isSetterName &&
argss.nonEmpty && argss.head.nonEmpty) // defensive conditions
accessRef.becomes(argss.head.head)
else
Expand All @@ -49,34 +47,60 @@ abstract class AccessProxies {
/** Add all needed accessors to the `body` of class `cls` */
def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] = {
val accDefs = accessorDefs(cls)
transforms.println(i"add accessors for $cls: $accDefs%, %")
if (accDefs.isEmpty) body else body ++ accDefs
}

trait Insert {
import ast.tpd._

def accessorNameKind: ClassifiedNameKind
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean

/** A fresh accessor symbol */
def newAccessorSymbol(owner: Symbol, name: TermName, info: Type, pos: Position)(implicit ctx: Context): TermSymbol = {
private def newAccessorSymbol(owner: Symbol, name: TermName, info: Type, pos: Position)(implicit ctx: Context): TermSymbol = {
val sym = ctx.newSymbol(owner, name, Synthetic | Method, info, coord = pos).entered
if (sym.allOverriddenSymbols.exists(!_.is(Deferred))) sym.setFlag(Override)
sym
}

/** An accessor symbol, create a fresh one unless one exists already */
private def accessorSymbol(owner: Symbol, accessorName: TermName, accessorInfo: Type, accessed: Symbol)(implicit ctx: Context) = {
def refersToAccessed(sym: Symbol) = accessedBy.get(sym) == Some(accessed)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accessedBy.get(sym).contains(accessed)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes. I'll roll that into another PR.

owner.info.decl(accessorName).suchThat(refersToAccessed).symbol.orElse {
val acc = newAccessorSymbol(owner, accessorName, accessorInfo, accessed.pos)
accessedBy(acc) = accessed
acc
}
}

/** Rewire reference to refer to `accessor` symbol */
private def rewire(reference: RefTree, accessor: Symbol)(implicit ctx: Context): Tree = {
reference match {
case Select(qual, _) => qual.select(accessor)
case Ident(name) => ref(accessor)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to desugar the Identifier here like in tpd.becomes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, since we always to refer to given accessor.

}
}.withPos(reference.pos)

/** Given a reference to a getter accessor, the corresponding setter reference */
def useSetter(getterRef: RefTree)(implicit ctx: Context): Tree = {
val getter = getterRef.symbol
val accessed = accessedBy(getter)
val accessedName = accessed.name.asTermName
val setterName = accessorNameKind(accessedName.setterName)
val setterInfo = MethodType(getter.info.widenExpr :: Nil, defn.UnitType)
val setter = accessorSymbol(getter.owner, setterName, setterInfo, accessed)
rewire(getterRef, setter)
}

/** Create an accessor unless one exists already, and replace the original
* access with a reference to the accessor.
*
* @param reference The original reference to the non-public symbol
* @param onLHS The reference is on the left-hand side of an assignment
*/
def useAccessor(reference: RefTree, onLHS: Boolean)(implicit ctx: Context): Tree = {

def nameKind = if (onLHS) setterName else getterName
def useAccessor(reference: RefTree)(implicit ctx: Context): Tree = {
val accessed = reference.symbol.asTerm

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

var accessorClass = hostForAccessorOf(accessed: Symbol)
if (!accessorClass.exists) {
val curCls = ctx.owner.enclosingClass
Expand All @@ -85,27 +109,11 @@ abstract class AccessProxies {
reference.pos)
accessorClass = curCls
}

val accessorRawInfo =
if (onLHS) MethodType(accessed.info :: Nil, defn.UnitType)
else accessed.info.ensureMethodic
val accessorName = accessorNameKind(accessed.name)
val accessorInfo =
accessorRawInfo.asSeenFrom(accessorClass.thisType, accessed.owner)
val accessorName = nameKind(accessed.name)

val accessorSymbol =
accessorClass.info.decl(accessorName).suchThat(refersToAccessed).symbol
.orElse {
val acc = newAccessorSymbol(accessorClass, accessorName, accessorInfo, accessed.pos)
accessedBy(acc) = accessed
acc
}

{ reference match {
case Select(qual, _) => qual.select(accessorSymbol)
case Ident(name) => ref(accessorSymbol)
}
}.withPos(reference.pos)
accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner)
val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed)
rewire(reference, accessor)
}

/** Replace tree with a reference to an accessor if needed */
Expand All @@ -115,15 +123,16 @@ abstract class AccessProxies {
ctx.error("Implementation restriction: cannot use private constructors in inline methods", tree.pos)
tree // TODO: create a proper accessor for the private constructor
}
else useAccessor(tree, onLHS = false)
case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) =>
cpy.Apply(tree)(useAccessor(lhs, onLHS = true), List(rhs))
else useAccessor(tree)
case _ =>
tree
}
}
}
object AccessProxies {
/** Where an accessor for the `accessed` symbol should be placed.
* This is the closest enclosing class that has `accessed` as a member.
*/
def hostForAccessorOf(accessed: Symbol)(implicit ctx: Context): Symbol =
ctx.owner.ownersIterator.findSymbol(_.derivesFrom(accessed.owner))
}
}
33 changes: 14 additions & 19 deletions compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import core.Flags._
import core.Decorators._
import MegaPhase.MiniPhase
import ast.Trees._
import util.Property

/** Add accessors for all protected accesses. An accessor is needed if
* according to the rules of the JVM a protected class member is not accesissible
Expand All @@ -18,8 +17,6 @@ import util.Property
object ProtectedAccessors {
val name = "protectedAccessors"

private val LHS = new Property.StickyKey[Unit]

/** Is the current context's owner inside the access boundary established by `sym`? */
def insideBoundaryOf(sym: Symbol)(implicit ctx: Context): Boolean = {
if (sym.is(JavaDefined)) {
Expand Down Expand Up @@ -56,32 +53,30 @@ class ProtectedAccessors extends MiniPhase {
override def phaseName = ProtectedAccessors.name

object Accessors extends AccessProxies {
def getterName = ProtectedGetterName
def setterName = ProtectedSetterName

val insert = new Insert {
def accessorNameKind = ProtectedAccessorName
def needsAccessor(sym: Symbol)(implicit ctx: Context) = ProtectedAccessors.needsAccessor(sym)
}
}

override def prepareForAssign(tree: Assign)(implicit ctx: Context) = {
tree.lhs match {
case lhs: RefTree if needsAccessor(lhs.symbol) => lhs.putAttachment(LHS, ())
case _ =>
}
ctx
}

private def isLHS(tree: RefTree) = tree.removeAttachment(LHS).isDefined

override def transformIdent(tree: Ident)(implicit ctx: Context): Tree =
if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree)
Accessors.insert.accessorIfNeeded(tree)

override def transformSelect(tree: Select)(implicit ctx: Context): Tree =
if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree)
Accessors.insert.accessorIfNeeded(tree)

override def transformAssign(tree: Assign)(implicit ctx: Context): Tree =
Accessors.insert.accessorIfNeeded(tree)
tree.lhs match {
case lhs: RefTree =>
lhs.name match {
case ProtectedAccessorName(name) =>
cpy.Apply(tree)(Accessors.insert.useSetter(lhs), tree.rhs :: Nil)
case _ =>
tree
}
case _ =>
tree
}

override def transformTemplate(tree: Template)(implicit ctx: Context): Tree =
cpy.Template(tree)(body = Accessors.addAccessorDefs(tree.symbol.owner, tree.body))
Expand Down
24 changes: 19 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import StdNames.nme
import Contexts.Context
import Names.{Name, TermName, EmptyTermName}
import NameOps._
import NameKinds.{ClassifiedNameKind, InlineGetterName, InlineSetterName}
import NameKinds.{ClassifiedNameKind, InlineAccessorName}
import ProtoTypes.selectionProto
import SymDenotations.SymDenotation
import Annotations._
Expand All @@ -32,13 +32,12 @@ object Inliner {
import tpd._

class InlineAccessors extends AccessProxies {
def getterName = InlineGetterName
def setterName = InlineSetterName

/** A tree map which inserts accessors for all non-public term members accessed
* from inlined code. Accessors are collected in the `accessors` buffer.
*/
class MakeInlineable(inlineSym: Symbol) extends TreeMap with Insert {
def accessorNameKind = InlineAccessorName

/** A definition needs an accessor if it is private, protected, or qualified private
* and it is not part of the tree that gets inlined. The latter test is implemented
Expand All @@ -54,8 +53,23 @@ object Inliner {
// This is quite tricky, as such types can appear anywhere, including as parts
// of types of other things. For the moment we do nothing and complain
// at the implicit expansion site if there's a reference to an inaccessible type.
override def transform(tree: Tree)(implicit ctx: Context): Tree =
super.transform(accessorIfNeeded(tree))
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
case tree: Assign =>
transform(tree.lhs) match {
case lhs1: RefTree =>
lhs1.name match {
case InlineAccessorName(name) =>
cpy.Apply(tree)(useSetter(lhs1), transform(tree.rhs) :: Nil)
case _ =>
super.transform(tree)
}
case _ =>
super.transform(tree)
}
case _ =>
super.transform(accessorIfNeeded(tree))
}

}

/** Adds accessors for all non-public term members accessed
Expand Down