https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115378
Bug ID: 115378
Summary: [ice-on-valid] code using friend injection is crashing
gcc since 14
Product: gcc
Version: 15.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: eric.niebler at gmail dot com
Target Milestone: ---
the following valid code crashes gcc-14 and gcc-trunk. clang accepts it, as
does gcc-13. see https://godbolt.org/z/s9frvq3Pv
#include <algorithm>
#include <cassert>
#include <concepts>
#include <functional>
#include <ranges>
#include <tuple>
#include <type_traits>
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wunknown-warning-option"
#pragma GCC diagnostic ignored "-Wnon-template-friend"
#endif
namespace std::execution {
template <class Env, class Query, class... Args>
concept __has_query =
requires (const Env& __env, Args&&... __args) {
__env.query(Query(), std::forward<Args>(__args)...);
};
// specialization for key/value pairs
template <class Query, class Value>
struct prop {
[[no_unique_address]] Query __query;
[[no_unique_address]] Value __value;
[[nodiscard]] constexpr const Value & query(Query) const noexcept {
return __value;
}
};
template <class Query, class Value>
prop(Query, Value) -> prop<Query, unwrap_reference_t<Value>>;
template <class Env>
struct __ref;
template <class Env>
struct __ref<Env&> {
Env& __env;
constexpr __ref(reference_wrapper<Env> __r) noexcept : __env(__r) {}
template <class Query, class... Args>
requires __has_query<Env, Query, Args...>
constexpr decltype(auto) query(Query __q, Args&&... args) const
noexcept(noexcept(__env.query(__q, std::forward<Args>(args)...))) {
return __env.query(__q, std::forward<Args>(args)...);
}
};
template<class Slot, size_t N>
struct __reader {
friend auto __counted_flag(__reader<Slot, N>);
};
template<class Slot, size_t N>
struct __writer {
friend auto __counted_flag(__reader<Slot, N>) {}
static constexpr size_t __n = N;
};
template<class Slot, auto Tag, size_t NextVal = 0>
consteval auto __counter_impl() {
constexpr bool __counted_past_value = requires(__reader<Slot, NextVal> __r)
{
__counted_flag(__r);
};
if constexpr (__counted_past_value) {
return __counter_impl<Slot, Tag, NextVal + 1>();
} else {
return __writer<Slot, NextVal>().__n;
}
}
template<class Slot, auto Tag = []{}, auto Val = __counter_impl<Slot, Tag>()>
constexpr auto __counter = Val;
template<class...> struct __list;
template <class, size_t>
struct __ignore {
constexpr __ignore(auto&&) {}
auto query(auto) const = delete;
};
template <class Env>
using __wrap = conditional_t<is_reference_v<Env>, __ref<Env>, Env>;
template <class Child, size_t Counter>
using _as_base_ = conditional_t<Counter == 0, __wrap<Child>, __ignore<Child,
Counter>>;
template <class Parent, class Child, size_t Counter =
__counter<__list<Parent, Child>>>
using _as_base = _as_base_<Child, Counter>;
// utility for joining multiple environments
template <class... Envs>
struct env : _as_base<env<Envs...>, Envs>... {
template <class Query, class... Args>
constexpr decltype(auto) __get_1st() const noexcept {
constexpr bool __flags[] = {__has_query<Envs, Query, Args...>...};
constexpr size_t __idx = ranges::find(__flags, true) - __flags;
auto __tup = tie(static_cast<const __wrap<Envs>&>(*this)...);
return get<__idx>(__tup);
}
template <class Query, class... Args>
requires (__has_query<Envs, Query, Args...> ||...)
constexpr decltype(auto) query(Query __q, Args&&... args) const
noexcept(noexcept(__get_1st<Query, Args...>().query(__q,
std::forward<Args>(args)...))) {
return __get_1st<Query, Args...>().query(__q,
std::forward<Args>(args)...);
}
};
template <class... Envs>
env(Envs...) -> env<unwrap_reference_t<Envs>...>;
} // std::execution
// Test code:
template <size_t>
struct get_value_t {
auto operator()(const auto& env) const noexcept -> decltype(env.query(*this))
{
static_assert(noexcept(env.query(*this)));
return env.query(*this);
}
};
template <size_t I>
inline constexpr get_value_t<I> get_value{};
int main() {
using namespace std::execution;
// can create an environment out of a query and a value
auto env1 = prop(get_value<0>, 42);
auto val = get_value<0>(env1);
assert(val == 42);
// can store a value by reference:
int i = 42;
auto env2 = prop(get_value<0>, std::ref(i));
int& j = get_value<0>(env2);
++j;
assert(i == 43);
// Can create an env from several envs with duplicates.
// Queries are resolved by asking the nested envs first to last.
auto env3 = env(prop(get_value<0>, 42),
prop(get_value<1>, 43),
prop(get_value<1>, 44),
prop(get_value<0>, 45),
prop(get_value<0>, 46));
assert(get_value<0>(env3) == 42);
assert(get_value<1>(env3) == 43);
// nested envs can be held by reference also:
auto env4 = env(prop(get_value<1>, 42), std::cref(env2));
assert(get_value<0>(env4) == 43);
assert(get_value<1>(env4) == 42);
}