[gnu.gcc.bug] Just when you thought it was safe - Son Of Lint II.

paul@UUNET.UU.NET (Paul Hudson) (03/22/89)

I've restored the infamous "possible loss of precision" lint warning. (But improved).

On assigment, return etc it warns 
	when the "signedness" of a variable is implicitly changed

	when a "longer" variable is assigned to a "shorter". Longer and shorter
	are interpreted in the wide sense that char is shorter than short is shorter than
	int is shorter than long (and commented out) is shorter than long long.

	when an enumeration variable is assigned to an enumertion variable of a
	different type.

These are sufficiently picky that many people won't want them on. They ought to be
given different -W options, but as supplied here they're on by default (not recommended).


Given the following C:

main()
{
    char c;
    short s;
    int i;
    long l;
    float f;
    double d;
    struct st
    {
	int i:9;
    } st;

    c = s;
    s = i;
    i = l;
    f = d;

    c = (char)s;
    s = (short)i;
    i = (int)l;
    f = (float)d;

    d = i;
    i = d;

    c = c;
    s = c;
    i = s;
    l = i;
    l = l;

    st.i = i;
    c = st.i;
    s = st.i;
    i = st.i;
}

It produces the following warnings ....

t.c: In function main:
t.c:14: warning: possible loss of precision on assignment
t.c:15: warning: possible loss of precision on assignment
t.c:16: warning: possible loss of precision on assignment
t.c:17: warning: double shortened to float on assignment
t.c:24: warning: implicit conversion of integer to real on assignment
t.c:25: warning: implicit conversion of real to integer on assignment
t.c:34: warning: possible loss of precision on assignment
t.c:35: warning: possible loss of precision on assignment

Note that casting prevents the warning!

Note that assigment to a short or char from a bit field produces a warning. I don't like this.
Perhaps some more knowledgable person could point out how I tell the type's really a bit field.
It's difficult to warn about bitfields because casts can't be used to remove the warning. What's
needed is an extension to casts to allow "(int :4)i" constructs.


The code at the end of this message is intended to be added to convert_for_assignment like this
(patch not used because I've made other changes)

  /* Arithmetic types all interconvert, and enum is treated like int.  */
  if ((codel == INTEGER_TYPE || codel == REAL_TYPE || codel == ENUMERAL_TYPE)
       &&
      (coder == INTEGER_TYPE || coder == REAL_TYPE || coder == ENUMERAL_TYPE))
    {
+      warn_on_assignment(TYPE_MAIN_VARIANT(type), TYPE_MAIN_VARIANT(rhstype), errtype);

      return convert (type, rhs);
    }


Paul Hudson 

Snail mail: Monotype ADG	Email:	...!ukc!acorn!moncam!paul
	    Science Park,		paul@moncam.co.uk
	    Milton Road,	"Sun Microsysytems:
	    Cambridge,		 The Company is Arrogant (TM)"
	    CB4 4FQ

/*
 * Make lots of picky warnings about assigments
 */
warn_on_assignment(lhs, rhs, errstr)
tree lhs, rhs;
char *errstr;
{

 if (TREE_CODE(lhs) == REAL_TYPE)
 {
     if (TREE_CODE(rhs) == INTEGER_TYPE)
	 warning ("implicit conversion of integer to real on %s", errstr);
     else if (TREE_CODE(rhs) == ENUMERAL_TYPE)
	 warning ("implicit conversion of enumeral to real on %s", errstr);
     else if (lhs == float_type_node && rhs == double_type_node)
	 warning ("%s implicitly converts double to float on %s", errstr);
 }
 else if (TREE_CODE(lhs) == INTEGER_TYPE)
 {
     if (TREE_CODE(rhs) == INTEGER_TYPE)
     {
	 if (TREE_UNSIGNED(lhs) && !TREE_UNSIGNED(rhs))
	 {
	     warning("%s of unsigned from signed", errstr);
	 }
	 else if (!TREE_UNSIGNED(lhs) && TREE_UNSIGNED(rhs))
	 {
	     warning("%s of signed from unsigned", errstr);
	 }
	 

	 /* here we want to warn about assigments of longs to ints, even though
	    usually they have the same number of bits */
	 if (size_order(lhs) < size_order(rhs))
	     warning("possible loss of precision on %s", errstr);
	 
     }
     else if (TREE_CODE(rhs) == ENUMERAL_TYPE)
     {
	 if (TYPE_PRECISION(rhs) > TYPE_PRECISION(lhs))
	     warning ("some values of enumeration may be lost on %s", errstr);
     }
     else 
     {
	 warning ("implicit conversion of real to integer on %s", errstr);
     }
 }
 else 
 {
     /* enumeration */
/*
  This doesn't work very well because enumeration constants seem to be ints 
     if (TREE_CODE(rhs) != ENUMERAL_TYPE)
	 warning ("implicit conversion of non-enumeral to enumeral on %s", errstr);
*/
     else if (lhs != rhs)
     {
	 warning ("%s of enumeral type from different enumeral type", errstr);
     }
 }
}

/*
 * Return a number in the same order as the ordering of the sizes of the types,
 * ie char < short < int < long < long long. This is used instead of TYPE_PRECISION
 * because for example we'd like to warn about assigments of ints from longs, but on many
 * machines they are both 32 bits.
 */
int size_order(type)
tree type;
{
    if (type == unsigned_char_type_node ||
	type == signed_char_type_node ||
	type == char_type_node)
	return 1;
    else if (type == short_integer_type_node ||
	     type == short_unsigned_type_node)
	return 2;
    else if (type == integer_type_node ||
	     type == unsigned_type_node)
	return 3;
    else if (type == long_integer_type_node ||
	     type == long_unsigned_type_node)
	return 4;
    return 0;
/*
	For some reason these aren't in c-tree.h (but are in c-decl.c)
    else if (type == long_long_integer_type_node ||
	     type == long_long_unsigned_type_node)
	return 5;
*/
}