summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala58
-rw-r--r--test/files/neg/t5882.check6
-rw-r--r--test/files/neg/t6359.check6
-rw-r--r--test/files/neg/valueclasses-impl-restrictions.check21
-rw-r--r--test/files/neg/valueclasses-impl-restrictions.scala28
-rw-r--r--test/pending/run/t5882.scala (renamed from test/files/run/t5882.scala)0
-rw-r--r--test/pending/run/t6408.scala (renamed from test/files/run/t6408.scala)0
7 files changed, 105 insertions, 14 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index c73263a101..d3e74f75b4 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1389,25 +1389,63 @@ 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
- for (stat <- body) {
- def notAllowed(what: String) = {
- val where = if (clazz.isTrait) "universal trait extending from class Any" else "value class"
- unit.error(stat.pos, s"$what is not allowed in $where")
+ 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)
}
- stat match {
- case _: Import | _: TypeDef | _: ClassDef | EmptyTree => // OK
- case DefDef(_, name, _, _, _, _) =>
+ }
+ 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")
- else if (stat.symbol != null && (stat.symbol hasFlag PARAMACCESSOR))
+ 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 =>
- notAllowed("nested object")
+ //see https://issues.scala-lang.org/browse/SI-6359
+ implRestriction(stat, "nested object")
case _ =>
notAllowed("this statement")
}
diff --git a/test/files/neg/t5882.check b/test/files/neg/t5882.check
index b9ca5a1b2a..e0958e19d9 100644
--- a/test/files/neg/t5882.check
+++ b/test/files/neg/t5882.check
@@ -1,7 +1,9 @@
-t5882.scala:4: error: nested object is not allowed in value class
+t5882.scala:4: error: implementation restriction: nested class is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
case class Scope()
^
-t5882.scala:5: error: nested object is not allowed in value class
+t5882.scala:5: error: implementation restriction: nested object is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
object Bar
^
two errors found
diff --git a/test/files/neg/t6359.check b/test/files/neg/t6359.check
index 2aa1ac5035..5bcdc57331 100644
--- a/test/files/neg/t6359.check
+++ b/test/files/neg/t6359.check
@@ -1,7 +1,9 @@
-t6359.scala:3: error: value class may not have nested module definitions
+t6359.scala:3: error: implementation restriction: nested object is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
object X
^
-t6359.scala:4: error: value class may not have nested class definitions
+t6359.scala:4: error: implementation restriction: nested class is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
class Y
^
two errors found
diff --git a/test/files/neg/valueclasses-impl-restrictions.check b/test/files/neg/valueclasses-impl-restrictions.check
new file mode 100644
index 0000000000..17d07ba960
--- /dev/null
+++ b/test/files/neg/valueclasses-impl-restrictions.check
@@ -0,0 +1,21 @@
+valueclasses-impl-restrictions.scala:3: error: implementation restriction: nested object is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
+ object X
+ ^
+valueclasses-impl-restrictions.scala:4: error: implementation restriction: lazy val is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
+ lazy val y = 1
+ ^
+valueclasses-impl-restrictions.scala:10: error: implementation restriction: nested trait is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
+ trait I2 {
+ ^
+valueclasses-impl-restrictions.scala:16: error: implementation restriction: nested class is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
+ val i2 = new I2 { val q = x.s }
+ ^
+valueclasses-impl-restrictions.scala:22: error: implementation restriction: nested class is not allowed in value class
+This restriction is planned to be removed in subsequent releases.
+ private[this] class I2(val q: String)
+ ^
+5 errors found
diff --git a/test/files/neg/valueclasses-impl-restrictions.scala b/test/files/neg/valueclasses-impl-restrictions.scala
new file mode 100644
index 0000000000..53396db958
--- /dev/null
+++ b/test/files/neg/valueclasses-impl-restrictions.scala
@@ -0,0 +1,28 @@
+class M(val t: Int) extends AnyVal {
+ def lazyString = {
+ object X
+ lazy val y = 1
+ () => X
+ }
+}
+
+class X1(val s: String) extends AnyVal {
+ trait I2 {
+ val q: String
+ def z = s + q
+ }
+
+ def y(x: X1) = {
+ val i2 = new I2 { val q = x.s }
+ i2.z
+ }
+}
+
+class X2(val s: String) extends AnyVal {
+ private[this] class I2(val q: String)
+
+ def y(i: Int) = {
+ val i2 = new I2(i.toString)
+ i2.q + s
+ }
+}
diff --git a/test/files/run/t5882.scala b/test/pending/run/t5882.scala
index 47996d3068..47996d3068 100644
--- a/test/files/run/t5882.scala
+++ b/test/pending/run/t5882.scala
diff --git a/test/files/run/t6408.scala b/test/pending/run/t6408.scala
index ff17480b35..ff17480b35 100644
--- a/test/files/run/t6408.scala
+++ b/test/pending/run/t6408.scala