Subject: Re: ansi.h merger
To: None <eeh@netbsd.org>
From: Todd Vierling <tv@wasabisystems.com>
List: tech-toolchain
Date: 07/28/2001 14:52:11
On 28 Jul 2001 eeh@netbsd.org wrote:

: | If the struct member really is "int", you have to cast it anyway -- you lose
: | signedness if you try to autoconvert it to size_t (which is unsigned).  This
: | is required.
: |
: | If the struct member is "unsigned [int]", you shouldn't have to cast it, as
: | the autoconversion rules in C99 will let this be promoted to "unsigned long"
: | (where that is the type of size_t) without a warning.
:
: OTOH, if you assign a size_t to that member, on some platforms you need
: to cast it since size_t is unsigned long and you lose precision, and on
: others (most) you don't.

If the member is "int", you still have to cast it, because a conversion from
unsigned to signed is not implicitly compatible.  This should be warned by
both lint and gcc.

If the member is "unsigned [int]", you might miss the warning on some
platforms, depending on how size_t is typedef'd.  However, provided size_t
is typedefed as "unsigned long", there are -W options to gcc that should
warn about this particular case on all platforms.

===

: The problem is that most people are running machines in the latter
: category and do not get warnings when they fail to add the correct casts
: that are required on 64-bit platforms.

There are ways to make gcc warn about this, actually.  For the size_t case,
if typedefed as "unsigned long", any conversion to "int" or "unsigned [int]"
can be flagged at compile time as an unportable implicit conversion.

In fact, you can add some gcc extensions (that might be enabled by a
compile-time "-D"efine) that will make all nonprimitive integer typedefs
incompatible for implicit conversion with primitive types, if you wish.
This would probably provide the type of auditing necessary to flag problems
on all platforms that you want, *without* going to the length of allowing
people to write sloppy code by flattening types arbitrarily.

If this gcc-extension method is employed, there are some specific
nonprimitive types that I believe should not be flattened, such as
"[u_]int64_t".

===

: | : | :     strncpy(foo, bar, 13);
: | : | :
: | : | : Well, is that 13U or 13UL? Or should I need to cast it to (size_t)13?
: | : |
: | : | C99 type conversion rules allow this case to be promoted without a warning,
: | : | because the default case of nonnegative "int" constant can be promoted to
: | : | "unsigned int" and then "unsigned long" automatically.

: | Then we should fix lint.  It shouldn't bitch on this case, because "13" is a
: | nonnegative constant.
:
: Or it should always bitch on all platforms whenever you do any implicit
: conversions since they could possibly be wrong on other platforms.

No, I was specifically pointing out that lint should not bitch when a
nonnegative integer *constant* that would otherwise be of type "int" or
"unsigned [int]" was implicitly converted to "unsigned long [int]".

Lint should bitch on all implicit conversions that involve downsizing of a
type or changing signedness, because those do break.  In most cases, if we
plan such an ansi.h merger carefully (and I'm not saying it cannot be done
at least partially), we can set up the typedefs to flag this in as many
cases as possible.

The problem goes both ways:

* If you define all 32-bit types as "[unsigned] int", you get warnings if
  trying to convert implicitly a "[unsigned] long" into the nonprimitive
  type.

* On the other hand, if you define 32-bit types on ILP32 as "[unsigned]
  long", you get warnings if trying to convert implicitly to an "[unsigned]
  int" primitive, such as in the struct assignment example above.

Neither situation necessarily alleviates the issues of badly written code,
or some platforms having to clean up after others.

===

: | : | Not all can be made MI, mind you.  I found clock_t as one such type already,
: | : | but I haven't scanned through everything.
:
: And clock_t cannot be made MI because?

It's unsigned on some platforms, and signed on others.  Signedness changes
the integer operations used by the compiler for comparisons and arithmetic,
and thus, signedness of a type is an ABI component.

You can't just move an "int" to an "unsigned [int]", or vice versa, and
expect all platforms to be happy with that when running older binaries.
(The problems, too, will show up as rather unobvious corner cases, and are
likely to cause hard to diagnose errors.)

===

: | IMNSHO, I'd rather see these typedef'd in terms of the bitsized types where
: | possible, to help readability, but that will again lead you down the path of
: | incongruous types on some platforms.  Then again, some of those types can
: | probably be collapsed to MI definitions too.
:
: I disagree about fixed-sized types.  Most of these types are relative to other
: types, rather than a fixed size.  That's really the issue.  A size_t or ptrdiff_t
: needs to be able to handle the size of a pointer, which is a non-issue if `int'
: and `long' are the same size but a very important issue if they are not.

This wasn't my point with the above statement.  Some things which are
already same-size quantities (32-bit, that is) on all platforms may be
better expressed in terms of the sized types, for readability purposes (and
nothing more).  size_t and ptrdiff_t do not fall into this category.  Hence
the words "where possible".

-- 
-- Todd Vierling <tv@wasabisystems.com>  *  Wasabi NetBSD:  Run with it.
-- NetBSD 1.5 now available on CD-ROM  --  http://www.wasabisystems.com/