c: Implement new -Wenum-int-mismatch warning [PR105131]

In C, an enumerated type is compatible with char, a signed integer type,
or an unsigned integer type (6.7.2.2/5).  Therefore this code compiles:

  enum E { l = -1, z = 0, g = 1 };
  int foo(void);
  enum E foo(void) { return z; }

if the underlying type of 'enum E' is 'int' (if not, we emit an error).
This is different for typedefs, where C11 permits typedefs to be
redeclared to the same type, but not to compatible types.  In C++, the
code above is invalid.

It seems desirable to emit a warning in the C case, because it is
probably a mistake and definitely a portability error, given that the
choice of the underlying type is implementation-defined.

To that end, this patch implements a new -Wenum-int-mismatch warning.
Conveniently, we already have comptypes_check_enum_int to detect such
mismatches.  This warning is enabled by either -Wall or -Wc++-compat.

	PR c/105131

gcc/c-family/ChangeLog:

	* c.opt (Wenum-int-mismatch): New.

gcc/c/ChangeLog:

	* c-decl.cc (diagnose_mismatched_decls): Warn about enum/integer type
	mismatches.
	* c-tree.h (comptypes_check_enum_int): Declare.
	* c-typeck.cc (comptypes): No longer static.

gcc/ChangeLog:

	* doc/invoke.texi: Document -Wenum-int-mismatch.

gcc/testsuite/ChangeLog:

	* gcc.dg/Wenum-int-mismatch-1.c: New test.
	* gcc.dg/Wenum-int-mismatch-2.c: New test.
	* gcc.dg/Wenum-int-mismatch-3.c: New test.
	* gcc.dg/Wenum-int-mismatch-4.c: New test.
	* gcc.dg/Wenum-int-mismatch-5.c: New test.
This commit is contained in:
Marek Polacek 2022-04-01 16:55:58 -04:00
parent 1875214cd1
commit 7da9a08960
10 changed files with 177 additions and 3 deletions

View File

@ -638,6 +638,10 @@ Wenum-conversion
C ObjC C++ ObjC++ Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra)
Warn about implicit conversion of enum types.
Wenum-int-mismatch
C ObjC Var(warn_enum_int_mismatch) Warning LangEnabledBy(C ObjC,Wall || Wc++-compat)
Warn about enum/integer type mismatches.
Werror
C ObjC C++ ObjC++
; Documented in common.opt

View File

