scala3 icon indicating copy to clipboard operation
scala3 copied to clipboard

Cannot disambiguate public overload from package-private

Open armanbilge opened this issue 3 years ago • 2 comments

Compiler version

3.2.1-RC1-bin-20220803-da2d44a-NIGHTLY

Minimized code

//> using scala "3.2.1-RC1-bin-20220803-da2d44a-NIGHTLY"

@main def main =
  foo.bar(42)

package object foo {
  def bar[F[_]]: Unit = ???
  def bar[F[_]](x: Int): Unit = ???
  private[foo] def bar[F[_]](x: Int)(implicit dummy: DummyImplicit): Unit = ???
}

Output

[error] ./bug.scala:4:3: Ambiguous overload. The overloaded alternatives of method bar in package foo with types
[error]  [F[_$3]](x: Int)(implicit dummy: DummyImplicit): Unit
[error]  [F[_$2]](x: Int): Unit
[error] both match arguments ((42 : Int))
[error]   foo.bar(42)
[error]   ^^^^^^^

Expectation

There should be no ambiguity because one of the "ambiguous" methods is package-private.

armanbilge avatar Aug 04 '22 17:08 armanbilge

In the OP example, it must be a package object; an ordinary object works. All three bar are required.

My expectation was that accessibility does not determine overloading; candidate overloads must be applicable, but applicable does not entail accessible. I'm surprised the example compiles in Scala 2, and I'm surprised an ordinary object works in Scala 3.

(Accessibility was added as a requirement for override eligibility, so maybe it was added for overload applicability.)

The "doesn't use implicits" rule selects B in the first main:

import annotation._

//@main def main = foo.bar(42)(implicitly[DummyImplicit])   // B
//@main def main = foo.bar(42)  // C

package object foo {
  def bar[F[_]]: Unit = println("A")
  def bar[F[_]](x: Int)(dummy: DummyImplicit): Unit = println("B")
  @targetName("privvy")
  private[foo] def bar[F[_]](x: Int)(implicit dummy: DummyImplicit): Unit = println("C")
}

I thought I witnessed differing results if the main is in a separate file and depending on whether it was compiled by scalac or the scala runner, but that must be due to https://github.com/lampepfl/dotty/issues/15015

The point of that exercise was that people hint that private implies "private to a file"; or the opposing camp says compilation unit should not matter. With some code in the empty package, it's not obvious what is intended or specified.

Unqualified private is also ambiguous, even with main in a separate file:

package object foo {
  def bar[F[_]]: Unit = println("A")
  def bar[F[_]](x: Int): Unit = println("B")
  private def bar[F[_]](x: Int)(implicit dummy: DummyImplicit): Unit = println("C")
}

som-snytt avatar Aug 04 '22 19:08 som-snytt

It's also correct in the case of usage top-level definitions in a separate file which would be an idiomatic way of the same code in Scala 3 (package objects are going to be removed):

/// foo.scala
package foo

def bar[F[_]]: Unit = ???
def bar[F[_]](x: Int): Unit = ???
private def bar[F[_]](x: Int)(implicit dummy: DummyImplicit): Unit = ???
// test.scala
@main def main = foo.bar(42)

In case of removal def bar[F[_]](x: Int): Unit = ??? code would work correctly

WojciechMazur avatar Aug 05 '22 09:08 WojciechMazur