Hi Jason,
On Tue Mar 4, 2025 at 11:47 PM CET, Jason Merrill wrote:
> On 2/14/25 12:08 PM, Simon Martin wrote:
>> We have been miscompiling the following valid code since GCC8, and
>> r8-3497-g281e6c1d8f1b4c
>>
>> === cut here ===
>> struct span {
>> span (const int (&__first)[1]) : _M_ptr (__first) {}
>> int operator[] (long __i) { return _M_ptr[__i]; }
>> const int *_M_ptr;
>> };
>> void foo () {
>> constexpr int a_vec[]{1};
>> auto vec{[&a_vec]() -> span { return a_vec; }()};
>> }
>> === cut here ===
>>
>> The problem is that perform_implicit_conversion_flags (via
>> mark_rvalue_use) replaces "a_vec" in the return statement by a
>> CONSTRUCTOR representing a_vec's constant value, and then takes its
>> address when invoking span's constructor. So we end up with an instance
>> that points to garbage instead of a_vec's storage.
>>
>> I've tried many things to somehow recover from this replacement, but I
>> actually think we should not do it when converting to a class type: we
>> have no idea whether the conversion will involve a constructor taking an
>> address or reference. So we should assume it's the case, and call
>> mark_lvalue_use, not mark_rvalue_use (I might very weel be overseeing
>> things, and feedback is more than welcome).
>
> Yeah, those mark_*_use calls seem misplaced, they should be called
> instead by the code that actually does the conversion.
>
> What if we replace all of them here with just mark_exp_read? Or nothing?
Thanks for the suggestions; simply removing those calls actually works. This
is what the attached updated patch does.
Successfully tested on x86_64-pc-linux-gnu. OK for trunk? And if so, OK for
branches after 2-3 weeks since it's a wrong code bug?
Simon
From cbb1fae22ff259b75f4304b1b0e6d6f84216b3d9 Mon Sep 17 00:00:00 2001
From: Simon Martin <[email protected]>
Date: Wed, 5 Mar 2025 12:42:37 +0100
Subject: [PATCH] c++: Don't replace INDIRECT_REFs by a const capture proxy too
eagerly [PR117504]
We have been miscompiling the following valid code since GCC8, and
r8-3497-g281e6c1d8f1b4c
=== cut here ===
struct span {
span (const int (&__first)[1]) : _M_ptr (__first) {}
int operator[] (long __i) { return _M_ptr[__i]; }
const int *_M_ptr;
};
void foo () {
constexpr int a_vec[]{1};
auto vec{[&a_vec]() -> span { return a_vec; }()};
}
=== cut here ===
The problem is that perform_implicit_conversion_flags (via
mark_rvalue_use) replaces "a_vec" in the return statement by a
CONSTRUCTOR representing a_vec's constant value, and then takes its
address when invoking span's constructor. So we end up with an instance
that points to garbage instead of a_vec's storage.
As per Jason's suggestion, this patch simply removes the calls to
mark_*_use from perform_implicit_conversion_flags, which fixes the PR.
Successfully tested on x86_64-pc-linux-gnu.
PR c++/117504
gcc/cp/ChangeLog:
* call.cc (perform_implicit_conversion_flags): Don't call
mark_{l,r}value_use.
gcc/testsuite/ChangeLog:
* g++.dg/cpp2a/constexpr-117504.C: New test.
* g++.dg/cpp2a/constexpr-117504a.C: New test.
---
gcc/cp/call.cc | 5 --
gcc/testsuite/g++.dg/cpp2a/constexpr-117504.C | 60 +++++++++++++++++++
.../g++.dg/cpp2a/constexpr-117504a.C | 12 ++++
3 files changed, 72 insertions(+), 5 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-117504.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-117504a.C
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index f7b4cccb1c7..c1c8987ec8b 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -13971,11 +13971,6 @@ perform_implicit_conversion_flags (tree type, tree
expr,
conversion *conv;
location_t loc = cp_expr_loc_or_input_loc (expr);
- if (TYPE_REF_P (type))
- expr = mark_lvalue_use (expr);
- else
- expr = mark_rvalue_use (expr);
-
if (error_operand_p (expr))
return error_mark_node;
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-117504.C
b/gcc/testsuite/g++.dg/cpp2a/constexpr-117504.C
new file mode 100644
index 00000000000..290d3dfd61e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-117504.C
@@ -0,0 +1,60 @@
+// PR c++/117504 - Initial report
+// { dg-do "run" { target c++20 } }
+
+struct span {
+ span (const int (&__first)[1]) : _M_ptr (__first) {}
+ int operator[] (long __i) { return _M_ptr[__i]; }
+ const int *_M_ptr;
+};
+
+constexpr int a_global_vec[]{1};
+span myFunctor() {
+ return a_global_vec;
+}
+
+int main() {
+ constexpr int a_vec[]{1};
+
+ //
+ // This PR's case, that used to be miscompiled.
+ //
+ auto lambda_1 = [&a_vec] () -> span { return a_vec; };
+ auto vec_1 { lambda_1 () };
+ if (vec_1[0] != 1)
+ __builtin_abort ();
+
+ // Variant that used to be miscompiled as well.
+ auto lambda_2 = [&] () -> span { return a_vec; };
+ auto vec_2 { lambda_2 () };
+ if (vec_2[0] != 1)
+ __builtin_abort ();
+
+ //
+ // Related cases that worked already.
+ //
+ auto lambda_3 = [&a_vec] () /* -> span */ { return a_vec; };
+ auto vec_3 { lambda_3 () };
+ if (vec_3[0] != 1)
+ __builtin_abort ();
+
+ auto lambda_4 = [&] () /* -> span */ { return a_vec; };
+ auto vec_4 { lambda_4 () };
+ if (vec_4[0] != 1)
+ __builtin_abort ();
+
+ const int (&vec_5)[1] = a_vec;
+ if (vec_5[0] != 1)
+ __builtin_abort ();
+
+ span vec_6 (a_vec);
+ if (vec_6[0] != 1)
+ __builtin_abort ();
+
+ auto vec_7 = myFunctor ();
+ if (vec_7[0] != 1)
+ __builtin_abort ();
+
+ const int (&vec_8)[1] { a_vec };
+ if (vec_8[0] != 1)
+ __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-117504a.C
b/gcc/testsuite/g++.dg/cpp2a/constexpr-117504a.C
new file mode 100644
index 00000000000..f6d4dc8cbc5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-117504a.C
@@ -0,0 +1,12 @@
+// PR c++/117504 - ICE discovered by ppalka@ when reducing.
+// { dg-do "compile" { target c++20 } }
+
+struct span {
+ span (const int* __first) : _M_ptr (__first) {}
+ int operator[] (long __i) { return _M_ptr[__i]; }
+ const int *_M_ptr;
+};
+int main() {
+ constexpr int a_vec[]{1};
+ auto vec { [&a_vec]() -> span { return a_vec; } () };
+}
--
2.44.0