bug icon indicating copy to clipboard operation
bug copied to clipboard

Fail to resolve overloaded method when type inference is required

Open scabug opened this issue 9 years ago • 8 comments

Here is a snippet to illustrate the problem:

class Foo[A] {
  def compose(a: Foo[A]): Foo[A] = new Foo[A]
  def compose(b: Bar[A]): Bar[A] = new Bar[A]

  // non overloaded method
  def composeBar(b: Bar[A]): Bar[A] = new Bar[A]
}

class Bar[A] {
  def compose(a: Foo[A]): Bar[A] = new Bar[A]
  def compose(b: Bar[A]): Bar[A] = new Bar[A]
}

object Test {
  val fooS = new Foo[String]
  val barS = new Bar[String]
  def barA[A] = new Bar[A]

  // foos and bars compose together
  fooS compose fooS
  barS compose barS
  fooS compose barS
  barS compose fooS

  // here it fails with the following error
  // error: overloaded method value compose with alternatives:
  // (b: Bar[String])Bar[String] <and>
  // (a: Foo[String])Foo[String]
  // cannot be applied to (Bar[Nothing])
  fooS compose barA
  
  // but it works if you add a type annotation or a non overloaded method
  fooS compose barA[String]
  fooS composeBar barA
}

This issue is particularly important for monocle because all optics (Iso, Lens, Prism, ...) compose together which means we need to define 8 different compose methods instead of a single method overloaded 8 times.

scabug avatar Nov 12 '16 12:11 scabug

Imported From: https://issues.scala-lang.org/browse/SI-10046?orig=1 Reporter: Julien Truffaut (julien-truffaut) Affected Versions: 2.11.8, 2.12.0

scabug avatar Nov 12 '16 12:11 scabug

Jean-Baptiste Giraudeau (jbgi) said: In case this could be an incentive to fix this issue, the test case compile just fine in java:

class Test {
  static class Foo<A> {
    Foo<A> compose(Foo<A> a) {
       return new Foo<>();
    }
    Bar<A> compose(Bar<A> b) {
      return new Bar<>();
    }
  }

  static class Bar<A> {
    Bar<A> compose(Foo<A> a) {
      return new Bar<>();
    }

    Bar<A> compose(Bar<A> b) {
      return new Bar<>();
    }
  }

  static <A> Bar<A> barA() {
    return new Bar<>();
  }

  public static void main(String[] args) {
    Foo<String> fooS = new Foo<>();

    // Compile just fine:
    fooS.compose(barA());
  }
}

This kind of limitations means that the scala user-experience (of java 8 libraries in particular) can be inferior to the java user-experience. Resolving regressions vs Java 8 should, imo, be given major priority (at least from a marketing pov ;-)).

scabug avatar Nov 12 '16 13:11 scabug

@som-snytt said: Maybe the missing param type exception for function literals can be extended to this case, so that a single missing type param is inferred from the type args of the candidate expected types.

Problem statement:

scala> class D[A] ; class C[A] { def f(x: C[A]): C[A] = new C[A] ; def f(x: D[A]): D[A] = new D[A] }
defined class D
defined class C

scala> def d[A]: D[A] = new D[A]
d: [A]=> D[A]

scala> new C[String]().f(d)
<console>:14: error: overloaded method value f with alternatives:
  (x: D[String])D[String] <and>
  (x: C[String])C[String]
 cannot be applied to (D[Nothing])
       new C[String]().f(d)
                       ^

This is not a workaround.

scala> class D[A] ; class C[A] { def f(x: A => C[A]): C[A] = new C[A] ; def f(x: A => D[A]): D[A] = new D[A] }
defined class D
defined class C

scala> def d[A]: D[A] = new D[A]
d: [A]=> D[A]

scala> new C[String]().f(x => d)
<console>:14: error: overloaded method value f with alternatives:
  (x: String => D[String])D[String] <and>
  (x: String => C[String])C[String]
 cannot be applied to (String => D[Nothing])
       new C[String]().f(x => d)
                       ^

scala> new C[String]().f(x => d[x.type])
<console>:14: error: overloaded method value f with alternatives:
  (x: String => D[String])D[String] <and>
  (x: String => C[String])C[String]
 cannot be applied to (String => D[_ <: String with Singleton])
       new C[String]().f(x => d[x.type])
                       ^

scabug avatar Nov 19 '16 20:11 scabug

Julien Truffaut (julien-truffaut) said: At the moment, I can see one way to fix the issue but I am very unfamiliar with the internal API. Here is what I found so far:

In Typers -> preSelectOverloaded, we could try to remove overloaded candidates that cannot match, e.g. there is no way that a Bar[A] can be a Foo[String]. If we can do that then the issue would be resolved because there would be only one alternative. However, it seems that the Tree barA hasn't been type checked in preSelectOverloaded, so I don't know how to access type information.

scabug avatar Dec 10 '16 22:12 scabug

@som-snytt said: I think it's a spec issue and not just a matter of implementation. It takes two cuts at which alternatives apply, first by shape and second after type checking the args (without an expected type).

scabug avatar Dec 11 '16 05:12 scabug

Julien Truffaut (julien-truffaut) said: Would you mind to point me at the part of the spec you mentioned?

scabug avatar Dec 11 '16 11:12 scabug

@som-snytt said: http://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#overloading-resolution

That is 6.26.3. (The table of contents is temporarily pointing to a phantom 2.13 version.)

scabug avatar Dec 11 '16 16:12 scabug

#10542 seems similar?

SethTisue avatar Oct 12 '20 15:10 SethTisue