@ -1993,9 +1993,12 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
bool pedwarned = false;
bool warned = false;
bool enum_and_int_p = false;
auto_diagnostic_group d;
if (!comptypes (oldtype, newtype))
int comptypes_result = comptypes_check_enum_int (oldtype, newtype,
&enum_and_int_p);
if (!comptypes_result)
{
if (TREE_CODE (olddecl) == FUNCTION_DECL
&& fndecl_built_in_p (olddecl, BUILT_IN_NORMAL)
@ -2137,6 +2140,13 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
return false;
}
}
/* Warn about enum/integer type mismatches. They are compatible types
(C2X 6.7.2.2/5), but may pose portability problems. */
else if (enum_and_int_p && TREE_CODE (newdecl) != TYPE_DECL)
warned = warning_at (DECL_SOURCE_LOCATION (newdecl),
OPT_Wenum_int_mismatch,
"conflicting types for %q+D due to enum/integer "
"mismatch; have %qT", newdecl, newtype);
/* Redeclaration of a type is a constraint violation (6.7.2.3p1),
but silently ignore the redeclaration if either is in a system
@ -2146,7 +2156,6 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
if (TREE_CODE (newdecl) == TYPE_DECL)
{
bool types_different = false;
int comptypes_result;
comptypes_result
= comptypes_check_different_types (oldtype, newtype, &types_different);

View File

@ -685,6 +685,7 @@ extern tree require_complete_type (location_t, tree);
extern bool same_translation_unit_p (const_tree, const_tree);
extern int comptypes (tree, tree);
extern int comptypes_check_different_types (tree, tree, bool *);
extern int comptypes_check_enum_int (tree, tree, bool *);
extern bool c_vla_type_p (const_tree);
extern bool c_mark_addressable (tree, bool = false);
extern void c_incomplete_type_error (location_t, const_tree, const_tree);

View File

@ -1055,7 +1055,7 @@ comptypes (tree type1, tree type2)
/* Like comptypes, but if it returns non-zero because enum and int are
compatible, it sets *ENUM_AND_INT_P to true. */
static int
int
comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
{
const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;

View File

@ -349,6 +349,7 @@ Objective-C and Objective-C++ Dialects}.
-Wno-div-by-zero -Wdouble-promotion @gol
-Wduplicated-branches -Wduplicated-cond @gol
-Wempty-body -Wno-endif-labels -Wenum-compare -Wenum-conversion @gol
-Wenum-int-mismatch @gol
-Werror -Werror=* -Wexpansion-to-defined -Wfatal-errors @gol
-Wfloat-conversion -Wfloat-equal -Wformat -Wformat=2 @gol
-Wno-format-contains-nul -Wno-format-extra-args @gol
@ -5759,6 +5760,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
-Wdangling-pointer=2 @gol
-Wduplicate-decl-specifier @r{(C and Objective-C only)} @gol
-Wenum-compare @r{(in C/ObjC; this is on by default in C++)} @gol
-Wenum-int-mismatch @r{(C and Objective-C only)} @gol
-Wformat @gol
-Wformat-overflow @gol
-Wformat-truncation @gol
@ -8735,6 +8737,25 @@ Warn when a value of enumerated type is implicitly converted to a
different enumerated type. This warning is enabled by @option{-Wextra}
in C@.
@item -Wenum-int-mismatch @r{(C and Objective-C only)}
@opindex Wenum-int-mismatch
@opindex Wno-enum-int-mismatch
Warn about mismatches between an enumerated type and an integer type in
declarations. For example:
@smallexample
enum E @{ l = -1, z = 0, g = 1 @};
int foo(void);
enum E foo(void);
@end smallexample
In C, an enumerated type is compatible with @code{char}, a signed
integer type, or an unsigned integer type. However, since the choice
of the underlying type of an enumerated type is implementation-defined,
such mismatches may cause portability issues. In C++, such mismatches
are an error. In C, this warning is enabled by @option{-Wall} and
@option{-Wc++-compat}.
@item -Wjump-misses-init @r{(C, Objective-C only)}
@opindex Wjump-misses-init
@opindex Wno-jump-misses-init

View File

@ -0,0 +1,43 @@
/* PR c/105131 */
/* { dg-do compile } */
/* { dg-options "-Wall -fno-short-enums" } */
enum E { E1 = -1, E2 = 0, E3 = 1 };
int foo(void); /* { dg-message "previous declaration" } */
enum E foo(void) { return E2; } /* { dg-warning "conflicting types" } */
void bar(int); /* { dg-message "previous declaration" } */
void bar(enum E); /* { dg-warning "conflicting types" } */
extern int arr[10]; /* { dg-message "previous declaration" } */
extern enum E arr[10]; /* { dg-warning "conflicting types" } */
extern int i; /* { dg-message "previous declaration" } */
extern enum E i; /* { dg-warning "conflicting types" } */
extern int *p; /* { dg-message "previous declaration" } */
extern enum E *p; /* { dg-warning "conflicting types" } */
enum E foo2(void) { return E2; } /* { dg-message "previous definition" } */
int foo2(void); /* { dg-warning "conflicting types" } */
void bar2(enum E); /* { dg-message "previous declaration" } */
void bar2(int); /* { dg-warning "conflicting types" } */
extern enum E arr2[10]; /* { dg-message "previous declaration" } */
extern int arr2[10]; /* { dg-warning "conflicting types" } */
extern enum E i2; /* { dg-message "previous declaration" } */
extern int i2; /* { dg-warning "conflicting types" } */
extern enum E *p2; /* { dg-message "previous declaration" } */
extern int *p2; /* { dg-warning "conflicting types" } */
enum F { F1 = -1, F2, F3 } __attribute__ ((__packed__));
enum F fn1(void); /* { dg-message "previous declaration" } */
signed char fn1(void); /* { dg-warning "conflicting types" } */
signed char fn2(void); /* { dg-message "previous declaration" } */
enum F fn2(void); /* { dg-warning "conflicting types" } */

View File

