Skip to content

implicitNotFound should allow type computation inside its pretty printer #22392

Open
@belamenso

Description

@belamenso

Compiler version

3.6.2

Minimized example

This is not a minimized example because the problem is simple and this better illustrates the benefits.

Say you are using implicits to perform some non-trivial compile-time verification of the input types. In that case, you probably are checking subcomponents of the input types, not only doing simple matches against the top-level types. In this toy example you check the shape of the values part of a named tuple.

You want to offer some helpful hints to the end-user of your library, and since the validation failed on some part inside of the top-level type, you want the error message to talk about this incorrect part only.

import language.experimental.namedTuples
import NamedTuple.{NamedTuple, AnyNamedTuple}

type OnlyInts[T <: Tuple] = T match
  case EmptyTuple => true
  case Int *: ts => OnlyInts[ts]
  case _ => false

type IsOnlyInts[T <: Tuple] = OnlyInts[T] =:= true

def f[A <: AnyNamedTuple](using @annotation.implicitNotFound("Types ${NamedTuple.DropNames[A]} contain non-ints") e: IsOnlyInts[NamedTuple.DropNames[A]]): Unit = ()
@main def main(): Unit = f[(a: Int, b: String, c: Int)]

Output Error/Warning message

[error] -- [E172] Type Error: Main.scala:13:55 
[error] 13 |@main def main(): Unit = f[(a: Int, b: String, c: Int)]
[error]    |                                                       ^
[error]    |                         Types ?NamedTuple.DropNames[A] contain non-ints
[error] one error found

Why this Error/Warning was not helpful

What happened here is that the pretty printer inside implicitNotFound has interpreted the whole NamedTuple.DropNames[A] as a type variable name and, since it could not find it, it printed ?.

Suggested improvement

To increase utility of annotations like implicitNotFound for explaining users what they should improve, the compiler should interpret expressions inside ${} as not only variables and normalize/simplify them before printing. What I would like to see here:

[error] 13 |@main def main(): Unit = f[(a: Int, b: String, c: Int)]
[error]    |                                                       ^
[error]    |                         Types (Int, String, Int) contain non-ints

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:annotationsarea:reportingError reporting including formatting, implicit suggestions, etcbetter-errorsIssues concerned with improving confusing/unhelpful diagnostic messagesitype:enhancement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions