boost spirit V2 qi bug associated with optimization level
Asked Answered
L

1

4

I develop my code in my spare time. Preferably in debug mode. Recently, when I tried to build release version, then I got the error (runtime, output: 1\n2\n then failure). I located the piece of code (below), which contains the error, and I found, that the error only occurs, when optimization level is -Os, -Ofast, -O2, -O3 but not -O, -O0, -O1, -Og. In release mode I am constrained in debug abilities. What is the cause of the error? What is the method to find such errors?

#!/usr/bin/env bash -vex WARN="-W -Wall -Wextra" INCLUDE="-isystem /c/libs/boost-trunk" OPT="-O2" g++ -x c++ - -std=gnu++1y $INCLUDE

$WARN $OPT -o a <<__EOF && ./a && echo -e "\e[1;32msucceeded\e[0m" || echo -e "\e[1;31mfailed\e[0m"

#include <iterator>
#include <string>
#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>

using namespace boost::spirit;

template< typename Iterator >
struct skipper
        : qi::grammar< Iterator >
{

    skipper();

private :

    typename skipper::base_type::start_type skipper_;

};

template< typename Iterator >
skipper< Iterator >::skipper()
    : skipper::base_type(skipper_, "skipper") 
{
    std::cerr << 1 << std::endl;
    auto const ana =
            *~qi::char_('*') > +qi::char_('*')
            ;
    std::cerr << 2 << std::endl;
    skipper_ =
            qi::space
            | ("/*" > ana > *(~qi::char_("/*") > ana) > '/')
            | ("//" > *(qi::char_ - qi::eol) > (qi::eol | qi::eoi))
            ; // R"(\s+|(\/\*[^*]*\*+([^/*][^*]*\*+)*\/)|(\/\/[^\r\n]*))"
    std::cerr << 3 << std::endl;
}

using input_type = std::string;
using input_iterator_type = std::istreambuf_iterator< typename input_type::value_type >;
using base_iterator_type = multi_pass< input_iterator_type >;

template struct skipper< base_iterator_type >;

using skipper_type = skipper< base_iterator_type >;

int main()
{
    skipper_type const skipper_;
    std::cerr << 4 << std::endl;
    return EXIT_SUCCESS;
}
__EOF

gcc -v 2>&1 | tail -n1:

gcc version 4.8.1 (rev5, Built by MinGW-W64 project) 
Lychnis answered 24/12, 2013 at 16:3 Comment(1)
I use valgrind to do these issues. valgrind --db-attach=yes is most helpful (on linux, of course). However, my setup is broken so I just inspected the code in an editor this time :/Furthermost
F
5

It's a bug in your code, nothing wrong with the compiler or the optimization levels.

The cinch is with expression templates (like the ones used by Boost Proto, and hence by Boost Spirit). They are only valid to the end of their enclosing full expression [1]

The canonical workaound is:

 BOOST_SPIRIT_AUTO(ana, *~qi::char_('*') > +qi::char_('*'));

You can find it here: http://svn.boost.org/svn/boost/trunk/libs/spirit/example/qi/typeof.cpp and it got first introduced in the comments at this blog post: http://boost-spirit.com/home/articles/qi-example/zero-to-60-mph-in-2-seconds/

The explicit fix I tested (which worked nicely on my box, no warnings remaining in valgrind):

auto const ana = boost::proto::deep_copy(
        *~qi::char_('*') > +qi::char_('*'))
        ;

Spirit X3 promises to remove this wart. Slightly related, I think Protox11 also removes this issue by being aware of references at all times.


[1] Grep the standard for lifetime extension of temporaries. The expression templates keep references to the literals used (the rest has value semantics anyways), but the temporaries aren't bound to (const) references. So they go out of scope. Undefined Behaviour results

Furthermost answered 24/12, 2013 at 21:25 Comment(18)
With respect to the footnote: why an optimization affect the existence of the error?Lychnis
It doesn't! It's just undefined behaviour and it happened to /appear to work/ only. This is why valgrind/purify etc are so important.Furthermost
Linux users are lucky because they have at their disposal such free tool as valgrind.Lychnis
Which is why all my projects are cross-platform, even if the customer isn't. If need be, I'd suggest my employer to buy the proper tools :)Furthermost
In boost trunk (probably it should be at boost.org/libs/spirit/example/qi/typeof.cpp in next version) I found boost::spirit::qi::copy from #include <boost/spirit/include/support_auto.hpp>, that solve the problem too.Lychnis
Nice addition, @DukalesFurthermost
*Not from specified header. My mistake, sorry. But exist anyways.Lychnis
what to do with auto const distinct_word = boost::spirit::repository::distinct(qi::alnum | '_'); local variables, where boost::spirit::repository::distinct from #include <boost/spirit/repository/include/qi_distinct.hpp>? I cannot make it as part of the grammar class, because I don't know required type of result expression, so I use auto to automatically deduce type, and therefore distinct is a local variable, used in rules definitions.Lychnis
Just use qi::rule like everyone else :) If you don't know the required type you can find out using something like struct {} _ = distinct(qi::alnum | '_') which will tell you the type. You might want to look at Spirit X3 which has a more lightweight style that you seem to prefer. However, it's not released yet.Furthermost
I found name by means of using abi::__cxa_demangle from <cxxabi.h> and keyword decltype() and typeid().name(), but it is too long, and therefore does not fit here.Lychnis
In any case, it does not matter. Which parameters should I set to qi::rule?Lychnis
Oh well, that trick is not needed if you read your compiler's message. Also, just std::cout << typeid(x).name() and pipe the output through c++filt would do :)Furthermost
@Dukales on the "which parameters" question, it depends on the context, obviously. Why don't you open a new question with the actual problem so we can answer that?Furthermost
Ok. I open the new question.Lychnis
@Dukales I just noticed your deleted question. You probably realized I don't have access to that BB repo. I hope you find the time to reduce to a SSCCE that you can include and (re)open that question? Good luckFurthermost
what is SSCCE? You have an answer to that question? Does it make sense to open the question and BB repo again? I found that you hint about repository::distinct(qi::copy(qi::alnum | '_')) is usefull and is a solution of the problem.Lychnis
@Dukales I can't know whether I have an answer, because the question doesn't include the relevant source. This is what sscce.org means. I'd be wary of "solving" things this way unless you have very good tool-verification in place. So, you decide whether you'd like to reopen the question :/Furthermost
Don't worry, I already solve that issue. I conclude, that no one part of the grammar should be the local.Lychnis

© 2022 - 2024 — McMap. All rights reserved.