Patchwork C++/c-common PATCH for c++/53524 (bogus warning about enum mismatch in ?:)

login
register
mail settings
Submitter Jason Merrill
Date July 2, 2012, 7:04 p.m.
Message ID <4FF1F0AC.2040605@redhat.com>
Download mbox | patch
Permalink /patch/168631/
State New
Headers show

Comments

Jason Merrill - July 2, 2012, 7:04 p.m.
While a C++ enumeration is being defined, the enumerators have the types 
of their initializers, rather than the enumeration type itself.  As a 
result, if two enumerators are initialized from two other enumerations, 
and then the two are used in a ?: to initialize a third, we end up with 
mismatched enumeration types, leading to a warning.  Since the actual 
expression involves two elements of the same enumeration, we would like 
to avoid the warning.

Accomplishing this involves some significant changes to the C++ front 
end's handling of enumerators.  First, we set their DECL_CONTEXT to the 
enumeration type rather than the enclosing context, and then adjust 
other code accordingly.  Second, we keep the CONST_DECLs around until 
decay_conversion time, which also requires some adjustment.

The c-common change is to deal with this; it seems appropriate to call 
default_conversion on attribute arguments in/under the 
handle_*_attribute function since only that function knows what it wants 
to do with the arguments.

Tested x86_64-pc-linux-gnu, applying to trunk.  This is a 4.7 regression 
as well, but it's a minor issue and too large a change for a release branch.
H.J. Lu - July 4, 2012, 3:25 p.m.
On Mon, Jul 2, 2012 at 12:04 PM, Jason Merrill <jason@redhat.com> wrote:
> While a C++ enumeration is being defined, the enumerators have the types of
> their initializers, rather than the enumeration type itself.  As a result,
> if two enumerators are initialized from two other enumerations, and then the
> two are used in a ?: to initialize a third, we end up with mismatched
> enumeration types, leading to a warning.  Since the actual expression
> involves two elements of the same enumeration, we would like to avoid the
> warning.
>
> Accomplishing this involves some significant changes to the C++ front end's
> handling of enumerators.  First, we set their DECL_CONTEXT to the
> enumeration type rather than the enclosing context, and then adjust other
> code accordingly.  Second, we keep the CONST_DECLs around until
> decay_conversion time, which also requires some adjustment.
>
> The c-common change is to deal with this; it seems appropriate to call
> default_conversion on attribute arguments in/under the handle_*_attribute
> function since only that function knows what it wants to do with the
> arguments.
>
> Tested x86_64-pc-linux-gnu, applying to trunk.  This is a 4.7 regression as
> well, but it's a minor issue and too large a change for a release branch.

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53848

Patch

commit 03858f56c3456cddfbce1fbc9ce31a022d9bb376
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jun 29 12:17:41 2012 -0400

    	PR c++/53524
    gcc/cp/
    	* call.c (build_conditional_expr_1): Don't warn about comparison of
    	two enumerators before their enumeration is complete.
    	(build_new_op_1): Call decay_conversion before warn_logical_operator.
    	* decl.c (build_enumerator): Set DECL_CONTEXT of an enumerator to
    	its enumeration.
    	* decl2.c (mark_used): Call used_types_insert for enums.
    	* semantics.c (finish_id_expression): Don't decay CONST_DECL.
    	(finish_member_declaration): Don't change DECL_CONTEXT of enumerators.
    	* class.c (check_field_decls): Don't change DECL_CONTEXT of enums.
    	* typeck.c (convert_for_assignment): Don't decay CONST_DECL.
    	(build_class_member_access_expr): Look through unscoped enums.
    	* search.c (context_for_name_lookup): Look through unscoped enums.
    	* pt.c (tsubst_copy_and_build): Don't decay CONST_DECL.
    	(tsubst_copy): Use DECL_CONTEXT to find the enumeration.
    	* tree.c (decl_linkage): Likewise.
    	* cvt.c (ocp_convert): Check decayed expr for enum range warning.
    gcc/c-family/
    	* c-common.c (get_priority): Call default_conversion.

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 61d7567..2a528e2 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -6587,11 +6587,12 @@  get_priority (tree args, bool is_destructor)
     }
 
   arg = TREE_VALUE (args);
+  arg = default_conversion (arg);
   if (!host_integerp (arg, /*pos=*/0)
       || !INTEGRAL_TYPE_P (TREE_TYPE (arg)))
     goto invalid;
 
