diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index bb950fbe43cd..47a47f10f905 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -105,8 +105,8 @@ object PrepareInlineable { def preTransform(tree: Tree)(using Context): Tree = tree match { case tree: RefTree if needsAccessor(tree.symbol) => if (tree.symbol.isConstructor) { - report.error("Implementation restriction: cannot use private constructors in inline methods", tree.srcPos) - tree // TODO: create a proper accessor for the private constructor + report.error("Private constructors used in inline methods require @publicInBinary", tree.srcPos) + tree } else val accessor = useAccessor(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala index 9a6a04621074..0f7dde993b17 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala @@ -20,11 +20,14 @@ import ValueClasses.* * Make private accessor in value class not-private. This is necessary to unbox * the value class when accessing it from separate compilation units * - * Also, make non-private any private parameter forwarders that forward to an inherited + * Make non-private any private parameter forwarders that forward to an inherited * public or protected parameter accessor with the same name as the forwarder. * This is necessary since private methods are not allowed to have the same name * as inherited public ones. * + * Also, make non-private any private constructor that is annotated with `@publicInBinary`. + * (See SIP-52) + * * See discussion in https://github.com/scala/scala3/pull/784 * and https://github.com/scala/scala3/issues/783 */ @@ -102,6 +105,8 @@ class ExpandPrivate extends MiniPhase with IdentityDenotTransformer { thisPhase override def transformDefDef(tree: DefDef)(using Context): DefDef = { val sym = tree.symbol tree.rhs match { + case _ if sym.isConstructor && sym.hasPublicInBinary => + sym.ensureNotPrivate.installAfter(thisPhase) case Apply(sel @ Select(_: Super, _), _) if sym.isAllOf(PrivateParamAccessor) && sel.symbol.is(ParamAccessor) && sym.name == sel.symbol.name => sym.ensureNotPrivate.installAfter(thisPhase) diff --git a/tests/neg/i22498.check b/tests/neg/i22498.check new file mode 100644 index 000000000000..39add35cbf1b --- /dev/null +++ b/tests/neg/i22498.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i22498.scala:7:32 ---------------------------------------------------------------------------------- +7 | inline def proxy: Foo = new Foo(0) // error + | ^^^ + | Private constructors used in inline methods require @publicInBinary diff --git a/tests/neg/i22498.scala b/tests/neg/i22498.scala new file mode 100644 index 000000000000..43a8c8502308 --- /dev/null +++ b/tests/neg/i22498.scala @@ -0,0 +1,7 @@ +//> using options -experimental + +import scala.annotation.publicInBinary + +class Foo: + private def this(x: Int) = this() + inline def proxy: Foo = new Foo(0) // error diff --git a/tests/run/i22497.check b/tests/run/i22497.check new file mode 100644 index 000000000000..7ef6ca89b9f4 --- /dev/null +++ b/tests/run/i22497.check @@ -0,0 +1,3 @@ +public Foo() +public Foo(int) +public Foo(java.lang.String) diff --git a/tests/run/i22497.scala b/tests/run/i22497.scala new file mode 100644 index 000000000000..a047a98e2b1e --- /dev/null +++ b/tests/run/i22497.scala @@ -0,0 +1,13 @@ +// scalajs: --skip + +import scala.annotation.publicInBinary +import scala.annotation.experimental + +@experimental +class Foo: + @publicInBinary private def this(i: Int) = this() + @publicInBinary protected def this(i: String) = this() + +@experimental +@main def Test = + println(classOf[Foo].getConstructors().mkString("\n")) diff --git a/tests/run/i22498.scala b/tests/run/i22498.scala new file mode 100644 index 000000000000..2208978e5e0d --- /dev/null +++ b/tests/run/i22498.scala @@ -0,0 +1,10 @@ +//> using options -experimental + +import scala.annotation.publicInBinary + +class Foo: + @publicInBinary private def this(x: Int) = this() + inline def proxy: Foo = new Foo(0) + +@main def Test = + val x = (new Foo).proxy