Bug 117504 - [12 Regression] Incorrect code emitted when using "constexpr std::array" since r8-3497-g281e6c1d8f1b4c
Summary: [12 Regression] Incorrect code emitted when using "constexpr std::array" sinc...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 14.2.1
: P2 normal
Target Milestone: 13.5
Assignee: Simon Martin
URL:
Keywords: ice-on-valid-code, wrong-code
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2024-11-08 13:54 UTC by Arnaud Desitter
Modified: 2025-03-25 15:55 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work: 15.0, 7.5.0
Known to fail:
Last reconfirmed: 2024-11-12 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Arnaud Desitter 2024-11-08 13:54:39 UTC
Considering:

#include <algorithm>
#include <array>
#include <iostream>
#include <span>
#include <utility>

auto f(int const n) {
    using s_t = std::pair<int, int>;

#if defined(WITH_CONST)
    const
#else
    // gcc 14 emits incorrect code with "constexpr"
    constexpr
#endif
        std::array a_vec{s_t{1, 123}};

    auto const vec{[&a_vec]() -> std::span<s_t const> { return a_vec; }()};

    {
        auto const it = std::ranges::find_if(vec, [n](auto const& v) {
            std::cout << v.first << ", " << v.second << '\n';
            return n >= v.first;
        });
        if (it != std::ranges::end(vec)) {
            return it->second;
        }
    }
    return -1;
}

int main() {
    auto const r = f(1);
    std::cout << "Found=" << r << " expected=123\n";
}


Using g++ 14.2

>g++ -std=c++20 -O0 -DWITH_CONST ex.cpp
>./a.out
1, 123
Found=123 expected=123

But
>g++ -std=c++20 -O0 exp.cpp
>./a.out
-2120994312, 32766
Found=32766 expected=123

Using "constepxr std::array" instead of "const std::array" results in incorrect code.


This can be reproduced with: https://godbolt.org/z/Ysoc59179
Comment 1 Patrick Palka 2024-11-12 16:16:50 UTC
Reduced:

struct span {
#if 1
  span(const int (&__first)[1]) : _M_ptr(__first) {}
#else
  span(const int* __first) : _M_ptr(__first) {}
#endif
  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; }()};
  if (vec[0] != 1)
    __builtin_abort();
}

Started with r8-3497-g281e6c1d8f1b4c.  If we toggle the #if then we instead ICE from build_address.
Comment 2 Simon Martin 2024-12-18 09:10:51 UTC
Working on this one.
Comment 3 Simon Martin 2025-02-11 13:50:46 UTC
Interestingly enough, it's the trailing type that's causing issues; things work perfectly fine without it.

That's what I'm digging into.
Comment 4 GCC Commits 2025-03-06 09:13:26 UTC
The master branch has been updated by Simon Martin <simartin@gcc.gnu.org>:

https://gcc.gnu.org/g:fdf846fdddcc0467b9f025757f081c5d54319d08

commit r15-7849-gfdf846fdddcc0467b9f025757f081c5d54319d08
Author: Simon Martin <simon@nasilyan.com>
Date:   Thu Mar 6 10:10:45 2025 +0100

    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.
    
            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.
Comment 5 Simon Martin 2025-03-06 09:19:30 UTC
Fixed in GCC 15.

I'll let it bake there for 2-3 weeks and then backport to active branches.
Comment 6 GCC Commits 2025-03-25 08:25:57 UTC
The releases/gcc-14 branch has been updated by Simon Martin <simartin@gcc.gnu.org>:

https://gcc.gnu.org/g:f078a613bf85eff138c2567b599779dee6ae4b22

commit r14-11445-gf078a613bf85eff138c2567b599779dee6ae4b22
Author: Simon Martin <simon@nasilyan.com>
Date:   Tue Mar 25 07:08:16 2025 +0100

    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.
    
            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.
    
    (cherry picked from commit fdf846fdddcc0467b9f025757f081c5d54319d08)
Comment 7 GCC Commits 2025-03-25 10:13:39 UTC
The releases/gcc-13 branch has been updated by Simon Martin <simartin@gcc.gnu.org>:

https://gcc.gnu.org/g:f10853a0087bc115c8ee1ddb5fc641bffdb7f1a4

commit r13-9450-gf10853a0087bc115c8ee1ddb5fc641bffdb7f1a4
Author: Simon Martin <simon@nasilyan.com>
Date:   Tue Mar 25 09:26:26 2025 +0100

    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.
    
            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.
    
    (cherry picked from commit fdf846fdddcc0467b9f025757f081c5d54319d08)
Comment 8 Simon Martin 2025-03-25 15:55:54 UTC
Fixed in GCC 13 and 14. I won't backport further since 12 will become inactive soon.