Hi! make_bit_field_ref and its callers are invoked not just during GIMPLE (where at least fold_truth_andor* isn't really effective; but from what I've tested it doesn't do a good job in many cases at GENERIC either, because we fold x.btfld == y.btfld too early into ((BIT_FIELD_REF ^ BIT_FIELD_REF) & cst) == 0 or similar and fold_truth_andor* can't handle that), but also during GENERIC and strip away the whole component access path if there are only constant offsets (various nested COMPONENT_REFs as well as ARRAY_REFs). That is not really good for constexpr evaluation (we should evaluate constexpr on saved fn trees before folding, but that is not GCC7 material), which then can't locate where to look for the value.
This patch just attempts to reuse as much as possible from orig_inner and only use adjusted BIT_FIELD_REF on that when in GENERIC (first I've tried to use instead COMPONENT_REF with DECL_BIT_FIELD_REPRESENTATIVE, but that isn't handled by constexpr.c either). Bootstrapped/regtested on x86_64/i686-linux, ok for trunk? 2017-02-28 Jakub Jelinek <ja...@redhat.com> PR c++/79681 * fold-const.c (make_bit_field_ref): During FE parsing if orig_inner is COMPONENT_REF, attempt to use its first operand as BIT_FIELD_REF base. * g++.dg/cpp1y/constexpr-79681-1.C: New test. * g++.dg/cpp1y/constexpr-79681-2.C: New test. --- gcc/fold-const.c.jj 2017-02-17 18:29:24.000000000 +0100 +++ gcc/fold-const.c 2017-02-27 15:05:43.743266260 +0100 @@ -3862,6 +3862,31 @@ make_bit_field_ref (location_t loc, tree { tree result, bftype; + /* Attempt not to lose access path if possible during FE folding. */ + if (!in_gimple_form && TREE_CODE (orig_inner) == COMPONENT_REF) + { + tree ninner = TREE_OPERAND (orig_inner, 0); + machine_mode nmode; + HOST_WIDE_INT nbitsize, nbitpos; + tree noffset; + int nunsignedp, nreversep, nvolatilep = 0; + tree base = get_inner_reference (ninner, &nbitsize, &nbitpos, + &noffset, &nmode, &nunsignedp, + &nreversep, &nvolatilep); + if (base == inner + && noffset == NULL_TREE + && nbitsize >= bitsize + && nbitpos <= bitpos + && bitpos + bitsize <= nbitpos + nbitsize + && !reversep + && !nreversep + && !nvolatilep) + { + inner = ninner; + bitpos -= nbitpos; + } + } + alias_set_type iset = get_alias_set (orig_inner); if (iset == 0 && get_alias_set (inner) != iset) inner = fold_build2 (MEM_REF, TREE_TYPE (inner), --- gcc/testsuite/g++.dg/cpp1y/constexpr-79681-1.C.jj 2017-02-27 15:11:39.177589060 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-79681-1.C 2017-02-27 15:11:28.000000000 +0100 @@ -0,0 +1,17 @@ +// PR c++/79681 +// { dg-do compile { target c++14 } } +// { dg-options "-O2" } + +struct A +{ + int i : 4; +}; + +constexpr bool +foo () +{ + A x[] = { 1 }; + return x[0].i; +} + +static_assert (foo(), ""); --- gcc/testsuite/g++.dg/cpp1y/constexpr-79681-2.C.jj 2017-02-27 15:11:42.252548562 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-79681-2.C 2017-02-27 15:10:13.000000000 +0100 @@ -0,0 +1,39 @@ +// PR c++/79681 +// { dg-do compile { target c++14 } } +// { dg-options "-O2" } + +struct A +{ + char i : 4; + char k : 1; + char l : 3; +}; +struct B +{ + char j : 4; +}; +struct C +{ + long long u; + A a[1]; + B b[1]; +}; + +constexpr bool +foo () +{ + C c = { 0, { { 5, 0, 2 } }, { { 6 } } }; + C d = { 0, { { 6, 0, 1 } }, { { 5 } } }; + return c.a[0].i == d.a[0].i && c.b[0].j == d.b[0].j; +} + +constexpr bool +bar () +{ + C c = { 0, { { 5, 0, 2 } }, { { 6 } } }; + C d = { 0, { { 6, 0, 1 } }, { { 5 } } }; + return c.a[0].i == d.a[0].i && c.a[0].l == d.a[0].l; +} + +static_assert (foo () == false, ""); +static_assert (bar () == false, ""); Jakub