October 02, 2007
The hazards of warning messages
Experienced programmers usually think that warning messages from compilers are a Good Thing. After all, such messages let us know when we have written programs that might not do what we expected, and sometimes save us from hours of debugging.
For example, if we intended to write
if (x == 0)
and instead wrote
if (x = 0)
the compiler would surely be providing a useful service by telling us that we had probably done something we didn't intend.
However, there is a dark side to compiler warnings that takes a while to appreciate. We can begin to see its shadows by asking what we should do when we compile someone else's program and see a dozen (or a hundred) warning messages.
The obvious answer is to locate the source of the warnings and change the program so that they no longer appear. After all, every warning represents a part of the program that might be hiding a serious bug, and if the program produces too many warnings, we have no way of knowing how many of those bugs might be waiting to bite us.
In effect, a conscientious programmer will treat compiler warning messages similarly to compiler error messages: as indicating problems that must be fixed before proceeding further. Indeed, it is not uncommon for commercial programming shops to decree that warning messages are unacceptable: All code must compile warning-free.
That's a Good Thing too, isn't it? After all, isn't it reasonable to insist that programmers refrain from writing programs that even a compiler can see are hazardous? Who could argue with such an aim?
Indeed, it is hard to argue with an aim expressed as this one is. However, if we take a step back, we shall see that the problem is not quite that simple. For if an organization insists on programs compiling warning-free, then the programmers are no longer writing their programs in the programming language that their manuals define. Instead, they are programming in the intersection of the language in the manual and the language that the compiler doesn't warn about. Suddenly, the language definition changes from one that is written down to one that must be inferred from the compiler's behavior.
The situation gets worse when we have to deal with more than one compiler--as we all do. Remember that each new release of a compiler is effectively a new compiler from the user's viewpoint. Suppose, for example, that a compiler developer wakes up one day and realizes that a class with a destructor and no constructor is usually asking for trouble. After suitable discussion and review, a new release of the compiler comes out that gives warning messages for such classes.
Now shift focus to a developer using that compiler. The developer wrote this code:
class Thing {
public:
virtual ~Thing() { }
};
The old compiler accepted it with nary a peep; the new compiler warns that there is a destructor but no constructor.
In a sense, that behavior is a compiler bug: If you treat warnings as errors, you've just lost the ability to have an empty virtual destructor in an abstract base class. In another sense, of course, one might argue that it is never a bug to issue a warning as long as it is warning about something that is actually happening.
This example shows why I think warning messages are a decidedly mixed blessing. They have certainly saved my hide, but equally certainly they have made it hard for me to do things I thought were entirely reasonable.
I'd love to see reader comments about their experiences with warning messages.
Posted by Andrew Koenig at 11:36 AM Permalink
|