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); }