summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2012-10-03 08:57:33 -0700
committerGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2012-10-03 08:57:33 -0700
commitcd847070254a2c6055e543c65ec82ea8429ee757 (patch)
tree157ce7c78909251cb9b1985d43cb23e479c10747 /src
parent19eea2b6a0228131b26636c531f1118db6cf79a8 (diff)
parent5d9cde105e804d14e2c15c3e15c147a56cb67ff1 (diff)
downloadscala-cd847070254a2c6055e543c65ec82ea8429ee757.tar.gz
scala-cd847070254a2c6055e543c65ec82ea8429ee757.tar.bz2
scala-cd847070254a2c6055e543c65ec82ea8429ee757.zip
Merge pull request #1443 from gkossakowski/anyval-restrictions
AnyVal/value classes restrictions
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeInfo.scala7
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala85
3 files changed, 69 insertions, 26 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala
index 9e46155d14..3e1d606877 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala
@@ -45,11 +45,4 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo {
def isInterface(mods: HasFlags, body: List[Tree]) =
mods.isTrait && (body forall isInterfaceMember)
-
- def isAllowedInUniversalTrait(stat: Tree): Boolean = stat match {
- case _: ValDef => false
- case Import(_, _) | EmptyTree => true
- case _: DefTree => true
- case _ => false
- }
}
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index 23b15a9033..4b9585bb93 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -127,7 +127,8 @@ abstract class Constructors extends Transform with ast.TreeDSL {
import CODE._
val result = mkAssign(to, Ident(from))
- if (from.name != nme.OUTER) result
+ if (from.name != nme.OUTER ||
+ from.tpe.typeSymbol.isPrimitiveValueClass) result
else localTyper.typedPos(to.pos) {
IF (from OBJ_EQ NULL) THEN Throw(NullPointerExceptionClass.tpe) ELSE result
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 335d93f387..5a9920c9e3 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1388,6 +1388,70 @@ trait Typers extends Modes with Adaptations with Tags {
}
}
+ private def checkEphemeral(clazz: Symbol, body: List[Tree]) = {
+ // NOTE: Code appears to be messy in this method for good reason: it clearly
+ // communicates the fact that it implements rather ad-hoc, arbitrary and
+ // non-regular set of rules that identify features that interact badly with
+ // value classes. This code can be cleaned up a lot once implementation
+ // restrictions are addressed.
+ val isValueClass = !clazz.isTrait
+ def where = if (isValueClass) "value class" else "universal trait extending from class Any"
+ def implRestriction(tree: Tree, what: String) =
+ unit.error(tree.pos, s"implementation restriction: $what is not allowed in $where" +
+ "\nThis restriction is planned to be removed in subsequent releases.")
+ /**
+ * Deeply traverses the tree in search of constructs that are not allowed
+ * in value classes (at any nesting level).
+ *
+ * All restrictions this object imposes are probably not fundamental but require
+ * fair amount of work and testing. We are conservative for now when it comes
+ * to allowing language features to interact with value classes.
+ * */
+ object checkEphemeralDeep extends Traverser {
+ override def traverse(tree: Tree): Unit = if (isValueClass) {
+ tree match {
+ case _: ModuleDef =>
+ //see https://issues.scala-lang.org/browse/SI-6359
+ implRestriction(tree, "nested object")
+ //see https://issues.scala-lang.org/browse/SI-6444
+ //see https://issues.scala-lang.org/browse/SI-6463
+ case _: ClassDef =>
+ implRestriction(tree, "nested class")
+ case x: ValDef if x.mods.isLazy =>
+ //see https://issues.scala-lang.org/browse/SI-6358
+ implRestriction(tree, "lazy val")
+ case _ =>
+ }
+ super.traverse(tree)
+ }
+ }
+ for (stat <- body) {
+ def notAllowed(what: String) = unit.error(stat.pos, s"$what is not allowed in $where")
+ stat match {
+ // see https://issues.scala-lang.org/browse/SI-6444
+ // see https://issues.scala-lang.org/browse/SI-6463
+ case ClassDef(mods, _, _, _) if isValueClass =>
+ implRestriction(stat, s"nested ${ if (mods.isTrait) "trait" else "class" }")
+ case _: Import | _: ClassDef | _: TypeDef | EmptyTree => // OK
+ case DefDef(_, name, _, _, _, rhs) =>
+ if (stat.symbol.isAuxiliaryConstructor)
+ notAllowed("secondary constructor")
+ else if (isValueClass && (name == nme.equals_ || name == nme.hashCode_))
+ notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.")
+ else if (stat.symbol != null && stat.symbol.isParamAccessor)
+ notAllowed("additional parameter")
+ checkEphemeralDeep.traverse(rhs)
+ case _: ValDef =>
+ notAllowed("field definition")
+ case _: ModuleDef =>
+ //see https://issues.scala-lang.org/browse/SI-6359
+ implRestriction(stat, "nested object")
+ case _ =>
+ notAllowed("this statement")
+ }
+ }
+ }
+
private def validateDerivedValueClass(clazz: Symbol, body: List[Tree]) = {
if (clazz.isTrait)
unit.error(clazz.pos, "only classes (not traits) are allowed to extend AnyVal")
@@ -1395,7 +1459,7 @@ trait Typers extends Modes with Adaptations with Tags {
unit.error(clazz.pos, "value class may not be a "+
(if (clazz.owner.isTerm) "local class" else "member of another class"))
if (!clazz.isPrimitiveValueClass) {
- clazz.info.decls.toList.filter(acc => acc.isMethod && (acc hasFlag PARAMACCESSOR)) match {
+ clazz.info.decls.toList.filter(acc => acc.isMethod && acc.isParamAccessor) match {
case List(acc) =>
def isUnderlyingAcc(sym: Symbol) =
sym == acc || acc.hasAccessorFlag && sym == acc.accessed
@@ -1403,25 +1467,12 @@ trait Typers extends Modes with Adaptations with Tags {
unit.error(acc.pos, "value class needs to have a publicly accessible val parameter")
else if (acc.tpe.typeSymbol.isDerivedValueClass)
unit.error(acc.pos, "value class may not wrap another user-defined value class")
- for (stat <- body)
- if (!treeInfo.isAllowedInUniversalTrait(stat) && !isUnderlyingAcc(stat.symbol))
- unit.error(stat.pos,
- if (stat.symbol != null && (stat.symbol hasFlag PARAMACCESSOR)) "illegal parameter for value class"
- else "this statement is not allowed in value class: " + stat)
+ checkEphemeral(clazz, body filterNot (stat => isUnderlyingAcc(stat.symbol)))
case x =>
unit.error(clazz.pos, "value class needs to have exactly one public val parameter")
}
}
- def valueClassMayNotHave(at: Tree, what: String) = unit.error(at.pos, s"value class may not have $what")
- body.foreach {
- case dd: DefDef if dd.symbol.isAuxiliaryConstructor => valueClassMayNotHave(dd, "secondary constructors")
- case t => t.foreach {
- case md: ModuleDef => valueClassMayNotHave(md, "nested module definitions")
- case cd: ClassDef => valueClassMayNotHave(cd, "nested class definitions")
- case _ =>
- }
- }
for (tparam <- clazz.typeParams)
if (tparam hasAnnotation definitions.SpecializedClass)
unit.error(tparam.pos, "type parameter of value class may not be specialized")
@@ -1668,9 +1719,7 @@ trait Typers extends Modes with Adaptations with Tags {
}
val impl2 = finishMethodSynthesis(impl1, clazz, context)
if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.normalize.typeSymbol == AnyClass)
- for (stat <- impl2.body)
- if (!treeInfo.isAllowedInUniversalTrait(stat))
- unit.error(stat.pos, "this statement is not allowed in universal trait extending from class Any: "+stat)
+ checkEphemeral(clazz, impl2.body)
if ((clazz != ClassfileAnnotationClass) &&
(clazz isNonBottomSubClass ClassfileAnnotationClass))
restrictionWarning(cdef.pos, unit,