diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 516028ec9f1f..a47c991c9fcc 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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 } } @@ -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)) ( @@ -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 = { diff --git a/compiler/src/dotty/tools/dotc/core/Comments.scala b/compiler/src/dotty/tools/dotc/core/Comments.scala index 06079a6e4c48..16f899eb939c 100644 --- a/compiler/src/dotty/tools/dotc/core/Comments.scala +++ b/compiler/src/dotty/tools/dotc/core/Comments.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index 029551e01589..f5f344de7a0f 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -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 = { diff --git a/tests/neg/caseclass-access.scala b/tests/neg/caseclass-access.scala new file mode 100644 index 000000000000..e43e6aaa5547 --- /dev/null +++ b/tests/neg/caseclass-access.scala @@ -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 +} diff --git a/tests/pos/caseclass-access.scala b/tests/pos/caseclass-access.scala new file mode 100644 index 000000000000..1d8dc03089e7 --- /dev/null +++ b/tests/pos/caseclass-access.scala @@ -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 +}