@ -0,0 +1,43 @@
/* PR c/105131 */
/* { dg-do compile } */
/* { dg-options "-Wall -fno-short-enums" } */
enum E { E1 = 0, E2, E3 };
unsigned int foo(void); /* { dg-message "previous declaration" } */
enum E foo(void) { return E2; } /* { dg-warning "conflicting types" } */
void bar(unsigned int); /* { dg-message "previous declaration" } */
void bar(enum E); /* { dg-warning "conflicting types" } */
extern enum E arr[10]; /* { dg-message "previous declaration" } */
extern unsigned int arr[10]; /* { dg-warning "conflicting types" } */
extern unsigned int i; /* { dg-message "previous declaration" } */
extern enum E i; /* { dg-warning "conflicting types" } */
extern unsigned int *p; /* { dg-message "previous declaration" } */
extern enum E *p; /* { dg-warning "conflicting types" } */
enum E foo2(void) { return E2; } /* { dg-message "previous definition" } */
unsigned int foo2(void); /* { dg-warning "conflicting types" } */
void bar2(enum E); /* { dg-message "previous declaration" } */
void bar2(unsigned int); /* { dg-warning "conflicting types" } */
extern unsigned int arr2[10]; /* { dg-message "previous declaration" } */
extern enum E arr2[10]; /* { dg-warning "conflicting types" } */
extern enum E i2; /* { dg-message "previous declaration" } */
extern unsigned int i2; /* { dg-warning "conflicting types" } */
extern enum E *p2; /* { dg-message "previous declaration" } */
extern unsigned int *p2; /* { dg-warning "conflicting types" } */
enum F { F1 = 1u, F2, F3 } __attribute__ ((__packed__));
enum F fn1(void); /* { dg-message "previous declaration" } */
unsigned char fn1(void); /* { dg-warning "conflicting types" } */
unsigned char fn2(void); /* { dg-message "previous declaration" } */
enum F fn2(void); /* { dg-warning "conflicting types" } */

View File

@ -0,0 +1,43 @@
/* PR c/105131 */
/* { dg-do compile } */
/* { dg-options "-Wc++-compat -fno-short-enums" } */
enum E { E1 = -1, E2 = 0, E3 = 1 };
int foo(void); /* { dg-message "previous declaration" } */
enum E foo(void) { return E2; } /* { dg-warning "conflicting types" } */
void bar(int); /* { dg-message "previous declaration" } */
void bar(enum E); /* { dg-warning "conflicting types" } */
extern int arr[10]; /* { dg-message "previous declaration" } */
extern enum E arr[10]; /* { dg-warning "conflicting types" } */
extern int i; /* { dg-message "previous declaration" } */
extern enum E i; /* { dg-warning "conflicting types" } */
extern int *p; /* { dg-message "previous declaration" } */
extern enum E *p; /* { dg-warning "conflicting types" } */
enum E foo2(void) { return E2; } /* { dg-message "previous definition" } */
int foo2(void); /* { dg-warning "conflicting types" } */
void bar2(enum E); /* { dg-message "previous declaration" } */
void bar2(int); /* { dg-warning "conflicting types" } */
extern enum E arr2[10]; /* { dg-message "previous declaration" } */
extern int arr2[10]; /* { dg-warning "conflicting types" } */
extern enum E i2; /* { dg-message "previous declaration" } */
extern int i2; /* { dg-warning "conflicting types" } */
extern enum E *p2; /* { dg-message "previous declaration" } */
extern int *p2; /* { dg-warning "conflicting types" } */
enum F { F1 = -1, F2, F3 } __attribute__ ((__packed__));
enum F fn1(void); /* { dg-message "previous declaration" } */
signed char fn1(void); /* { dg-warning "conflicting types" } */
signed char fn2(void); /* { dg-message "previous declaration" } */
enum F fn2(void); /* { dg-warning "conflicting types" } */

View File

@ -0,0 +1,5 @@
/* PR c/105131 */
/* { dg-do compile } */
/* { dg-options "-fno-short-enums" } */
#include "Wenum-int-mismatch-1.c"

View File

@ -0,0 +1,5 @@
/* PR c/105131 */
/* { dg-do compile } */
/* { dg-options "-fno-short-enums" } */
#include "Wenum-int-mismatch-2.c"