summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-09-08 18:37:18 +0000
committerPaul Phillips <paulp@improving.org>2011-09-08 18:37:18 +0000
commitf32a32b1b33c9d7ccd62467e3e10cb69930023c8 (patch)
tree47ca34246fbdd255f2191ced68bd708e609d22d2
parent52c1d019d63d2fffadc8f3f159d654187ac61ece (diff)
downloadscala-f32a32b1b33c9d7ccd62467e3e10cb69930023c8.tar.gz
scala-f32a32b1b33c9d7ccd62467e3e10cb69930023c8.tar.bz2
scala-f32a32b1b33c9d7ccd62467e3e10cb69930023c8.zip
Allow for the overriding of objects.
Various and sundry manipulations to allow for objects to be overridden when the mood is right. It is not enabled by default. The major contributor of change turned out to be the decoupling of the FINAL flag (and the "isFinal" test which examines only that flag) and the many semantics which were attributed to this interpretation of finality in different circumstances. Since objects no longer have the FINAL flag automatically applied (only top-level objects and those marked final in source code do) we need apply a more nuanced test. Fortunately there is such a nuanced test: isEffectivelyFinal, which is always true if the FINAL flag is set but also in various other circumstances. In almost every case, you should be testing "isEffectivelyFinal", not "isFinal". To enable overridable objects, use: -Yoverride-objects -Xexperimental // includes the above and others Remain to be done: working out transition logistics. Most likely this would involve bumping the scala signature version, and all objects in versions before that would be assumed final. Review by moors.
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala9
-rw-r--r--src/compiler/scala/reflect/internal/Flags.scala2
-rw-r--r--src/compiler/scala/reflect/internal/Symbols.scala48
-rw-r--r--src/compiler/scala/reflect/internal/settings/MutableSettings.scala1
-rw-r--r--src/compiler/scala/reflect/runtime/Settings.scala3
-rw-r--r--src/compiler/scala/reflect/std/ReflectSettings.scala3
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/Inliners.scala25
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala76
-rw-r--r--src/library/scala/reflect/generic/HasFlags.scala71
-rw-r--r--test/files/neg/override-object-flag.check5
-rw-r--r--test/files/neg/override-object-flag.scala3
-rw-r--r--test/files/neg/override-object-no.check23
-rw-r--r--test/files/neg/override-object-no.flags1
-rw-r--r--test/files/neg/override-object-no.scala45
-rw-r--r--test/files/neg/t276.check2
-rw-r--r--test/files/pos/override-object-yes.flags1
-rw-r--r--test/files/pos/override-object-yes.scala40
23 files changed, 247 insertions, 132 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index 2bffca6335..a37ae9cd4d 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -131,10 +131,13 @@ trait Definitions extends reflect.api.StandardDefinitions {
RootClass.sourceModule = rp
rp
}
- // This is the actual root of everything, including the package _root_.
- lazy val RootClass: ModuleClassSymbol = NoSymbol.newModuleClass(NoPosition, tpnme.ROOT)
- .setFlag(FINAL | MODULE | PACKAGE | JAVA).setInfo(rootLoader)
+ // This is the actual root of everything, including the package _root_.
+ lazy val RootClass: ModuleClassSymbol = (
+ NoSymbol.newModuleClass(NoPosition, tpnme.ROOT)
+ setFlag (FINAL | MODULE | PACKAGE | JAVA)
+ setInfo rootLoader
+ )
// The empty package, which holds all top level types without given packages.
lazy val EmptyPackage = RootClass.newPackage(NoPosition, nme.EMPTY_PACKAGE_NAME).setFlag(FINAL)
lazy val EmptyPackageClass = EmptyPackage.moduleClass
diff --git a/src/compiler/scala/reflect/internal/Flags.scala b/src/compiler/scala/reflect/internal/Flags.scala
index 201a960ec7..de3607ffe9 100644
--- a/src/compiler/scala/reflect/internal/Flags.scala
+++ b/src/compiler/scala/reflect/internal/Flags.scala
@@ -243,7 +243,7 @@ class Flags extends ModifierFlags {
final val ConstrFlags: Long = JAVA
/** Module flags inherited by their module-class */
- final val ModuleToClassFlags: Long = AccessFlags | MODULE | PACKAGE | CASE | SYNTHETIC | JAVA
+ final val ModuleToClassFlags: Long = AccessFlags | MODULE | PACKAGE | CASE | SYNTHETIC | JAVA | FINAL
def getterFlags(fieldFlags: Long): Long = ACCESSOR + (
if ((fieldFlags & MUTABLE) != 0) fieldFlags & ~MUTABLE & ~PRESUPER
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala
index 2a38e6ee9b..60b0c411da 100644
--- a/src/compiler/scala/reflect/internal/Symbols.scala
+++ b/src/compiler/scala/reflect/internal/Symbols.scala
@@ -90,20 +90,31 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
newMethod(pos, name).setFlag(LABEL)
final def newConstructor(pos: Position) =
newMethod(pos, nme.CONSTRUCTOR)
- final def newModule(pos: Position, name: TermName, clazz: ClassSymbol) =
- new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL)
- .setModuleClass(clazz)
- final def newModule(name: TermName, clazz: Symbol, pos: Position = NoPosition) =
- new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL)
- .setModuleClass(clazz.asInstanceOf[ClassSymbol])
- final def newModule(pos: Position, name: TermName) = {
- val m = new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL)
- m.setModuleClass(new ModuleClassSymbol(m))
- }
- final def newPackage(pos: Position, name: TermName) = {
+
+ private def finishModule(m: ModuleSymbol, clazz: ClassSymbol): ModuleSymbol = {
+ // Top-level objects can be automatically marked final, but others
+ // must be explicitly marked final if overridable objects are enabled.
+ val flags = if (isPackage || !settings.overrideObjects.value) MODULE | FINAL else MODULE
+ m setFlag flags
+ m setModuleClass clazz
+ m
+ }
+ private def finishModule(m: ModuleSymbol): ModuleSymbol =
+ finishModule(m, new ModuleClassSymbol(m))
+
+ final def newModule(pos: Position, name: TermName, clazz: ClassSymbol): ModuleSymbol =
+ finishModule(new ModuleSymbol(this, pos, name), clazz)
+
+ final def newModule(name: TermName, clazz: Symbol, pos: Position = NoPosition): ModuleSymbol =
+ newModule(pos, name, clazz.asInstanceOf[ClassSymbol])
+
+ final def newModule(pos: Position, name: TermName): ModuleSymbol =
+ finishModule(new ModuleSymbol(this, pos, name))
+
+ final def newPackage(pos: Position, name: TermName): ModuleSymbol = {
assert(name == nme.ROOT || isPackageClass)
val m = newModule(pos, name).setFlag(JAVA | PACKAGE)
- m.moduleClass.setFlag(JAVA | PACKAGE)
+ m.moduleClass setFlag (JAVA | PACKAGE)
m
}
final def newThisSym(pos: Position) =
@@ -522,8 +533,15 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
isPackageClass || isModuleClass && isStatic
/** Is this symbol effectively final? I.e, it cannot be overridden */
- final def isEffectivelyFinal: Boolean = isFinal || isTerm && (
- hasFlag(PRIVATE) || isLocal || owner.isClass && owner.hasFlag(FINAL | MODULE))
+ final def isEffectivelyFinal: Boolean = (
+ isFinal
+ || hasModuleFlag && !settings.overrideObjects.value
+ || isTerm && (
+ isPrivate
+ || isLocal
+ || owner.isClass && owner.isEffectivelyFinal
+ )
+ )
/** Is this symbol locally defined? I.e. not accessed from outside `this` instance */
final def isLocal: Boolean = owner.isTerm
@@ -2314,7 +2332,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
private var module: Symbol = null
def this(module: TermSymbol) = {
this(module.owner, module.pos, module.name.toTypeName)
- setFlag(module.getFlag(ModuleToClassFlags) | MODULE | FINAL)
+ setFlag(module.getFlag(ModuleToClassFlags) | MODULE)
sourceModule = module
}
override def sourceModule = module
diff --git a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala
index 0dfd12cc2d..3bf296760f 100644
--- a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala
+++ b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala
@@ -29,6 +29,7 @@ abstract class MutableSettings extends AbsSettings {
}
}
+ def overrideObjects: SettingValue { type T = Boolean }
def printtypes: SettingValue { type T = Boolean }
def debug: SettingValue { type T = Boolean }
def YdepMethTpes: SettingValue { type T = Boolean }
diff --git a/src/compiler/scala/reflect/runtime/Settings.scala b/src/compiler/scala/reflect/runtime/Settings.scala
index 088ea019e2..4bd72ce47b 100644
--- a/src/compiler/scala/reflect/runtime/Settings.scala
+++ b/src/compiler/scala/reflect/runtime/Settings.scala
@@ -19,6 +19,7 @@ class Settings extends internal.settings.MutableSettings {
v = x
}
+ val overrideObjects = new BooleanSetting(false)
val debug = new BooleanSetting(false)
val YdepMethTpes = new BooleanSetting(false)
val Ynotnull = new BooleanSetting(false)
@@ -29,4 +30,4 @@ class Settings extends internal.settings.MutableSettings {
val printtypes = new BooleanSetting(false)
val Yrecursion = new IntSetting(0)
val maxClassfileName = new IntSetting(255)
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/reflect/std/ReflectSettings.scala b/src/compiler/scala/reflect/std/ReflectSettings.scala
index 178f1e5c0c..d445b04bde 100644
--- a/src/compiler/scala/reflect/std/ReflectSettings.scala
+++ b/src/compiler/scala/reflect/std/ReflectSettings.scala
@@ -10,6 +10,7 @@ class ReflectSettings extends internal.settings.MutableSettings {
override def isDefault = v == init
}
+ val overrideObjects = newSetting(false)
val debug = newSetting(false)
val YdepMethTpes = newSetting(false)
val Ynotnull = newSetting(false)
@@ -20,4 +21,4 @@ class ReflectSettings extends internal.settings.MutableSettings {
val printtypes = newSetting(false)
val Yrecursion = newSetting(0)
val maxClassfileName = newSetting(255)
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index f24b4ac6bb..48b5d7cdce 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -1901,7 +1901,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
// avoid breaking proxy software which depends on subclassing, we avoid
// insisting on their finality in the bytecode.
val finalFlag = (
- ((sym.rawflags & Flags.FINAL) != 0)
+ ((sym.rawflags & (Flags.FINAL | Flags.MODULE)) != 0)
&& !sym.enclClass.isInterface
&& !sym.isClassConstructor
)
diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
index 9522f4b3ea..311a3b916a 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
@@ -270,7 +270,7 @@ abstract class DeadCodeElimination extends SubComponent {
}
private def isPure(sym: Symbol) = (
- (sym.isGetter && sym.isFinal && !sym.isLazy)
+ (sym.isGetter && sym.isEffectivelyFinal && !sym.isLazy)
|| (sym.isPrimaryConstructor && (sym.enclosingPackage == RuntimePackage || inliner.isClosureClass(sym.owner)))
)
/** Is 'sym' a side-effecting method? TODO: proper analysis. */
diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
index 3bded8cca4..f79cec532d 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
@@ -139,13 +139,18 @@ abstract class Inliners extends SubComponent {
icodes.load(concreteMethod.enclClass)
def isAvailable = icodes available concreteMethod.enclClass
- def isCandidate = isClosureClass(receiver) || concreteMethod.isEffectivelyFinal || receiver.isFinal
+ def isCandidate = (
+ isClosureClass(receiver)
+ || concreteMethod.isEffectivelyFinal
+ || receiver.isEffectivelyFinal
+ )
def isApply = concreteMethod.name == nme.apply
- def isCountable = !(isClosureClass(receiver)
- || isApply
- || isMonadicMethod(concreteMethod)
- || receiver.enclosingPackage == definitions.RuntimePackage
- ) // only count non-closures
+ def isCountable = !(
+ isClosureClass(receiver)
+ || isApply
+ || isMonadicMethod(concreteMethod)
+ || receiver.enclosingPackage == definitions.RuntimePackage
+ ) // only count non-closures
debuglog("Treating " + i
+ "\n\treceiver: " + receiver
@@ -260,8 +265,12 @@ abstract class Inliners extends SubComponent {
*/
def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = {
// TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info)
- def needsLookup = (clazz != NoSymbol) && (clazz != sym.owner) && !sym.isEffectivelyFinal && clazz.isFinal
-
+ def needsLookup = (
+ (clazz != NoSymbol)
+ && (clazz != sym.owner)
+ && !sym.isEffectivelyFinal
+ && clazz.isEffectivelyFinal
+ )
def lookup(clazz: Symbol): Symbol = {
// println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner)
if (sym.owner == clazz || isBottomType(clazz)) sym
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index ca7ca5b41f..68496b61ce 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -93,7 +93,7 @@ trait ScalaSettings extends AbsScalaSettings
// Experimental Extensions
val Xexperimental = BooleanSetting ("-Xexperimental", "Enable experimental extensions.") .
- withPostSetHook(set => List(YdepMethTpes, YmethodInfer) foreach (_.value = set.value)) //YvirtClasses,
+ withPostSetHook(set => List(YdepMethTpes, YmethodInfer, overrideObjects) foreach (_.value = set.value)) //YvirtClasses,
/** Compatibility stubs for options whose value name did
* not previously match the option name.
@@ -108,6 +108,7 @@ trait ScalaSettings extends AbsScalaSettings
/**
* -Y "Private" settings
*/
+ val overrideObjects = BooleanSetting ("-Yoverride-objects", "Allow member objects to be overridden.")
val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options.")
val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after")
val check = PhasesSetting ("-Ycheck", "Check the tree at the end of")
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index e29f085de7..dc8585e8cb 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -215,7 +215,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
// the symbol is an outer accessor of a final class which does not override another outer accessor. )
def maybeOmittable(sym: Symbol) = sym.owner == clazz && (
sym.isParamAccessor && sym.isPrivateLocal ||
- sym.isOuterAccessor && sym.owner.isFinal && !sym.isOverridingSymbol &&
+ sym.isOuterAccessor && sym.owner.isEffectivelyFinal && !sym.isOverridingSymbol &&
!(clazz isSubClass DelayedInitClass)
)
@@ -228,7 +228,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
override def traverse(tree: Tree) = {
tree match {
case DefDef(_, _, _, _, _, body)
- if (tree.symbol.isOuterAccessor && tree.symbol.owner == clazz && clazz.isFinal) =>
+ if (tree.symbol.isOuterAccessor && tree.symbol.owner == clazz && clazz.isEffectivelyFinal) =>
log("outerAccessors += " + tree.symbol.fullName)
outerAccessors ::= ((tree.symbol, body))
case Select(_, _) =>
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index 992a746e1e..8db759266b 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -150,7 +150,7 @@ abstract class ExplicitOuter extends InfoTransform
val restpe = if (clazz.isTrait) clazz.outerClass.tpe else clazz.outerClass.thisType
decls1 enter (clazz.newOuterAccessor(clazz.pos) setInfo MethodType(Nil, restpe))
if (hasOuterField(clazz)) { //2
- val access = if (clazz.isFinal) PRIVATE | LOCAL else PROTECTED
+ val access = if (clazz.isEffectivelyFinal) PRIVATE | LOCAL else PROTECTED
decls1 enter (
clazz.newValue(clazz.pos, nme.OUTER_LOCAL)
setFlag (SYNTHETIC | PARAMACCESSOR | access)
@@ -215,7 +215,7 @@ abstract class ExplicitOuter extends InfoTransform
val outerFld =
if (outerAcc.owner == currentClass &&
base.tpe =:= currentClass.thisType &&
- outerAcc.owner.isFinal)
+ outerAcc.owner.isEffectivelyFinal)
outerField(currentClass) suchThat (_.owner == currentClass)
else
NoSymbol
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 5a336d9027..c3062b3b2e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -93,7 +93,7 @@ trait Namers { self: Analyzer =>
else 0l
def moduleClassFlags(moduleFlags: Long) =
- (moduleFlags & ModuleToClassFlags) | FINAL | inConstructorFlag
+ (moduleFlags & ModuleToClassFlags) | inConstructorFlag
def updatePosFlags(sym: Symbol, pos: Position, flags: Long): Symbol = {
debuglog("overwriting " + sym)
@@ -244,9 +244,9 @@ trait Namers { self: Analyzer =>
/** Enter a module symbol. The tree parameter can be either a module definition
* or a class definition */
def enterModuleSymbol(tree : ModuleDef): Symbol = {
- // .pos, mods.flags | MODULE | FINAL, name
+ // .pos, mods.flags | MODULE, name
var m: Symbol = context.scope.lookup(tree.name)
- val moduleFlags = tree.mods.flags | MODULE | FINAL
+ val moduleFlags = tree.mods.flags | MODULE
if (m.isModule && !m.isPackage && inCurrentScope(m) && (currentRun.canRedefine(m) || m.isSynthetic)) {
updatePosFlags(m, tree.pos, moduleFlags)
setPrivateWithin(tree, m, tree.mods)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index c05a5e721e..0ff886dea6 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -204,6 +204,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
* overrides some other member.
*/
private def checkAllOverrides(clazz: Symbol, typesOnly: Boolean = false) {
+ val self = clazz.thisType
+ def classBoundAsSeen(tp: Type) = {
+ tp.typeSymbol.classBound.asSeenFrom(self, tp.typeSymbol.owner)
+ }
case class MixinOverrideError(member: Symbol, msg: String)
@@ -223,8 +227,17 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
}
- val self = clazz.thisType
+ def isConformingObjectOverride(tp1: Type, tp2: Type) = {
+ tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && {
+ val cb1 = classBoundAsSeen(tp1)
+ val cb2 = classBoundAsSeen(tp2)
+ (cb1 <:< cb2) && {
+ log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2))
+ true
+ }
+ }
+ }
def isAbstractTypeWithoutFBound(sym: Symbol) = // (part of DEVIRTUALIZE)
sym.isAbstractType && !sym.isFBounded
@@ -248,10 +261,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
rtp1 <:< rtp2
case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
rtp1 <:< rtp2
- case (TypeRef(_, sym, _), _) if (sym.isModuleClass) =>
+ case (TypeRef(_, sym, _), _) if sym.isModuleClass =>
overridesType(NullaryMethodType(tp1), tp2)
case _ =>
- tp1 <:< tp2
+ (tp1 <:< tp2) || isConformingObjectOverride(tp1, tp2)
}
/** Check that all conditions for overriding `other` by `member`
@@ -260,24 +273,43 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
def checkOverride(clazz: Symbol, member: Symbol, other: Symbol) {
def noErrorType = other.tpe != ErrorType && member.tpe != ErrorType
def isRootOrNone(sym: Symbol) = sym == RootClass || sym == NoSymbol
+ def objectOverrideErrorMsg = (
+ "overriding " + other.fullLocationString + " with " + member.fullLocationString + ":\n" +
+ "an overriding object must conform to the overridden object's class bound" +
+ analyzer.foundReqMsg(classBoundAsSeen(member.tpe), classBoundAsSeen(other.tpe))
+ )
+
+ def overrideErrorMsg(msg: String): String = {
+ val isConcreteOverAbstract =
+ (other.owner isSubClass member.owner) && other.isDeferred && !member.isDeferred
+ val addendum =
+ if (isConcreteOverAbstract)
+ ";\n (Note that %s is abstract,\n and is therefore overridden by concrete %s)".format(
+ infoStringWithLocation(other),
+ infoStringWithLocation(member)
+ )
+ else ""
+
+ "overriding %s;\n %s %s%s".format(
+ infoStringWithLocation(other), infoString(member), msg, addendum
+ )
+ }
+ def emitOverrideError(fullmsg: String) {
+ if (member.owner == clazz) unit.error(member.pos, fullmsg)
+ else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
+ }
def overrideError(msg: String) {
- if (noErrorType) {
- val fullmsg =
- "overriding "+infoStringWithLocation(other)+";\n "+
- infoString(member)+" "+msg+
- (if ((other.owner isSubClass member.owner) && other.isDeferred && !member.isDeferred)
- ";\n (Note that "+infoStringWithLocation(other)+" is abstract,"+
- "\n and is therefore overridden by concrete "+infoStringWithLocation(member)+")"
- else "")
- if (member.owner == clazz) unit.error(member.pos, fullmsg)
- else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
- }
+ if (noErrorType)
+ emitOverrideError(overrideErrorMsg(msg))
}
def overrideTypeError() {
if (noErrorType) {
- overrideError("has incompatible type")
+ emitOverrideError(
+ if (member.isModule && other.isModule) objectOverrideErrorMsg
+ else overrideErrorMsg("has incompatible type")
+ )
}
}
@@ -331,11 +363,11 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
if (!isOverrideAccessOK) {
overrideAccessError()
- } else if (other.isClass || other.isModule) {
- overrideError("cannot be used here - classes and objects cannot be overridden");
- } else if (!other.isDeferred && (member.isClass || member.isModule)) {
- overrideError("cannot be used here - classes and objects can only override abstract types");
- } else if (other hasFlag FINAL) { // (1.2)
+ } else if (other.isClass) {
+ overrideError("cannot be used here - class definitions cannot be overridden");
+ } else if (!other.isDeferred && member.isClass) {
+ overrideError("cannot be used here - classes can only override abstract types");
+ } else if (other.isFinal) { // (1.2)
overrideError("cannot override final member");
} else if (!other.isDeferred && !(member hasFlag (OVERRIDE | ABSOVERRIDE | SYNTHETIC))) { // (1.3), SYNTHETIC because of DEVIRTUALIZE
overrideError("needs `override' modifier");
@@ -1006,9 +1038,9 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
else if (isWarnable) {
if (isNew(qual)) // new X == y
nonSensibleWarning("a fresh object", false)
- else if (isNew(args.head) && (receiver.isFinal || isReferenceOp)) // object X ; X == new Y
+ else if (isNew(args.head) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y
nonSensibleWarning("a fresh object", false)
- else if (receiver.isFinal && !(receiver isSubClass actual)) { // object X, Y; X == Y
+ else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual)) { // object X, Y; X == Y
if (isEitherNullable)
nonSensible("non-null ", false)
else
diff --git a/src/library/scala/reflect/generic/HasFlags.scala b/src/library/scala/reflect/generic/HasFlags.scala
index 7f869bfdc2..33d0a51243 100644
--- a/src/library/scala/reflect/generic/HasFlags.scala
+++ b/src/library/scala/reflect/generic/HasFlags.scala
@@ -6,76 +6,7 @@
package scala.reflect
package generic
-/** ISSUE #1: Flag names vs. Test method names
- *
- * The following methods from Symbol have a name of
- * the form isFoo where FOO is the name of a flag, but where the method
- * body tests for more than whether the flag is set.
- *
- * There are two possibilities with such methods. Either the extra
- * tests are strictly to partition among overloaded flags (which is
- * the case we can live with in the short term, if each such flag's
- * partitioning assumptions are documented) or they aren't.
- *
- * The second case implies that "x hasFlag FOO" and "x.isFoo" have
- * different semantics, and this we can't live with, because even if
- * we're smart enough to avoid being tripped up by that, the next guy isn't.
- *
- * No extreme measures necessary, only renaming isFoo to something
- * which hews more closely to its implementation. (Or renaming the flag.)
- *
- // Defined in the compiler Symbol
- //
- final def isLabel = isMethod && !hasAccessorFlag && hasFlag(LABEL)
- final def isLocal: Boolean = owner.isTerm
- final def isModuleVar: Boolean = isVariable && hasFlag(MODULEVAR)
- final def isStable =
- isTerm &&
- !hasTraitFlag &&
- (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)) &&
- !(tpe.isVolatile && !hasAnnotation(uncheckedStableClass))
- final def isStatic: Boolean =
- hasFlag(STATIC) || isRoot || owner.isStaticOwner
- override final def isTrait: Boolean =
- isClass && hasFlag(TRAIT | notDEFERRED) // A virtual class becomes a trait (part of DEVIRTUALIZE)
-
- // Defined in the library Symbol
- //
- def isTrait: Boolean = isClass && hasFlag(TRAIT) // refined later for virtual classes.
- final def isContravariant = isType && hasFlag(CONTRAVARIANT)
- final def isCovariant = isType && hasFlag(COVARIANT)
- final def isMethod = isTerm && hasFlag(METHOD)
- final def isModule = isTerm && hasFlag(MODULE)
- final def isPackage = isModule && hasFlag(PACKAGE)
- *
- */
-
-/** ISSUE #2: Implicit flag relationships must be made explicit.
- *
- * For instance, every time the MODULE flag is set, the FINAL flag is
- * set along with it:
- *
- .setFlag(FINAL | MODULE | PACKAGE | JAVA)
- .setFlag(FINAL | MODULE | PACKAGE | JAVA).setInfo(rootLoader)
- new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL)
- new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL)
- val m = new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL)
- setFlag(module.getFlag(ModuleToClassFlags) | MODULE | FINAL)
- sourceModule.flags = MODULE | FINAL
-
- * However the same is not true of when the MODULE flag is cleared:
-
- sym.resetFlag(MODULE)
- .setFlag(sym.flags | STABLE).resetFlag(MODULE)
- sym.resetFlag(MODULE | FINAL | CASE)
-
- * It's not relevant whether this example poses any issues: we must
- * not tolerate these uncertainties. If the flags are to move together
- * then both setting and clearing have to be encapsulated. If there
- * is a useful and used distinction between the various permutations
- * of on and off, then it must be documented. It's the only way!
- */
-
+/** Comment deleted, see internal.HasFlags. */
import Flags._
/** Common code utilized by `Modifiers` (which carry the flags associated
diff --git a/test/files/neg/override-object-flag.check b/test/files/neg/override-object-flag.check
new file mode 100644
index 0000000000..152d31ff8a
--- /dev/null
+++ b/test/files/neg/override-object-flag.check
@@ -0,0 +1,5 @@
+override-object-flag.scala:3: error: overriding object Foo in trait A of type object B.this.Foo;
+ object Foo cannot override final member
+trait B extends A { override object Foo }
+ ^
+one error found
diff --git a/test/files/neg/override-object-flag.scala b/test/files/neg/override-object-flag.scala
new file mode 100644
index 0000000000..74d00dd445
--- /dev/null
+++ b/test/files/neg/override-object-flag.scala
@@ -0,0 +1,3 @@
+// no flag enabling it, fail
+trait A { object Foo }
+trait B extends A { override object Foo }
diff --git a/test/files/neg/override-object-no.check b/test/files/neg/override-object-no.check
new file mode 100644
index 0000000000..c509634b3f
--- /dev/null
+++ b/test/files/neg/override-object-no.check
@@ -0,0 +1,23 @@
+override-object-no.scala:14: error: overriding object Bar in trait Foo with object Bar in trait Foo2:
+an overriding object must conform to the overridden object's class bound;
+ found : case1.Bippy with ScalaObject
+ required: case1.Bippy with case1.Bippo with ScalaObject
+ override object Bar extends Bippy { // err
+ ^
+override-object-no.scala:21: error: overriding object Bar in trait Quux1 with object Bar in trait Quux2:
+an overriding object must conform to the overridden object's class bound;
+ found : java.lang.Object with ScalaObject{def g: java.lang.String}
+ required: java.lang.Object with ScalaObject{def g: Int}
+ trait Quux2 extends Quux1 { override object Bar { def g = "abc" } } // err
+ ^
+override-object-no.scala:25: error: overriding object Bar in trait Quux3 of type object Quux4.this.Bar;
+ object Bar cannot override final member
+ trait Quux4 extends Quux3 { override object Bar } // err
+ ^
+override-object-no.scala:43: error: overriding object A in class Foo with object A in class P2:
+an overriding object must conform to the overridden object's class bound;
+ found : case2.Bar[List[String]] with ScalaObject
+ required: case2.Bar[Traversable[String]] with ScalaObject
+ override object A extends Bar[List[String]] // err
+ ^
+four errors found
diff --git a/test/files/neg/override-object-no.flags b/test/files/neg/override-object-no.flags
new file mode 100644
index 0000000000..22e9a95c4f
--- /dev/null
+++ b/test/files/neg/override-object-no.flags
@@ -0,0 +1 @@
+-Yoverride-objects \ No newline at end of file
diff --git a/test/files/neg/override-object-no.scala b/test/files/neg/override-object-no.scala
new file mode 100644
index 0000000000..745cdb2332
--- /dev/null
+++ b/test/files/neg/override-object-no.scala
@@ -0,0 +1,45 @@
+// See also pos/override-object-yes.scala
+
+package case1 {
+ // Missing interface in overriding object
+ class Bippy { def f = 1 }
+ trait Bippo
+
+ trait Foo {
+ object Bar extends Bippy with Bippo { override def f = 2 }
+ def f(x: Bippo)
+ def g = f(Bar)
+ }
+ trait Foo2 extends Foo {
+ override object Bar extends Bippy { // err
+ override def f = 3
+ }
+ }
+
+ // type mismatch in member
+ trait Quux1 { object Bar { def g = 55 } }
+ trait Quux2 extends Quux1 { override object Bar { def g = "abc" } } // err
+
+ // still can't override final objects!
+ trait Quux3 { final object Bar { } }
+ trait Quux4 extends Quux3 { override object Bar } // err
+}
+
+// type parameter as-seen-from business
+package case2 {
+ // invariance (see pos for the covariant case)
+ class Bar[T]
+
+ class Foo[T] {
+ object A extends Bar[T]
+ }
+
+ class Baz[S] extends Foo[S] {
+ override object A extends Bar[S]
+ }
+
+ class P1 extends Foo[Traversable[String]]
+ class P2 extends P1 {
+ override object A extends Bar[List[String]] // err
+ }
+}
diff --git a/test/files/neg/t276.check b/test/files/neg/t276.check
index 7d37f5cfa1..b241953a22 100644
--- a/test/files/neg/t276.check
+++ b/test/files/neg/t276.check
@@ -1,5 +1,5 @@
t276.scala:6: error: overriding type Bar in class Foo, which equals (Int, Int);
- class Bar cannot be used here - classes and objects can only override abstract types
+ class Bar cannot be used here - classes can only override abstract types
class Bar
^
one error found
diff --git a/test/files/pos/override-object-yes.flags b/test/files/pos/override-object-yes.flags
new file mode 100644
index 0000000000..22e9a95c4f
--- /dev/null
+++ b/test/files/pos/override-object-yes.flags
@@ -0,0 +1 @@
+-Yoverride-objects \ No newline at end of file
diff --git a/test/files/pos/override-object-yes.scala b/test/files/pos/override-object-yes.scala
new file mode 100644
index 0000000000..858f9b21fc
--- /dev/null
+++ b/test/files/pos/override-object-yes.scala
@@ -0,0 +1,40 @@
+package case1 {
+ class Bippy {
+ def f = 1
+ }
+
+ trait Foo {
+ object Bar extends Bippy {
+ override def f = 2
+ }
+ }
+
+ trait Foo2 extends Foo {
+ override object Bar extends Bippy {
+ override def f = 3
+ }
+ }
+
+ trait Foo3 {
+ object Bar {
+ def g: Traversable[Int] = Nil
+ }
+ }
+ trait Foo4 extends Foo3 {
+ override object Bar {
+ def g: List[Int] = Nil
+ }
+ }
+}
+
+package case2 {
+ class Bar[T]
+
+ class Foo[T] {
+ object A extends Bar[T]
+ }
+
+ class Baz[S] extends Foo[S] {
+ override object A extends Bar[S]
+ }
+}