Hi! The recent gimple-range-fold.cc changes broke various C++ projects. The problem is that the C++ FE tends to create different RECORD_TYPEs for pointer-to-member functions types, each time it is a type with __pfn and __delta members, the latter always ptrdiff_t type, the former some pointer to METHOD_TYPE. Now, accumulate_range_from_component_ref searches for exact FIELD_DECL pointer equality, if it doesn't find it, it punts. With the C++ FE possibly creating more types like that, we can run into mismatches, where the CONSTRUCTOR uses one of the types and the fld FIELD_DECL is from a different RECORD_TYPE. Most of the GIMPLE opts can deal with it just fine, e.g. gimple-fold.cc fold_ctor* etc. just rely on offsets and sizes. Now, in the C++ case, the different types even have the same TYPE_CANONICAL, so they are considered equivalent by the middle-end. Thus, the following patch keeps using FIELD_DECL pointer equality for the common case where the CONSTRUCTOR and fld agree on the same RECORD_TYPE/UNION_TYPE/QUAL_UNION_TYPE, if they are different but have the same TYPE_CANONICAL, it falls back to using bit offset/bit size comparison (and punts on variable length structures, too complicated to verify that), and if even TYPE_CANONICAL is different, punts as well.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2026-10-13 Jakub Jelinek <[email protected]> PR c++/127685 * gimple-range-fold.cc (ctor_ref_range::accumulate_range_from_component_ref): Workaround for the case where the fld FIELD_DECL isn't from the same aggregate type as the CONSTRUCTOR, but a canonically compatible one - fallback to bit offset/size comparisons. * g++.dg/torture/pr127685.C: New test. --- a/gcc/gimple-range-fold.cc 2026-10-02 22:27:52.632018759 +0200 +++ b/gcc/gimple-range-fold.cc 2026-10-12 18:02:58.424640471 +0200 @@ -1112,12 +1112,42 @@ ctor_ref_range::accumulate_range_from_co { unsigned ix; tree index, val; - FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix, index, val) + if (TYPE_MAIN_VARIANT (TREE_TYPE (init)) == DECL_CONTEXT (fld)) + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix, index, val) + { + if (index != fld) + continue; + return accumulate_range (val, i); + } + else if (TYPE_CANONICAL (TYPE_MAIN_VARIANT (TREE_TYPE (init))) + == TYPE_CANONICAL (DECL_CONTEXT (fld))) { - if (index != fld) - continue; - return accumulate_range (val, i); + // If fld is from a different record/union than the CONSTRUCTOR, + // fallback to bit offset/size comparison. This happens e.g. + // with C++ pointer-to-members, where we can get different + // __ptrmemfunc_type RECORD_TYPEs with __pfn and __delta + // FIELD_DECLs at the same offsets. + tree off = bit_position (fld); + tree sz = DECL_SIZE (fld); + if (TREE_CODE (off) != INTEGER_CST + || TREE_CODE (sz) != INTEGER_CST + || int_size_in_bytes (TREE_TYPE (init)) == -1) + return false; + offset_int woff = wi::to_offset (off); + offset_int wsz = wi::to_offset (sz); + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix, index, val) + { + off = bit_position (index); + sz = DECL_SIZE (index); + if (TREE_CODE (off) != INTEGER_CST || TREE_CODE (sz) != INTEGER_CST) + return false; + if (wi::to_offset (off) != woff || wi::to_offset (sz) != wsz) + continue; + return accumulate_range (val, i); + } } + else + return false; if (TREE_CODE (TREE_TYPE (init)) == RECORD_TYPE) { accumulate_range_from_missing_constructor_elt (); --- a/gcc/testsuite/g++.dg/torture/pr127685.C 2026-10-12 17:56:01.133227185 +0200 +++ b/gcc/testsuite/g++.dg/torture/pr127685.C 2026-10-12 17:55:37.290544517 +0200 @@ -0,0 +1,23 @@ +// PR c++/127685 +// { dg-do run } + +struct C { + bool foo (bool) { return false; } + bool bar (bool) { return false; } + bool baz (bool); +}; + +__attribute__ ((noipa)) +bool +C::baz (bool b) +{ + static bool (C::*A[]) (bool) = { &C::foo, &C::bar, }; + return (this->*A[(int) b]) (b); +} + +int +main () +{ + C o; + o.baz (false); +} Jakub
