diff options
author | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-10-03 08:57:33 -0700 |
---|---|---|
committer | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-10-03 08:57:33 -0700 |
commit | cd847070254a2c6055e543c65ec82ea8429ee757 (patch) | |
tree | 157ce7c78909251cb9b1985d43cb23e479c10747 /src | |
parent | 19eea2b6a0228131b26636c531f1118db6cf79a8 (diff) | |
parent | 5d9cde105e804d14e2c15c3e15c147a56cb67ff1 (diff) | |
download | scala-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.scala | 7 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Constructors.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 85 |
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, |