diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index 10fc6a9fa46e..eb3cce818ee5 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -149,9 +149,11 @@ object NamerOps: */ def addConstructorApplies(scope: MutableScope, cls: ClassSymbol, modcls: ClassSymbol)(using Context): scope.type = def proxy(constr: Symbol): Symbol = + var flags = ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags) + if cls.is(Protected) && !modcls.is(Protected) then flags |= Protected newSymbol( modcls, nme.apply, - ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags), + flags, ApplyProxyCompleter(constr), cls.privateWithin, constr.coord) @@ -175,9 +177,11 @@ object NamerOps: /** A new symbol that is the constructor companion for class `cls` */ def classConstructorCompanion(cls: ClassSymbol)(using Context): TermSymbol = + var flags = ConstructorCompanionFlags + if cls.is(Protected) then flags |= Protected val companion = newModuleSymbol( cls.owner, cls.name.toTermName, - ConstructorCompanionFlags, ConstructorCompanionFlags, + flags, flags, constructorCompanionCompleter(cls), coord = cls.coord, compUnitInfo = cls.compUnitInfo) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 2b67bfe61a81..96590cb84544 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1198,9 +1198,8 @@ trait Applications extends Compatibility { // // summonFrom { // case given A[t] => - // summonFrom + // summonFrom: // case given `t` => ... - // } // } // // the second `summonFrom` should expand only once the first `summonFrom` is diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1eb48756136d..fd35471da07e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4176,9 +4176,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer readapt(tree.appliedToNone) // insert () to primary constructors else errorTree(tree, em"Missing arguments for $methodStr") - case _ => tryInsertApplyOrImplicit(tree, pt, locked) { - errorTree(tree, MethodDoesNotTakeParameters(tree)) - } + case _ => + tryInsertApplyOrImplicit(tree, pt, locked): + errorTree(tree, MethodDoesNotTakeParameters(tree)) } def adaptNoArgsImplicitMethod(wtp: MethodType): Tree = { diff --git a/docs/_docs/reference/other-new-features/creator-applications.md b/docs/_docs/reference/other-new-features/creator-applications.md index 8b1de02b2f25..f3b58b87bc48 100644 --- a/docs/_docs/reference/other-new-features/creator-applications.md +++ b/docs/_docs/reference/other-new-features/creator-applications.md @@ -39,8 +39,11 @@ The precise rules are as follows: - the class has a companion object (which might have been generated in step 1), and - that companion object does not already define a member named `apply`. - Each generated `apply` method forwards to one constructor of the class. It has the - same type and value parameters as the constructor. + Each generated `apply` method forwards to one constructor of the class. + It has the same type and value parameters as the constructor, + as well as the same access restriction as the class. + If the class is `protected`, then either the companion object must be `protected` + or the `apply` method will be made `protected`. Constructor proxy companions cannot be used as values by themselves. A proxy companion object must be selected with `apply` (or be applied to arguments, in which case the `apply` is implicitly diff --git a/tests/neg/i22560.scala b/tests/neg/i22560.scala new file mode 100644 index 000000000000..2957ac4a1bf1 --- /dev/null +++ b/tests/neg/i22560.scala @@ -0,0 +1,22 @@ + +class A: + protected class B + +// This fails to compile, as expected +val x = A().B() // error + +object C: + protected val p = "protected" + protected def getString() = "Hello!" + protected class D: + def d = D() // ok + +// This fails to compile +// val y = C.p + +// And this also fails to compile +// val z = C.getString() + +// However, this compiles just fine. +val alpha = C.D() // error +val beta = new C.D() // error diff --git a/tests/neg/i22560b.scala b/tests/neg/i22560b.scala new file mode 100644 index 000000000000..bce079e0eeb2 --- /dev/null +++ b/tests/neg/i22560b.scala @@ -0,0 +1,18 @@ + +class Enumeration: + protected class Val(i: Int): + def this() = this(42) + object Val + +class Test extends Enumeration: + val Hearts = Val(27) // error + val Diamonds = Val() // error + +package p: + private[p] class C(i: Int) // ctor proxy gets privateWithin of class + private[p] class D(i: Int) + object D + +package q: + def f() = p.C(42) // error + def g() = p.D(42) // error diff --git a/tests/pos/i22560.scala b/tests/pos/i22560.scala new file mode 100644 index 000000000000..947923aba259 --- /dev/null +++ b/tests/pos/i22560.scala @@ -0,0 +1,22 @@ + +package companionless: + + class Enumeration: + protected class Val(i: Int): + def this() = this(42) + + class Test extends Enumeration: + val Hearts = Val(27) + val Diamonds = Val() + + +package companioned: + + class Enumeration: + protected class Val(i: Int): + def this() = this(42) + protected object Val + + class Test extends Enumeration: + val Hearts = Val(27) + val Diamonds = Val()