[C++,3/4] PR c++/82165 - enum bitfields and operator overloading.
diff mbox series

Message ID 20190916043328.3739-3-jason@redhat.com
State New
Headers show
Series
  • [C++,1/4] Handle location wrappers better in warn_logical_operator.
Related show

Commit Message

Jason Merrill Sept. 16, 2019, 4:33 a.m. UTC
In this testcase, !f.b0 was failing to call the overloaded operator because
TREE_TYPE is the magic bitfield integer type, and we weren't using
unlowered_expr_type the way we do in other places.  It would be nice if we
could give bit-field COMPONENT_REFs their declared type until genericization
time...

Tested x86_64-pc-linux-gnu, applying to trunk.

	* call.c (build_new_op_1): Use unlowered_expr_type.
---
 gcc/cp/call.c                          | 31 ++++++++++++----------
 gcc/testsuite/g++.dg/expr/bitfield13.C | 36 ++++++++++++++++++++++++++
 gcc/cp/ChangeLog                       |  3 +++
 3 files changed, 56 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/expr/bitfield13.C

Patch
diff mbox series

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 457fa6605c2..b780b0af58e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5815,6 +5815,9 @@  build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
     }
   tree fnname = ovl_op_identifier (ismodop, ismodop ? code2 : code);
 
+  tree arg1_type = unlowered_expr_type (arg1);
+  tree arg2_type = arg2 ? unlowered_expr_type (arg2) : NULL_TREE;
+
   arg1 = prep_operand (arg1);
 
   bool memonly = false;
@@ -5846,8 +5849,8 @@  build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
     case EQ_EXPR:
     case NE_EXPR:
       /* These are saved for the sake of maybe_warn_bool_compare.  */
-      code_orig_arg1 = TREE_CODE (TREE_TYPE (arg1));
-      code_orig_arg2 = TREE_CODE (TREE_TYPE (arg2));
+      code_orig_arg1 = TREE_CODE (arg1_type);
+      code_orig_arg2 = TREE_CODE (arg2_type);
       break;
 
       /* =, ->, [], () must be non-static member functions.  */
@@ -5870,8 +5873,8 @@  build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
   if (code == COND_EXPR)
     /* Use build_conditional_expr instead.  */
     gcc_unreachable ();
-  else if (! OVERLOAD_TYPE_P (TREE_TYPE (arg1))
-	   && (! arg2 || ! OVERLOAD_TYPE_P (TREE_TYPE (arg2))))
+  else if (! OVERLOAD_TYPE_P (arg1_type)
+	   && (! arg2 || ! OVERLOAD_TYPE_P (arg2_type)))
     goto builtin;
 
   if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
@@ -5903,11 +5906,11 @@  build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
   args[2] = NULL_TREE;
 
   /* Add class-member operators to the candidate set.  */
-  if (CLASS_TYPE_P (TREE_TYPE (arg1)))
+  if (CLASS_TYPE_P (arg1_type))
     {
       tree fns;
 
-      fns = lookup_fnfields (TREE_TYPE (arg1), fnname, 1);
+      fns = lookup_fnfields (arg1_type, fnname, 1);
       if (fns == error_mark_node)
 	{
 	  result = error_mark_node;
@@ -5927,7 +5930,7 @@  build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
      has an enumeration type, or T2 or reference to cv-qualified-opt
      T2 for the second argument, if the second argument has an
      enumeration type.  Filter out those that don't match.  */
-  else if (! arg2 || ! CLASS_TYPE_P (TREE_TYPE (arg2)))
+  else if (! arg2 || ! CLASS_TYPE_P (arg2_type))
     {
       struct z_candidate **candp, **next;
 
@@ -5947,9 +5950,9 @@  build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
 
 	      if (TYPE_REF_P (parmtype))
 		parmtype = TREE_TYPE (parmtype);
-	      if (TREE_CODE (TREE_TYPE (args[i])) == ENUMERAL_TYPE
+	      if (TREE_CODE (unlowered_expr_type (args[i])) == ENUMERAL_TYPE
 		  && (same_type_ignoring_top_level_qualifiers_p
-		      (TREE_TYPE (args[i]), parmtype)))
+		      (unlowered_expr_type (args[i]), parmtype)))
 		break;
 
 	      parmlist = TREE_CHAIN (parmlist);
@@ -6124,15 +6127,15 @@  build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
 	    case LE_EXPR:
 	    case EQ_EXPR:
 	    case NE_EXPR:
-	      if (TREE_CODE (TREE_TYPE (arg1)) == ENUMERAL_TYPE
-		  && TREE_CODE (TREE_TYPE (arg2)) == ENUMERAL_TYPE
-		  && (TYPE_MAIN_VARIANT (TREE_TYPE (arg1))
-		      != TYPE_MAIN_VARIANT (TREE_TYPE (arg2)))
+	      if (TREE_CODE (arg1_type) == ENUMERAL_TYPE
+		  && TREE_CODE (arg2_type) == ENUMERAL_TYPE
+		  && (TYPE_MAIN_VARIANT (arg1_type)
+		      != TYPE_MAIN_VARIANT (arg2_type))
 		  && (complain & tf_warning))
 		{
 		  warning (OPT_Wenum_compare,
 			   "comparison between %q#T and %q#T",
-			   TREE_TYPE (arg1), TREE_TYPE (arg2));
+			   arg1_type, arg2_type);
 		}
 	      break;
 	    default:
diff --git a/gcc/testsuite/g++.dg/expr/bitfield13.C b/gcc/testsuite/g++.dg/expr/bitfield13.C
new file mode 100644
index 00000000000..3f57d5f0397
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/bitfield13.C
@@ -0,0 +1,36 @@ 
+// PR c++/82165
+// { dg-do compile { target c++11 } }
+
+struct flags {
+  enum field { f0, f1, no_field };
+  field b0 : 4;
+  field b1 : 4;
+  field a0, a1;
+};
+
+constexpr bool operator!(flags::field f) {
+  return f == flags::no_field;
+}
+
+#define SA(X) static_assert ((X), #X)
+
+int main() {
+  constexpr flags f { flags::f0, flags::f1, flags::f0, flags::f1 };
+
+  SA( flags::f0 == 0 ); // 0
+  SA( flags::f1 == 1 ); // 1
+  SA( flags::no_field == 2 ); // 2
+  SA( !flags::f0 == 0 ); // (!) 0
+  SA( !flags::f1 == 0 ); // (!) 0
+  SA( !flags::no_field == 1 ); // (!) 1
+
+  SA( f.a0 == 0 ); // 0
+  SA( f.a1 == 1 ); // 1
+  SA( !f.a0 == 0 ); // (!) 0
+  SA( !f.a1 == 0 ); // (!) 0
+
+  SA( f.b0 == 0 ); // 0
+  SA( f.b1 == 1 ); // 1
+  SA( !f.b0 == 0 ); // expected "(!) 0", but got "1"
+  SA( !f.b1 == 0 ); // expected "(!) 0", but got "0"
+}
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index a03a428109b..bed72c90a2e 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,8 @@ 
 2019-09-15  Jason Merrill  <jason@redhat.com>
 
+	PR c++/82165 - enum bitfields and operator overloading.
+	* call.c (build_new_op_1): Use unlowered_expr_type.
+
 	* call.c (build_new_op_1): Don't apply any standard conversions to
 	the operands of a built-in operator.  Don't suppress conversions in
 	cp_build_unary_op.