16

I don't plan on writing a compiler in the near future; still, I'm quite interested with compiler technologies, and how this stuff could be made better.

Starting with compiled languages, most compilers have two error levels: warnings and errors, the first being most of the time non-fatal stuff you should fix, and errors indicating most of the time that it's impossible to produce machine- (or byte-) code from the input.

Though, this is a pretty weak definition. In some languages like Java, certain warnings are simply impossible to get rid of without using the @SuppressWarning directive. Also, Java treats certain non-fatal problems as errors (for instance, unreachable code in Java triggers an error for a reason I'd like to know).

C# doesn't have the same problems, but it does have a few. It seems that compilation occurs in several passes, and a pass failing will keep the further passes from executing. Because of that, the error count you get when your build fails is often grossly underestimated. On one run it might say you have two errors, but once you fix them maybe you'll get 26 new ones.

Digging to C and C++ simply shows a bad combination on Java and C#'s compilation diagnostic weaknesses (though it might be more accurate to say that Java and C# just went their way with half the problems each). Some warnings really ought to be errors (for instance when not all code paths return a value) and still they're warnings because, I suppose, at the time they wrote the standard, compiler technology wasn't good enough to make these kind of checks mandatory. In the same vein, compilers often check for more than the standard says, but still use the "standard" warning error level for the additional findings. And often, compilers won't report all the errors they could find right away; it might take a few compiles to get rid of all of them. Not to mention the cryptic errors C++ compilers like to spit, where a single mistake can cause tens of error messages.

Now adding that many build systems are configurable to report failures when the compilers emit warnings, we just get a strange mix: not all errors are fatal but some warnings should; not all warnings are deserved but some are explicitly suppressed without further mention of their existence; and sometimes all warnings become errors.

Non-compiled languages still have their share of crappy error reporting. Typos in Python won't be reported until the code is actually run, and you can never really kick of more than one error at a time because the script will stop executing after it meets one.

PHP, on its side, has a bunch of more or less significant error levels, and exceptions. Parse errors are reported one at a time, warnings are often so bad they should abort your script (but don't by default), notices really often show grave logic problems, some errors really aren't bad enough to stop your script but still do, and as usual with PHP, there are some really weird things down there (why the hell do we need an error level for fatal errors that aren't really fatal? E_RECOVERABLE_E_ERROR, I'm talking to you).

It seems to me that every single implementation of compiler error reporting I can think of is broken. Which is a real shame, since how all good programmers insist on how important it is to correctly deal with errors and yet can't get their own tools to do so.

What do you think should be the right way to report compiler errors?

10
  • -1: "Non-compiled languages still have their share of crappy error reporting" Subjective and argumentative. Really unhelpful. Is this a question or a complaint?
    – S.Lott
    Jan 25, 2011 at 3:51
  • 3
    @S.Lott I think you're being a bit on the edge here. I find I was much harder on compiled languages, and it didn't seem to bother you.
    – zneak
    Jan 25, 2011 at 4:45
  • 2
    @S.Lott Am I wrong stating that Python indicates one error at a time?
    – zneak
    Jan 25, 2011 at 14:29
  • 2
    @S.Lott Then things must have changed, because last time I tried, any syntax error would cause Python to stop trying to "compile" and a name error would throw an exception and not check the rest of the function (though this did leave room for reporting one error per testable unit). My subjective and argumentative statement was an introduction to what I believed to be a fact, but if it isn't true anymore I'll go and edit my question. How does it work now?
    – zneak
    Jan 25, 2011 at 14:42
  • 1
    @S.Lott I expected you to tell me how it works now first.
    – zneak
    Jan 26, 2011 at 0:17

2 Answers 2

7

Your question doesn't seem to actually be about how we report compiler errors - rather, it's about the classification of problems and what to do about them.

If we start by assuming, for the moment, that the warning/error dichotomy is correct, let's see how well we can build on top of that. Some ideas:

  1. Different "levels" of warning. A lot of compilers sort-of implement this (for example GCC has lots of switches for configuring exactly what it will warn about), but it needs work - for example, reporting what severity a reported warning is, and the ability to set "warnings are errors" for only warnings above a specified severity.

  2. Sane classification of errors and warnings. An error should only be reported if the code doesn't meet the specification, and hence cannot be compiled. Unreachable statements, while probably a coding error, should be a warning, not an error - the code is still "valid", and there are legitimate instances in which one would want to compile with unreachable code (quick modifications for debugging, for instance).

Now things I disagree with you on:

  1. Making extra effort to report every problem. If there's an error, that breaks the build. The build is broken. The build will not work until that error is fixed. Hence, it's better to report that error immediately, rather than "carrying on" in order to try and identify everything else "wrong" with the code. Especially when a lot of those things are probably caused by the initial error anyway.

  2. Your specific example of a warning-that-should-be-an-error. Yes, it's probably a programmer mistake. No, it shouldn't break the build. If I know the input to the function is such that it will always return a value, I should be able to run the build and do some tests without having to add those extra checks. Yes, it should be a warning. And a damn high-severity one at that. But it shouldn't break the build in and of itself, unless compiling with warnings-are-errors.

Thoughts?

1
  • I agree with you, except for the points where we disagree (duh), so that's +1 from me. I think it's easy enough to make every code path either return a value or abort your program, considering how bad it is when you actually fall in the case of the undefined behavior.
    – zneak
    Jan 25, 2011 at 1:04
10

One issue you brought up was incomplete reporting of errors -- e.g., reporting 2 errors, and when you fix them, you get a bunch more.

This is (largely) a compromise on the part of the compiler writer. Depending on what error you've made, it's very easy for the compiler to start to misunderstand what you do have badly enough that it starts to report errors that have very little to do with reality. Just for example, consider a simple typo where you have something like itn x; instead of int x;. Unless you've done something else that makes itn mean something, this is going to be reported as an error. That's fine as far as it goes, but now consider what happens next -- the compiler looks at lots of code that tries to use x as a variable. Should it A) stop and let you fix that, or B) spew 2000 errors about error: "x": undeclared identifier or something on that order? Consider another possibility:

