Opened 7 years ago

Closed 7 years ago

Last modified 7 years ago

#12016 closed Bugs (fixed)

x3::symbols moves result and therefore removes it from the symbol_parser itself

Reported by: UsYer Owned by: Joel de Guzman
Milestone: To Be Determined Component: spirit
Version: Boost 1.60.0 Severity: Regression
Keywords: spirit, x3, symbols Cc:

Description

So, I ran into strange behaviour with boost::spirit::x3 as supplied in boost 1.59:

I defined a 'dynamic' symbol table via:

   struct instructions : x3::symbols<OpCode> {
                instructions()
                {
                        name("instructions");
                }

                void set_instruction_set(const std::unordered_map<std::string, OpCode>& instruction_set) {
                        for (const auto& var : instruction_set) {
                                add(var.first, var.second);
                        }
                }
        } instructions_parser;

OpCode is defined as :

    struct OpCode
    {
            std::string mnemonic;
            std::vector<...> variants;// actual type in vector<> not important.
    };

now, with the symbol table embedded in the neccessary rules, when parsing an input string like

  mov   r2      r1
  mov   r1      @80

the resulting ast only contains the first mov with its operands. The second mov is missing but the operands are correctly parsed. This may look as followed, when printing the resulting AST:

  mov r2 r1 
      r1 @80

With the debugger I located the source of the error in symbols.hpp in symbol_parser::parse():

  template <typename Iterator, typename Context, typename Attribute>
      bool parse(Iterator& first, Iterator const& last
        , Context const& context, unused_type, Attribute& attr) const
      {
          x3::skip_over(first, last, context);

          if (value_type* val_ptr
              = lookup->find(first, last, get_case_compare<Encoding>(context)))
          {
              x3::traits::move_to(*val_ptr, attr); //<- the error originates from here
              return true;
          }
          return false;
      }

with move_to beeing:

  template <typename T>
  inline void move_to(T& src, T& dest)
  {
      if (boost::addressof(src) != boost::addressof(dest))
          dest = std::move(src);
  }

As you can see, the src which is my OpCode instance added in the symbol_parser is moved. This means after the first call its empty again and that's why only the first instructions appears. It's, simply put, moved out of the symbol table.

''Live example (shows the bug is also present in 1.60)'' provided by cv_and_he @stackoverflow

Temporary Workaround:

By declaring the template parameter as const one can suppress move semantics. The copy-ctor is then called.

I.E.:

 x3::symbols<const OpCode>

Change History (6)

comment:1 by anonymous, 7 years ago

Keywords: spirit x3 symbols added

comment:2 by Joel de Guzman, 7 years ago

Fixed in develop branch. Please confirm! Thanks for the report!

comment:3 by Joel de Guzman, 7 years ago

Resolution: fixed
Status: newclosed

comment:4 by bugs@…, 7 years ago

Excellent report.

It strikes me that the const-ness of the value seems to be _the_ way to indicate a parser's result value is not to be moved from.

(This seems to be by design? It seems a bit odd that the library would not strictly perfectly forward all temporaries and only move from known rvalues)

I suggested a patch based on const mapped values https://github.com/boostorg/spirit/pull/174

comment:5 by anonymous, 7 years ago

Oh wow. That's fast

comment:6 by Joel de Guzman, 7 years ago

I agree! Excellent report! I beat sehe to a fix, but thanks a lot sehe, I really appreciate it. You rock! Esp. on the spirit list and stack-exchange.

PS> Sometimes I try to post a solution to stack-exchange, but then it says I do not have enough credibility points :-)

Note: See TracTickets for help on using tickets.