Hi,

On 14 Feb 2025, at 18:08, 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).
>
> This is what the patch does, successfully tested on 
> x86_64-pc-linux-gnu.
Friendly ping.

Thanks! Simon

>       PR c++/117504
>
> gcc/cp/ChangeLog:
>
>       * call.cc (perform_implicit_conversion_flags): When possibly
>       converting to a class, call mark_lvalue_use.
>
> gcc/testsuite/ChangeLog:
>
>       * g++.dg/cpp2a/constexpr-117504.C: New test.
>       * g++.dg/cpp2a/constexpr-117504a.C: New test.
>
> ---
>  gcc/cp/call.cc                                |  4 ++
>  gcc/testsuite/g++.dg/cpp2a/constexpr-117504.C | 60 
> +++++++++++++++++++
>  .../g++.dg/cpp2a/constexpr-117504a.C          | 12 ++++
>  3 files changed, 76 insertions(+)
>  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 38a8f7fdcda..097b1fa55a4 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -13973,6 +13973,10 @@ perform_implicit_conversion_flags (tree type, 
> tree expr,
>
>    if (TYPE_REF_P (type))
>      expr = mark_lvalue_use (expr);
> +  else if (MAYBE_CLASS_TYPE_P (type))
> +    /* We might convert using a constructor that takes the address of 
> EXPR, so
> +       assume that it will be the case.  */
> +    expr = mark_lvalue_use (expr);
>    else
>      expr = mark_rvalue_use (expr);
>
> 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

Reply via email to