
#9173: Better type error messages -------------------------------------+------------------------------------- Reporter: simonpj | Owner: goldfire Type: bug | Status: new Priority: high | Milestone: Component: Compiler | Version: 7.8.2 Resolution: | Keywords: | TypeErrorMessages Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): I think the time is ripe for this ticket, and it shouldn't be hard. Here are the pieces: 1. Update `TcErrors.mkExpectedActualMsg` to print out the `uo_thing` field of the `TypeEqOrigin` passed in. For example, if we're reporting an actual of `Maybe Int` and an expected of `Bool`, then `uo_thing` might hold `Just 5`. This step can likely be done by changing `msg1` of `mkExpectedActualMsg`. 2. Rip out the special treatment of kinds in `mkExpectedActualMsg`. Other than swapping out the word "kind" for "type", kinds and types should be treated the same. This special treatment is currently there solely to match pre-8.0 behavior. Doing this step should improve the error messages worsened by #14066 and fix, e.g., #14887. Concretely, this step is essentially to remove `msg5` from `mkExpectedActualMsg`. Those two steps, by themselves, would nail this ticket. But Simon and I think we can do better. Currently, the `uo_thing` of a `TypeEqOrigin` tracks the term (or type, during kind-checking) that has the "actual" type. (It's the `Just 5` mentioned in step 1.) However, we can also track the aspect of the context that leads us to expect the "expected" type. For example, this string could be something like "required as the condition of an `if`" or "of the second argument of `($)`" or "of the type used in a type signature". Here are example error messages: Code: `if 'x' then foo else bar` {{{ Type mismatch: `Char' /= `Bool' Actual: `Char' is the type of `'x'' Expected: `Bool` is the type required as the condition of an `if' }}} Code: `not $ 'x'` {{{ Type mismatch: `Char' /= `Bool' Actual: `Char` is the type of `'x'' Expected: `Bool` is the type of the second argument of ($) }}} Code: `foo :: Maybe` {{{ Kind mismatch: `* -> *' /= `*' Actual: `* -> *' is the kind of `Maybe' Expected: `*` is the kind of the type used in a type signature }}} Aren't these just lovely? To do this: 3. Add an `SDoc` field to the `Check` constructor of an `ExpType` (in `TcType`). Then, in every `mkCheckExpType`, supply an appropriate message. Ideally, this message will not mention any types, because there will be no chance to zonk them later. 4. Add a new field `uo_context` to `TypeEqOrigin` (of type `Maybe SDoc`) that will get the doc from the `Check`. This seems like it will happen in `TcUnify.tcSubTypeDS_NC_O`, but perhaps in other places, too. It's worth checking every place we make a `TypeEqOrigin`. 5. Teach `TcErrors.mkExpectedActualMsg` to use this new info. We could do steps 1-2 separately from 3-5, but given the very large number of error messages one would have to sweep through to do so, it seems best to common these changes up. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/9173#comment:30 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler