scala / bug

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

Inconsistent resolution of implicits #10376

Open charpov opened 7 years ago

charpov commented 7 years ago
object Base {

  import scala.language.implicitConversions

  implicit def conv[A, B](x: Base[A])(implicit f: A => B): Base[B] = new C[B]()
}

abstract class Base[A] {

  def +(n: A): Base[A] = this
  def +(that: Base[A]): Base[A]
}

case class C[A]() extends Base[A] {
  override def +(that: Base[A]): Base[A] = that
}

The idea is to lift an implicit conversion from A to B into one from C[A] to C[B]. Let A be Int and B be BigInt:

import Base._

  val x = new C[Int]
  val X = new C[BigInt]

The expression X + x compiles fine: argument x is lifted into a C[BigInt]. However, x + X does not compile (x is not lifted as a target). It seems to me that it should work.

Moreover, the expression x + X can be compiled if:

I don't think the specs say anything about overloading or abstract/concrete having such an impact on how implicits are resolved, making this behavior buggy (or at least very mysterious).

This is observed in 2.12.2.

charpov commented 7 years ago

No one thinks this is a bug? I had no replies on the Scala user list either.

milessabin commented 7 years ago

The reason that the first compiles and the second doesn't is that the standard library provides an implicit conversion from Int => BigInt, but not vice versa.

Welcome to Scala 2.12.2-20170419-092530-unknown (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.

scala> implicitly[Int => BigInt]
res0: Int => BigInt = $$Lambda$1836/347136295@10823d72

scala> implicitly[BigInt => Int]
<console>:12: error: No implicit view available from BigInt => Int.
       implicitly[BigInt => Int]
                 ^

Thus the precondition for your implicit def conv[Int, BigInt] is met, but it's not for conv[BigInt, Int].

charpov commented 7 years ago

No. I expect x to be converted to C[BigInt], using the implicit conversion from Int to BigInt.

How do you explain that, if I replace def +(that: Base[A]): Base[A] in Base with def +(that: Base[A]): Base[A] = ???, then everything works?

charpov commented 7 years ago

See also discussion at: https://users.scala-lang.org/t/implicits-resolution/981

schlichtanders commented 6 years ago

I experienced similar brokeness with the following simple example:

import scala.util.Try
implicit class MyOption[+A](opt: Option[A]){
  def orElse[B >: A](alternative: => Try[B]): Option[B] = opt.orElse(alternative.toOption)
}
Option(1) orElse Try(2)

Intellij IDEA linter says everything is all right, however the compiler gives

Error:(5, 88) type mismatch;
 found   : scala.util.Try[Int]
 required: Option[?]
def get$$instance$$res0 = /* ###worksheet### generated $$end$$ */ Option(4) orElse Try(2)
                                                                                      ^

(same happens when analogously overwriting flatMap)

According to language specs http://scala-lang.org/files/archive/spec/2.12/07-implicits.html#views point 3) this should work, but doesn't apparently. So this is a bug, isn't it?

Is this the same issue?, or should I open a new one?

charpov commented 6 years ago

Agreed. It looks like the same behavior, inconsistent with the specs.

I had another compiler bug, somewhat related, recently resolved by @som-snytt . He seems to know his stuff. I wish he could have a look at this one too.

Jasper-M commented 6 years ago

@schlichtanders I think you encountered this other related issue: https://github.com/scala/bug/issues/9523

SethTisue commented 3 years ago

Is it fixed in Dotty?

som-snytt commented 3 years ago
➜  snips cat t10376.scala

object Base {

  import scala.language.implicitConversions

  implicit def conv[A, B](x: Base[A])(implicit f: A => B): Base[B] = C[B](s"cv $x")
}

abstract class Base[A] {
  def +(n: A): Base[A] = this
  def +(that: Base[A]): Base[A]
}

case class C[A](tag: String) extends Base[A] {
  override def +(that: Base[A]): Base[A] = that
}

object Main extends App {
  import Base._

  val x = new C[Int]("x")
  val y = new C[BigInt]("y")
  println((x + y, y + x))
}
➜  snips scalac -version
Scala compiler version 3.0.0-M3 -- Copyright 2002-2020, LAMP/EPFL
➜  snips scalac -d /tmp t10376.scala && scala -classpath /tmp Main
(C(y),C(cv C(x)))
➜  snips

but

➜  snips scalac -d /tmp t10376b.scala && scala -classpath /tmp Main
-- [E007] Type Mismatch Error: t10376b.scala:7:24 ------------------------------
7 |    Option(1) orElse Try(2)
  |                     ^^^^^^
  |                     Found:    scala.util.Try[Int]
  |                     Required: Option[Any]
1 error found