summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Flags.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala55
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala41
-rw-r--r--test/files/jvm/protectedacc.check1
-rw-r--r--test/files/jvm/protectedacc.scala16
6 files changed, 93 insertions, 27 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Flags.scala b/src/compiler/scala/tools/nsc/symtab/Flags.scala
index fae47dd0f3..21023ec72c 100644
--- a/src/compiler/scala/tools/nsc/symtab/Flags.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Flags.scala
@@ -61,6 +61,7 @@ object Flags extends Enumeration {
final val MODULEVAR = 0x40000000 // for variables: is the variable caching a module value
final val SYNTHETICMETH = 0x40000000 // for methods: synthetic method, but without SYNTHETIC flag
final val MONOMORPHIC = 0x40000000 // for type symbols: does not have type parameters
+ final val PROTACCESSOR = 0x40000000 // for methods: a protected accessor
final val LAZY = 0x80000000L // symbol is a lazy val. can't have MUTABLE unless transformed by typer
final val IS_ERROR = 0x100000000L // symbol is an error symbol
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index b038dec27a..6481bb07b9 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -306,7 +306,7 @@ trait Symbols {
final def isImplOnly: Boolean =
hasFlag(PRIVATE) ||
(owner.isImplClass || owner.isTrait) &&
- ((hasFlag(notPRIVATE | LIFTED) && !hasFlag(ACCESSOR | SUPERACCESSOR | MODULE) || isConstructor) ||
+ ((hasFlag(notPRIVATE | LIFTED) && !hasFlag(ACCESSOR | SUPERACCESSOR | PROTACCESSOR | MODULE) || isConstructor) ||
(hasFlag(LIFTED) && isModule && isMethod))
/** Is this symbol a module variable ? */
@@ -1295,14 +1295,14 @@ trait Symbols {
}
override def alias: Symbol =
- if (hasFlag(SUPERACCESSOR | PARAMACCESSOR | MIXEDIN)) initialize.referenced
+ if (hasFlag(SUPERACCESSOR | PARAMACCESSOR | MIXEDIN | PROTACCESSOR)) initialize.referenced
else NoSymbol
def setAlias(alias: Symbol): TermSymbol = {
assert(alias != NoSymbol, this)
assert(!(alias hasFlag OVERLOADED), alias)
- assert(hasFlag(SUPERACCESSOR | PARAMACCESSOR | MIXEDIN), this)
+ assert(hasFlag(SUPERACCESSOR | PARAMACCESSOR | MIXEDIN | PROTACCESSOR), this)
referenced = alias
this
}
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index a0b66a6644..e4b60e4b3a 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -44,7 +44,7 @@ abstract class Mixin extends InfoTransform {
private def isImplementedStatically(sym: Symbol) =
sym.owner.isImplClass && sym.isMethod &&
(!sym.isModule || sym.hasFlag(PRIVATE | LIFTED)) &&
- (!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.hasFlag(LAZY))
+ (!(sym hasFlag (ACCESSOR | SUPERACCESSOR | PROTACCESSOR)) || sym.hasFlag(LAZY))
/** A member of a trait is static only if it belongs only to the
* implementation class, not the interface, and it is implemented
@@ -266,7 +266,8 @@ abstract class Mixin extends InfoTransform {
assert(member1.alias != NoSymbol, member1)
val alias1 = rebindSuper(clazz, member.alias, mixinClass)
member1.asInstanceOf[TermSymbol] setAlias alias1
-
+ } else if (member hasFlag PROTACCESSOR) {
+ addMember(clazz, member.cloneSymbol(clazz)) setPos clazz.pos
} else if (member.isMethod && member.isModule && !(member hasFlag (LIFTED | BRIDGE))) {
// mixin objects: todo what happens with abstract objects?
addMember(clazz, member.cloneSymbol(clazz))
@@ -524,6 +525,49 @@ abstract class Mixin extends InfoTransform {
stat
}
+ /**
+ * Complete the given abstract method to be a protected accessor. It
+ * assumes the accessed symbol is in the 'alias' field of the method's
+ * symbol.
+ *
+ * If the accessed symbol comes from Java, and the access happens
+ * in a trait method, it ignores the receiver parameter and uses 'this' instead.
+ * This is because of the rules for protected access in Java, which prevent an
+ * access from a subclass if the static type of the receiver is not a subtype
+ * of the class where the access happens. For traits, the access always happens
+ * in a class that cannot fulfill this requirement (an error for this case is
+ * issued in 'SuperAccessors', so we are safe assuming 'this' here).
+ */
+ def completeProtectedAccessor(stat: Tree) = stat match {
+ case DefDef(mods, name, tparams, List(vparams), tpt, EmptyTree)
+ if stat.symbol.hasFlag(PROTACCESSOR) =>
+ val sym = stat.symbol
+ assert(sym.alias != NoSymbol, sym)
+ log("Adding protected accessor for " + sym)
+ sym.resetFlag(lateDEFERRED)
+
+ val obj = vparams.head.symbol // receiver
+ val receiver = if (sym.alias.hasFlag(JAVA) && sym.hasFlag(MIXEDIN)) This(clazz) else Ident(obj)
+ val selection = Select(receiver, sym.alias)
+ val rhs0 =
+ if (sym.alias.isMethod)
+ Apply(selection, vparams.tail.map(v => Ident(v.symbol)))
+ else
+ selection // a Java field
+ val rhs1 = localTyper.typed(atPos(stat.pos)(rhs0), stat.symbol.tpe.resultType)
+ val rhs2 = atPhase(currentRun.mixinPhase)(transform(rhs1))
+ copy.DefDef(stat, mods, name, tparams, List(vparams), tpt, rhs2)
+ case _ =>
+ stat
+ }
+
+ /** Complete either a super accessor or a protected accessor. */
+ def completeAbstractAccessors(stat: Tree) =
+ if (stat.symbol.hasFlag(SUPERACCESSOR))
+ completeSuperAccessor(stat)
+ else
+ completeProtectedAccessor(stat)
+
import lazyVals._
/** return a 'lazified' version of rhs.
@@ -656,8 +700,8 @@ abstract class Mixin extends InfoTransform {
} else if (!sym.isMethod) {
// add fields
addDef(position(sym), ValDef(sym))
- } else if (sym hasFlag SUPERACCESSOR) {
- // add superaccessors
+ } else if (sym hasFlag (SUPERACCESSOR | PROTACCESSOR)) {
+ // add superaccessors/protected accessors
addDefDef(sym, vparams => EmptyTree)
} else {
// add forwarders
@@ -669,7 +713,8 @@ abstract class Mixin extends InfoTransform {
}
}
stats1 = add(stats1, newDefs.toList)
- if (!clazz.isTrait) stats1 = stats1 map completeSuperAccessor
+ if (!clazz.isTrait)
+ stats1 = stats1 map completeAbstractAccessors
stats1
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index db98d44e76..1dbb1a8a01 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -199,6 +199,13 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
case Select(qual, name) =>
val sym = tree.symbol
if (needsProtectedAccessor(sym, tree.pos)) {
+ if (currentOwner.enclClass.isTrait
+ && sym.hasFlag(JAVA)
+ && !qual.isInstanceOf[This])
+ unit.error(tree.pos,
+ "Implementation restriction: Cannot access Java protected member inside a trait,"
+ + " when the qualifier is not 'this'.")
+
if (settings.debug.value) log("Adding protected accessor for tree: " + tree);
transform(makeAccessor(tree.asInstanceOf[Select], List(EmptyTree)))
} else
@@ -269,8 +276,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val hasArgs = sym.tpe.paramTypes != Nil
val memberType = sym.tpe // transform(sym.tpe)
- // 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
+ // if the result type depends on the 'this' type of an enclosing class, the accessor
+ // has to take a singleton type, otherwise it takes the type of this
val objType = if (isThisType(memberType.finalResultType)) clazz.thisType else clazz.typeOfThis
val accType = memberType match {
case PolyType(tparams, restpe) =>
@@ -282,19 +289,12 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
var protAcc = clazz.info.decl(accName).suchThat(_.tpe == accType)
if (protAcc == NoSymbol) {
- protAcc = clazz.newMethod(tree.pos, nme.protName(sym.originalName)).setInfo(accType)
+ protAcc = clazz.newMethod(tree.pos, accName)
+ .setInfo(accType)
+ .setFlag(SYNTHETIC | PROTACCESSOR | lateDEFERRED)
+ .setAlias(sym)
clazz.info.decls.enter(protAcc);
- val code = DefDef(protAcc, vparamss => {
- val obj = vparamss.head.head // receiver
- vparamss.tail.zip(allParamTypes(sym.tpe)).foldLeft(Select(Ident(obj), sym): Tree) (
- (fun, pvparams) => {
- Apply(fun, (List.map2(pvparams._1, pvparams._2) { (v, origTpe) => makeArg(v, obj, origTpe) } ))
- })
- })
-
- if (settings.debug.value)
- log(code)
- accDefBuf(clazz) += typers(clazz).typed(code)
+ accDefBuf(clazz) += typers(clazz).typed(DefDef(protAcc, vparamss => EmptyTree))
}
var res: Tree = atPos(tree.pos) {
if (targs.head == EmptyTree)
@@ -304,6 +304,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
}
if (settings.debug.value)
log("Replaced " + tree + " with " + res)
+
if (hasArgs) typer.typedOperator(res) else typer.typed(res)
}
@@ -388,21 +389,23 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
* classes, this has to be signaled as error.
*/
private def needsProtectedAccessor(sym: Symbol, pos: Position): Boolean = {
- val res = /* settings.debug.value && */
+ val accNeeded = /* settings.debug.value && */
((sym hasFlag PROTECTED)
- && (!validCurrentOwner || !(currentOwner.enclClass.thisSym isSubClass sym.owner))
+ && (!validCurrentOwner
+ || !(currentOwner.enclClass.thisSym isSubClass sym.owner)
+ || currentOwner.enclClass.isTrait)
&& (enclPackage(sym.owner) != enclPackage(currentOwner))
&& (enclPackage(sym.owner) == enclPackage(sym.accessBoundary(sym.owner))))
- if (res) {
+ if (accNeeded) {
val host = hostForAccessorOf(sym, currentOwner.enclClass)
if (host.thisSym != host) {
if (host.thisSym.tpe.typeSymbol.hasFlag(JAVA) || currentOwner.enclClass.isTrait)
unit.error(pos, "Implementation restriction: " + currentOwner.enclClass + " accesses protected "
+ sym + " from self type " + host.thisSym.tpe)
false
- } else res
- } else res
+ } else accNeeded
+ } else accNeeded
}
/** Return the enclosing package of the given symbol. */
diff --git a/test/files/jvm/protectedacc.check b/test/files/jvm/protectedacc.check
index aaac0d613e..d36d85fef8 100644
--- a/test/files/jvm/protectedacc.check
+++ b/test/files/jvm/protectedacc.check
@@ -13,3 +13,4 @@ count after: 4
meth1(1) = 2
meth2(1)(1) = 10
100 = 100
+Foo
diff --git a/test/files/jvm/protectedacc.scala b/test/files/jvm/protectedacc.scala
index f5d05e21b4..59869c63ae 100644
--- a/test/files/jvm/protectedacc.scala
+++ b/test/files/jvm/protectedacc.scala
@@ -19,9 +19,25 @@ object Test {
(new ji.Inner).m;
(new p.b.OuterObj.Inner).m
+ cloneable.MainClone.run
}
}
+package cloneable {
+object MainClone {
+ trait Foo extends Cloneable {
+ def copy : Foo = clone.asInstanceOf[Foo]
+ override def toString = "Foo"
+ }
+ def run : Unit = {
+ val foo = new Foo {}
+ Console.println(foo.copy)
+ }
+}
+
+}
+
+
package p {
package a {