Skip to content

Conversation

@lrytz
Copy link
Member

@lrytz lrytz commented Feb 19, 2018

This allows a more specific message for individual implicit parameters
that are not found.

This allows improving error message, for example in overloading
resolution:

scala> trait Iterable[A] { def map[B](f: A => B): Iterable[B] }
scala> trait SortedSet[A] { def map[B: Ordering](f: A => B): SortedSet[B] }

scala> def t(s: SortedSet[Int]) = s.map(x => x) // OK
t: (s: SortedSet[Int])SortedSet[Int]

scala> def t[T](s: SortedSet[T]) = s.map(x => x) // Confusing error
                                        ^
       error: No implicit Ordering defined for T.

better:

scala> trait SortedSet[A] { def map[B](f: A => B)(implicit @annotation.implicitNotFound("No implicit Ordering[${B}] found to build a SortedSet[${B}]. You can build an unordered Set[${B}] by calling `unsorted` before `map`.") ord: Ordering[B]): SortedSet[B] }
defined trait SortedSet

scala> def t[T](s: SortedSet[T]) = s.map(x => x)
                                        ^
       error: No implicit Ordering[T] found to build a SortedSet[T]. You can build an unordered Set[T] by calling `unsorted` before `map`.

@scala-jenkins scala-jenkins added this to the 2.13.0-M4 milestone Feb 19, 2018
@lrytz
Copy link
Member Author

lrytz commented Feb 19, 2018

See also scala/collection-strawman#432

@lrytz lrytz requested a review from szeiger February 20, 2018 15:27
@soronpo
Copy link

soronpo commented Feb 28, 2018

What happens in the following case?

@implicitNotFound("BarError")
trait Bar
@implicitNotFound("FooError")
trait Foo
object Foo {
  implicit def ev(implicit @implicitNotFound("BarParamError") bar : Bar) : Foo = ???
}

implicitly[Foo]

@lrytz
Copy link
Member Author

lrytz commented Feb 28, 2018

It tells you

scala> implicitly[Foo]
                 ^
       error: FooError

With -Xlog-implicits

scala> implicitly[Foo]
                 ^
       Foo.ev is not a valid implicit value for Foo because:
       hasMatchingSymbol reported error: BarParamError
                 ^
       error: FooError

So it's picked up here as well.

trait Meh2[-From, +To]
^
t2462b.scala:13: warning: Invalid implicitNotFound message for value theC:
The type parameter Uuh referenced in the message of the @implicitNotFound annotation is not defined by value theC.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this message confusing. The type parameter is supposed to be defined by m, not by theC.

private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName)
private def typeArgsAtSym(paramTp: Type) = paramTp.baseType(sym).typeArgs
private lazy val typeParamNames: List[String] = {
val s = if (sym.isParameter) sym.enclMethod else sym
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can (and should) we get the full scope here instead of only the method's type parameters? This doesn't work:

scala> trait I[X, Y]
defined trait I

scala> trait T[A] { def f[B](implicit @scala.annotation.implicitNotFound("Missing I[${A}, ${B}]") i: I[A, B]): Unit = () }
                                                                                                  ^
       warning: Invalid implicitNotFound message for value i:
       The type parameter A referenced in the message of the @implicitNotFound annotation is not defined by value i.
defined trait T

@szeiger szeiger modified the milestones: 2.13.0-M4, 2.13.0-M5 May 2, 2018
This allows a more specific message for individual implicit parameters
that are not found.

This allows improving error message, for example in overloading
resolution:

```
scala> trait Iterable[A] { def map[B](f: A => B): Iterable[B] }
scala> trait SortedSet[A] { def map[B: Ordering](f: A => B): SortedSet[B] }

scala> def t(s: SortedSet[Int]) = s.map(x => x) // OK
t: (s: SortedSet[Int])SortedSet[Int]

scala> def t[T](s: SortedSet[T]) = s.map(x => x) // Confusing error
                                        ^
       error: No implicit Ordering defined for T.
```

better:

```
scala> trait SortedSet[A] { def map[B](f: A => B)(implicit @annotation.implicitNotFound("No implicit Ordering[${B}] found to build a SortedSet[${B}]. You can build an unordered Set[${B}] by calling `unsorted` before `map`.") ord: Ordering[B]): SortedSet[B] }
defined trait SortedSet

scala> def t[T](s: SortedSet[T]) = s.map(x => x)
                                        ^
       error: No implicit Ordering[T] found to build a SortedSet[T]. You can build an unordered Set[T] by calling `unsorted` before `map`.
```
@lrytz lrytz force-pushed the implicitNotFoundParam branch from 77c974a to 3d2b107 Compare June 15, 2018 08:34
@lrytz
Copy link
Member Author

lrytz commented Jun 15, 2018

Updated. When putting an @implicitNotFound on a parameter, the message can now refer to any type paramters in scope. (hmm, that makes me think if it should support type members too.. that might be hard to implement).

t2462c.scala:36: error: I see no C[Foo[Int]]
h[Foo[Int]]
^
t2462c.scala:40: error: String List[?] List[C[_]] Int Option[Long] -- .
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The List[?] is not great here. The formatParameterMessage method could be improved for the case of type constructors. cc @adriaanm.

Copy link
Contributor

@adriaanm adriaanm Jun 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider me sniped :-) I pushed a commit that adds some ad-hoc pretty printing for these. Feel free to g push -f HEAD^

