Skip to content

Commit b69f6f7

Browse files
authored
Merge pull request #5908 from Jasper-M/fix/5857
Fix #5857: handle qualified private/protected case class constructor
2 parents 9ba6a04 + b84b6f6 commit b69f6f7

File tree

5 files changed

+106
-5
lines changed

5 files changed

+106
-5
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ object desugar {
499499
val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
500500
cpy.ValDef(vparam)(rhs = EmptyTree))
501501
DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr)
502-
.withFlags(Synthetic | constr1.mods.flags & copiedAccessFlags) :: Nil
502+
.withMods(Modifiers(Synthetic | constr1.mods.flags & copiedAccessFlags, constr1.mods.privateWithin)) :: Nil
503503
}
504504
}
505505

@@ -577,11 +577,18 @@ object desugar {
577577
else
578578
enumApplyResult(cdef, parents, derivedEnumParams, appliedRef(enumClassRef, derivedEnumParams))
579579

580+
// true if access to the apply method has to be restricted
581+
// i.e. if the case class constructor is either private or qualified private
582+
def restrictedAccess = {
583+
val mods = constr1.mods
584+
mods.is(Private) || (!mods.is(Protected) && mods.hasPrivateWithin)
585+
}
586+
580587
val companionParent =
581588
if (constrTparams.nonEmpty ||
582589
constrVparamss.length > 1 ||
583590
mods.is(Abstract) ||
584-
constr.mods.is(Private)) anyRef
591+
restrictedAccess) anyRef
585592
else
586593
// todo: also use anyRef if constructor has a dependent method type (or rule that out)!
587594
(constrVparamss :\ (if (isEnumCase) applyResultTpt else classTypeRef)) (
@@ -592,8 +599,13 @@ object desugar {
592599
if (mods is Abstract) Nil
593600
else {
594601
val copiedFlagsMask = DefaultParameterized | (copiedAccessFlags & Private)
602+
val appMods = {
603+
val mods = Modifiers(Synthetic | constr1.mods.flags & copiedFlagsMask)
604+
if (restrictedAccess) mods.withPrivateWithin(constr1.mods.privateWithin)
605+
else mods
606+
}
595607
val app = DefDef(nme.apply, derivedTparams, derivedVparamss, applyResultTpt, widenedCreatorExpr)
596-
.withFlags(Synthetic | constr1.mods.flags & copiedFlagsMask)
608+
.withMods(appMods)
597609
app :: widenDefs
598610
}
599611
val unapplyMeth = {

compiler/src/dotty/tools/dotc/core/Comments.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ object Comments {
123123
tree match {
124124
case tree: untpd.DefDef =>
125125
val newName = ctx.freshNames.newName(tree.name, NameKinds.DocArtifactName)
126-
tree.copy(name = newName)
126+
untpd.cpy.DefDef(tree)(name = newName)
127127
case _ =>
128128
ctx.error(ProperDefinitionNotFound(), ctx.source.atSpan(codePos))
129129
tree

compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) {
7070
val bodyText = " {" ~~ toTextGlobal(impl.body, "\n") ~ "}"
7171
parentsText.provided(parents.nonEmpty) ~ bodyText
7272
}
73-
else super.toTextTemplate(impl.copy(parentsOrDerived = parents, preBody = body), ofNew)
73+
else super.toTextTemplate(untpd.cpy.Template(impl)(parents = parents, body = body), ofNew)
7474
}
7575

7676
override protected def typeApplyText[T >: Untyped](tree: TypeApply[T]): Text = {

tests/neg/caseclass-access.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
case class A private (i: Int)
2+
object A
3+
object ATest {
4+
def a1: A = A(1) // error: apply is private
5+
def a2: A = a1.copy(2) // error: copy is private
6+
}
7+
8+
case class B private (i: Int) // ok: no user-defined companion object
9+
object BTest {
10+
def b1: B = B(1) // error: apply is private
11+
def b2: B = b1.copy(2) // error: copy is private
12+
}
13+
14+
object qualified_private {
15+
case class C private[qualified_private] (i: Int)
16+
object C
17+
18+
case class D private[qualified_private] (i: Int) // ok: no user-defined companion object
19+
}
20+
object QPrivTest {
21+
import qualified_private._
22+
def c1: C = C(1) // error: apply is private
23+
def c2: C = c1.copy(2) // error: copy is private
24+
25+
def d1: D = D(1) // error: apply is private
26+
def d2: D = d1.copy(2) // error: copy is private
27+
}
28+
29+
case class E protected (i: Int)
30+
object ETest {
31+
def e1: E = E(1)
32+
def e2: E = e2.copy(2) // error: copy is protected
33+
}
34+
35+
object qualified_protected {
36+
case class F protected[qualified_protected] (i: Int)
37+
}
38+
object QProtTest {
39+
import qualified_protected._
40+
def f1: F = F(1)
41+
def f2: F = f2.copy(2) // error: copy is protected
42+
}

tests/pos/caseclass-access.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
case class A private (i: Int)
2+
object A {
3+
def a = A(1).copy(2) // apply and copy are accessible in companion
4+
}
5+
6+
case class B private (i: Int) { // no user-defined companion object, should compile
7+
def b = B(1).copy(2) // apply and copy are accessible
8+
}
9+
10+
object qualified_private {
11+
case class A private[qualified_private] (i: Int)
12+
object A {
13+
def a = A(1).copy(2) // apply and copy are accessible in companion
14+
}
15+
16+
def a = A(1).copy(2) // apply and copy are accessible in qualified_private object
17+
18+
case class B private[qualified_private] (i: Int) { // no user-defined companion object, should compile
19+
def b = B(1).copy(2) // apply and copy are accessible
20+
}
21+
22+
def b = B(1).copy(2) // apply and copy are accessible in qualified_private object
23+
}
24+
25+
case class C protected (i: Int)
26+
class CSub extends C(1) {
27+
def c = copy(2) // copy is accessible in subclass
28+
}
29+
object CTest {
30+
def c = C(1) // apply is public
31+
}
32+
33+
object qualified_protected {
34+
case class C protected[qualified_protected] (i: Int)
35+
class CSub extends C(1) {
36+
def c = copy(2) // copy is accessible in subclass
37+
}
38+
object CTest {
39+
def c = C(1) // apply is public
40+
def checkExtendsFunction: Int => C = C // companion extends (Int => C)
41+
}
42+
43+
def c = C(1).copy(2)
44+
}
45+
object CQualifiedTest {
46+
def c = qualified_protected.C(1) // apply is public
47+
}

0 commit comments

Comments
 (0)