diff mbox series

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

Message ID 01020195ce4ffc07-913ce014-8f74-47e8-ab3f-864232e6cb03-000000@eu-west-1.amazonses.com
State New
Headers show
Series c++: Properly fold <COND_EXPR>.*<COMPONENT> [PR114525] | expand

Commit Message

Simon Martin March 25, 2025, 5:18 p.m. UTC
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.

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

Comments

Jason Merrill March 25, 2025, 5:45 p.m. UTC | #1
On 3/25/25 1:18 PM, 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)?

OK, and yes.

> 	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.
> 
> ---
>   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;
> +}
Marek Polacek March 25, 2025, 5:50 p.m. UTC | #2
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
Jason Merrill March 25, 2025, 5:52 p.m. UTC | #3
On 3/25/25 1:50 PM, Marek Polacek wrote:
> 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.

Sure, that does sound better.

Jason
Simon Martin March 25, 2025, 7:33 p.m. UTC | #4
Hi,

On Tue Mar 25, 2025 at 6:52 PM CET, Jason Merrill wrote:
> On 3/25/25 1:50 PM, Marek Polacek wrote:
>> 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.
Good call out, thanks Marek.

I've merged the patch with the suggested test rename as
r15-8911-g35ce9afc84a63f.

I'll reply to this thread in 2-3 weeks when I've backported to 13 and
14.

Simon
Simon Martin April 14, 2025, 1:43 p.m. UTC | #5
Hi,

On Tue Mar 25, 2025 at 8:33 PM CET, Simon Martin wrote:
> Hi,
>
> On Tue Mar 25, 2025 at 6:52 PM CET, Jason Merrill wrote:
>> On 3/25/25 1:50 PM, Marek Polacek wrote:
>>> 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.
> Good call out, thanks Marek.
>
> I've merged the patch with the suggested test rename as
> r15-8911-g35ce9afc84a63f.
>
> I'll reply to this thread in 2-3 weeks when I've backported to 13 and
> 14.
For information I have just backported this to 14 (r14-11602) and 13
(r13-9523).

Simon
diff mbox series

Patch

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