summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2010-10-17 21:01:31 +0000
committerMartin Odersky <odersky@gmail.com>2010-10-17 21:01:31 +0000
commitc39f26382dddaa7e000022704792c813f8bf7f2b (patch)
tree0b23f4558a82f6519cb6715147fb25b6df7d2b8f /src
parent491ecd7b8b2e5a0ee1f185f26042a80e11e46568 (diff)
downloadscala-c39f26382dddaa7e000022704792c813f8bf7f2b.tar.gz
scala-c39f26382dddaa7e000022704792c813f8bf7f2b.tar.bz2
scala-c39f26382dddaa7e000022704792c813f8bf7f2b.zip
Closes #3871.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala48
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala25
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala23
4 files changed, 75 insertions, 24 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index 1933893373..810c0d4f83 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -217,7 +217,6 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
sym setFlag (STABLE | SYNTHETIC)
if (isTrait) sym setFlag DEFERRED
sym.expandName(this)
- // todo: stop depending on compiler bug (ticket #3871) to set referenced.
sym.referenced = this
sym
}
@@ -1663,7 +1662,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
privateWithin = NoSymbol
- protected var referenced: Symbol = NoSymbol
+ var referenced: Symbol = NoSymbol
def cloneSymbolImpl(owner: Symbol): Symbol =
new TermSymbol(owner, pos, name).copyAttrsFrom(this)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 4906838e36..3e7f755b8e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -32,6 +32,8 @@ trait Contexts { self: Analyzer =>
global.definitions.RootClass.info.decls)
}
+ var lastAccessCheckDetails: String = ""
+
/** List of objects and packages to import from in
* a root context. This list is sensitive to the
* compiler settings.
@@ -324,6 +326,12 @@ trait Contexts { self: Analyzer =>
" " + scope.toList + "\n:: " + outer.toString()
}
+ /** Is `sub' a subclass of `base' or a companion object of such a subclass?
+ */
+ def isSubClassOrCompanion(sub: Symbol, base: Symbol) =
+ sub.isNonBottomSubClass(base) ||
+ sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)
+
/** Return closest enclosing context that defines a superclass of `clazz', or a
* companion module of a superclass of `clazz', or NoContext if none exists */
def enclosingSuperClassContext(clazz: Symbol): Context = {
@@ -335,11 +343,12 @@ trait Contexts { self: Analyzer =>
c
}
- /** Return closest enclosing context that defines a subclass of `clazz', or NoContext
- * if none exists */
+ /** Return closest enclosing context that defines a subclass of `clazz' or a companion
+ * object thereof, or NoContext if no such context exists
+ */
def enclosingSubClassContext(clazz: Symbol): Context = {
var c = this.enclClass
- while (c != NoContext && !c.owner.isNonBottomSubClass(clazz))
+ while (c != NoContext && !isSubClassOrCompanion(c.owner, clazz))
c = c.outer.enclClass
c
}
@@ -353,6 +362,8 @@ trait Contexts { self: Analyzer =>
* @return ...
*/
def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean): Boolean = {
+ lastAccessCheckDetails = ""
+
@inline def accessWithinLinked(ab: Symbol) = {
val linked = ab.linkedClassOfClass
// don't have access if there is no linked class
@@ -364,7 +375,7 @@ trait Contexts { self: Analyzer =>
/** Are we inside definition of `ab'? */
def accessWithin(ab: Symbol) = {
// #3663: we must disregard package nesting if sym isJavaDefined
- if(sym.isJavaDefined) {
+ if (sym.isJavaDefined) {
// is `o` or one of its transitive owners equal to `ab`?
// stops at first package, since further owners can only be surrounding packages
@tailrec def abEnclosesStopAtPkg(o: Symbol): Boolean =
@@ -392,6 +403,28 @@ trait Contexts { self: Analyzer =>
case _ => false
}
+ /** Is protected access to target symbol permitted */
+ def isProtectedAccessOK(target: Symbol) = {
+ val c = enclosingSubClassContext(sym.owner)
+ if (c == NoContext)
+ lastAccessCheckDetails =
+ "\n Access to protected "+target+" not permitted because"+
+ "\n "+"enclosing class "+this.enclClass.owner+this.enclClass.owner.locationString+" is not a subclass of "+
+ "\n "+sym.owner+sym.owner.locationString+" where target is defined"
+ c != NoContext && {
+ val res =
+ isSubClassOrCompanion(pre.widen.typeSymbol, c.owner) ||
+ c.owner.isModuleClass &&
+ isSubClassOrCompanion(pre.widen.typeSymbol, c.owner.linkedClassOfClass)
+ if (!res)
+ lastAccessCheckDetails =
+ "\n Access to protected "+target+" not permitted because"+
+ "\n prefix type "+pre.widen+" does not conform to"
+ "\n "+c.owner+c.owner.locationString+" where the access take place"
+ res
+ }
+ }
+
(pre == NoPrefix) || {
val ab = sym.accessBoundary(sym.owner)
( (ab.isTerm || ab == definitions.RootClass)
@@ -403,9 +436,12 @@ trait Contexts { self: Analyzer =>
)
|| (sym hasFlag PROTECTED) &&
( superAccess
+ || pre.isInstanceOf[ThisType]
|| sym.isConstructor
- || (pre.widen.typeSymbol.isNonBottomSubClass(sym.owner) &&
- (isSubClassOfEnclosing(pre.widen.typeSymbol) || phase.erasedTypes))
+ || phase.erasedTypes
+ || isProtectedAccessOK(sym)
+ || (sym.allOverriddenSymbols exists isProtectedAccessOK)
+ // that last condition makes protected access via self types work.
)
)
// note: phase.erasedTypes disables last test, because after addinterfaces
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index dc143666a1..fdcb692e45 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -235,6 +235,14 @@ trait Infer {
def explainTypes(tp1: Type, tp2: Type) =
withDisambiguation(tp1, tp2)(global.explainTypes(tp1, tp2))
+ def accessError(tree: Tree, sym: Symbol, pre: Type, explanation: String): Tree = {
+ val realsym = underlying(sym)
+
+ errorTree(tree, realsym + realsym.locationString + " cannot be accessed in " +
+ (if (sym.isClassConstructor) context.enclClass.owner else pre.widen) +
+ explanation)
+ }
+
/* -- Tests & Checks---------------------------------------------------- */
/** Check that <code>sym</code> is defined and accessible as a member of
@@ -247,14 +255,6 @@ trait Infer {
if (sym.isError) {
tree setSymbol sym setType ErrorType
} else {
- def accessError(explanation: String): Tree = {
- val realsym = underlying(sym)
-
- errorTree(tree, realsym + realsym.locationString + " cannot be accessed in " +
- (if (sym.isClassConstructor) context.enclClass.owner else pre.widen) +
- explanation)
- }
-
val topClass = context.owner.toplevelClass
if (context.unit != null)
context.unit.depends += sym.toplevelClass
@@ -268,8 +268,10 @@ trait Infer {
Console.println(tree)
Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType))
}
- accessError(
- if (settings.check.isDefault) "" else {
+ accessError(tree, sym, pre,
+ if (settings.check.isDefault) {
+ analyzer.lastAccessCheckDetails
+ } else {
"\n because of an internal error (no accessible symbol):" +
"\nsym = " + sym +
"\nunderlying(sym) = " + underlying(sym) +
@@ -294,7 +296,8 @@ trait Infer {
if (settings.debug.value) ex.printStackTrace
val sym2 = underlying(sym1)
val itype = pre.memberType(sym2)
- accessError("\n because its instance type "+itype+
+ accessError(tree, sym, pre,
+ "\n because its instance type "+itype+
(if ("malformed type: "+itype.toString==ex.msg) " is malformed"
else " contains a "+ex.msg))
ErrorType
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index fa62e24d96..2c8d1c845f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3664,9 +3664,12 @@ trait Typers { self: Analyzer =>
def ambiguousError(msg: String) =
error(tree.pos, "reference to " + name + " is ambiguous;\n" + msg)
- var defSym: Symbol = tree.symbol // the directly found symbol
- var pre: Type = NoPrefix // the prefix type of defSym, if a class member
- var qual: Tree = EmptyTree // the qualifier tree if transformed tree is a select
+ var defSym: Symbol = tree.symbol // the directly found symbol
+ var pre: Type = NoPrefix // the prefix type of defSym, if a class member
+ var qual: Tree = EmptyTree // the qualifier tree if transformed tree is a select
+ var inaccessibleSym: Symbol = NoSymbol // the first symbol that was found but that was discarded
+ // for being inaccessible; used for error reporting
+ var inaccessibleExplanation: String = ""
// A symbol qualifies if it exists and is not stale. Stale symbols
// are made to disappear here. In addition,
@@ -3699,7 +3702,13 @@ trait Typers { self: Analyzer =>
cx = cx.enclClass
defSym = pre.member(name) filter (
sym => qualifies(sym) && context.isAccessible(sym, pre, false))
- if (defSym == NoSymbol) cx = cx.outer
+ if (defSym == NoSymbol) {
+ if (inaccessibleSym eq NoSymbol) {
+ inaccessibleSym = pre.member(name) filter qualifies
+ inaccessibleExplanation = analyzer.lastAccessCheckDetails
+ }
+ cx = cx.outer
+ }
}
}
@@ -3769,7 +3778,11 @@ trait Typers { self: Analyzer =>
if (settings.debug.value) {
log(context.imports)//debug
}
- error(tree.pos, "not found: "+decodeWithNamespace(name))
+ if (inaccessibleSym eq NoSymbol) {
+ error(tree.pos, "not found: "+decodeWithNamespace(name))
+ } else accessError(
+ tree, inaccessibleSym, context.enclClass.owner.thisType,
+ inaccessibleExplanation)
defSym = context.owner.newErrorSymbol(name)
}
}