-  pri = tree_low_cst (TREE_VALUE (args), /*pos=*/0);
+  pri = tree_low_cst (arg, /*pos=*/0);
   if (pri < 0 || pri > MAX_INIT_PRIORITY)
     goto invalid;
 
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 09965b3..72394f4 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4365,6 +4365,7 @@  build_conditional_expr_1 (tree arg1, tree arg2, tree arg3,
   struct z_candidate *candidates = 0;
   struct z_candidate *cand;
   void *p;
+  tree orig_arg2, orig_arg3;
 
   /* As a G++ extension, the second argument to the conditional can be
      omitted.  (So that `a ? : c' is roughly equivalent to `a ? a :
@@ -4404,6 +4405,8 @@  build_conditional_expr_1 (tree arg1, tree arg2, tree arg3,
      array-to-pointer (_conv.array_), and function-to-pointer
      (_conv.func_) standard conversions are performed on the second
      and third operands.  */
+  orig_arg2 = arg2;
+  orig_arg3 = arg3;
   arg2_type = unlowered_expr_type (arg2);
   arg3_type = unlowered_expr_type (arg3);
   if (VOID_TYPE_P (arg2_type) || VOID_TYPE_P (arg3_type))
@@ -4701,7 +4704,12 @@  build_conditional_expr_1 (tree arg1, tree arg2, tree arg3,
       if (TREE_CODE (arg2_type) == ENUMERAL_TYPE
 	  && TREE_CODE (arg3_type) == ENUMERAL_TYPE)
         {
-          if (complain & tf_warning)
+	  if (TREE_CODE (orig_arg2) == CONST_DECL
+	      && TREE_CODE (orig_arg3) == CONST_DECL
+	      && DECL_CONTEXT (orig_arg2) == DECL_CONTEXT (orig_arg3))
+	    /* Two enumerators from the same enumeration can have different
+	       types when the enumeration is still being defined.  */;
+          else if (complain & tf_warning)
             warning (OPT_Wenum_compare, 
                      "enumeral mismatch in conditional expression: %qT vs %qT",
                      arg2_type, arg3_type);
@@ -5221,16 +5229,20 @@  build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
 
 	  if (arg2)
 	    {
+	      conv = cand->convs[1];
+	      if (conv->kind == ck_ref_bind)
+		conv = next_conversion (conv);
+	      else
+		arg2 = decay_conversion (arg2, complain);
+
 	      /* We need to call warn_logical_operator before
-		 converting arg2 to a boolean_type.  */
+		 converting arg2 to a boolean_type, but after
+		 decaying an enumerator to its value.  */
 	      if (complain & tf_warning)
 		warn_logical_operator (loc, code, boolean_type_node,
 				       code_orig_arg1, arg1,
 				       code_orig_arg2, arg2);
 
-	      conv = cand->convs[1];
-	      if (conv->kind == ck_ref_bind)
-		conv = next_conversion (conv);
 	      arg2 = convert_like (conv, arg2, complain);
 	    }
 	  if (arg3)
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 6307e84..264258c 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -3114,7 +3114,8 @@  check_field_decls (tree t, tree *access_decls,
 
       /* If we've gotten this far, it's a data member, possibly static,
 	 or an enumerator.  */
-      DECL_CONTEXT (x) = t;
+      if (TREE_CODE (x) != CONST_DECL)
+	DECL_CONTEXT (x) = t;
 
       /* When this goes into scope, it will be a non-local reference.  */
       DECL_NONLOCAL (x) = 1;
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 998d4eb..9550f15 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -741,8 +741,8 @@  ocp_convert (tree type, tree expr, int convtype, int flags,
 	     values. Otherwise, the resulting enumeration value is
 	     unspecified.  */
 	  if ((complain & tf_warning)
-	      && TREE_CODE (expr) == INTEGER_CST
-	      && !int_fits_type_p (expr, ENUM_UNDERLYING_TYPE (type)))
+	      && TREE_CODE (e) == INTEGER_CST
+	      && !int_fits_type_p (e, ENUM_UNDERLYING_TYPE (type)))
 	    warning_at (loc, OPT_Wconversion, 
 			"the result of the conversion is unspecified because "
 			"%qE is outside the range of type %qT",
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index ab56019..18beaa9 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -12570,7 +12570,7 @@  incremented enumerator value is too large for %<long%>");
        a function could mean local to a class method.  */
     decl = build_decl (loc, CONST_DECL, name, type);
   
-  DECL_CONTEXT (decl) = FROB_CONTEXT (context);
+  DECL_CONTEXT (decl) = enumtype;
   TREE_CONSTANT (decl) = 1;
   TREE_READONLY (decl) = 1;
   DECL_INITIAL (decl) = value;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index d8c7260..cbb1053 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4205,6 +4205,10 @@  mark_used (tree decl)
   if (DECL_CLONED_FUNCTION_P (decl))
     TREE_USED (DECL_CLONED_FUNCTION (decl)) = 1;
 
+  /* Mark enumeration types as used.  */
+  if (TREE_CODE (decl) == CONST_DECL)
+    used_types_insert (DECL_CONTEXT (decl));
+
   if (TREE_CODE (decl) == FUNCTION_DECL
       && DECL_DELETED_FN (decl))
     {
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 7e1c46f..f8f416a 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12128,7 +12128,7 @@  tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	   When we instantiate f<7>::S::g(), say, lookup_name is not
 	   clever enough to find f<7>::a.  */
 	enum_type
-	  = tsubst_aggr_type (TREE_TYPE (t), args, complain, in_decl,
+	  = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
 			      /*entering_scope=*/0);
 
 	for (v = TYPE_VALUES (enum_type);
@@ -14385,17 +14385,6 @@  tsubst_copy_and_build (tree t,
 	return stmt_expr;
       }
 
-    case CONST_DECL:
-      t = tsubst_copy (t, args, complain, in_decl);
-      /* As in finish_id_expression, we resolve enumeration constants
-	 to their underlying values.  */
-      if (TREE_CODE (t) == CONST_DECL && !processing_template_decl)
-	{
-	  used_types_insert (TREE_TYPE (t));
-	  return DECL_INITIAL (t);
-	}
-      return t;
-
     case LAMBDA_EXPR:
       {
 	tree r = build_lambda_expr ();
diff --git a/gcc/cp/search.c b/gcc/cp/search.c
index e6d6be8..d112c05 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -579,7 +579,8 @@  context_for_name_lookup (tree decl)
      declared.  */
   tree context = DECL_CONTEXT (decl);
 
-  while (context && TYPE_P (context) && ANON_AGGR_TYPE_P (context))
+  while (context && TYPE_P (context)
+	 && (ANON_AGGR_TYPE_P (context) || UNSCOPED_ENUM_P (context)))
     context = TYPE_CONTEXT (context);
   if (!context)
     context = global_namespace;
@@ -623,9 +624,7 @@  dfs_access_in_type (tree binfo, void *data)
   else
     {
       /* First, check for an access-declaration that gives us more
-	 access to the DECL.  The CONST_DECL for an enumeration
-	 constant will not have DECL_LANG_SPECIFIC, and thus no
-	 DECL_ACCESS.  */
+	 access to the DECL.  */
       if (DECL_LANG_SPECIFIC (decl) && !DECL_DISCRIMINATOR_P (decl))
 	{
 	  tree decl_access = purpose_member (type, DECL_ACCESS (decl));
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index d45a6e2..f1a94c1 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2631,8 +2631,10 @@  finish_member_declaration (tree decl)
       TREE_PROTECTED (DECL_TEMPLATE_RESULT (decl)) = TREE_PROTECTED (decl);
     }
 
-  /* Mark the DECL as a member of the current class.  */
-  DECL_CONTEXT (decl) = current_class_type;
+  /* Mark the DECL as a member of the current class, unless it's
+     a member of an enumeration.  */
+  if (TREE_CODE (decl) != CONST_DECL)
+    DECL_CONTEXT (decl) = current_class_type;
 
   /* Check for bare parameter packs in the member variable declaration.  */
   if (TREE_CODE (decl) == FIELD_DECL)
@@ -3060,18 +3062,6 @@  finish_id_expression (tree id_expression,
 	}
       return r;
     }
-  /* Similarly, we resolve enumeration constants to their
-     underlying values.  */
-  else if (TREE_CODE (decl) == CONST_DECL)
-    {
-      *idk = CP_ID_KIND_NONE;
-      if (!processing_template_decl)
-	{
-	  used_types_insert (TREE_TYPE (decl));
-	  return DECL_INITIAL (decl);
-	}
-      return decl;
-    }
   else
     {
       bool dependent_p;
@@ -3100,6 +3090,9 @@  finish_id_expression (tree id_expression,
       if (!processing_template_decl)
 	/* No names are dependent outside a template.  */
 	;
+      else if (TREE_CODE (decl) == CONST_DECL)
+	/* We don't want to treat enumerators as dependent.  */
+	;
       /* A template-id where the name of the template was not resolved
 	 is definitely dependent.  */
       else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR
@@ -3234,15 +3227,17 @@  finish_id_expression (tree id_expression,
 	 marked either below or after overload resolution.  */
       if (TREE_CODE (decl) == VAR_DECL
 	  || TREE_CODE (decl) == PARM_DECL
+	  || TREE_CODE (decl) == CONST_DECL
 	  || TREE_CODE (decl) == RESULT_DECL)
 	mark_used (decl);
 
       /* Only certain kinds of names are allowed in constant
-	 expression.  Enumerators and template parameters have already
+	 expression.  Template parameters have already
 	 been handled above.  */
       if (! error_operand_p (decl)
 	  && integral_constant_expression_p
 	  && ! decl_constant_var_p (decl)
+	  && TREE_CODE (decl) != CONST_DECL
 	  && ! builtin_valid_in_constant_expr_p (decl))
 	{
 	  if (!allow_non_integral_constant_expression_p)
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2b541cd..a03f845 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -3212,7 +3212,7 @@  decl_linkage (tree decl)
   /* Linkage of a CONST_DECL depends on the linkage of the enumeration
      type.  */
   if (TREE_CODE (decl) == CONST_DECL)
-    return decl_linkage (TYPE_NAME (TREE_TYPE (decl)));
+    return decl_linkage (TYPE_NAME (DECL_CONTEXT (decl)));
 
   /* Some things that are not TREE_PUBLIC have external linkage, too.
      For example, on targets that don't have weak symbols, we make all
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 971f386..3bc3ead 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2199,7 +2199,7 @@  build_class_member_access_expr (tree object, tree member,
   /* If MEMBER is from an anonymous aggregate, MEMBER_SCOPE will
      presently be the anonymous union.  Go outwards until we find a
      type related to OBJECT_TYPE.  */
-  while (ANON_AGGR_TYPE_P (member_scope)
+  while ((ANON_AGGR_TYPE_P (member_scope) || UNSCOPED_ENUM_P (member_scope))
 	 && !same_type_ignoring_top_level_qualifiers_p (member_scope,
 							object_type))
     member_scope = TYPE_CONTEXT (member_scope);
@@ -7582,10 +7582,6 @@  convert_for_assignment (tree type, tree rhs,
       return error_mark_node;
     }
 
-  /* Simplify the RHS if possible.  */
-  if (TREE_CODE (rhs) == CONST_DECL)
-    rhs = DECL_INITIAL (rhs);
-
   if (c_dialect_objc ())
     {
       int parmno;
diff --git a/gcc/testsuite/g++.dg/cpp0x/scoped_enum.C b/gcc/testsuite/g++.dg/cpp0x/scoped_enum.C
index c52a3fe..4b0317c 100644
--- a/gcc/testsuite/g++.dg/cpp0x/scoped_enum.C
+++ b/gcc/testsuite/g++.dg/cpp0x/scoped_enum.C
@@ -13,7 +13,7 @@  enum struct Color2 {
   Blue,
   Indigo = Green + 2,
   Violet,
-  Red // { dg-error "redefinition" }
+  Red // { dg-error "redeclaration" }
 };
 
 enum Color {
diff --git a/gcc/testsuite/g++.dg/other/ptrmem10.C b/gcc/testsuite/g++.dg/other/ptrmem10.C
index a17df7f..ff8c843 100644
--- a/gcc/testsuite/g++.dg/other/ptrmem10.C
+++ b/gcc/testsuite/g++.dg/other/ptrmem10.C
@@ -3,7 +3,7 @@ 
 
 template <class C, void (C::*M) ()>
 static
-void foo(void *obj)		// { dg-message "note" }
+void foo(void *obj)
 {
   C *p = static_cast<C*>(obj);
   (p->*M)();
@@ -13,8 +13,7 @@  template <class C>
 static void
 bar(C *c, void (C::*m) ())
 {
-  foo<C,m>((void *)c);// { dg-error "(not a valid template arg|pointer-to-member|no matching fun|could not convert)" }
-  // { dg-message "candidate" "candidate note" { target *-*-* } 16 }
+  foo<C,m>((void *)c);// { dg-error "(not a valid template arg|pointer-to-member|no matching fun|could not convert|constant)" }
 }
 
 struct S
diff --git a/gcc/testsuite/g++.dg/other/ptrmem11.C b/gcc/testsuite/g++.dg/other/ptrmem11.C
index e73164e..e18abdd 100644
--- a/gcc/testsuite/g++.dg/other/ptrmem11.C
+++ b/gcc/testsuite/g++.dg/other/ptrmem11.C
@@ -5,7 +5,7 @@  struct A {};
 
 template <int A::* p>
 int
-foo(A* q)			// { dg-message "note" }
+foo(A* q)
 {
   return q->*p;
 }
@@ -14,8 +14,7 @@  template <typename T>
 int
 bar(int T::* p)
 {
-  return foo<p>(0);// { dg-error "(not a valid template arg|no matching func|pointer-to-member|could not convert)" }
-  // { dg-message "candidate" "candidate note" { target *-*-* } 17 }
+  return foo<p>(0);// { dg-error "(not a valid template arg|no matching func|pointer-to-member|could not convert|constant)" }
 }
 
 int i = bar<A>(0);
diff --git a/gcc/testsuite/g++.dg/template/enum7.C b/gcc/testsuite/g++.dg/template/enum7.C
new file mode 100644
index 0000000..8c464a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/enum7.C
@@ -0,0 +1,8 @@ 
+// PR c++/53524
+
+template <class T> struct A { enum EA { ea }; };
+template <class T, class U> struct B {
+  enum EB { eb1 = A<T>::ea, eb2 = A<U>::ea, eb3 = 0 ? eb1 : eb2  };
+};
+
+B<int,char> b;