C++ PATCH for comparison of pointers to union members

Message ID CADzB+2nUnLcAa=AJ=dOgk9TRJE_iNFf6XEyHU+_dT40xSux6_A@mail.gmail.com
State New
Headers show
Series
  • C++ PATCH for comparison of pointers to union members
Related show

Commit Message

Jason Merrill June 11, 2018, 9 p.m.
At the C++ meeting last week it came up that pointers to different
members of the same union are specified to compare as equal, which we
were getting wrong.

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

Patch

commit bdaca661a7dd74d703f089af7b4484e098884510
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jun 5 15:57:06 2018 +0200

    Fix ptrmem comparison for unions.
    
            * constexpr.c (cxx_eval_binary_expression): Special case comparison
            of pointers to members of the same union.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 944c1cdf11e..97a338535db 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2051,8 +2051,22 @@  cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 
       if (TREE_CODE (lhs) == PTRMEM_CST
 	  && TREE_CODE (rhs) == PTRMEM_CST)
-	r = constant_boolean_node (cp_tree_equal (lhs, rhs) == is_code_eq,
-				   type);
+	{
+	  tree lmem = PTRMEM_CST_MEMBER (lhs);
+	  tree rmem = PTRMEM_CST_MEMBER (rhs);
+	  bool eq;
+	  if (TREE_CODE (lmem) == TREE_CODE (rmem)
+	      && TREE_CODE (lmem) == FIELD_DECL
+	      && TREE_CODE (DECL_CONTEXT (lmem)) == UNION_TYPE
+	      && same_type_p (DECL_CONTEXT (lmem),
+			      DECL_CONTEXT (rmem)))
+	    /* If both refer to (possibly different) members of the same union
+	       (12.3), they compare equal. */
+	    eq = true;
+	  else
+	    eq = cp_tree_equal (lhs, rhs);
+	  r = constant_boolean_node (eq == is_code_eq, type);
+	}
       else if ((TREE_CODE (lhs) == PTRMEM_CST
 		|| TREE_CODE (rhs) == PTRMEM_CST)
 	       && (null_member_pointer_value_p (lhs)
diff --git a/gcc/testsuite/g++.dg/expr/ptrmem10.C b/gcc/testsuite/g++.dg/expr/ptrmem10.C
new file mode 100644
index 00000000000..71d2df85860
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/ptrmem10.C
@@ -0,0 +1,28 @@ 
+/* [expr.eq] If both refer to (possibly different) members of the same union
+   (12.3), they compare equal. */
+// { dg-do run { target c++11 } }
+// { dg-additional-options -O }
+
+union U
+{
+  int i;
+  int j;
+};
+
+#define SA(X) static_assert ((X),#X)
+SA (&U::i == &U::j);
+SA (!(&U::i != &U::j));
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while(0)
+
+void f (int U::*p, int U::*q)
+{
+  assert (p==q);
+  assert (!(p!=q));
+}
+
+int main()
+{
+  assert (&U::i == &U::j);
+  assert (!(&U::i != &U::j));
+}