scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

Parenless overloaded methods are used in ambiguous cases #9395

Open scabug opened 9 years ago

scabug commented 9 years ago

This code:

class MyClass {
    def something(in: String): String = {
        in + "_z"
    }

    def something: (String) => String = {
        case _ => "Fixed"
    }
}

val my = new MyClass()

println(List("x", "y").map(my.something))

Prints List(Fixed, Fixed).

The type checker is calling the no-argument variant of my.something(), and then silently taking the return type and passing it to map(). This ignores the other matching one-argument version of my.something.

The below line (arguably correctly) produces a compile error about an "ambiguous reference to overloaded definition":

val fun: String => String = my.something(_)

Adding parentheses to the no-argument definition of the method removes the issue. Changing the return type of the no-argument method to be anything other than String => String removes the issue.

I think this is incorrect behavior; where there is a conflict between the result of an implicit conversion (the my.something() return value) and an unconverted correct type, either the unconverted type should win or a compile error should be thrown telling the user about ambiguity. In other words, the above code should either output List(x_z, y_z) or fail to compile. Currently the implicitly converted version is silently taking precedence.

scabug commented 9 years ago

Imported From: https://issues.scala-lang.org/browse/SI-9395?orig=1 Reporter: Bryan Jacobs (bryanjacobs) Affected Versions: 2.11.4 See #8342, #8344, #8800

scabug commented 9 years ago

Ian Bellamy (imbellish) said (edited by @lrytz on Jul 15, 2015 6:16:55 AM UTC): Possible debug script can be found here:

scala> class MyClass {
     | def something(in: String): String = {
     | in + "_z"
     | }
     | def something: (String) => String = {
     | case _ => "Fixed"
     | }
     | }

scala> val my = new MyClass()
my: MyClass = MyClass@155cf22

scala> println(List("x", "y").map(my.something))
List(Fixed, Fixed)

scala> class MyClass {
     | def something(in: String): String = {
     | in + "_z"
     | }
     | def somethingelse: (String) => String = {
     | case _ => "Fixed"
     | }
     | }
defined class MyClass

scala> println(List("x", "y").map(my.something))
List(Fixed, Fixed)

scala> val my = new MyClass()
my: MyClass = MyClass@15a4a5

scala> println(List("x", "y").map(my.something))
List(x_z, y_z)

scala> println(List("x", "y").map(my.somethingelse))
List(Fixed, Fixed)
scabug commented 9 years ago

@lrytz said (edited on Jul 15, 2015 6:49:52 AM UTC): In my reading of the spec, this should be ambiguous. Shorter example:

object Test extends App {
  def f(s: String): String = "1"
  val f: (String) => String = s => "2"

  val t: String => String = f

  println(t("")) // 2
}

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#overloading-resolution

The reference to f is not an application or a type application (last paragraph of overloading resolution). Both definitions of f are compatible to the expected type, the method via eta-expansion (http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#implicit-conversions), so the most specific version is chosen:

If I'm wrong in reading the spec, it would be good to add an example to it explaining this case.

som-snytt commented 1 year ago

Dotty says 1.

[[syntax trees at end of                     typer]] // t9395.scala
package <empty> {
  final lazy module val Test: Test = new Test()
  final module class Test() extends Object(), App { this: Test.type =>
    def f(s: String): String = "1"
    val f: String => String =
      {
        def $anonfun(s: String): String = "2"
        closure($anonfun)
      }
    val t: String => String =
      {
        def $anonfun(s: String): String = Test.f(s)
        closure($anonfun)
      }
    println(Test.t.apply(""))
  }
}

Is this like the Monty Hall problem where you always pick the other door?

som-snytt commented 1 year ago

https://github.com/lampepfl/dotty/issues/18294