boostorg/fusion

Potential regression: incomplete type in tag_of is a hard error

Closed this issue · 4 comments

This issue was initially reported as boostorg/hana#386. The following is a hard error in Boost trunk:

#include <boost/fusion/include/tag_of.hpp>
struct incomplete;
using X = ::boost::fusion::traits::tag_of<incomplete>::type;

int main() { }

I believe this started happening with boostorg/type_traits@ac35139. fusion::tag_of is using boost::is_convertible, which requires its input to be a complete type, but I don't think it is documented anywhere that tags must be complete types. Possible workarounds are

  1. Only call is_convertible if boost::is_complete is true
  2. Request that tags be complete types (but that's a breaking change)
  3. Reimplement the is_convertible without the assertion

I think (1) is the best solution, even though I am skeptical that a is_complete trait can even be implemented without compiler support. But since Boost.TypeTraits provides it, it seems like it's possible.

The full error message (with Clang) is:

In file included from [snip].cpp:5:
In file included from [snip]/boost/fusion/include/tag_of.hpp:11:
In file included from [snip]/boost/fusion/support/tag_of.hpp:14:
In file included from [snip]/boost/fusion/support/detail/is_mpl_sequence.hpp:16:
[snip]/boost/type_traits/is_convertible.hpp:498:4: error: static_assert failed "From argument type to is_convertible must be a complete type"
   BOOST_STATIC_ASSERT_MSG(boost::is_complete<From>::value || boost::is_void<From>::value || boost::is_array<From>::value, "From argument type to is_convertible must be a complete type");
   ^                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[snip]/boost/static_assert.hpp:31:45: note: expanded from macro 'BOOST_STATIC_ASSERT_MSG'
#     define BOOST_STATIC_ASSERT_MSG( ... ) static_assert(__VA_ARGS__)
                                            ^             ~~~~~~~~~~~
[snip]/boost/mpl/aux_/nested_type_wknd.hpp:27:7: note: in instantiation of template class 'boost::is_convertible<incomplete, boost::fusion::detail::from_sequence_convertible_type>' requested here
    : T::type
      ^
[snip]/boost/mpl/not.hpp:41:11: note: in instantiation of template class 'boost::mpl::aux::nested_type_wknd<boost::is_convertible<incomplete, boost::fusion::detail::from_sequence_convertible_type> >' requested here
          BOOST_MPL_AUX_NESTED_TYPE_WKND(T)::value
          ^
[snip]/boost/mpl/aux_/nested_type_wknd.hpp:38:24: note: expanded from macro 'BOOST_MPL_AUX_NESTED_TYPE_WKND'
    ::boost::mpl::aux::nested_type_wknd<T> \
                       ^
[snip]/boost/mpl/aux_/nested_type_wknd.hpp:27:7: note: in instantiation of template class 'boost::mpl::not_<boost::is_convertible<incomplete, boost::fusion::detail::from_sequence_convertible_type> >' requested here
    : T::type
      ^
[snip]/boost/mpl/aux_/preprocessed/gcc/and.hpp:51:11: note: in instantiation of template class 'boost::mpl::aux::nested_type_wknd<boost::mpl::not_<boost::is_convertible<incomplete, boost::fusion::detail::from_sequence_convertible_type> > >' requested here
          BOOST_MPL_AUX_NESTED_TYPE_WKND(T1)::value
          ^
[snip]/boost/mpl/aux_/nested_type_wknd.hpp:38:24: note: expanded from macro 'BOOST_MPL_AUX_NESTED_TYPE_WKND'
    ::boost::mpl::aux::nested_type_wknd<T> \
                       ^
[snip]/boost/fusion/support/detail/is_mpl_sequence.hpp:22:11: note: in instantiation of template class 'boost::mpl::and_<boost::mpl::not_<boost::is_convertible<incomplete, boost::fusion::detail::from_sequence_convertible_type> >, boost::mpl::is_sequence<incomplete>, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true> >' requested here
        : mpl::and_<
          ^
[snip]/boost/mpl/if.hpp:63:68: note: in instantiation of template class 'boost::fusion::detail::is_mpl_sequence<incomplete>' requested here
          BOOST_MPL_AUX_STATIC_CAST(bool, BOOST_MPL_AUX_VALUE_WKND(T1)::value)
                                                                   ^
[snip]/boost/fusion/support/tag_of.hpp:53:18: note: in instantiation of template class 'boost::mpl::if_<boost::fusion::detail::is_mpl_sequence<incomplete>, boost::mpl::identity<boost::fusion::mpl_sequence_tag>, boost::mpl::identity<boost::fusion::non_fusion_tag> >' requested here
          : mpl::if_<fusion::detail::is_mpl_sequence<Sequence>,
                 ^
[snip]/boost/fusion/support/tag_of.hpp:71:15: note: in instantiation of template class 'boost::fusion::detail::tag_of_impl<incomplete, void>' requested here
            : boost::fusion::detail::tag_of_impl<Sequence, Active>
              ^
[snip].cpp:7:36: note: in instantiation of template class 'boost::fusion::traits::tag_of<incomplete, void>' requested here
using X = ::boost::fusion::traits::tag_of<incomplete>::type;
                                   ^
Flast commented

Is hana tag a sequence? It seems hana passes tag type to tag_of.

../../../boost/fusion/support/tag_of.hpp:70:16: required from ‘struct boost::fusion::traits::tag_of<boost::hana::ext::boost::fusion::deque_tag, void>’ (from boostorg/hana#386)

This is expected. hana::tag_of is idempotent, so hana::tag_of<boost::hana::ext::boost::fusion::deque_tag>::type should be boost::hana::ext::boost::fusion::deque_tag. More precisely, here is what happens:

hana::any_of(fusion::make_deque(...), pred)
using Tag = hana::tag_of<decltype(fusion::make_deque(...))>::type;
if constexpr (hana::Searchable<Tag>::value) {
  // do something
}

But then, hana::Searchable<Tag> is really hana::Searchable<boost::hana::ext::boost::fusion::deque_tag>, which does (with Tag = boost::hana::ext::boost::fusion::deque_tag:

hana::integral_constant<bool,
  !is_default<any_of_impl<hana::tag_of<Tag>::type>>::value &&
  !is_default<find_if_impl<tag_of<Tag>::type>>::value
>

Here, tag_of<Tag>::type is expected to return just Tag if it's already a tag, but it hard-errors when the fusion adapters are included and the tag is incomplete, because we probe for whether the Tag is a fusion tag in that case.

Regardless of Hana's adapters, other clients of Fusion may have been using incomplete types for tags since this wasn't documented anywhere.

By design, we should be able to use incomplete types.

Flast commented

OK. Additionally, the change also should be applied to fusion::is_sequence.