boostorg / spirit

Boost.org spirit module
http://boost.org/libs/spirit
392 stars 161 forks source link

A | A not being simplified to A in 1.78.0 #722

Open allopislozano opened 2 years ago

allopislozano commented 2 years ago

Another functionality that has changed in 78. When having a disjunction A|A it is no longer simplified into a single A, the synthesized attribute is variant<A,A>. Is this expected?

https://godbolt.org/z/Mjoc9aKMa

cppljevans commented 2 years ago

Try something like the type_sequence in following in attribute _of_binary.hpp (of course you'll have to remove all the debug code I put in there to see what was happening):

//Purpose:
//  Prototype a **MAYBE** solution to https://github.com/boostorg/spirit/issues/722
//  The MAYBE is because it's unknown whether this change to type_sequence will
//  break other parts of code such as that in alternative.hpp or detail/alternative.hpp.
//========
#include <boost/mp11.hpp>
namespace mp11=boost::mp11;
#include <boost/utility/trace_scope.hpp>
#include <boost/iostreams/utility/templ_expr/demangle_fmt_type.hpp>
#define USE_TRANSFER_VARIANT_UNIQUE
  #include <boost/spirit/home/x3/support/traits/is_variant.hpp>
  #include <boost/utility/enable_if.hpp>
  namespace x3=boost::spirit::x3;

    template <typename... T>
    struct type_sequence
      //A prototype of detail::type_sequence in:
      //  attribute_of_binary.hpp
      //which "unique-izes" the template args to boost::variant.
      {
        #ifdef USE_TRANSFER_VARIANT_UNIQUE
            template 
            < template <typename...> class U
            , typename Enable = void
            >
          struct maybe_variant_transfer
            //If U is not a variant, simply transfer to U.
            { using type=U<T...>;
              static void trace_tmpl()
              {
              ; boost::trace_scope ts(stringify(":__LINE__=",__LINE__))
              ; std::cout<<":type="<<demangle_fmt_type<type>()<<";\n";
              ;}
            };
            template 
            < template <typename...> class U
            >
          struct maybe_variant_transfer
            < U
            , typename boost::enable_if
              < typename x3::traits::is_variant
                < U
                  < void//anything, just to allow is_variant to work.
                  >
                >
              >::type
            >
            //If U is a variant, assure the types are unique.
            { 
              using mp_list_T=mp11::mp_list<T...>
                ;
              using unique_t=mp11::mp_unique<mp_list_T>
                ;
                template
                < typename S
                >
              struct unpack_transfer
                ;
                template
                < typename... S
                >
              struct unpack_transfer
                < mp11::mp_list<S...>
                >
                { using type=U<S...>;
                };
              using type=typename unpack_transfer< unique_t>::type
                ;
              static void trace_tmpl()
              {
              ; boost::trace_scope ts(stringify(":__LINE__=",__LINE__))
              ; std::cout<<":type="<<demangle_fmt_type<type>()<<";\n";
              ;}
            };
          template <template <typename...> class U>
          using maybe_transfer = maybe_variant_transfer<U>;

          template <template <typename...> class U>
          using transfer_to = typename maybe_transfer<U>::type;

          template<template <typename...> class U>
          static void trace_tmpl()
          {
          ; boost::trace_scope ts(stringify(":__LINE__=",__LINE__))
          ; std::cout<<":transfer_to=\n"<<demangle_fmt_type<transfer_to<U>>()<<";\n"
          ; std::cout<<":maybe_transfer<U>=\n"<<demangle_fmt_type<maybe_transfer<U>>()<<";\n"
          ; std::cout<<":maybe_transfer<U>::trace_tmpl():\n"
          ; maybe_transfer<U>::trace_tmpl()
          ;}
        #else        
          template <template <typename...> class U>
          using transfer_to = U<T...>;
          static void trace_tmpl()
          {
          ; boost::trace_scope ts(stringify(":__LINE__=",__LINE__))
          ;}
        #endif//USE_TRANSFER_VARIANT_UNIQUE

      };//type_sequence

      template 
      < typename...
      >
    struct not_var
      {};

int main()
  { 
      boost::iostreams::indent_scoped_ostreambuf<char>
    indent_outbuf(std::cout,2)
  ; using seq=type_sequence<int,double,int>;
  ; std::cout<<":seq=\n"<<demangle_fmt_type<seq>()<<";\n"
  ; using xfr_var=typename seq::template transfer_to<boost::variant>;
  ; std::cout<<":xfr_var=\n"<<demangle_fmt_type<xfr_var>()<<";\n"
  ; std::cout<<":seq::trace_tmpl<xfr_var>():\n";
  ; seq::trace_tmpl<boost::variant>()
  ; using xfr_not=typename seq::template transfer_to<not_var>;
  ; std::cout<<":xfr_not=\n"<<demangle_fmt_type<xfr_not>()<<";\n"
  ; std::cout<<":seq::trace_tmpl<not_var>():\n";
  ; seq::trace_tmpl<not_var>()
  ;}
OBorce commented 2 years ago

Any workaround for this?

Kojoley commented 2 years ago

Repeated types not being unified is much older thing #610, so I doubt it is the cause. Reversed order was reported in #721 though matching the order does not help this example. There is a meta issue that affects alternative parser https://github.com/boostorg/spirit/issues/707#issuecomment-1001748725 and highly likely the source of this bug too.

eido79 commented 1 year ago

This and #721 seem to be the consequence of the changes done in #702, combined with the issue described in #610. IMHO the parameter order was indeed correct, and the issue is in the definition of is_substitute_impl for variant types.

I've implemented a prototype (crude) fix here: https://github.com/eido79/spirit/tree/variant_substitute