diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2013-09-19 14:53:03 +0200 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2013-09-29 22:37:17 +0200 |
commit | 4595ac665674b25af776d499ae1da61bb297d379 (patch) | |
tree | 5ce5c591fba9237a10abc82e7efeb6e61b755952 /src/compiler | |
parent | 0aaf59149871f817a67c1fefcc3b0457fcb5e4fc (diff) | |
download | scala-4595ac665674b25af776d499ae1da61bb297d379.tar.gz scala-4595ac665674b25af776d499ae1da61bb297d379.tar.bz2 scala-4595ac665674b25af776d499ae1da61bb297d379.zip |
SI-7859 Value classes may wrap a non-public member
We allow value class constructors to be non-public, so to be regular,
we should also allow the same for the param accessor.
This commit uses the 'makeNotPrivate' machinery to ensure that
the backend can generate the requisite unboxing calls.
This commit:
- refactors the code that enforced the restrictions, improving
a few error messages and positions. The remaining restrictions
needed some rewording in light of this change.
- allows value classes to have non-public, val parameters.
private[this] / protected[this] are still disallowed as value
classes don't have a concept of `this`, and because trying to
accomdate then would complicate the implementation.
This means that `class C(x: Int) extends AnyVal` is not allowed,
the user still must write `private val x: Int` or `val x: Int`.
- Outlaw `class C()()(val x: Int) extends AnyVal` to curtail any
bugs that might lurk in such a formulation.
The tests:
- Show that the privacy is respected in the typer phase, under
joint and separate compilation. We don't want a repeat performance
of SI-6601.
- Show that code that needs compiler-generated unboxes works under
both compilation scenarios
- Checks that the remaining restrictions are enforced and well
communicated.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 30 |
2 files changed, 22 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 6a405295cf..a381019372 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -191,6 +191,9 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { checkNonCyclic(currentOwner.pos, Set(), currentOwner) */ extensionDefs(currentOwner.companionModule) = new mutable.ListBuffer[Tree] currentOwner.primaryConstructor.makeNotPrivate(NoSymbol) + // SI-7859 make param accessors accessible so the erasure can generate unbox operations. + val paramAccessors = currentOwner.info.decls.filter(sym => sym.isParamAccessor && sym.isMethod) + paramAccessors.foreach(_.makeNotPrivate(currentOwner)) super.transform(tree) } else if (currentOwner.isStaticOwner) { super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 157c6ba4de..e3ec8b7fb8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1356,17 +1356,25 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper 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.isParamAccessor) match { - case List(acc) => - def isUnderlyingAcc(sym: Symbol) = - sym == acc || acc.hasAccessorFlag && sym == acc.accessed - if (acc.accessBoundary(clazz) != rootMirror.RootClass) - 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") - checkEphemeral(clazz, body filterNot (stat => isUnderlyingAcc(stat.symbol))) - case x => - unit.error(clazz.pos, "value class needs to have exactly one public val parameter") + clazz.primaryConstructor.paramss match { + case List(List(param)) => + val decls = clazz.info.decls + val paramAccessor = clazz.constrParamAccessors.head + if (paramAccessor.isMutable) + unit.error(paramAccessor.pos, "value class parameter must not be a var") + val accessor = decls.toList.find(x => x.isMethod && x.accessedOrSelf == paramAccessor) + accessor match { + case None => + unit.error(paramAccessor.pos, "value class parameter must be a val and not be private[this]") + case Some(acc) if acc.isProtectedLocal => + unit.error(paramAccessor.pos, "value class parameter must not be protected[this]") + case Some(acc) => + if (acc.tpe.typeSymbol.isDerivedValueClass) + unit.error(acc.pos, "value class may not wrap another user-defined value class") + checkEphemeral(clazz, body filterNot (stat => stat.symbol != null && stat.symbol.accessedOrSelf == paramAccessor)) + } + case _ => + unit.error(clazz.pos, "value class needs to have exactly one val parameter") } } |