Bug 105131

Summary: Warning for mismatched declaration/definition with enum
Product: gcc Reporter: Richard Henderson <rth>
Component: cAssignee: Marek Polacek <mpolacek>
Status: RESOLVED FIXED    
Severity: enhancement CC: egallager, mpolacek, sjames
Priority: P3 Keywords: diagnostic
Version: 12.0   
Target Milestone: ---   
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2022-04-01 00:00:00
Bug Depends on:    
Bug Blocks: 87403    

Description Richard Henderson 2022-04-01 19:56:26 UTC
For a testcase such as

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

If the implementation type of 'enum E' is not 'int',
we will correctly emit an error (e.g. -fshort-enums).

It would be desirable to emit a warning in this case,
because it is probably a mistake and definitely a
portability error.
Comment 1 Marek Polacek 2022-04-01 20:17:26 UTC
Confirmed.
Comment 2 Marek Polacek 2022-04-01 20:59:07 UTC
Maybe something like the attached patch would work (but needs a new option, maybe -Wenum-int-mismatch, possibly enabled by -Wall?).  With it, the following test

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

void bar(int);
void bar(enum E);

extern enum E arr[10];
extern int arr[10];

is diagnosed like this:

105131.c:3:8: warning: conflicting types for ‘foo’ due to enum/integer mismatch; have ‘enum E(void)’
    3 | enum E foo(void) { return z; }
      |        ^~~
105131.c:2:5: note: previous declaration of ‘foo’ with type ‘int(void)’
    2 | int foo(void);
      |     ^~~
105131.c:6:6: warning: conflicting types for ‘bar’ due to enum/integer mismatch; have ‘void(enum E)’
    6 | void bar(enum E);
      |      ^~~
105131.c:5:6: note: previous declaration of ‘bar’ with type ‘void(int)’
    5 | void bar(int);
      |      ^~~
105131.c:9:12: warning: conflicting types for ‘arr’ due to enum/integer mismatch; have ‘int[10]’
    9 | extern int arr[10];
      |            ^~~
105131.c:8:15: note: previous declaration of ‘arr’ with type ‘enum E[10]’
    8 | extern enum E arr[10];
      |               ^~~



diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c701f07befe..60a0bb3ea36 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -1995,9 +1995,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)
@@ -2139,6 +2142,14 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
 	  return false;
 	}
     }
+  else if (enum_and_int_p && TREE_CODE (newdecl) != TYPE_DECL)
+    {
+      location_t newloc = DECL_SOURCE_LOCATION (newdecl);
+      auto_diagnostic_group d;
+      warned = warning_at (newloc, 0, "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
@@ -2148,7 +2159,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);
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index c70f0ba5ab6..2bcb9662620 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -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);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 6c4af5e4cde..b6a45fd9836 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -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;
Comment 3 GCC Commits 2022-05-18 23:13:17 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:7da9a089608b0ca09683332ce014fb6184842724

commit r13-627-g7da9a089608b0ca09683332ce014fb6184842724
Author: Marek Polacek <polacek@redhat.com>
Date:   Fri Apr 1 16:55:58 2022 -0400

    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.
Comment 4 Marek Polacek 2022-05-18 23:14:17 UTC
Implemented in GCC 13.