On Tue, Mar 25, 2025 at 05:18:23PM +0000, Simon Martin wrote: > We've been miscompiling the following since r0-51314-gd6b4ea8592e338 (I > did not go compile something that old, and identified this change via > git blame, so might be wrong) > > === cut here === > struct Foo { int x; }; > Foo& get (Foo &v) { return v; } > void bar () { > Foo v; v.x = 1; > (true ? get (v) : get (v)).*(&Foo::x) = 2; > // v.x still equals 1 here... > } > === cut here === > > The problem lies in build_m_component_ref, that computes the address of > the COND_EXPR using build_address to build the representation of > (true ? get (v) : get (v)).*(&Foo::x); > and gets something like > &(true ? get (v) : get (v)) // #1 > instead of > (true ? &get (v) : &get (v)) // #2 > and the write does not go where want it to, hence the miscompile. > > This patch replaces the call to build_address by a call to > cp_build_addr_expr, which gives #2, that is properly handled. > > Successfully tested on x86_64-pc-linux-gnu. OK for trunk? And for active > branches after 2-3 weeks since it's a nasty one (albeit very old)? > > PR c++/114525 > > gcc/cp/ChangeLog: > > * typeck2.cc (build_m_component_ref): Call cp_build_addr_expr > instead of build_address. > > gcc/testsuite/ChangeLog: > > * g++.dg/parse/pr114525.C: New test.
g++.dg/expr/cond18.C seems like a more appropriate place, but the patch itself LGTM. > > --- > gcc/cp/typeck2.cc | 2 +- > gcc/testsuite/g++.dg/parse/pr114525.C | 36 +++++++++++++++++++++++++++ > 2 files changed, 37 insertions(+), 1 deletion(-) > create mode 100644 gcc/testsuite/g++.dg/parse/pr114525.C > > diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc > index 1adc05aa86d..45edd180173 100644 > --- a/gcc/cp/typeck2.cc > +++ b/gcc/cp/typeck2.cc > @@ -2387,7 +2387,7 @@ build_m_component_ref (tree datum, tree component, > tsubst_flags_t complain) > (cp_type_quals (type) > | cp_type_quals (TREE_TYPE (datum)))); > > - datum = build_address (datum); > + datum = cp_build_addr_expr (datum, complain); > > /* Convert object to the correct base. */ > if (binfo) > diff --git a/gcc/testsuite/g++.dg/parse/pr114525.C > b/gcc/testsuite/g++.dg/parse/pr114525.C > new file mode 100644 > index 00000000000..326985eed50 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/pr114525.C > @@ -0,0 +1,36 @@ > +/* PR c++/114525 */ > +/* { dg-do run } */ > + > +struct Foo { > + int x; > +}; > + > +Foo& get (Foo& v) { > + return v; > +} > + > +int main () { > + bool cond = true; > + > + /* Testcase from PR; v.x would wrongly remain equal to 1. */ > + Foo v_ko; > + v_ko.x = 1; > + (cond ? get (v_ko) : get (v_ko)).*(&Foo::x) = 2; > + if (v_ko.x != 2) > + __builtin_abort (); > + > + /* Those would already work, i.e. x be changed to 2. */ > + Foo v_ok_1; > + v_ok_1.x = 1; > + (cond ? get (v_ok_1) : get (v_ok_1)).x = 2; > + if (v_ok_1.x != 2) > + __builtin_abort (); > + > + Foo v_ok_2; > + v_ok_2.x = 1; > + get (v_ok_2).*(&Foo::x) = 2; > + if (v_ok_2.x != 2) > + __builtin_abort (); > + > + return 0; > +} > -- > 2.44.0 > Marek