Skip to content

Protected case class constructor -> non-public apply #14266

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 21, 2022
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
16 changes: 2 additions & 14 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -693,23 +693,11 @@ object desugar {
// For all other classes, the parent is AnyRef.
val companions =
if (isCaseClass) {

// 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 applyMeths =
if (mods.is(Abstract)) Nil
else {
val copiedFlagsMask = copiedAccessFlags & Private
val appMods = {
val mods = Modifiers(Synthetic | constr1.mods.flags & copiedFlagsMask)
if (restrictedAccess) mods.withPrivateWithin(constr1.mods.privateWithin)
else mods
}
val appMods =
Modifiers(Synthetic | constr1.mods.flags & copiedAccessFlags).withPrivateWithin(constr1.mods.privateWithin)
val appParamss =
derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) =>
ap.withMods(ap.mods | (cp.mods.flags & HasDefault)))
Expand Down
16 changes: 12 additions & 4 deletions tests/neg/caseclass-access.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
case class A private (i: Int)
object A
object ATest {
def a0: A = new A(0) // error: constructor is private
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 b0: B = new B(0) // error: constructor is private
def b1: B = B(1) // error: apply is private
def b2: B = b1.copy(2) // error: copy is private
}
Expand All @@ -19,24 +21,30 @@ object qualified_private {
}
object QPrivTest {
import qualified_private.*
def c0: C = new C(0) // error: constructor is private
def c1: C = C(1) // error: apply is private
def c2: C = c1.copy(2) // error: copy is private

def d0: D = new D(0) // error: constructor 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
def e0: E = new E(0) // error: constructor is protected
def e1: E = E(1) // error: apply is protected
def e2: E = e1.copy(2) // error: copy is protected
def eta: Int => E = E // error: apply 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
def f0: F = new F(0) // error: constructor is protected
def f1: F = F(1) // error: apply is protected
def f2: F = f1.copy(2) // error: copy is protected
def eta: Int => F = F // error: apply is protected
}
7 changes: 7 additions & 0 deletions tests/neg/i14187.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
sealed case class Foo protected (i: Int, j: Int)

final class Bar(n: Int) extends Foo(n, n)

class Other:
def foo = Foo(1, 2) // error
def foo2 = Foo.apply(1, 2) // error
20 changes: 9 additions & 11 deletions tests/pos/caseclass-access.scala
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
case class A private (i: Int)
object A {
def a0 = new A(0) // constructor is accessible in companion
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 b0 = new B(0) // constructor is accessible
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 a0 = new A(0) // constructor is accessible in companion
def a = A(1).copy(2) // apply and copy are accessible in companion
}

def a0 = new A(0) // constructor is accessible in qualified_private object
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 b0 = new B(0) // constructor is accessible
def b = B(1).copy(2) // apply and copy are accessible
}

def b0 = new B(0) // constructor is accessible in qualified_private object
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 eta: Int => C = C // can eta-expand C.apply method

def c = C(1).copy(2)
}
object CQualifiedTest {
def c = qualified_protected.C(1) // apply is public
def c0 = new C(0) // constructor is accessible in qualified_protected object
def c = C(1).copy(2) // apply and copy are accessible in qualified_protected object
}