summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-01-30 12:42:26 -0800
committerPaul Phillips <paulp@improving.org>2012-01-30 22:18:25 -0800
commita9eb9c5b69071a944d2a5225aa320babdf33ad42 (patch)
tree52c6353685c24c2101f8a963a66068a703405584
parent74a252e668959dd2cf4808132473d8b15bb606af (diff)
downloadscala-a9eb9c5b69071a944d2a5225aa320babdf33ad42.tar.gz
scala-a9eb9c5b69071a944d2a5225aa320babdf33ad42.tar.bz2
scala-a9eb9c5b69071a944d2a5225aa320babdf33ad42.zip
More work on inline classes.
Fail compile if AnyVal is inherited by a trait, a non-@inline class, or a class with an AnyRef parent somewhere. Added tests. Added logging, like [log extmethods] Inline class class Bippy spawns extension method. Old: def getClass: Class[_ <: Bippy] New: final def extension$getClass($this: Bippy): Class[_ <: Bippy] Fixed what I hope was a bug in ExtensionMethods where the original method params were dropped. Since adding a NonNull parent was also inflicting an AnyRef on AnyVal subclasses, suppressed that for those. Had the bright idea that AnyVal could extend NotNull. It doesn't seem to accomplish much, but then, I don't think NotNull accomplishes much. Still, maybe it's time to restrict the ways one can use AnyVal so one can't do this: scala> var x: AnyVal = _ x: AnyVal = null
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala8
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala13
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala23
-rw-r--r--src/library/scala/AnyVal.scala2
-rw-r--r--src/library/scala/NotNull.scala2
-rw-r--r--test/files/neg/anyval-children-2.check4
-rw-r--r--test/files/neg/anyval-children-2.scala1
-rw-r--r--test/files/neg/anyval-children.check26
-rw-r--r--test/files/neg/anyval-children.scala14
-rw-r--r--test/files/pos/anyval-children.scala2
12 files changed, 88 insertions, 15 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index f8342444c8..ce0505bf44 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -910,6 +910,12 @@ trait Definitions extends reflect.api.StandardDefinitions {
private lazy val scalaValueClassesSet = ScalaValueClasses.toSet
private lazy val boxedValueClassesSet = boxedClass.values.toSet + BoxedUnitClass
+ /** Now that AnyVal is unsealing we need less ambiguous names
+ * for when we need to distinguish the Nine Original AnyVals
+ * from the heathen masses.
+ */
+ def isPrimitiveValueClass(sym: Symbol) = scalaValueClassesSet(sym)
+
/** Is symbol a value class? */
def isValueClass(sym: Symbol) = scalaValueClassesSet(sym)
def isNonUnitValueClass(sym: Symbol) = (sym != UnitClass) && isValueClass(sym)
@@ -1029,7 +1035,7 @@ trait Definitions extends reflect.api.StandardDefinitions {
/** Removing the anyref parent they acquire from having a source file.
*/
- setParents(AnyValClass, anyparam)
+ setParents(AnyValClass, List(NotNullClass.tpe, AnyClass.tpe))
ScalaValueClasses foreach { sym =>
setParents(sym, anyvalparam)
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index eb7c5a6699..3ee8b62bc6 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -2757,7 +2757,7 @@ self =>
Template(List(scalaDot(tpnme.AnyVal)), self, body)
}
else if (parents0 exists isReferenceToAnyVal) {
- // TODO - enforce @inline annotation, and no other parents
+ // @inline and other restrictions enforced in refchecks
Template(parents0, self, body)
}
else if (name == tpnme.AnyVal) {
diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
index 5f62dfab39..c308a3633e 100644
--- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
@@ -84,10 +84,11 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = {
var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth)
val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpe))
- val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType
+ val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType
def transform(clonedType: Type): Type = clonedType match {
case MethodType(params, restpe) =>
- MethodType(List(thisParam), clonedType)
+ // I assume it was a bug that this was dropping params...
+ MethodType(thisParam :: params, clonedType)
case NullaryMethodType(restpe) =>
MethodType(List(thisParam), restpe)
}
@@ -116,7 +117,13 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
val extensionMeth = companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE | FINAL)
.setAnnotations(origMeth.annotations)
companion.info.decls.enter(extensionMeth)
- extensionMeth.setInfo(extensionMethInfo(extensionMeth, origMeth.info, currentOwner))
+ val newInfo = extensionMethInfo(extensionMeth, origMeth.info, currentOwner)
+ extensionMeth setInfo newInfo
+ log("Inline class %s spawns extension method.\n Old: %s\n New: %s".format(
+ currentOwner,
+ origMeth.defString,
+ extensionMeth.defString)) // extensionMeth.defStringSeenAs(origInfo
+
def thisParamRef = gen.mkAttributedIdent(extensionMeth.info.params.head setPos extensionMeth.pos)
val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info
val origTpeParams = origMeth.typeParams ::: currentOwner.typeParams
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index b9f3adec75..2f31347829 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1407,7 +1407,9 @@ trait Namers extends MethodSynthesis {
}
if (sym.isClass && sym.hasAnnotation(ScalaInlineClass) && !phase.erasedTypes) {
- ensureParent(NotNullClass)
+ if (!sym.isSubClass(AnyValClass))
+ ensureParent(NotNullClass)
+
sym setFlag FINAL
}
@@ -1437,7 +1439,7 @@ trait Namers extends MethodSynthesis {
checkNoConflict(PRIVATE, PROTECTED)
// checkNoConflict(PRIVATE, OVERRIDE) // this one leads to bad error messages like #4174, so catch in refchecks
// checkNoConflict(PRIVATE, FINAL) // can't do this because FINAL also means compile-time constant
- checkNoConflict(ABSTRACT, FINAL)
+ // checkNoConflict(ABSTRACT, FINAL) // this one gives a bad error for non-@inline classes which extend AnyVal
// @PP: I added this as a sanity check because these flags are supposed to be
// converted to ABSOVERRIDE before arriving here.
checkNoConflict(ABSTRACT, OVERRIDE)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 4449116fd1..f7a6815905 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -683,16 +683,16 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
if (abstractErrors.nonEmpty)
unit.error(clazz.pos, abstractErrorMessage)
- } else if (clazz.isTrait) {
- // prevent abstract methods in interfaces that override final members in Object; see #4431
- if (!(clazz isSubClass AnyValClass)) {
+ }
+ else if (clazz.isTrait && !(clazz isSubClass AnyValClass)) {
+ // For non-AnyVal classes, prevent abstract methods in interfaces that override
+ // final members in Object; see #4431
for (decl <- clazz.info.decls.iterator) {
val overridden = decl.overriddenSymbol(ObjectClass)
if (overridden.isFinal)
unit.error(decl.pos, "trait cannot redefine final method from class AnyRef")
}
}
- }
/** Returns whether there is a symbol declared in class `inclazz`
* (which must be different from `clazz`) whose name and type
@@ -1527,6 +1527,19 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
)
case _ => ()
}
+
+ // verify classes extending AnyVal meet the requirements
+ // (whatever those are to be, but at least: @inline annotation)
+ private def checkAnyValSubclass(clazz: Symbol) = {
+ if ((clazz isSubClass AnyValClass) && (clazz ne AnyValClass) && !isPrimitiveValueClass(clazz)) {
+ if (!clazz.hasAnnotation(ScalaInlineClass))
+ unit.error(clazz.pos, "Only @inline classes are allowed to extend AnyVal")
+ if (clazz.isTrait)
+ unit.error(clazz.pos, "Only @inline classes (not traits) are allowed to extend AnyVal")
+ if (clazz.tpe <:< AnyRefClass.tpe)
+ unit.error(clazz.pos, "Classes which extend AnyVal may not have an ancestor which inherits AnyRef")
+ }
+ }
override def transform(tree: Tree): Tree = {
val savedLocalTyper = localTyper
@@ -1562,6 +1575,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
checkOverloadedRestrictions(currentOwner)
val bridges = addVarargBridges(currentOwner)
checkAllOverrides(currentOwner)
+ checkAnyValSubclass(currentOwner)
+
if (bridges.nonEmpty) treeCopy.Template(tree, parents, self, body ::: bridges)
else tree
diff --git a/src/library/scala/AnyVal.scala b/src/library/scala/AnyVal.scala
index fb36d61c57..ed32fb7302 100644
--- a/src/library/scala/AnyVal.scala
+++ b/src/library/scala/AnyVal.scala
@@ -25,7 +25,7 @@ package scala
* The ''integer types'' include the subrange types as well as [[scala.Int]] and [[scala.Long]].
* The ''floating point types'' are [[scala.Float]] and [[scala.Double]].
*/
-trait AnyVal {
+trait AnyVal extends NotNull {
// disabled for now to make the standard build go through.
// Once we have a new strap we can uncomment this and delete the AnyVal_getClass entry in Definitions.
// def getClass(): Class[_ <: AnyVal] = ???
diff --git a/src/library/scala/NotNull.scala b/src/library/scala/NotNull.scala
index d47d47a83e..f90b95c789 100644
--- a/src/library/scala/NotNull.scala
+++ b/src/library/scala/NotNull.scala
@@ -6,8 +6,6 @@
** |/ **
\* */
-
-
package scala
/**
diff --git a/test/files/neg/anyval-children-2.check b/test/files/neg/anyval-children-2.check
new file mode 100644
index 0000000000..cb327faeeb
--- /dev/null
+++ b/test/files/neg/anyval-children-2.check
@@ -0,0 +1,4 @@
+anyval-children-2.scala:1: error: Only @inline classes (not traits) are allowed to extend AnyVal
+@inline trait NotOkDingus1 extends AnyVal // fail
+ ^
+one error found
diff --git a/test/files/neg/anyval-children-2.scala b/test/files/neg/anyval-children-2.scala
new file mode 100644
index 0000000000..4034eb22dd
--- /dev/null
+++ b/test/files/neg/anyval-children-2.scala
@@ -0,0 +1 @@
+@inline trait NotOkDingus1 extends AnyVal // fail
diff --git a/test/files/neg/anyval-children.check b/test/files/neg/anyval-children.check
new file mode 100644
index 0000000000..cbb5a2b1d1
--- /dev/null
+++ b/test/files/neg/anyval-children.check
@@ -0,0 +1,26 @@
+anyval-children.scala:7: error: illegal inheritance; superclass Bippy
+ is not a subclass of the superclass Object
+ of the mixin trait ScalaObject
+class NotOkBippy1 extends Bippy // fail
+ ^
+anyval-children.scala:9: error: illegal inheritance; superclass Bippy
+ is not a subclass of the superclass Object
+ of the mixin trait Immutable
+class NotOkBippy2 extends Bippy with Immutable //fail
+ ^
+anyval-children.scala:9: error: illegal inheritance; superclass Bippy
+ is not a subclass of the superclass Object
+ of the mixin trait ScalaObject
+class NotOkBippy2 extends Bippy with Immutable //fail
+ ^
+anyval-children.scala:11: error: illegal inheritance; superclass Bippy
+ is not a subclass of the superclass Object
+ of the mixin trait Immutable
+@inline class NotOkBippy3 extends Bippy with Immutable //fail
+ ^
+anyval-children.scala:11: error: illegal inheritance; superclass Bippy
+ is not a subclass of the superclass Object
+ of the mixin trait ScalaObject
+@inline class NotOkBippy3 extends Bippy with Immutable //fail
+ ^
+5 errors found
diff --git a/test/files/neg/anyval-children.scala b/test/files/neg/anyval-children.scala
new file mode 100644
index 0000000000..5a6109f786
--- /dev/null
+++ b/test/files/neg/anyval-children.scala
@@ -0,0 +1,14 @@
+class Bippy extends AnyVal // fail
+
+@inline class NotOkDingus2 extends Immutable with AnyVal // fail
+
+@inline object NotOkDingus3 extends AnyVal // fail
+
+class NotOkBippy1 extends Bippy // fail
+
+class NotOkBippy2 extends Bippy with Immutable //fail
+
+@inline class NotOkBippy3 extends Bippy with Immutable //fail
+
+
+@inline class OkBippy extends AnyVal // ok
diff --git a/test/files/pos/anyval-children.scala b/test/files/pos/anyval-children.scala
index 7a2eda8b3f..4ef10a094f 100644
--- a/test/files/pos/anyval-children.scala
+++ b/test/files/pos/anyval-children.scala
@@ -1 +1 @@
-class Bippy extends AnyVal \ No newline at end of file
+@inline class Bippy extends AnyVal \ No newline at end of file