-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow @implicitNotFound messages as explanations #16893
Allow @implicitNotFound messages as explanations #16893
Conversation
A problem of the @implicitNotFOund mechanism so far was that the user defined message replaced the compiler-generated one, which might lose valuable information. This commit adds an alternative where an @implicitNotFound message that starts with `...` is taken as an explanation (without the ...) enabled under -explain. The compiler-generated message is then kept as the explicit error message. We apply the mechanism for an @implicitNotFound message for `boundary.Label`. This now produces messages like this one: ``` -- [E172] Type Error: tests/neg-custom-args/explain/labelNotFound.scala:2:30 ------------------------------------------- 2 | scala.util.boundary.break(1) // error | ^ |No given instance of type scala.util.boundary.Label[Int] was found for parameter label of method break in object boundary |--------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | A Label is generated from an enclosing `scala.util.boundary` call. | Maybe that boundary is missing? --------------------------------------------------------------------------------------------------------------------- ```
@nicolasstucki We can use an analogous scheme to improve the situation for #16888. |
Should we somehow document this new behaviour or is it supposed to be used only internally? |
Unfortunately, the spec of A solution to this might be to show the custom message and hide the compiler-generated message under The message Maybe this is how an error message should look
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not break the contract of implicitNotFound
In theory yes, in practice, I would be really surprised if someone started an |
But I guess it's OK to add an |
Adding a new annotation would have to be done in a minor release, unless we make it experimental |
the release candidates (e.g. 3.3.0-RC2) still have experimental tasty version, so its ok to release for 3.3.0? |
So somebody should do it quickly! These are real and very important usability improvements. We should not block them by procedural reasons. |
How would we like this new annotation to be used then? Should it be orthogonal to |
Yes, I think that's reasonable. |
In Scala 2, |
@smarter I did not know that Scala 2 behaved differently. Yes, let's go back to that then the whole discussion about explain becomes redundant. |
It does not seem to be the case. In Scala 2 only the custom message is shown. It seems that the behaviour is the same as in Scala 3. @scala.annotation.implicitNotFound("Foo[${T}] was not found")
final class Foo[T]
class C {
def test = implicitly[Foo[Int]]
}
|
It looks like the behaviour in scala 2 depends on whether the annotation is inherited or not. import annotation.implicitNotFound
@implicitNotFound("Missing type: Foo")
trait Foo
@implicitNotFound("Missing type: Bar")
trait Bar
trait Baz extends Bar
object Test {
def myImplicitly[T](implicit @implicitNotFound("Missing parameter: ${T}") t: T): T = t
val a = implicitly[Int]
val b = implicitly[Foo]
val c = implicitly[Baz]
val x = myImplicitly[Int]
val y = myImplicitly[Foo]
val z = myImplicitly[Baz]
} Compiling with scala 2: scala-cli compile Test.scala -S 2.13.10 [error] ./Test.scala:14:11: could not find implicit value for parameter e: Int
[error] val a = implicitly[Int]
[error] ^^^^^^^^^^^^^^^
[error] ./Test.scala:15:11: Missing type: Foo
[error] val b = implicitly[Foo]
[error] ^^^^^^^^^^^^^^^
[error] ./Test.scala:16:11: could not find implicit value for parameter e: Baz (Missing type: Bar)
[error] val c = implicitly[Baz]
[error] ^^^^^^^^^^^^^^^
[error] ./Test.scala:18:11: Missing parameter: Int
[error] val x = myImplicitly[Int]
[error] ^^^^^^^^^^^^^^^^^
[error] ./Test.scala:19:11: Missing parameter: Foo
[error] val y = myImplicitly[Foo]
[error] ^^^^^^^^^^^^^^^^^
[error] ./Test.scala:20:11: Missing parameter: Baz
[error] val z = myImplicitly[Baz]
[error] ^^^^^^^^^^^^^^^^^ Compiling with scala 3: scala-cli compile Test.scala -S 3.3.0-RC2 [error] ./Test.scala:14:26: No given instance of type Int was found for parameter e of method implicitly in object Predef
[error] val a = implicitly[Int]
[error] ^
[error] ./Test.scala:15:26: Missing type: Foo
[error] val b = implicitly[Foo]
[error] ^
[error] ./Test.scala:16:26: Missing type: Bar
[error] val c = implicitly[Baz]
[error] ^
[error] ./Test.scala:18:28: Missing parameter: Int
[error] val x = myImplicitly[Int]
[error] ^
[error] ./Test.scala:19:28: Missing parameter: Foo
[error] val y = myImplicitly[Foo]
[error] ^
[error] ./Test.scala:20:28: Missing parameter: Baz
[error] val z = myImplicitly[Baz]
[error] ^ |
I'd vote for showing both messages always. It seems the implicit not found messages are not always that specific, so adding info which implicit parameter was not found is useful. |
Sounds good. But we should put the custom error first. |
I am not sure about that. Can we do play out some scenarios how it would look? For the missing label we'd get with custom error last:
With custom error first this makes no sense. Another use case is the implicitNotFound for
Or, custom error last:
Here it's debatable, but I think in the end I would also prefer custom error last. |
I believe we have two kinds of use cases:
package calendar
import annotation.implicitNotFound
trait DateFormat
object DateFormats {
implicit object DayMonthYear extends DateFormat
implicit object MonthDayYear extends DateFormat
}
class Date(day: Int, month: Int, year: Int) {
def show(implicit @implicitNotFound("To show a date one needs to specify a date format, e.g. with `import calendar.DateFormats.DayMonthYear`") dateFormat: DateFormat): String = ???
} In this situation it might make sense to:
The problem is that it's not so easy to tell which situation we're dealing with in general |
In general it seems like |
I believe the cats example shows that we want additional info, not a replacement of the error message. E.g. see this comment:
|
Yeah, I would say cats classifies as case 2 |
And I would argue the DateFormat case also classifies as case 2, not 1. Taken alone, the implicitNotFound message is more confusing than helpful. If it is shown after the regular message it makes sense. I think by now we have not seen a single case where custom first is clearly better and lots of cases where it makes not sense at all. Let's close this issue and implement it. |
This approach mirrors the format used in `@noWarn`. There it is possible to tag encode the category and message in the string. ``` @nowarn("cat=deprecation&msg=Implementation ...") ``` The current implementation for `explain=` only allows this tag at the start of the string. Making the message either a old-style message or an message in the explain. We could extend this to support `msg=` to be able to have both a custom message and an explain. Furthermore we could insert some flags to explicitly state how to display the original message (for example: not-found=hidden, not-found=in-message, not-found=in-explain).
I ended up keeping the current scheme for generating I also added the implicit not found explain for |
What do you call "the spec part of it"? The commit message? I see no spec changes in the PR diff. |
I meant the addition of the |
Can I create a copy of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small typo, otherwise LGTM
That seems fine to me. |
Good, then it is ready to be merged. |
A problem of the @implicitNotFOund mechanism so far was that the user defined message replaced the compiler-generated one, which might lose valuable information.
This commit adds an alternative where an @implicitNotFound message that starts with
...
is taken as an explanation (without the ...) enabled under -explain. The compiler-generated message is then kept as the explicit error message.We apply the mechanism for an @implicitNotFound message for
boundary.Label
. This now produces messages like this one: