scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.73k stars 1.04k forks source link

Improve error message for missing private companion #16055

Open megri opened 1 year ago

megri commented 1 year ago

Compiler version

3.2.0

Minimized code


package a:
  class Foo()
  private[this] object Foo

import a.Foo

@main def run() =
  val f = Foo()

Output

[error] ./Test.scala:10:11: Not found: Foo
[error]   val f = Foo()
[error]           ^^^

Expectation

I expected the call Foo() to be interpreted as new Foo() but it seems that rather, an apply() method is generated in the companion object, which is private. The intent here seems right but the end result is confusing as the constructor in Foo is indeed public, and the error message does not help either.

bishabosha commented 1 year ago

They are called "universal apply methods" because as you noted they are in fact an apply method in the companion object, so if the companion is private, then there will be no apply method

bishabosha commented 1 year ago

Im not sure what else we can do here because this is documented well - this would need SIP to change

bishabosha commented 1 year ago

Ok, sorry we should revisit the error message to see if we can say that Foo is not accessible from the current scope, but this error message is not specific to universal apply methods

megri commented 1 year ago

They are called "universal apply methods" because they are in fact an apply method in the companion object, so if the companion is private, then there will be no apply method

I agree with this sentiment, but isn't the basic goal of the idea shrouded by the name of its implementation in this case? It's quite difficult to explain to my coworkers that if they make the companion object private the static forwarders are not inserted. From the documentation the implementation itself boils down to an inlined new Foo() so that this is prevented by the companion object being unavailable on the call site seems an oversight 🤔

som-snytt commented 1 year ago

I think a "special" message for "unable to access constructor proxy" is called for.

I agree the documentation is fine. Companion apply is a common "pattern", even aside from case classes. A sentence that the generated apply is a normal method with normal access rules is warranted. (Recall that Scala 2 did a magic rewrite of C() to new C(), so it's useful to say there is no further magic.) Maybe the doc can say the converse: Here is how to disable universal apply, which may be to write a private apply, or similar. (See abstract final case class in Scala 2, or whatever.)

The bit about shadowing (or rather, not shadowing) could use an example. Apparently, it means I can't import a Foo module that supplies the apply (perhaps with special semantics). Probably that also needs a special message if it doesn't have one. (I'll try it out on my lunch break.)