scala3 icon indicating copy to clipboard operation
scala3 copied to clipboard

Irrelevant given considered and reported in error message

Open mrdziuban opened this issue 6 months ago • 7 comments

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

mrdziuban avatar May 23 '25 14:05 mrdziuban

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.

som-snytt avatar May 23 '25 15:05 som-snytt

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?

mrdziuban avatar May 23 '25 15:05 mrdziuban

I see what you mean. It distinguishes "no matching implicits" and "mismatched implicit". I will ponder the difference.

som-snytt avatar May 23 '25 19:05 som-snytt

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)
*/

mrdziuban avatar May 23 '25 19:05 mrdziuban

That will infer i[Nothing], so it all works out except for your illegal cast.

som-snytt avatar May 23 '25 20:05 som-snytt

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?

som-snytt avatar May 30 '25 01:05 som-snytt

It would also help if the test corpus were more literate about what is tested. Every test requires archaeology skills.

som-snytt avatar May 30 '25 01:05 som-snytt