@lrytz
Copy link
Member Author

lrytz commented Jun 15, 2018

Maybe we can say this is good enough to merge? It's only about error messages. We can always accept contributions that improve it :) I'd like to not spend much more time on this.

@soronpo
Copy link

soronpo commented Jun 15, 2018

What will happen when an implicit parameter is missing from an implicit conversion? :

@implicitNotFound("BarError")
trait Bar
@implicitNotFound("FooError")
trait Foo
object Foo {
  implicit def fromBar(bar : Bar)(implicit @implicitNotFound("FooParamError") foo : Foo) : Foo = foo
}

val foo : Foo = new Bar{}

Currently (and sadly) scalac does not even report FooError. Will it now report FooParamError ?

@adriaanm
Copy link
Contributor

adriaanm commented Jun 15, 2018

What will happen when an implicit parameter is missing from an implicit conversion?

Same as before. No (user-defined) error. Having some form of interactive implicit search debugger would be great, but for error messages I don't think we should try to guess secondary issues.

@soronpo
Copy link

soronpo commented Jun 15, 2018

Same as before. No (user-defined) error. Having some form of interactive implicit search debugger would be great, but for error messages I don't think we should try to guess secondary issues.

In many cases I don't think it's guessing and I would have loved a custom error instead of just a type mismatch. More specifically, in case where an implicit conversion only fails due to a missing implicit parameter and there is only one possible implicit conversion definition in scope.

@adriaanm adriaanm force-pushed the implicitNotFoundParam branch from 8badb82 to e907e31 Compare June 15, 2018 10:00
val paramSyms = paramNames.map(lookupTypeParam).filterNot(_ == NoSymbol)
val paramTypeRefs = paramSyms.map(sym => typeRef(NoPrefix, sym, sym.typeParams.map(_ => WildcardType)))
val prefix = fun match {
case TypeApply(Select(qual, _), _) => qual.tpe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could collapse these two cases (and also deal with repeated Apply) using: case treeInfo.Applied(Select(qual, _), _, _) => qual.tpe

t2462c.scala:36: error: I see no C[Foo[Int]]
h[Foo[Int]]
^
t2462c.scala:40: error: String List[?] List[C[_]] Int Option[Long] -- .
Copy link
Contributor

@adriaanm adriaanm Jun 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider me sniped :-) I pushed a commit that adds some ad-hoc pretty printing for these. Feel free to g push -f HEAD^

@adriaanm
Copy link
Contributor

We could discuss that in a ticket and a different PR, but it's definitely out of scope for this one.

@adriaanm
Copy link
Contributor

@lrytz, others: the first commit LGTM, but I'll let you decide on my follow-up :-)

@soronpo
Copy link

soronpo commented Jun 15, 2018

We could discuss that in a ticket and a different PR, but it's definitely out of scope for this one.

@adriaanm I created a separate ticket for that
scala/bug#10946

With some help I can create a PR, if you'd consider accepting it.

h[Foo[Int]]
^
t2462c.scala:40: error: String List[?] List[C[_]] Int Option[Long] -- .
t2462c.scala:40: error: String List [?T0, ZZ] -> List[C[_]] Int Option[Long] -- .
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People will complain why this syntax doesn't work in their code 😏

@adriaanm
Copy link
Contributor

adriaanm commented Jun 15, 2018 via email

@lrytz lrytz merged commit 76c4ef4 into scala:2.13.x Jun 15, 2018
@adriaanm
Copy link
Contributor

With some help I can create a PR, if you'd consider accepting it.

let's first discuss on the ticket -- I'm not against on principle or anything, but it's probably worth talking through before embarking on the implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-notes worth highlighting in next release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants