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.
commit bdaca661a7dd74d703f089af7b4484e098884510 Author: Jason Merrill <[email protected]> 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)); +}
