Skip to content

Fix #5857: handle qualified private/protected case class constructor #5908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ object desugar {
val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
cpy.ValDef(vparam)(rhs = EmptyTree))
DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr)
.withFlags(Synthetic | constr1.mods.flags & copiedAccessFlags) :: Nil
.withMods(Modifiers(Synthetic | constr1.mods.flags & copiedAccessFlags, constr1.mods.privateWithin)) :: Nil
}
}

Expand Down Expand Up @@ -577,11 +577,18 @@ object desugar {
else
enumApplyResult(cdef, parents, derivedEnumParams, appliedRef(enumClassRef, derivedEnumParams))

// true if access to the apply method has to be restricted
// i.e. if the case class constructor is either private or qualified private
def restrictedAccess = {
val mods = constr1.mods
mods.is(Private) || (!mods.is(Protected) && mods.hasPrivateWithin)
}

val companionParent =
if (constrTparams.nonEmpty ||
constrVparamss.length > 1 ||
mods.is(Abstract) ||
constr.mods.is(Private)) anyRef
restrictedAccess) anyRef
else
// todo: also use anyRef if constructor has a dependent method type (or rule that out)!
(constrVparamss :\ (if (isEnumCase) applyResultTpt else classTypeRef)) (
Expand All @@ -592,8 +599,13 @@ object desugar {
if (mods is Abstract) Nil
else {
val copiedFlagsMask = DefaultParameterized | (copiedAccessFlags & Private)
val appMods = {
val mods = Modifiers(Synthetic | constr1.mods.flags & copiedFlagsMask)
if (restrictedAccess) mods.withPrivateWithin(constr1.mods.privateWithin)
else mods
}
val app = DefDef(nme.apply, derivedTparams, derivedVparamss, applyResultTpt, widenedCreatorExpr)
.withFlags(Synthetic | constr1.mods.flags & copiedFlagsMask)
.withMods(appMods)
app :: widenDefs
}
val unapplyMeth = {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Comments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ object Comments {
tree match {
case tree: untpd.DefDef =>
val newName = ctx.freshNames.newName(tree.name, NameKinds.DocArtifactName)
tree.copy(name = newName)
untpd.cpy.DefDef(tree)(name = newName)
case _ =>
ctx.error(ProperDefinitionNotFound(), ctx.source.atSpan(codePos))
tree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) {
val bodyText = " {" ~~ toTextGlobal(impl.body, "\n") ~ "}"
parentsText.provided(parents.nonEmpty) ~ bodyText
}
else super.toTextTemplate(impl.copy(parentsOrDerived = parents, preBody = body), ofNew)
else super.toTextTemplate(untpd.cpy.Template(impl)(parents = parents, body = body), ofNew)
}

override protected def typeApplyText[T >: Untyped](tree: TypeApply[T]): Text = {
Expand Down
42 changes: 42 additions & 0 deletions tests/neg/caseclass-access.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
case class A private (i: Int)
object A
object ATest {
def a1: A = A(1) // error: apply is private
def a2: A = a1.copy(2) // error: copy is private
}

case class B private (i: Int) // ok: no user-defined companion object
object BTest {
def b1: B = B(1) // error: apply is private
def b2: B = b1.copy(2) // error: copy is private
}

object qualified_private {
case class C private[qualified_private] (i: Int)
object C

case class D private[qualified_private] (i: Int) // ok: no user-defined companion object
}
object QPrivTest {
import qualified_private._
def c1: C = C(1) // error: apply is private
def c2: C = c1.copy(2) // error: copy is private

def d1: D = D(1) // error: apply is private
def d2: D = d1.copy(2) // error: copy is private
}

case class E protected (i: Int)
object ETest {
def e1: E = E(1)
def e2: E = e2.copy(2) // error: copy is protected
}

object qualified_protected {
case class F protected[qualified_protected] (i: Int)
}
object QProtTest {
import qualified_protected._
def f1: F = F(1)
def f2: F = f2.copy(2) // error: copy is protected
}
47 changes: 47 additions & 0 deletions tests/pos/caseclass-access.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
case class A private (i: Int)
object A {
def a = A(1).copy(2) // apply and copy are accessible in companion
}

case class B private (i: Int) { // no user-defined companion object, should compile
def b = B(1).copy(2) // apply and copy are accessible
}

object qualified_private {
case class A private[qualified_private] (i: Int)
object A {
def a = A(1).copy(2) // apply and copy are accessible in companion
}

def a = A(1).copy(2) // apply and copy are accessible in qualified_private object

case class B private[qualified_private] (i: Int) { // no user-defined companion object, should compile
def b = B(1).copy(2) // apply and copy are accessible
}

def b = B(1).copy(2) // apply and copy are accessible in qualified_private object
}

case class C protected (i: Int)
class CSub extends C(1) {
def c = copy(2) // copy is accessible in subclass
}
object CTest {
def c = C(1) // apply is public
}

object qualified_protected {
case class C protected[qualified_protected] (i: Int)
class CSub extends C(1) {
def c = copy(2) // copy is accessible in subclass
}
object CTest {
def c = C(1) // apply is public
def checkExtendsFunction: Int => C = C // companion extends (Int => C)
}

def c = C(1).copy(2)
}
object CQualifiedTest {
def c = qualified_protected.C(1) // apply is public
}