aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-07-01 14:43:18 +0200
committerMartin Odersky <odersky@gmail.com>2014-07-17 11:01:59 +0200
commit3ab2784948d084557e88cd7eb5c55a29613742d0 (patch)
tree62ce5b62fc08afc070e9e705687e9fa05a6e1947 /src
parent8112a39d3a00f53a68af794d0a83cf995faf31e2 (diff)
downloaddotty-3ab2784948d084557e88cd7eb5c55a29613742d0.tar.gz
dotty-3ab2784948d084557e88cd7eb5c55a29613742d0.tar.bz2
dotty-3ab2784948d084557e88cd7eb5c55a29613742d0.zip
Added phase: SuperAccessors
Rewrote SuperAccessors (more to be done; see comments), and added stuff here and there to make it work smoother.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala1
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala9
-rw-r--r--src/dotty/tools/dotc/core/Signature.scala2
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala45
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala12
-rw-r--r--src/dotty/tools/dotc/core/Types.scala9
-rw-r--r--src/dotty/tools/dotc/core/pickling/ClassfileParser.scala2
-rw-r--r--src/dotty/tools/dotc/transform/MacroTransform.scala15
-rw-r--r--src/dotty/tools/dotc/transform/SuperAccessors.scala557
10 files changed, 639 insertions, 14 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index a4a8fbbc8..55452d6ff 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -20,6 +20,7 @@ class Compiler {
def phases: List[List[Phase]] =
List(
List(new FrontEnd),
+ List(new SuperAccessors),
List(new LazyValsCreateCompanionObjects,
new TailRec), //force separataion between lazyVals and LVCreateCO
List(new PatternMatcher,
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 264f9aa46..120f8e0f8 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -574,6 +574,7 @@ object Denotations {
/** Install this denotation to be the result of the given denotation transformer.
* This is the implementation of the same-named method in SymDenotations.
* It's placed here because it needs access to private fields of SingleDenotation.
+ * @pre Can only be called in `phase.next`.
*/
protected def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = {
val targetId = phase.next.id
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index 7c10bfd4d..404a0844a 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -132,6 +132,9 @@ object NameOps {
if (flags is (ModuleClass, butNot = Package)) name.asTypeName.moduleClassName.asInstanceOf[N]
else name
+ /** The superaccessor for method with given name */
+ def superName: TermName = (nme.SUPER_PREFIX ++ name).toTermName
+
/** The expanded name of `name` relative to this class `base` with given `separator`
*/
def expandedName(base: Symbol, separator: Name = nme.EXPAND_SEPARATOR)(implicit ctx: Context): N = {
@@ -255,11 +258,11 @@ object NameOps {
/** The name of an accessor for protected symbols. */
def protectedAccessorName: TermName =
- PROTECTED_PREFIX ++ name
+ PROTECTED_PREFIX ++ name.unexpandedName()
/** The name of a setter for protected symbols. Used for inherited Java fields. */
- def protectedSetterName(name: Name): TermName =
- PROTECTED_SET_PREFIX ++ name
+ def protectedSetterName: TermName =
+ PROTECTED_SET_PREFIX ++ name.unexpandedName()
def moduleVarName: TermName =
name ++ MODULE_VAR_SUFFIX
diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala
index eb85fbb99..22d038d11 100644
--- a/src/dotty/tools/dotc/core/Signature.scala
+++ b/src/dotty/tools/dotc/core/Signature.scala
@@ -49,7 +49,7 @@ object Signature {
* a type different from PolyType, MethodType, or ExprType.
*/
val NotAMethod = Signature(List(), EmptyTypeName)
-
+
/** The signature of an overloaded denotation.
*/
val OverloadedSignature = Signature(List(tpnme.OVERLOADED), EmptyTypeName)
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 802762045..643237038 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -395,6 +395,14 @@ object SymDenotations {
/** Is this a user defined "def" method? Excluded are accessors. */
final def isSourceMethod(implicit ctx: Context) = this is (Method, butNot = Accessor)
+ /** This this a method in a value class that is implemented as an extension method? */
+ final def isMethodWithExtension(implicit ctx: Context) =
+ isSourceMethod &&
+ owner.isDerivedValueClass &&
+ !isConstructor &&
+ !is(SuperAccessor) &&
+ !is(Macro)
+
/** Is this a setter? */
final def isGetter(implicit ctx: Context) = (this is Accessor) && !originalName.isSetterName
@@ -447,7 +455,7 @@ object SymDenotations {
def accessWithin(boundary: Symbol) =
ctx.owner.isContainedIn(boundary) &&
(!(this is JavaDefined) || // disregard package nesting for Java
- ctx.owner.enclosingPackage == boundary.enclosingPackage)
+ ctx.owner.enclosingPackageClass == boundary.enclosingPackageClass)
/** Are we within definition of linked class of `boundary`? */
def accessWithinLinked(boundary: Symbol) = {
@@ -572,6 +580,12 @@ object SymDenotations {
NoSymbol
}
+ /** The field accessed by this getter or setter */
+ def accessedField(implicit ctx: Context): Symbol = {
+ val fieldName = if (isSetter) name.asTermName.setterToGetter else name
+ owner.info.decl(fieldName).suchThat(d => !(d is Method)).symbol
+ }
+
/** The chain of owners of this denotation, starting with the denoting symbol itself */
final def ownersIterator(implicit ctx: Context) = new Iterator[Symbol] {
private[this] var current = symbol
@@ -624,8 +638,8 @@ object SymDenotations {
}
/** The package class containing this denotation */
- final def enclosingPackage(implicit ctx: Context): Symbol =
- if (this is PackageClass) symbol else owner.enclosingPackage
+ final def enclosingPackageClass(implicit ctx: Context): Symbol =
+ if (this is PackageClass) symbol else owner.enclosingPackageClass
/** The module object with the same (term-) name as this class or module class,
* and which is also defined in the same scope and compilation unit.
@@ -747,7 +761,6 @@ object SymDenotations {
loop(base.info.baseClasses.dropWhile(owner != _).tail)
}
-
/** A a member of class `base` is incomplete if
* (1) it is declared deferred or
* (2) it is abstract override and its super symbol in `base` is
@@ -895,6 +908,15 @@ object SymDenotations {
case _ => Nil
}
+ /** The symbol of the superclass, NoSymbol if no superclass exists */
+ def superClass(implicit ctx: Context): Symbol = classParents match {
+ case parent :: _ =>
+ val cls = parent.classSymbol
+ if (cls is Trait) NoSymbol else cls
+ case _ =>
+ NoSymbol
+ }
+
/** The denotation is fully completed: all attributes are fully defined.
* ClassDenotations compiled from source are first completed, then fully completed.
* @see Namer#ClassCompleter
@@ -1292,6 +1314,21 @@ object SymDenotations {
def underlyingOfValueClass: Type = ???
def valueClassUnbox: Symbol = ???
+
+ /** If this class has the same `decls` scope reference in `phase` and
+ * `phase.next`, install a new denotation with a cloned scope in `phase.next`.
+ * @pre Can only be called in `phase.next`.
+ */
+ def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = {
+ assert(ctx.phaseId == phase.next.id)
+ val prevCtx = ctx.withPhase(phase)
+ val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo
+ if (classInfo(prevCtx).decls eq decls) {
+ copySymDenotation(
+ info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo),
+ initFlags = this.flags &~ Frozen).installAfter(phase)
+ }
+ }
}
/** The denotation of a package class.
diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala
index 26553ddff..cfd5bdf23 100644
--- a/src/dotty/tools/dotc/core/Symbols.scala
+++ b/src/dotty/tools/dotc/core/Symbols.scala
@@ -16,6 +16,7 @@ import printing.Printer
import Types._
import Annotations._
import util.Positions._
+import DenotTransformers._
import StdNames._
import NameOps._
import ast.tpd.{TreeTypeMap, Tree}
@@ -372,6 +373,17 @@ object Symbols {
this
}
+ /** Enter this symbol in its class owner after given `phase`. Create a fresh
+ * denotation for its owner class if the class has not yet already one
+ * that starts being valid after `phase`.
+ * @pre Symbol is a class member
+ */
+ def enteredAfter(phase: DenotTransformer)(implicit ctx: Context): this.type = {
+ val nextCtx = ctx.withPhase(phase.next)
+ this.owner.asClass.ensureFreshScopeAfter(phase)(nextCtx)
+ entered(nextCtx)
+ }
+
/** This symbol, if it exists, otherwise the result of evaluating `that` */
def orElse(that: => Symbol)(implicit ctx: Context) =
if (this.exists) this else that
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index dad88bc60..a92b252b5 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -1609,10 +1609,15 @@ object Types {
protected def computeSignature(implicit ctx: Context): Signature
- protected def resultSignature(implicit ctx: Context) = resultType match {
+ protected def resultSignature(implicit ctx: Context) = try resultType match {
case rtp: SignedType => rtp.signature
case tp => Signature(tp, isJava = false)
}
+ catch {
+ case ex: AssertionError =>
+ println(i"failure while taking result signture of $resultType")
+ throw ex
+ }
final override def signature(implicit ctx: Context): Signature = {
if (ctx.runId != mySignatureRunId) {
@@ -1717,6 +1722,8 @@ object Types {
def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType
def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType =
apply(paramNames, paramTypes)(_ => resultType)
+ def apply(paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType =
+ apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp)
def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType =
apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType)
def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = {
diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
index 0ed301732..0f0747597 100644
--- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
+++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
@@ -752,7 +752,7 @@ class ClassfileParser(
private def setPrivateWithin(denot: SymDenotation, jflags: Int)(implicit ctx: Context): Unit = {
if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC)) == 0)
- denot.privateWithin = denot.enclosingPackage
+ denot.privateWithin = denot.enclosingPackageClass
}
private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0
diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala
index 4113b2d8e..eacbd1717 100644
--- a/src/dotty/tools/dotc/transform/MacroTransform.scala
+++ b/src/dotty/tools/dotc/transform/MacroTransform.scala
@@ -21,9 +21,17 @@ abstract class MacroTransform extends Phase {
unit.tpdTree = newTransformer.transform(unit.tpdTree)
}
- def newTransformer: TransformerMap
+ protected def newTransformer(implicit ctx: Context): Transformer
- class TransformerMap extends TreeMap {
+ class Transformer extends TreeMap {
+
+ protected def localCtx(tree: Tree)(implicit ctx: Context) =
+ ctx.fresh.setTree(tree).setOwner(tree.symbol)
+
+ /** The current enclosing class
+ * @pre We must be inside a class
+ */
+ def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass
def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = {
val exprCtx = ctx.withOwner(exprOwner)
@@ -36,10 +44,9 @@ abstract class MacroTransform extends Phase {
}
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
- def localCtx = ctx.fresh.setTree(tree).setOwner(tree.symbol)
tree match {
case _: PackageDef | _: MemberDef =>
- super.transform(tree)(localCtx)
+ super.transform(tree)(localCtx(tree))
case Template(constr, parents, self, body) =>
cpy.Template(tree,
transformSub(constr),
diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala
new file mode 100644
index 000000000..fd784862e
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala
@@ -0,0 +1,557 @@
+package dotty.tools.dotc
+package transform
+
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
+import dotty.tools.dotc.ast.{Trees, tpd}
+import scala.collection.{ mutable, immutable }
+import mutable.ListBuffer
+import scala.annotation.tailrec
+import core._
+import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._
+import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._
+import util.Positions._
+import Decorators._
+import Symbols._
+
+/** This phase performs the following functions, each of which could be split out in a
+ * mini-phase:
+ *
+ * (1) Adds super accessors for all super calls that either
+ * appear in a trait or have as a target a member of some outer class.
+ *
+ * (2) Converts parameter fields that have the same name as a corresponding
+ * public parameter field in a superclass to a forwarder to the superclass
+ * field (corresponding = super class field is initialized with subclass field)
+ *
+ * (3) Adds protected accessors if the access to the protected member happens
+ * in a class which is not a subclass of the member's owner.
+ *
+ * (4) Finally, the phase used to mangle the names of class-members which are
+ * private up to an enclosing non-package class, in order to avoid overriding conflicts.
+ * This is currently disabled, and class-qualified private is deprecated.
+ *
+ * It also checks that:
+ *
+ * (1) Symbols accessed from super are not abstract, or are overridden by
+ * an abstract override.
+ *
+ * (2) If a symbol accessed accessed from super is defined in a real class (not a trait),
+ * there are no abstract members which override this member in Java's rules
+ * (see SI-4989; such an access would lead to illegal bytecode)
+ *
+ * (3) Super calls do not go to some synthetic members of Any (see isDisallowed)
+ *
+ * (4) Super calls do not go to synthetic field accessors
+ *
+ * (5) A class and its companion object do not both define a class or module with the
+ * same name.
+ *
+ * TODO: Rename phase to "Accessors" because it handles more than just super accessors
+ */
+class SuperAccessors extends MacroTransform with DenotTransformer { thisTransformer =>
+
+ import tpd._
+
+ /** the following two members override abstract members in Transform */
+ val name: String = "superaccessors"
+
+ protected def newTransformer(implicit ctx: Context): Transformer =
+ new SuperAccTransformer
+
+ /** No transformation here, but new denotations are installed by the tree traversal */
+ def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref
+
+ class SuperAccTransformer extends Transformer {
+
+ /** validCurrentOwner arrives undocumented, but I reverse engineer it to be
+ * a flag for needsProtectedAccessor which is false while transforming either
+ * a by-name argument block or a closure. This excludes them from being
+ * considered able to access protected members via subclassing (why?) which in turn
+ * increases the frequency with which needsProtectedAccessor will be true.
+ */
+ private var validCurrentOwner = true
+
+ private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]()
+
+ private def storeAccessorDefinition(clazz: Symbol, tree: Tree) = {
+ val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for "+clazz))
+ buf += tree
+ }
+
+ private def ensureSigned(tpe: Type)(implicit ctx: Context) = tpe match {
+ case tpe: SignedType => tpe
+ case _ => ExprType(tpe)
+ }
+
+ private def ensureAccessor(sel: Select)(implicit ctx: Context) = {
+ val Select(qual, name) = sel
+ val sym = sel.symbol
+ val clazz = qual.symbol.asClass
+ val supername = name.superName
+
+ val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse {
+ ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz")
+ val acc = ctx.newSymbol(
+ clazz, supername, SuperAccessor | Private | Artifact,
+ ensureSigned(sel.tpe.widenSingleton), coord = sym.coord).enteredAfter(thisTransformer)
+ // Diagnostic for SI-7091
+ if (!accDefs.contains(clazz))
+ ctx.error(s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz is Package}. Accessor required for ${sel} (${sel.show})", sel.pos)
+ else storeAccessorDefinition(clazz, DefDef(acc, EmptyTree))
+ acc
+ }
+
+ Select(This(clazz), superAcc) withPos sel.pos
+ }
+
+ private def transformArgs(formals: List[Type], args: List[Tree])(implicit ctx: Context) =
+ args.zipWithConserve(formals) {(arg, formal) =>
+ formal match {
+ case _: ExprType => withInvalidOwner(transform(arg))
+ case _ => transform(arg)
+ }
+ }
+
+ /** Check that a class and its companion object to not both define
+ * a class or module with same name
+ */
+ private def checkCompanionNameClashes(cls: ClassSymbol)(implicit ctx: Context): Unit =
+ if (!(cls.owner is ModuleClass)) {
+ val other = cls.owner.linkedClass.info.decl(cls.name)
+ if (other.symbol.isClass)
+ ctx.error(s"name clash: ${cls.owner} defines $cls" + "\n" +
+ s"and its companion ${cls.owner.companionModule} also defines $other",
+ cls.pos)
+ }
+
+ /** Expand all declarations in this class which are private within a class.
+ * Note: It's not sure whether this is the right way. Persumably, we expand
+ * qualified privates to prvent them from overriding or be overridden by
+ * symbols that are defined in classes where the qualified private is not
+ * visible. But it seems a bit dubiuous to do this between type checking
+ * and refchecks.
+ */
+ def expandQualifiedPrivates(cls: ClassSymbol)(implicit ctx: Context) = {
+ val decls = cls.info.decls
+ val decls1: MutableScope = newScope
+ def needsExpansion(sym: Symbol) =
+ sym.privateWithin.isClass &&
+ !(sym is Protected) &&
+ !(sym.privateWithin is ModuleClass) &&
+ !(sym is ExpandedName) &&
+ !sym.isConstructor
+ val nextCtx = ctx.withPhase(thisTransformer.next)
+ for (s <- decls) {
+ // !!! hacky to do this by mutation; would be better to do with an infotransformer
+ // !!! also, why is this done before pickling?
+ if (needsExpansion(s)) {
+ ctx.deprecationWarning(s"private qualified with a class has been deprecated, use package enclosing ${s.privateWithin} instead", s.pos)
+ /* disabled for now
+ decls.asInstanceOf[MutableScope].unlink(s)
+ s.copySymDenotation(name = s.name.expandedName(s.privateWithin))
+ .installAfter(thisTransformer)
+ decls1.enter(s)(nextCtx)
+ ctx.log(i"Expanded ${s.name}, ${s.name(nextCtx)}, sym")
+ */
+ }
+ }
+ /* Disabled for now:
+ if (decls1.nonEmpty) {
+ for (s <- decls)
+ if (!needsExpansion(s)) decls1.enter(s)(nextCtx)
+ val ClassInfo(pre, _, ps, _, selfInfo) = cls.classInfo
+ cls.copySymDenotation(info = ClassInfo(pre, cls, ps, decls1, selfInfo))
+ .installAfter(thisTransformer)
+ }
+ */
+ }
+
+ private def transformSuperSelect(sel: Select)(implicit ctx: Context): Tree = {
+ val Select(sup @ Super(_, mix), name) = sel
+ val sym = sel.symbol
+ assert(sup.symbol.exists, s"missing symbol in $sel: ${sup.tpe}")
+ val clazz = sup.symbol.asClass
+
+ if (sym is Deferred) {
+ val member = sym.overridingSymbol(clazz)
+ if (mix != tpnme.EMPTY ||
+ !member.exists ||
+ !(member is AbsOverride) && member.isIncompleteIn(clazz))
+ ctx.error(
+ i"${sym.showLocated} is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override'",
+ sel.pos)
+ }
+ else if (mix == tpnme.EMPTY && !(sym.owner is Trait))
+ // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract.
+ for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) {
+ val overriding = sym.overridingSymbol(intermediateClass)
+ if ((overriding is (Deferred, butNot = AbsOverride)) && !(overriding.owner is Trait))
+ ctx.error(
+ s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract",
+ sel.pos)
+
+ }
+ if (name.isTermName && mix == tpnme.EMPTY &&
+ ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentOwner))
+ ensureAccessor(sel)(ctx.withPhase(thisTransformer.next))
+ else sel
+ }
+
+ // Disallow some super.XX calls targeting Any methods which would
+ // otherwise lead to either a compiler crash or runtime failure.
+ private def isDisallowed(sym: Symbol)(implicit ctx: Context) = {
+ val d = defn
+ import d._
+ (sym eq Any_isInstanceOf) ||
+ (sym eq Any_asInstanceOf) ||
+ (sym eq Any_==) ||
+ (sym eq Any_!=) ||
+ (sym eq Any_##)
+ }
+
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = {
+ val sym = tree.symbol
+
+ def mayNeedProtectedAccessor(sel: Select, targs: List[Tree], goToSuper: Boolean) =
+ if (sym.exists && needsProtectedAccessor(sym, tree.pos)) {
+ ctx.debuglog("Adding protected accessor for " + tree)
+ transform(makeAccessor(sel, targs))
+ }
+ else if (goToSuper) super.transform(tree)
+ else tree
+
+ try tree match {
+ // Don't transform patterns or strange trees will reach the matcher (ticket #4062)
+ // TODO Drop once this runs after pattern matcher
+ case CaseDef(pat, guard, body) =>
+ cpy.CaseDef(tree, pat, transform(guard), transform(body))
+
+ case TypeDef(_, _, impl: Template) =>
+ val cls = sym.asClass
+ checkCompanionNameClashes(cls)
+ expandQualifiedPrivates(cls)
+ super.transform(tree)
+
+ case impl: Template =>
+
+ /** For all parameter accessors
+ *
+ * val x: T = ...
+ *
+ * if
+ * (1) x is forwarded in the supercall to a parameter that's also named `x`
+ * (2) the superclass parameter accessor for `x` is accessible from the current class to
+ * change the accessor to
+ *
+ * def x: T = super.x.asInstanceOf[T]
+ *
+ * Do the same also if there are intermediate inaccessible parameter accessor forwarders.
+ * The aim of this transformation is to avoid redundant parameter accessor fields.
+ */
+ def forwardParamAccessors(stats: List[Tree]): List[Tree] = {
+ val (superArgs, superParamNames) = impl.parents match {
+ case superCall @ Apply(fn, args) :: _ =>
+ fn.tpe.widen match {
+ case MethodType(paramNames, _) => (args, paramNames)
+ case _ => (Nil, Nil)
+ }
+ case _ => (Nil, Nil)
+ }
+ def inheritedAccessor(sym: Symbol): Symbol = {
+ val candidate = sym.owner.asClass.superClass
+ .info.decl(sym.name).suchThat(_ is (ParamAccessor, butNot = Mutable)).symbol
+ if (candidate.isAccessibleFrom(currentClass.thisType, superAccess = true)) candidate
+ else if (candidate is Method) inheritedAccessor(candidate)
+ else NoSymbol
+ }
+ def forwardParamAccessor(stat: Tree): Tree = {
+ stat match {
+ case stat: ValDef =>
+ val sym = stat.symbol.asTerm
+ if (sym is (PrivateLocalParamAccessor, butNot = Mutable)) {
+ val idx = superArgs.indexWhere(_.symbol == sym)
+ if (idx >= 0 && superParamNames(idx) == stat.name) { // supercall to like-named parameter
+ val alias = inheritedAccessor(sym)
+ if (alias.exists) {
+ def forwarder(implicit ctx: Context) = {
+ sym.copySymDenotation(initFlags = sym.flags | Method, info = ensureSigned(sym.info))
+ .installAfter(thisTransformer)
+ val superAcc =
+ Select(Super(This(currentClass), tpnme.EMPTY, inConstrCall = false), alias)
+ DefDef(sym, ensureConforms(superAcc, sym.info.widen))
+ }
+ return forwarder(ctx.withPhase(thisTransformer.next))
+ }
+ }
+ }
+ case _ =>
+ }
+ stat
+ }
+ stats map forwardParamAccessor
+ }
+
+ def transformTemplate = {
+ val ownStats = new ListBuffer[Tree]
+ accDefs(currentClass) = ownStats
+ val body1 = forwardParamAccessors(transformStats(impl.body, tree.symbol))
+ accDefs -= currentClass
+ ownStats ++= body1
+ cpy.Template(tree, impl.constr, impl.parents, impl.self, body1)
+ }
+ transformTemplate
+
+ case TypeApply(sel @ Select(This(_), name), args) =>
+ mayNeedProtectedAccessor(sel, args, goToSuper = false)
+
+ case sel @ Select(qual, name) =>
+ def transformSelect = {
+
+ qual match {
+ case This(_) =>
+ // warn if they are selecting a private[this] member which
+ // also exists in a superclass, because they may be surprised
+ // to find out that a constructor parameter will shadow a
+ // field. See SI-4762.
+ /* to be added
+ if (settings.lint) {
+ if (sym.isPrivateLocal && sym.paramss.isEmpty) {
+ qual.symbol.ancestors foreach { parent =>
+ parent.info.decls filterNot (x => x.isPrivate || x.isLocalToThis) foreach { m2 =>
+ if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) {
+ unit.warning(sel.pos,
+ sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name
+ + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within "
+ + sym.owner + " - you may want to give them distinct names.")
+ }
+ }
+ }
+ }
+ }
+ */
+
+ /*
+ * A trait which extends a class and accesses a protected member
+ * of that class cannot implement the necessary accessor method
+ * because its implementation is in an implementation class (e.g.
+ * Foo$class) which inherits nothing, and jvm access restrictions
+ * require the call site to be in an actual subclass. So non-trait
+ * classes inspect their ancestors for any such situations and
+ * generate the accessors. See SI-2296.
+ */
+ // FIXME - this should be unified with needsProtectedAccessor, but some
+ // subtlety which presently eludes me is foiling my attempts.
+ val shouldEnsureAccessor = (
+ (currentClass is Trait)
+ && (sym is Protected)
+ && sym.enclosingClass != currentClass
+ && !(sym.owner is PackageClass) // SI-7091 no accessor needed package owned (ie, top level) symbols
+ && !(sym.owner is Trait)
+ && sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass
+ && qual.symbol.info.member(sym.name).exists
+ && !needsProtectedAccessor(sym, tree.pos))
+ if (shouldEnsureAccessor) {
+ ctx.log("Ensuring accessor for call to protected " + sym.showLocated + " from " + currentClass)
+ ensureAccessor(sel)
+ } else
+ mayNeedProtectedAccessor(sel, Nil, goToSuper = false)
+
+ case Super(_, mix) =>
+ if ((sym.isTerm) && !(sym is Method) || (sym is Accessor)) {
+ ctx.error(s"super may be not be used on ${sym.accessedField orElse sym}", tree.pos)
+ } else if (isDisallowed(sym)) {
+ ctx.error(s"super not allowed here: use this.${name.decode} instead", tree.pos)
+ }
+ transformSuperSelect(sel)
+
+ case _ =>
+ mayNeedProtectedAccessor(sel, Nil, goToSuper = true)
+ }
+ }
+ transformSelect
+
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ val rhs1 = if (sym.isMethodWithExtension) withInvalidOwner(transform(rhs)) else transform(rhs)
+ cpy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1)
+
+ case TypeApply(sel @ Select(qual, name), args) =>
+ mayNeedProtectedAccessor(sel, args, goToSuper = true)
+
+ case Assign(lhs @ Select(qual, name), rhs) =>
+ def transformAssign = {
+ if ((lhs.symbol is Mutable) &&
+ (lhs.symbol is JavaDefined) &&
+ needsProtectedAccessor(lhs.symbol, tree.pos)) {
+ ctx.debuglog("Adding protected setter for " + tree)
+ val setter = makeSetter(lhs)
+ ctx.debuglog("Replaced " + tree + " with " + setter)
+ transform(Apply(setter, qual :: rhs :: Nil))
+ } else
+ super.transform(tree)
+ }
+ transformAssign
+
+ case Apply(fn, args) =>
+ val MethodType(_, formals) = fn.tpe.widen
+ cpy.Apply(tree, transform(fn), transformArgs(formals, args))
+
+ case _ =>
+ super.transform(tree)
+ }
+ catch {
+ case ex : AssertionError =>
+ if (sym != null && sym != NoSymbol)
+ Console.println("TRANSFORM: " + tree.symbol.sourceFile)
+
+ Console.println("TREE: " + tree)
+ throw ex
+ }
+ }
+
+ private def withInvalidOwner[A](trans: => A): A = {
+ val saved = validCurrentOwner
+ validCurrentOwner = false
+ try trans
+ finally validCurrentOwner = saved
+ }
+
+ /** Add a protected accessor, if needed, and return a tree that calls
+ * the accessor and returns the same member. The result is already
+ * typed.
+ * TODO why is targs needed? It looks like we can do without.
+ */
+ private def makeAccessor(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = {
+ val Select(qual, _) = tree
+ val sym = tree.symbol.asTerm
+ val clazz = hostForAccessorOf(sym, currentClass)
+ assert(clazz.exists, sym)
+ ctx.debuglog("Decided for host class: " + clazz)
+
+ val accName = sym.name.protectedAccessorName
+
+ // if the result type depends on the this type of an enclosing class, the accessor
+ // has to take an object of exactly this type, otherwise it's more general
+ val receiverType = if (isThisType(sym.info.finalResultType)) clazz.thisType else clazz.classInfo.selfType
+ val accType = {
+ def accTypeOf(tpe: Type): Type = tpe match {
+ case tpe: PolyType =>
+ tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType))
+ case _ =>
+ MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0)))
+ }
+ accTypeOf(sym.info)
+ }
+ val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse {
+ val newAcc = ctx.newSymbol(
+ clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisTransformer)
+ val code = polyDefDef(newAcc, targs => argss => {
+ val (receiver :: _) :: tail = argss
+ val base = Select(receiver, sym).appliedToTypes(targs)
+ (base /: argss)(Apply(_, _))
+ })
+ ctx.debuglog("created protected accessor: " + code)
+ storeAccessorDefinition(clazz, code)
+ newAcc
+ }
+ val res =
+ Apply(Select(This(clazz), protectedAccessor).appliedToTypeTrees(targs), qual :: Nil)
+ .withPos(tree.pos)
+ ctx.debuglog(s"Replaced $tree with $res")
+ res
+ }
+
+ /** Add an accessor for field, if needed, and return a selection tree for it .
+ * The result is not typed.
+ */
+ private def makeSetter(tree: Select)(implicit ctx: Context): Tree = {
+ val field = tree.symbol.asTerm
+ val clazz = hostForAccessorOf(field, currentClass)
+ assert(clazz.exists, field)
+ ctx.debuglog("Decided for host class: " + clazz)
+
+ val accName = field.name.protectedSetterName
+ val accType = MethodType(clazz.classInfo.selfType :: field.info :: Nil, defn.UnitType)
+ val protectedAccessor = clazz.info.decl(accName).symbol orElse {
+ val newAcc = ctx.newSymbol(
+ clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisTransformer)
+ val code = DefDef(newAcc, argss => {
+ val (receiver :: value :: Nil) :: Nil = argss
+ Assign(Select(receiver, field), value).withPos(tree.pos)
+ })
+ ctx.debuglog("created protected setter: " + code)
+ storeAccessorDefinition(clazz, code)
+ newAcc
+ }
+ Select(This(clazz), protectedAccessor).withPos(tree.pos)
+ }
+
+ /** Does `sym` need an accessor when accessed from `currentClass`?
+ * A special case arises for classes with explicit self-types. If the
+ * self type is a Java class, and a protected accessor is needed, we issue
+ * an error. If the self type is a Scala class, we don't add an accessor.
+ * An accessor is not needed if the access boundary is larger than the
+ * enclosing package, since that translates to 'public' on the host sys.
+ * (as Java has no real package nesting).
+ *
+ * If the access happens inside a 'trait', access is more problematic since
+ * the implementation code is moved to an '$class' class which does not
+ * inherit anything. Since we can't (yet) add accessors for 'required'
+ * classes, this has to be signaled as error.
+ * FIXME Need to better understand this logic
+ */
+ private def needsProtectedAccessor(sym: Symbol, pos: Position)(implicit ctx: Context): Boolean = {
+ val clazz = currentClass
+ val host = hostForAccessorOf(sym, clazz)
+ val selfType = host.classInfo.selfType
+ def accessibleThroughSubclassing =
+ validCurrentOwner && (selfType <:< sym.owner.typeRef) && !clazz.is(Trait)
+
+ val isCandidate = (
+ sym.is(Protected)
+ && sym.is(JavaDefined)
+ && !sym.effectiveOwner.is(Package)
+ && !accessibleThroughSubclassing
+ && (sym.enclosingPackageClass != currentClass.enclosingPackageClass)
+ && (sym.enclosingPackageClass == sym.accessBoundary(sym.enclosingPackageClass))
+ )
+ def isSelfType = !(host.typeRef <:< selfType) && {
+ if (selfType.typeSymbol.is(JavaDefined))
+ ctx.restrictionError(s"cannot accesses protected $sym from within $clazz with self type $selfType", pos)
+ true
+ }
+ def isJavaProtected = host.is(Trait) && sym.is(JavaDefined) && {
+ ctx.restrictionError(
+ s"""$clazz accesses protected $sym inside a concrete trait method.
+ |Add an accessor in a class extending ${sym.enclosingClass} as a workaround.""".stripMargin,
+ pos
+ )
+ true
+ }
+ isCandidate && !host.is(Package) && !isSelfType && !isJavaProtected
+ }
+
+ /** Return the innermost enclosing class C of referencingClass for which either
+ * of the following holds:
+ * - C is a subclass of sym.owner or
+ * - C is declared in the same package as sym's owner
+ */
+ private def hostForAccessorOf(sym: Symbol, referencingClass: ClassSymbol)(implicit ctx: Context): ClassSymbol =
+ if (referencingClass.derivesFrom(sym.owner)
+ || referencingClass.classInfo.selfType <:< sym.owner.typeRef
+ || referencingClass.enclosingPackageClass == sym.owner.enclosingPackageClass) {
+ assert(referencingClass.isClass, referencingClass)
+ referencingClass
+ }
+ else if(referencingClass.owner.enclosingClass.exists)
+ hostForAccessorOf(sym, referencingClass.owner.enclosingClass.asClass)
+ else
+ referencingClass
+
+ /** Is 'tpe' the type of a member of an enclosing class? */
+ private def isThisType(tpe: Type)(implicit ctx: Context): Boolean = tpe match {
+ case ThisType(cls) => !cls.is(PackageClass)
+ case tpe: TypeProxy => isThisType(tpe.underlying)
+ case _ => false
+ }
+ }
+}