diff options
author | Simon Martin <simon@nasilyan.com> | 2025-03-25 20:11:19 +0100 |
---|---|---|
committer | Simon Martin <simon@nasilyan.com> | 2025-03-25 20:31:42 +0100 |
commit | 35ce9afc84a63fb647a90cbecb2adf3e748178be (patch) | |
tree | 76870267526a8af19f76a9d9c21e1e74ab276cc6 /gcc | |
parent | 0fb10aca02852b2e8d78a78c07aa2f62aec6a07e (diff) | |
download | gcc-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.cc | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/expr/cond18.C | 36 |
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; +} |