As the testcase shows, elt can become a NULL tree during FOR_EACH_CONSTRUCTOR_VALUE. So guard against this possibility before calling reduced_constant_expression_p() recursively.
Tested on ppc64le. OK for trunk? PR c++/80294 * constexpr.c (reduced_constant_expression_p): Guard against NULL tree before recursing. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 3ca356071810..9ee794d5bf37 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1716,7 +1716,7 @@ reduced_constant_expression_p (tree t) /* And we need to handle PTRMEM_CST wrapped in a CONSTRUCTOR. */ tree elt; unsigned HOST_WIDE_INT idx; FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (t), idx, elt) - if (!reduced_constant_expression_p (elt)) + if (elt && !reduced_constant_expression_p (elt)) return false; return true; diff --git a/gcc/testsuite/g++.dg/torture/pr80294.C b/gcc/testsuite/g++.dg/torture/pr80294.C new file mode 100644 index 000000000000..859a10ba3e31 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/pr80294.C @@ -0,0 +1,167 @@ +// { dg-do compile } + +namespace std { +typedef long unsigned size_t; +template <typename _Tp> struct integral_constant { + static constexpr _Tp value = 0; +}; +template <unsigned long, typename> struct tuple_element; +template <typename _Tp, _Tp> struct integer_sequence; +} // namespace std +namespace meta { +using std::integer_sequence; +template <typename...> struct list; +template <template <typename...> class, typename...> struct defer; +template <typename T> using _t = typename T::type; +template <int> using size_t = std::integral_constant<int>; +namespace detail { +enum indices_strategy_ { recurse }; +constexpr indices_strategy_ strategy_(std::size_t, std::size_t) { + return recurse; +} +template <std::size_t, typename State, indices_strategy_> struct make_indices_ { + using type = State; +}; +} // namespace detail +template <long... Is> +using index_sequence = integer_sequence<unsigned long, Is...>; +template <std::size_t> +using make_index_sequence = + _t<detail::make_indices_<0, index_sequence<0>, detail::strategy_(0, 0)>>; +template <typename F, typename... Args> +using invoke = typename F::template invoke<Args...>; +namespace lazy { +template <typename F, typename... Args> +using invoke = defer<invoke, F, Args...>; +} +template <typename> struct id { using type = int; }; +namespace detail { +struct defer_if_ { + template <template <typename...> class C, typename... Ts> struct result { + using type = C<Ts...>; + }; + template <template <typename...> class C, typename... Ts> + result<C, Ts...> try_(); +}; +template <template <typename...> class C, typename... Ts> +using defer_ = decltype(defer_if_{}.try_<C, Ts...>()); +} // namespace detail +template <template <typename...> class C, typename... Ts> +struct defer : detail::defer_<C, Ts...> {}; +template <template <typename...> class C> struct quote { + template <typename... Ts> using invoke = _t<detail::defer_<C, Ts...>>; +}; +template <template <typename> class> using quote_trait = int; +template <typename F, typename... Ts> struct bind_front { + template <typename... Us> using invoke = invoke<F, Ts..., Us...>; +}; +namespace extension { +template <typename, typename> struct apply; +template <typename F, typename Ret, typename... Args> +struct apply<F, Ret(Args...)> : lazy::invoke<F, Ret, Args...> {}; +} // namespace extension +template <typename C, typename List> +using apply = _t<extension::apply<C, List>>; +template <typename, typename Q = quote<list>> using curry = Q; +template <typename F> using uncurry = bind_front<quote<apply>, F>; +namespace detail { +template <typename T, int> using first_ = T; +template <typename, typename> struct repeat_n_c_; +template <typename T, unsigned long... Is> +struct repeat_n_c_<T, index_sequence<Is...>> { + using type = list<first_<T, Is>...>; +}; +} // namespace detail +template <int, typename T> +using repeat_n_c = _t<detail::repeat_n_c_<T, make_index_sequence<0>>>; +template <typename N, typename T> using repeat_n = repeat_n_c<N::value, T>; +namespace detail { +template <typename> struct at_impl_; +template <typename... VoidPtrs> struct at_impl_<list<VoidPtrs...>> { + template <typename T> static T eval(VoidPtrs..., T *); +}; +template <typename, typename> struct at_; +template <typename... Ts, typename N> +struct at_<list<Ts...>, N> : decltype(at_impl_<repeat_n<N, void *>>::eval( + static_cast<id<Ts> *>(0)...)) {}; +} // namespace detail +template <typename List, typename N> using at = _t<detail::at_<List, N>>; +template <typename List, int> using at_c = at<List, size_t<0>>; +namespace detail { +template <typename> struct front_; +template <typename Head, typename... List> struct front_<list<Head, List...>> { + using type = Head; +}; +} // namespace detail +template <typename List> using front = _t<detail::front_<List>>; +namespace detail { +template <typename> struct back_; +template <typename Head, typename... List> struct back_<list<Head, List...>> { + using type = at_c<list<Head, List...>, sizeof...(List)>; +}; +} // namespace detail +template <typename List> using back = _t<detail::back_<List>>; +namespace detail { +template <typename Sequence> +struct as_list_ : lazy::invoke<uncurry<curry<quote_trait<id>>>, Sequence> {}; +} // namespace detail +template <typename Sequence> using as_list = _t<detail::as_list_<Sequence>>; +} // namespace meta +namespace detail { +template <typename T> using tag_spec = meta::front<meta::as_list<T>>; +template <typename T> using tag_elem = meta::back<meta::as_list<T>>; +template <std::size_t, typename T> constexpr auto adl_get(T) {} +} // namespace detail +template <typename, unsigned long, typename...> struct chain { + using type = int; +}; +template <typename Base, unsigned long I, typename First, typename... Rest> +struct chain<Base, I, First, Rest...> { + using type = + typename First::template getter<Base, 0, + meta::_t<chain<Base, 0, Rest...>>>; +}; +template <typename Base, typename... Tags> +struct Trans_NS_tagged_detail_tagged : meta::_t<chain<Base, 0, Tags...>> {}; +namespace compressed_tuple_detail { +template <typename, typename> struct compressed_tuple_; +template <typename... Ts> +using compressed_tuple = + compressed_tuple_<meta::list<Ts...>, + meta::make_index_sequence<sizeof...(Ts)>>; +} // namespace compressed_tuple_detail +using compressed_tuple_detail::compressed_tuple; +template <typename... Ts> +using tagged_compressed_tuple = + Trans_NS_tagged_detail_tagged<compressed_tuple<detail::tag_elem<Ts>...>, + detail::tag_spec<Ts>...>; +struct first { + template <typename, int, typename Next> struct getter : Next {}; +}; +struct second { + template <typename Untagged, int, typename> struct getter { + constexpr meta::_t<std::tuple_element<0, Untagged>> second() { + detail::adl_get<0>(*this); + } + }; +}; +template <typename Second> +struct compressed_pair : tagged_compressed_tuple<first(int), second(Second)> {}; +namespace std { +template <size_t I, typename... Ts, size_t... Is> +struct tuple_element<I, compressed_tuple_detail::compressed_tuple_< + meta::list<Ts...>, meta::index_sequence<Is...>>> { + using type = meta::at_c<meta::list<Ts...>, 0>; +}; +} // namespace std +template <typename T, typename P, typename E = int> +struct Constrained : compressed_pair<int> { + int value_; + constexpr T enforce_constraint(T) { executor(); } + constexpr Constrained(T) : Constrained(0, 0, 0) {} + template <typename T_> + constexpr Constrained(T_, P, E) : value_{enforce_constraint(0)} {} + constexpr E executor() { second(); } +}; +using Positive = Constrained<int, int>; +Positive n = 0; -- Markus