Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] std::ranges::copy in libstdc++ not constant expression. #92315

Open
SainoNamkho opened this issue May 15, 2024 · 4 comments
Open

[Clang] std::ranges::copy in libstdc++ not constant expression. #92315

SainoNamkho opened this issue May 15, 2024 · 4 comments
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" libstdc++ GNU libstdc++ C++ standard library

Comments

@SainoNamkho
Copy link

SainoNamkho commented May 15, 2024

https://godbolt.org/z/j3KnMGocn

#include <array>
#include <ranges>
#include <algorithm>
#include <print>

constexpr auto arr = [] {
    std::array<int, 2> arr;
    std::ranges::copy(std::array{std::array{1}, std::array{2}} | std::views::join, arr.data());
    return arr;
}();

int main()
{
    for(auto i : arr)
        std::println("{}",i);
}

Clang together with libstdc++ reports

<source>:6:16: error: constexpr variable 'arr' must be initialized by a constant expression
    5 | constexpr auto arr = [] {
      |                ^     ~~~~
    6 |     std::array<int, 2> arr;
      |     ~~~~~~~~~~~~~~~~~~~~~~~
    7 |     std::ranges::copy(std::array{std::array{1}, std::array{2}} | std::views::join, arr.data());
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    8 |     return arr;
      |     ~~~~~~~~~~~
    9 | }();
      | ~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/bits/ranges_algobase.h:291:12: note: subobject '_M_value' is not initialized
  291 |           return {std::move(__first), std::move(__result)};
      |                   ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/bits/ranges_algobase.h:291:12: note: in call to '_Iterator(__first)'
  291 |           return {std::move(__first), std::move(__result)};
      |                   ^~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/optional:230:8: note: subobject declared here
  230 |           _Up _M_value;
      |               ^

I can't see why _M_value is not initialized, the implementation of ranges library from libstdc++ is almost the same as that from libc++, so the difference might be from std::optional.

It is only rejected since clang 17: https://godbolt.org/z/354TGGrGq
@YexuanXiao

@github-actions github-actions bot added the clang Clang issues not falling into any other category label May 15, 2024
@SainoNamkho SainoNamkho changed the title [Clang] std::ranges::copy in libstdc++ not constant expresion. [Clang] std::ranges::copy in libstdc++ not constant expression. May 15, 2024
@shafik
Copy link
Collaborator

shafik commented May 16, 2024

Note works fine w/ libc++: https://godbolt.org/z/7fe1PdjWW

@frederick-vs-ja
Copy link
Contributor

This correctly compiles (Godbolt link).

#include <array>
#include <ranges>
// #include <algorithm>

constexpr auto arr = [] {
    std::array<int, 2> arr{};
    for (auto it = arr.data();
         decltype(auto) x : std::array{std::array{1}, std::array{2}} | std::views::join) {
        *it = static_cast<decltype(x)&&>(x);
        ++it;
    }
    // std::ranges::copy(arrarr | std::views::join, arr.data());
    return arr;
}();

static_assert(arr == std::array<int, 2>{1, 2});

Note that libstdc++'s join_view's iterator holds an optional. The optional in the iterator doesn't contain a value when being moved in the posted operations.

Reduced example (Godbolt link):

#include <optional>
#include <utility>

constexpr int zero = []
{
    std::optional<int> opt{};
    opt.emplace(42);
    opt.reset();
    auto opt2 [[maybe_unused]] = std::move(opt);
    return 0;
}();

@frederick-vs-ja
Copy link
Contributor

libstdc++'s optional::reset unconditionally destroys the contained value (subobject), while libc++'s only does so when the optional is not trivially destructible.

https://github.com/gcc-mirror/gcc/blob/e656656e711949ef42a7e284f7cf81ca56f37374/libstdc%2B%2B-v3/include/std/optional#L280-L285

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
if (__engaged_) {
__val_.~value_type();
__engaged_ = false;
}
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
if (__engaged_) {
__engaged_ = false;
}
}

I think I've reached a library-free example Godbolt link.

constexpr int zero = []
{
    using I = int;
    struct OptionalLike {
        union U {
            I n;
        };
        U u;
    };

    OptionalLike opt;
    opt.u.n = 42;
    opt.u.n.~I();

    auto opt2 [[maybe_unused]] = opt;
    return 0;
}();

GCC & MSVC accept this, EDG & Clang reject this with the same reason. (ICC is probably buggy here and rejects this with an unreasonable reason.)

IIUC this issue is probably covered by CWG2658.

@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" libstdc++ GNU libstdc++ C++ standard library and removed clang Clang issues not falling into any other category labels May 16, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented May 16, 2024

@llvm/issue-subscribers-clang-frontend

Author: None (SainoNamkho)

https://godbolt.org/z/j3KnMGocn ```c++ #include <array> #include <ranges> #include <algorithm> #include <print>

constexpr auto arr = [] {
std::array<int, 2> arr;
std::ranges::copy(std::array{std::array{1}, std::array{2}} | std::views::join, arr.data());
return arr;
}();

int main()
{
for(auto i : arr)
std::println("{}",i);
}


Clang together with libstdc++ reports

<source>:6:16: error: constexpr variable 'arr' must be initialized by a constant expression
5 | constexpr auto arr = [] {
| ^ ~~~~
6 | std::array<int, 2> arr;
| ~~~~~~~~~~~~~~~~~~~~~~~
7 | std::ranges::copy(std::array{std::array{1}, std::array{2}} | std::views::join, arr.data());
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 | return arr;
| ~~~~~~~~~~~
9 | }();
| ~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/bits/ranges_algobase.h:291:12: note: subobject '_M_value' is not initialized
291 | return {std::move(__first), std::move(__result)};
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/bits/ranges_algobase.h:291:12: note: in call to '_Iterator(__first)'
291 | return {std::move(__first), std::move(__result)};
| ^~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/optional:230:8: note: subobject declared here
230 | _Up _M_value;
| ^


I can't see why `_M_value` is not initialized, the implementation of ranges library from libstdc++ is almost the same as that from libc++, so the difference might be from `std::optional`.

It is only rejected since clang 17: https://godbolt.org/z/354TGGrGq
@<!-- -->YexuanXiao
</details>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" libstdc++ GNU libstdc++ C++ standard library
Projects
None yet
Development

No branches or pull requests

5 participants