https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89074
--- Comment #10 from Jakub Jelinek <jakub at gcc dot gnu.org> --- So, for #c5, if (&a[1] == &b[0]) instead of if (a+1 == b+0) works right, that is handled by the match.pd (cmp (convert1?@2 addr@0) (convert2? addr@1)) address_compare simplification. And it also works for GIMPLE, where we have in gimple-fold.c the: /* Translate &x + CST into an invariant form suitable for further propagation. */ if (subcode == POINTER_PLUS_EXPR) { tree op0 = (*valueize) (gimple_assign_rhs1 (stmt)); tree op1 = (*valueize) (gimple_assign_rhs2 (stmt)); if (TREE_CODE (op0) == ADDR_EXPR && TREE_CODE (op1) == INTEGER_CST) { tree off = fold_convert (ptr_type_node, op1); return build1_loc (loc, ADDR_EXPR, TREE_TYPE (op0), fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (op0)), unshare_expr (op0), off)); } } I think we don't do this for GENERIC because that can seriously break the constant evaluation, the details on which exact field is accessed can be lost in there. But, if we take e.g. struct S { int s; }; struct T : public S {}; struct U : public T {}; constexpr bool test() { U a[] = { 1, 2, 3, 4 }; U b[] = { 5, 6, 7, 8 }; T *c = (T *) a + 1; S *d = (S *) c + 2; S *e = (S *) b + 1; if (a+0 == b+0) // OK return false; if (d == e) // ERROR return false; return true; } static_assert( test() ); then we can see the comparison is tried on ‘((((S*)((& a[0].U::<anonymous>) + 4)) + 8) == ((& b[0].U::<anonymous>.T::<anonymous>) + 4))’ is not a constant expression so the multiple pointer_plus aren't merged in there either. Hasving a GENERIC guarded variant of the address_compare with one pointer_plus in one or the other or both operands might be still doable, but the above shows that it wouldn't be sufficient. So, perhaps instead if simple comparison fails during constexpr evaluation, we could call some helper function that looks through cast and optimizes the POINTER_PLUS into MEM_REF that it folds and then we'd try to compare it again (or call that helper function regardless on both comparison operands)? Another thing is that the following testcase isn't rejected: 2022-01-05 Jakub Jelinek <ja...@redhat.com> PR c++/89074 * fold-const.c (address_compare): Punt on comparison of address of one object with address of end of another object if folding_initializer. * g++.dg/cpp1y/constexpr-89074-1.C: New test. --- gcc/fold-const.c.jj 2022-01-05 20:30:08.731806756 +0100 +++ gcc/fold-const.c 2022-01-05 20:34:52.277822349 +0100 @@ -16627,7 +16627,7 @@ address_compare (tree_code code, tree ty /* If this is a pointer comparison, ignore for now even valid equalities where one pointer is the offset zero of one object and the other to one past end of another one. */ - else if (!INTEGRAL_TYPE_P (type)) + else if (!folding_initializer && !INTEGRAL_TYPE_P (type)) ; /* Assume that automatic variables can't be adjacent to global variables. */ --- gcc/testsuite/g++.dg/cpp1y/constexpr-89074-1.C.jj 2022-01-05 20:43:03.696917484 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-89074-1.C 2022-01-05 20:42:12.676634044 +0100 @@ -0,0 +1,28 @@ +// PR c++/89074 +// { dg-do compile { target c++14 } } + +constexpr bool +foo () +{ + int a[] = { 1, 2 }; + int b[] = { 3, 4 }; + + if (&a[0] == &b[0]) + return false; + + if (&a[1] == &b[0]) + return false; + + if (&a[1] == &b[1]) + return false; + + if (&a[2] == &b[1]) + return false; + + if (&a[2] == &b[0]) // { dg-error "is not a constant expression" } + return false; + + return true; +} + +constexpr bool a = foo ();