aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorSimon Martin <simon@nasilyan.com>2025-03-25 20:11:19 +0100
committerSimon Martin <simon@nasilyan.com>2025-03-25 20:31:42 +0100
commit35ce9afc84a63fb647a90cbecb2adf3e748178be (patch)
tree76870267526a8af19f76a9d9c21e1e74ab276cc6 /gcc
parent0fb10aca02852b2e8d78a78c07aa2f62aec6a07e (diff)
downloadgcc-35ce9afc84a63fb647a90cbecb2adf3e748178be.zip
gcc-35ce9afc84a63fb647a90cbecb2adf3e748178be.tar.gz
gcc-35ce9afc84a63fb647a90cbecb2adf3e748178be.tar.bz2
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/typeck2.cc2
-rw-r--r--gcc/testsuite/g++.dg/expr/cond18.C36
2 files changed, 37 insertions, 1 deletions
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 1adc05a..45edd18 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/expr/cond18.C b/gcc/testsuite/g++.dg/expr/cond18.C
new file mode 100644
index 0000000..326985e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond18.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;
+}