c++: Properly fold <COND_EXPR>.*<COMPONENT> [PR114525]

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.

	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/expr/cond18.C: New test.
This commit is contained in:
Simon Martin 2025-03-25 20:11:19 +01:00
parent 0fb10aca02
commit 35ce9afc84
2 changed files with 37 additions and 1 deletions

View file

@ -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)

View file

@ -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;
}