Irrelevant given considered and reported in error message
Compiler version
3.3.6 through latest nightly 3.7.2-RC1-bin-20250523-ee14905-NIGHTLY
Minimized code
trait Show[A] { def apply(a: A): String }
object Show {
given str[S <: String]: Show[S] = s => s
}
case class Test()
summon[Show[Test]]
Output
-- [E172] Type Error: ----------------------------------------------------------
8 |summon[Show[Test]]
| ^
|No given instance of type Show[Test] was found for parameter x of method summon in object Predef.
|I found:
|
| Show.str[S]
|
|But given instance str in object Show does not match type Show[Test].
Expectation
Show.str should not be considered or reported as part of the implicit search for Show[Test] since Test is not a subtype of String
Implicits in the companions of all parts of the type are in implicit scope.
I think the message is helpful because it correctly says why the implicit doesn't match. But maybe I incorrectly thought it would.
Implicits in the companions of all parts of the type are in implicit scope.
Right, I understand that, but maybe I'm misunderstanding another part -- I thought that just because an instance is in implicit scope doesn't mean the compiler will consider it.
For example if I make Show.str just an instance of Show[String] then the compiler doesn't report it as part of the search for Show[Test]
trait Show[A] { def apply(a: A): String }
object Show {
given str: Show[String] = s => s
}
case class Test()
summon[Show[Test]]
The error is:
-- [E172] Type Error: ----------------------------------------------------------
8 |summon[Show[Test]]
| ^
|No given instance of type Show[Test] was found for parameter x of method summon in object Predef
So why does the subtyping S <: String cause it to be considered?
I see what you mean. It distinguishes "no matching implicits" and "mismatched implicit". I will ponder the difference.
This may or may not be related, but I just discovered something strange. Let me know if you think it deserves its own issue.
Defining a given for a subtype of Int and then trying to summon a String seems to resolve that given and causes a ClassCastException in this case. Clearly this given is nonsensical, but it still feels like surprising behavior.
given i[I <: Int]: I = 1.asInstanceOf[I]
scala.util.Try(summon[String]).fold(_.printStackTrace, println)
/*
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at rs$line$11$.$init$$$anonfun$1(rs$line$11:1)
at scala.util.Try$.apply(Try.scala:217)
at rs$line$11$.<clinit>(rs$line$11:1)
at rs$line$11.res3(rs$line$11)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at dotty.tools.repl.Rendering.$anonfun$4(Rendering.scala:120)
at scala.Option.flatMap(Option.scala:283)
at dotty.tools.repl.Rendering.valueOf(Rendering.scala:120)
at dotty.tools.repl.Rendering.renderVal(Rendering.scala:160)
at dotty.tools.repl.ReplDriver.$anonfun$8(ReplDriver.scala:420)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
at scala.collection.immutable.List.foreach(List.scala:334)
at dotty.tools.repl.ReplDriver.extractAndFormatMembers$1(ReplDriver.scala:420)
at dotty.tools.repl.ReplDriver.renderDefinitions$$anonfun$2(ReplDriver.scala:458)
at scala.Option.map(Option.scala:242)
at dotty.tools.repl.ReplDriver.renderDefinitions(ReplDriver.scala:457)
at dotty.tools.repl.ReplDriver.compile$$anonfun$2(ReplDriver.scala:362)
at scala.util.Either.fold(Either.scala:197)
at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:344)
at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:303)
at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:221)
at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:224)
at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:258)
at dotty.tools.repl.ReplDriver.runBody$$anonfun$1(ReplDriver.scala:232)
at dotty.tools.runner.ScalaClassLoader$.asContext(ScalaClassLoader.scala:80)
at dotty.tools.repl.ReplDriver.runBody(ReplDriver.scala:232)
at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:224)
at dotty.tools.repl.ReplDriver.tryRunning(ReplDriver.scala:161)
at dotty.tools.repl.Main$.main(Main.scala:7)
at dotty.tools.repl.Main.main(Main.scala)
*/
That will infer i[Nothing], so it all works out except for your illegal cast.
Aside from the simple example where the problem is "obvious", it's not clear that reducing the "helpful" addendum is an improvement. For example,
|I found:
|
| scala.collection.BuildFrom.buildFromIterableOps[CC, A0, A]
|
|But method buildFromIterableOps in trait BuildFromLowPriority2 does not match type scala.collection.BuildFrom[List[Int], Int, List[String]].
The base message seems true but doesn't tell me anything (unless I understand well the semantics of CanBuildFrom):
Cannot construct a collection of type List[String] with elements of type Int based on a collection of type List[Int].
The extra explanation doesn't add much except that you'd have no idea what was tried. It's obvious that anything in scope doesn't match; otherwise, it would have succeeded.
Maybe it's the job of an IDE to offer advanced analysis; maybe any error text longer than one line should be hidden behind -explain.
Also from the tests, I'm looking at the short test source and I have no idea what the (existing) message was trying to convey, but the one thing I appreciate is seeing what implicits were available or adaptable (or whatever).
25 | (charClassIntersection.rep() | classItem.rep()) // error
| ^
- |No given instance of type pkg.Implicits.Repeater[pkg.RegexTree, V] was found for parameter repeater of method rep in package pkg.
- |I found:
- |
- | pkg.Implicits.Repeater.GenericRepeaterImplicit[T]
- |
- |But method GenericRepeaterImplicit in object Repeater does not match type pkg.Implicits.Repeater[pkg.RegexTree, V]
+ |No given instance of type pkg.Implicits.Repeater[pkg.RegexTree, V] was found for parameter repeater of method rep in package pkg
|
|where: V is a type variable with constraint <: Seq[pkg.CharClassIntersection]
It doesn't tell me what the type of GenericRepeaterImplicit is and why it's the wrong type. Is a type arg? variance? what?
It would also help if the test corpus were more literate about what is tested. Every test requires archaeology skills.