bug icon indicating copy to clipboard operation
bug copied to clipboard

compiler silently ignores ambiguous implicits, pretends none exist (unless in -Xlog-implicits)

Open scabug opened this issue 12 years ago • 10 comments

The appended error message is seen under -Ylog:typer, but under normal compilation scalac offers nothing.

Secondarily, it would be nice if this situation were recognized as the non-conflict which it is: the "conflicting" implicits are prefix-insensitive and the identical method implementation. This situation arises very easily when mixing implicits into package objects and trying to evolve codebases.

But at the very least how about we give people a hint what happened.

trait Impy { implicit def string2int(s: String): Int = s.length }
object o1 extends Impy
object o2 extends Impy

object Test {
  import o1._
  import o2._

  val x: Int = "abc"
}
// [log typer] Import is genuinely ambiguous:
//   types:  o2.type =:= o1.type  false  members: true
//   member type 1: (s: String)Int
//   member type 2: (s: String)Int
// [log typer] implicit adapt failed: reference to string2int is ambiguous;
// it is imported twice in the same scope by
// import o2._
// and import o1._

scabug avatar Jul 26 '13 19:07 scabug

Imported From: https://issues.scala-lang.org/browse/SI-7697?orig=1 Reporter: @paulp Affected Versions: 2.10.4, 2.11.5

scabug avatar Jul 26 '13 19:07 scabug

@retronym said: The terminology is confusing here. "Ambiguous implicits" describes competing, applicable implicits which score equally on the specificity test. Here, we have have an ambiguous binding. This is really an "invalid implicit", as diagnosed by -Xlog-implicits

qscalac -Xlog-implicits sandbox/test1.scala
sandbox/test1.scala:9: <string2int: error> is not a valid implicit value for String("abc") => Int because:
reference to string2int is ambiguous;
it is imported twice in the same scope by
import o2._
and import o1._
  val x: Int = "abc"
               ^
sandbox/test1.scala:9: error: type mismatch;
 found   : String("abc")
 required: Int
  val x: Int = "abc"
               ^
one error found
Secondarily, it would be nice if this situation were recognized as the non-conflict which it is: the "conflicting" implicits are prefix-insensitive and the identical method implementation.

Given separate compilation, we can only base our decision on the type signature, not on the body of the method.

We should figure out how to emit the useful output of -Xlog-implicits when and where it is needed. I've never been able to figure out the right balance here, though.

scabug avatar Feb 20 '15 05:02 scabug

@paulp said: I don't understand the relevance of separate compilation. I didn't mean go decompiling the bytecode to look at the the method, I meant they are literally the identical method, in the same sense that "Fido the dog" and "Fido the dog" are known to be equal because there is only one Fido the dog. They each have their own synthetic forwarder and each one exists for no reason but delivering Fido the dog.

scabug avatar Feb 20 '15 05:02 scabug

@retronym said: They could, however, predicate some logic on the identity of Impy.this.

scabug avatar Feb 20 '15 06:02 scabug

@paulp said: Shoot. You got me.

scabug avatar Feb 20 '15 06:02 scabug

@paulp said: I don't know if someone else ever reported this - guessing not - I'll just throw it in here as the closest thing to an applicable ticket. Fair warning, this is probably a slide in every talk I give until I die or it is changed, whichever comes first.

https://gist.github.com/paulp/36b67d27400a9f3be96a

class Bippy(override val toString: String)

trait A            { implicit def lowPriority: Bippy  = new Bippy("A") }
object B extends A { implicit def highPriority: Bippy = new Bippy("B") }
object C           { implicit def highPriority: Bippy = new Bippy("C") }

object Test {
  def main(args: Array[String]): Unit = {
    import B._, C._
    println( implicitly[Bippy] ) // Prints: A
  }
}

scabug avatar Feb 20 '15 06:02 scabug

@retronym said: Yep, -Xlog-implicts tells you about that one, too.

qscalac -Xlog-implicits sandbox/test1.scala
sandbox/test1.scala:10: <highPriority: error> is not a valid implicit value for Bippy because:
reference to highPriority is ambiguous;
it is imported twice in the same scope by
import C._
and import B._
    println( implicitly[Bippy] ) // Prints: A
                       ^

But one man's trash is another man's treasure when it comes to these diagnostics. I don't think they are in a state to turn on by default.

scabug avatar Feb 20 '15 06:02 scabug

@paulp said: It's good of -Xlog-implicits to note the ambiguity, but I might once have naively thought it would also fail the compile. It's quite interesting that it can echo a message including "<highPriority: error>" and "reference to highPriority is ambiguous" on its way to a successful compilation run.

scabug avatar Feb 20 '15 07:02 scabug

Scala 3.6.3 accepts the code

SethTisue avatar Feb 12 '25 18:02 SethTisue

Scala 3 rejects the slide example, since there is no more naming and shadowing.

   |Ambiguous given instances: both method highPriority in object C and method lowPriority in trait A match type Bippy of parameter e of method implicitly in object Predef

It accepts the OP but:

import language.implicitConversions

trait Impy:
  given Conversion[String, Int] = s => s.length + getClass.getSimpleName.takeWhile(_ == 'o').length

object o1 extends Impy
object oo2 extends Impy

@main def test = println:
  import o1.given
  import oo2.given

  val x: Int = "abc"
  x

is 5. Spooky. Not sure why, is it the "nesting level" of the imports, which means context nesting of the import as opposed to scope.

som-snytt avatar Feb 12 '25 21:02 som-snytt