diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 58 | ||||
-rw-r--r-- | test/files/neg/t5882.check | 6 | ||||
-rw-r--r-- | test/files/neg/t6359.check | 6 | ||||
-rw-r--r-- | test/files/neg/valueclasses-impl-restrictions.check | 21 | ||||
-rw-r--r-- | test/files/neg/valueclasses-impl-restrictions.scala | 28 | ||||
-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 |