I've previously sent https://gcc.gnu.org/pipermail/gcc-patches/2022-December/608077.html adding ADDR_EXPR_NONZERO and there were comments from Jason where I just realized I ignored ARRAY_REF for the following. Anyway, here's a more aggressive variant not going for an extra flag set by the frontend but instead have the middle-end treat all &*.component as non-NULL (all handled_component_p).
This passes bootstrap for all languages, testing there isn't complete but it already shows for example gcc.c-torture/execute/pr44555.c explicitely testing that we keep &p->z NULL when p is NULL and z is at offset zero. There's also execute FAILs for gfortran.dg/class_optional_2.f90 and some optimization dump scan fails I did not yet investigate. Nevertheless I'd like to hear opinions on whether a middle-end implementation without frontend help is the way to go and what the reasonable restrictions should be there? Is gcc.c-torture/execute/pr44555.c sanctioned by the C standard? If so I think we have a lost cause without some help from the frontend? Thanks, Richard. -- The following avoids a bogus -Wstringop-overflow diagnostic by properly recognizing that &d->m_mutex cannot be nullptr even if m_mutex is at offset zero. The C++ frontend already diagnoses a &d->m_mutex != nullptr comparison and the following transfers this knowledge to the middle-end in the most general way. To avoid the bogus diagnostic this avoids separating the nullptr path via jump-threading by eliminating the nullptr check. PR tree-optimization/104475 * fold-const.cc (tree_single_nonzero_warnv_p): An ADDR_EXPR of a component reference can never be null. * g++.dg/opt/pr104475.C: New testcase. --- gcc/fold-const.cc | 11 ++++++++++- gcc/testsuite/g++.dg/opt/pr104475.C | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/opt/pr104475.C diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc index db54bfc5662..c5c923e059d 100644 --- a/gcc/fold-const.cc +++ b/gcc/fold-const.cc @@ -15368,7 +15368,16 @@ tree_single_nonzero_warnv_p (tree t, bool *strict_overflow_p) tree base = TREE_OPERAND (t, 0); if (!DECL_P (base)) - base = get_base_address (base); + { + gcc_checking_assert (TREE_CODE (base) != WITH_SIZE_EXPR); + /* Any component reference, even if at offset zero, requires + a non-null base. */ + if (handled_component_p (base) + && !targetm.addr_space.zero_address_valid + (TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (t))))) + return true; + base = get_base_address (base); + } if (base && TREE_CODE (base) == TARGET_EXPR) base = TARGET_EXPR_SLOT (base); diff --git a/gcc/testsuite/g++.dg/opt/pr104475.C b/gcc/testsuite/g++.dg/opt/pr104475.C new file mode 100644 index 00000000000..013c70302c6 --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/pr104475.C @@ -0,0 +1,12 @@ +// { dg-do compile } +// { dg-require-effective-target c++11 } +// { dg-options "-O -Waddress -fdump-tree-original" } + +struct X { int i; }; + +bool foo (struct X *p) +{ + return &p->i != nullptr; /* { dg-warning "never be NULL" } */ +} + +/* { dg-final { scan-tree-dump "return <retval> = 1;" "original" } } */ -- 2.35.3