[External] : Re: Error examples page

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Wed Feb 8 01:57:29 UTC 2023


Hi,
there are two classes of errors that are hard. The first, as your 
example demonstrates, has to do with parser errors. When parsing it is 
hard to recover from errors. Javac tries to do its best, but if you are 
parsing a production, and you find a token that is unexpected for that 
production, it is not clear what should happen next. I mean, an error 
should be reported - but what then? Javac tries to figure out where to 
start parsing again - in some cases this can be done with some success 
(e.g. finding the matching closing brace, parens, etc.). In other cases 
it's harder, and it can lead to pathological situations such as the ones 
you describe. Some of these issues might be made worse by actual bugs 
(so it is possible that in the case of "expected class" there's an 
actual issue with javac - would be nice to have a reproducer).

The other class of errors that are hard are errors related to overload 
resolution and inference. The problem there is that the error has to 
cover so much ground (too much). When you have an overload resolution 
failure, you can, at least in the general case, have many overload 
candidates, each of which fails for a different reason (which might be 
interesting or not). If there's inference, the reason as to why a 
candidate is not applicable might be obscure (e.g. incompatible bounds 
on an inference variable), and if you have nested or chained generic 
method calls, things tend to explode pretty quickly, so when your 
typical stream chain fails to type it's hard to understand exactly what 
needs to be fixed - often because the error typicall shows up in the 
wrong place. For instance, you might get an error message when you call 
`toList()` on a stream and assign it to a `List<String>` - but the real 
problem is that the type of the stream is not `Stream<String>` as you 
expect: something sent inference down the wrong path in an earlier call 
in the method chain, which caused the stream type to become 
`Stream<Object>`.

These things can (and should!) be improved, but there's a limit on how 
much you can improve things when working with the command line 
interface. For instance, when diagnosing inference issues inside an IDE, 
I find myself hoovering on each component of the call chain, or try to 
use auto-complete after each subexpression, to have a rough idea of what 
the type might be at that point. Even doing that is not always possible: 
methods that are generic in their return type can be "influenced" by 
their surrounding context, so breaking up a method chain can sometimes 
result in changes to how a subexpression is typed. So... it's an hard 
problem - and the limited visual aids a terminal offer, combined with 
the lack of interaction makes these issues particularly pesky. Other 
langauges have explored the idea of a type debugger [1] - although I 
don't know what's the state of these initiatives.

Cheers
Maurizio

[1] - https://infoscience.epfl.ch/record/179877?ln=en



On 04/02/2023 03:48, Ethan McCue wrote:
> 1. The error messages that the student got were not helpful for them 
> seeing their error. I don't have their original code so I can't dive 
> in to why and say "ah, we could obviously make this case better", but 
> I choose to believe there was some way to not vomit "class, interface, 
> enum, or record expected" nine times.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20230208/fd074eb5/attachment.htm>


More information about the compiler-dev mailing list