int main()[

This is another pretty obvious typo -- obviously it should be a { instead of a [. The compiler can tell you that part pretty easily -- but should it then go on to report an error for something like x=1; saying something like error: statement only allowed inside a function?

Note that these are even fairly trivial problems -- much worse ones are easy to find (especially, as most of us know, when you get into C++ templates). The bottom line is that the compiler writer is usually stuck with trying to compromise between reporting false errors (i.e., reporting something as an error, even though it's fine) and failing to report real errors. There are some rules of thumb most follow to try to keep from going too far wrong in either direction, but almost none of them is anywhere close to perfect.

One other problem you mentioned was Java and @SupressWarning. This is quite different from the above -- it would be fairly trivial to fix. The only reason it's not fixed is that doing so doesn't fit with the basic "character" of Java -- i.e., in their opinion, "that's not a bug, it's a feature." Even though it's usually a joke, in this case the people involved are so misguided that they really believe it's true.

The problem you mention in C and C++ with code paths that don't return a value isn't really to allow for primitive compilers. It's to allow for decades of existing code, some of which nobody wants to fix, touch, or even read. It's ancient and ugly but it works, and nobody wants anything but for it to continue working. For better or worse, the language committees are pretty much stuck with maintaining that backward compatibility, so they continue to allow things that nobody really likes -- but some people (at least think they) need.

3
  • 3
    In addition to your point about early errors causing many others, there's also the fact that later passes are often built to require earlier passes to have completed successfully. For example, one of the early passes in the C# compiler checks to make sure that there are no cycles in the inheritance graph - you don't have A inherit from B which inherits from A. If you wanted to carry on and generate a list of all errors after that, each later pass would have to be able to cope with cycles - making it significantly slower even on "good" compiles.
    – Anon.
    Jan 25, 2011 at 1:53
  • @Anon. The Java compiler makes much better efforts at surviving early passes, and I don't find it significantly slower. To me it's somewhat annoying how quick csc gives up.
    – zneak
    Jan 25, 2011 at 4:35
  • 1
    @zneak: As Jerry says, it's a compromise on the part of the developers of the compilers. Writing good error diagnostics is actually a very difficult problem (look at clang for an example of how far you can really take it). See here for a good discussion of the C# compiler's phases and passes. Jan 25, 2011 at 5:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.