Description
Compiler version
3.5.0-RC3 or 3.6 nightly
Minimized code
//> using scala 3.nightly
//> using mainClass reproduction.opaqueoverwarning.Main
package reproduction.opaqueoverwarning
opaque type FromEnd = Int
object FromEnd:
inline def apply(i: Int): FromEnd = i
extension (fe: FromEnd)
inline def value: Int = fe
// Warning appears when extension is in same namespace as opaque type
// under 3.5.0-RC3 (and 3.6.0 nightly)
extension [A](a: Array[A])
inline def apply(fe: FromEnd): A =
a(a.length - 1 - FromEnd.value(fe))
object Main:
def run(): Unit =
val xs = Array(1, 2, 3)
println(s"First element = ${xs(0)}")
println(s"Last element = ${xs(FromEnd(0))}")
def main(args: Array[String]): Unit =
run()
Output
[warn] ./Reproduction.scala:15:14
[warn] Extension method apply will never be selected
[warn] because Array already has a member with the same name and compatible parameter types.
[warn] inline def apply(fe: FromEnd): A =
[warn] ^
First element = 1
Last element = 3
Expectation
First element = 1
Last element = 3
because, obviously, the extension method apply was selected, so it certainly isn't the case that it will "never be selected".
If we want to retain the warning in case someone wants to try to use the extension within the same file, at least it should go away when the full path is used (e.g. reproduction.opaqueoverwarning.FromEnd
) as a signal that you are taking the external rather than the privileged view of the opaque type.
(Note that there are a number of cases where the full path has to be used already in order to get the desired behavior--in inline matches, for example.)
The warning doesn't happen if the opaque type and extension method are inside an object (see e.g. https://scastie.scala-lang.org/SHWSAqOPSVGV7gdA80WJTA and contrast to https://scastie.scala-lang.org/fes52gRsSmiFpSlHP7r7QA).