pointer <-> integer round-up

Tom Lord lord@emf.net
Tue Mar 19 12:06:00 GMT 2002


fjh actually made a point that, while it doesn't change my mind,
does correct a misunderstanding I've had.

Once again, the practical problem is how to pass range-limited
integers through `void *' parameters, fields, or variables with code
that (1) doesn't generate a warning, (2) won't truncate those integers
more than necessary on any existing or future system.

Conversions from integer to pointer types are legal in some
circumstances, but not as an implicit (castless) conversion, as in an
assignment to an integer variable.  I thought they were legal in that
case (not wise to use, certainly worthy of a warning, but legal).
That's the traditional behavior of C.  It's what GCC compiles.  I'd
bet we can find old code that relies on that implicit conversion.  But
according to the strictest reading of the standard, GCC _could_ treat
such an assignment as an error and refuse to compile the program.  My
mistake.

So what we're left with is this: the pointer <-> integer conversions
are legal (with casts), though their meaning is undefined.  When used
for range-limited integers passed through a `void *' variable, they
have a conventional meaning that is reliably provided on pretty much
every system we can imagine: reasonable programs can use that
conversion.  It's one of the core strengths of C that programmers have
access to such features.

At the same time, people often use those same conversions to pass a
_pointer_ value through an integer variable.  That too has no fixed
meaning, but reasonable programs can use it on many systems we can
imagine wanting to use -- assuming that the integer type is carefully
chosen.  People often enough don't make sure the integer type is wide
enough for this purpose that, for example, rth has had to deal with
many such bugs doing an alpha port of Linux.

The current warnings in GCC make the pessimal assumption: that code
which legally casts between pointer <-> integer is likely expecting to
preserve all the bits in both directions.  If the types differ in
size, it issues a warning and that warning can not be suppressed.  (If
you're going to have such a warning, why not make it platform
independent so that the problems show up before rth has to deal with
them?)

A variety of double-cast workarounds have been suggested as the means
to suppress the warning.  None of these really solves the problem: we
can imagine (perhaps even find) systems on which the widest integer
type that is not wider than a pointer is too small to hold our
range-limited integers -- on such systems, the conversion invoked will
be different from the single-cast conversion.  Additionally, there is
no guarantee that we'll find any integer type which is wide enough to
hold a pointer, but no wider.   The double-cast trick won't suppress
warnings and will perform an incorrect conversion on systems with:

	type		sizeof(type)

	short		2
	int		8
	long		8
	intptr_t	8
	void *		4

fjh proposes that, if such systems ever become important, gcc can
treat double-casts as a special case (by not giving warnings).  I
think the idea of incorporating such a special case into the design
decisions of GCC is distasteful, but a more objective reason why it's
a bad idea is that it doesn't solve the problem of an
incorrectly-truncating conversion.

So what are reasonable levels of warning to make available through
various options (it's not worth arguing about what the default should
be in a context like this, where our worldviews about C are already so
diverged)? 

A traditional level watches for constructs that represent certain
kinds of common editing error: unused variables and labels; clearly
unreachable code; not clearly unreachable implicit returns from
non-void functions; problematic conversions compiled by default, but
not marked by an explicit cast.  One makes some changes to a program,
compiles with those warnings enabled, and _every_ warning generated
points out a place where a change had unexpected effects, worthy of
review.

Another level of warnings watches for constructs that people sometimes
use incorrectly: pointer to integer conversions, for example.  One
compiles a (non-portable) program on a new platform, and those
warnings point out parts of the program that _might_ be the source of
the non-portability.

These are both useful functionality to have.  GCC used to have the
traditional level of warning.  Now it doesn't.  Please reconsider.

The burden of helping to fix non-portable programs shouldn't rest
entirely on the compiler anyway.  A beefed-up lint, particularly an
extensible lint that could learn about program-specific invariants, is
a better long term solution.  (I used to have one lying about the place,
too.)

-t



More information about the Gcc mailing list