https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103663
Bug ID: 103663 Summary: Diagnostic is missing multiple instantiation frames to help point to where the problem happens Product: gcc Version: 12.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: barry.revzin at gmail dot com Target Milestone: --- I'm sorry for how not-remotely-reduced these examples are, but I'm not sure how to do better. ======================================================= Consider this bug in range-v3 that we're trying to figure out how to solve (this is a range-v3 library issue, not a gcc or libstdc++ issue): #include <range/v3/view/zip.hpp> struct Abstract { virtual auto f() -> int = 0; }; struct V { auto begin() -> Abstract*; auto end() -> Abstract*; }; void use(V v) { auto z = ranges::views::zip(v); z.begin(); } This code doesn't compile. With gcc 11.2 -std=c++20 (https://godbolt.org/z/h4d14jeMa), the diagnostics I get on compiler explorer are: In file included from /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/zip.hpp:17, from <source>:1: /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple: In instantiation of 'struct std::_Head_base<0, Abstract, false>': /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:407:12: required from 'struct std::_Tuple_impl<0, Abstract>' /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:599:11: required from 'class std::tuple<Abstract>' /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/functional/invoke.hpp:138:40: required from 'constexpr decltype ((F&&)(f)((Args&&(ranges::invoke_fn::operator()::args))...)) ranges::invoke_fn::operator()(F&&, Args&& ...) const [with F = ranges::detail::indirect_zip_fn_&; Args = {ranges::copy_tag, Abstract*}; decltype ((F&&)(f)((Args&&(ranges::invoke_fn::operator()::args))...)) = std::tuple<Abstract>]' /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/functional/concepts.hpp:36:5: required by substitution of 'template<bool Const> requires Const && (and_v<range<const Rngs>...>) && (zippable_with<Fun, typename meta::detail::_if_<std::integral_constant<bool, Const>, const Rngs>::type ...>) ranges::iter_zip_with_view<ranges::detail::indirect_zip_fn_, ranges::ref_view<V> >::cursor<Const> ranges::iter_zip_with_view<ranges::detail::indirect_zip_fn_, ranges::ref_view<V> >::begin_cursor<Const>() const [with bool Const = ranges::detail::indirect_zip_fn_]' /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/detail/range_access.hpp:85:31: required by substitution of 'template<class Rng> static constexpr decltype (rng.begin_cursor()) ranges::range_access::begin_cursor(Rng&) [with Rng = const ranges::iter_zip_with_view<ranges::detail::indirect_zip_fn_, ranges::ref_view<V> >]' /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/facade.hpp:39:39: required by substitution of 'template<class Derived> using facade_iterator_t = ranges::basic_iterator<typename std::decay<decltype (ranges::range_access::begin_cursor(declval<Derived&>()))>::type> [with Derived = const ranges::iter_zip_with_view<ranges::detail::indirect_zip_fn_, ranges::ref_view<V> >]' /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/facade.hpp:106:24: required by substitution of 'template<class D> requires same_as<D, Derived> constexpr ranges::detail::facade_iterator_t<const D> ranges::view_facade<ranges::iter_zip_with_view<ranges::detail::indirect_zip_fn_, ranges::ref_view<V> >, ranges::finite>::begin<D>() const [with D = ranges::iter_zip_with_view<ranges::detail::indirect_zip_fn_, ranges::ref_view<V> >]' <source>:14:12: required from here /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:224:13: error: cannot declare field 'std::_Head_base<0, Abstract, false>::_M_head_impl' to be of abstract type 'Abstract' 224 | _Head _M_head_impl; | ^~~~~~~~~~~~ <source>:3:8: note: because the following virtual functions are pure within 'Abstract': 3 | struct Abstract { | ^~~~~~~~ <source>:4:18: note: 'virtual int Abstract::f()' 4 | virtual auto f() -> int = 0; | ^ The instantiation stack points to tuple, invoke.hpp, concepts.hpp, range_access.hpp, facade.hpp. But the problem comes from zip_with.hpp. This is not really possible to track down from the diagnostic. To help illustrate what I mean, this line: /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/detail/range_access.hpp:85:31: required by substitution of 'template<class Rng> static constexpr decltype (rng.begin_cursor()) ranges::range_access::begin_cursor(Rng&) [with Rng = const ranges::iter_zip_with_view<ranges::detail::indirect_zip_fn_, ranges::ref_view<V> >]' points us to here: template<typename Rng> static constexpr auto CPP_auto_fun(begin_cursor)(Rng &rng) ( return rng.begin_cursor() ) Which is a macro that expands to: template<typename Rng> static constexpr auto begin_cursor (Rng &rng) noexcept(noexcept(rng.begin_cursor())) -> decltype(rng.begin_cursor()) { return (rng.begin_cursor()); } The next instantiation stack frame points us to here: template<typename Fun, typename... Args> CPP_requires(invocable_, requires(Fun && fn) // ( invoke((Fun &&) fn, std::declval<Args>()...) )); Which is a macro that expands to: template<typename Fun, typename... Args> concept invocable__requires_ = requires(Fun && fn) { invoke((Fun &&) fn, std::declval<Args>()...); } ; template<typename Fun, typename... Args> concept invocable = ranges::invocable__requires_<Fun, Args...>; But... how did we get from the begin_cursor call (which doesn't use concepts anywhere) to this concept? The clue is kind of in the text afterwards. We try to call this particular begin_cursor() (this is zip_with.hpp:329-336): template(bool Const = true)( /// \pre requires Const AND and_v<range<Rngs const>...> AND views::zippable_with<Fun, meta::if_c<Const, Rngs const>...>) cursor<Const> begin_cursor() const { return {fun_, tuple_transform(rngs_, ranges::begin)}; } And in that zip_with, we check zippable_with preprocesses out into: template<typename Fun, typename... Rngs > concept zippable_with__concept_ = invocable<Fun&, iterator_t<Rngs>...> && invocable<Fun&, copy_tag, iterator_t<Rngs>...> && invocable<Fun&, move_tag, iterator_t<Rngs>...> ; template<typename Fun, typename ...Rngs> concept zippable_with = and_v<input_range<Rngs>...> && copy_constructible<Fun> && views::zippable_with__concept_<Fun, Rngs...>; And there now is the invocable check. But this would've been a lot easier to track down if these two zip_with.hpp frames were present. ============================== A similar, shorter example of disappearing frames: #include <range/v3/view/zip.hpp> struct Abstract { virtual void f() = 0; }; using T = ranges::common_reference_t<ranges::common_tuple<Abstract&>, std::tuple<Abstract>&>; which gives me (https://godbolt.org/z/MdvrYGxYr): /opt/compiler-explorer/gcc-trunk-20211211/include/c++/12.0.0/tuple:416:12: required from 'struct std::_Tuple_impl<0, Abstract>' /opt/compiler-explorer/gcc-trunk-20211211/include/c++/12.0.0/tuple:609:11: required from 'class std::tuple<Abstract>' /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:92:41: required by substitution of 'template<class T, class U> struct concepts::detail::_builtin_common_2<T, U, meta::void_<decltype ((true ? declval<T>() : declval<U>()))> > [with T = ranges::common_tuple<Abstract&>; U = std::tuple<Abstract>&]' /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:365:12: recursively required by substitution of 'template<class T, class U> struct concepts::detail::_common_reference2<T, U, typename concepts::detail::if_<std::is_reference<typename concepts::detail::_builtin_common<T, U>::type>::value>::invoke<void> > [with T = ranges::common_tuple<Abstract&>; U = std::tuple<Abstract>&]' /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:365:12: required from 'struct concepts::common_reference<ranges::common_tuple<Abstract&>, std::tuple<Abstract>&>' /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:370:11: required by substitution of 'template<class ... Ts> using common_reference_t = typename concepts::common_reference::type [with Ts = {ranges::common_tuple<Abstract&>, std::tuple<Abstract>&}]' /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/utility/common_type.hpp:47:11: required by substitution of 'template<class ... Ts> using common_reference_t = concepts::common_reference_t<Ts ...> [with Ts = {ranges::common_tuple<Abstract&>, std::tuple<Abstract>&}]' <source>:7:92: required from here But there are a lot of steps from type_traits.hpp:365 to type_traits.hpp:92. Clang hides them by default, but let's me expose them (https://godbolt.org/z/b6dne6c85): /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:92:41: note: in instantiation of template class 'std::tuple<Abstract>' requested here using _cond_res = decltype(true ? std::declval<T>() : std::declval<U>()); ^ /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:146:52: note: in instantiation of template type alias '_cond_res' requested here struct _builtin_common_2<T, U, meta::void_<_cond_res<T, U>>> ^ /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:151:15: note: during template argument deduction for class template partial specialization '_builtin_common_2<T, U>' [with T = ranges::common_tuple<Abstract &>, U = std::tuple<Abstract> &] : _builtin_common_2<T, U> ^ /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:151:15: note: in instantiation of template class 'concepts::detail::_builtin_common_2<ranges::common_tuple<Abstract &>, std::tuple<Abstract> &>' requested here /opt/compiler-explorer/libs/rangesv3/trunk/include/meta/meta_fwd.hpp:298:18: note: in instantiation of template class 'concepts::detail::_builtin_common<ranges::common_tuple<Abstract &>, std::tuple<Abstract> &>' requested here typename T::type; ^ /opt/compiler-explorer/libs/rangesv3/trunk/include/meta/meta_fwd.hpp:298:9: note: in instantiation of requirement here typename T::type; ^~~~~~~~~~~~~~~~ /opt/compiler-explorer/libs/rangesv3/trunk/include/meta/meta_fwd.hpp:296:26: note: while substituting template arguments into constraint expression here META_CONCEPT trait = requires ^~~~~~~~ /opt/compiler-explorer/libs/rangesv3/trunk/include/meta/meta.hpp:140:36: note: while checking the satisfaction of concept 'trait<concepts::detail::_builtin_common<ranges::common_tuple<Abstract &>, std::tuple<Abstract> &>>' requested here template <META_TYPE_CONSTRAINT(trait) T> ^ /opt/compiler-explorer/libs/rangesv3/trunk/include/meta/meta.hpp:140:36: note: while substituting template arguments into constraint expression here template <META_TYPE_CONSTRAINT(trait) T> ^~~~~ /opt/compiler-explorer/libs/rangesv3/trunk/include/meta/meta_fwd.hpp:155:35: note: expanded from macro 'META_TYPE_CONSTRAINT' #define META_TYPE_CONSTRAINT(...) __VA_ARGS__ ^~~~~~~~~~~ /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:89:9: note: while checking constraint satisfaction for template '_t<concepts::detail::_builtin_common<ranges::common_tuple<Abstract &>, std::tuple<Abstract> &>>' required here using _builtin_common_t = meta::_t<_builtin_common<T, U>>; ^~~~~ /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:346:64: note: in instantiation of template type alias '_builtin_common_t' requested here struct _common_reference2<T, U, if_t<std::is_reference<_builtin_common_t<T, U>>::value>> ^ /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:366:11: note: during template argument deduction for class template partial specialization '_common_reference2<T, U, if_t<std::is_reference<_builtin_common_t<T, U>>::value>>' [with T = ranges::common_tuple<Abstract &>, U = std::tuple<Abstract> &] : detail::_common_reference2<T, U> ^ /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:366:11: note: in instantiation of template class 'concepts::detail::_common_reference2<ranges::common_tuple<Abstract &>, std::tuple<Abstract> &>' requested here /opt/compiler-explorer/libs/rangesv3/trunk/include/concepts/type_traits.hpp:370:5: note: in instantiation of template class 'concepts::common_reference<ranges::common_tuple<Abstract &>, std::tuple<Abstract> &>' requested here using common_reference_t = typename common_reference<Ts...>::type; ^ Which is also a lot more helpful.