[Date Prev][Date Next][Thread Prev][Thread Next][Author Index][Date Index][Thread Index]

easy to miss bug (even in structured debug)



Abstract: One way to manage NULL pointers explicitly is to consider
NULL to be of a separate type rather than as a valid pointer of any
type.  This restricts the use of polymorhism between NULL and other
pointers without imparing our programming power (just like strong
typing does).  I believe XLint can statically check for NULL pointer
bugs in programs of this style.
------

To vaoid the subjuctive mood, I make the suggestions below as if we
should implement them all right now.  There is certainly a very smooth
low road for the below suggestions that won't impact our development
schedule.  I will try to indicate it at the end.  I also start with an
unimplementable syntax.  I'll write the implementable syntax afterwards.
NOTE: this doesn't have to go to developers!  We can use it solely for
the backend.

Consider NULL to be a pointer to type BOTTOM.  Heaper or Tofu is a
virtual subclass of BOTTOM.  (We can't implement it this way, but this
suggests the appropriate type distinctions).  BOTTOM is the class that
declares an empty protocol.  The conversion from a pointer which could
be NULL to a types pointer is just a conversion down the type
hierarchy.  The first translation of michael's program is thus:

   sub(BOTTOM AS(T) * p)
   {
	   if (p == NULL) {
		   SubTakingNULL(p);
	   } else {
		   SubTakingOnlyT(CAST(T,p));
	   }
   }

This is still making a type distinction based on a runtime value.
Let's use mechansim we already understand for that:

   sub(BOTTOM AS(T) * p)
   {
	   if (IS_KIND_OF(p,T)) {
		   SubTakingOnlyT(CAST(T,p));
	   } else {
		   SubTakingNULL(p);
	   }
   }

I'd really like to just use the DISPATCH macro for this.  Is it
possible to overload a function for NULL?  I suspect so using 'void *'.

   #define BOTTOM void
   sub(BOTTOM * p)
   {
	/* I'm not sure this is the right syntax
	   for DISPATCH (or is it CHOOSE?). */
	DISPATCH((T), castp, {
	   return sub(castp);
	});
	SubTakingNULL(p);
   }
   sub(T * p)
   {
	SubTakingOnlyT(p);
   }

This parallels the way we make other runtime *behavioral*
distinctions.  This also follows the notion that type distinctions are
exactly distinctions in message protocol.  

Actual implementation:

I think BOTTOM is actually equivalent to 'void'.  BOTTOM AS(T) could
be defined as a single macro (void_AS(type)) if we want to have it
expand to just T.  

XLint only needs to enforce the additional constraint that NULL can
only be returned if the base return type is 'void *'.  I believe this
should be easy.  Roland?

I thikn there's a hole in my overloading scheme.  I'll let it bounce
around a bit.

The low road essentially lets us define the macros and start using
them whenever someone has time.  When the checker gets eventually
added to XLint, all the places where we return NULL and the type isn't
defined as BOTTOM (void *), will show up as warnings.  We can
incrementally correct the type declarations in the code to turn off
the warnings.

Comments?

dean