Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC Segfaults When `decltype` Used in Nested Lambda

I created a macro that conveniently builds lambda functions using which I can iterate through tensor objects in a library that I wrote. However, nesting these macros seemed to cause GCC to undergo an internal segmentation fault. Upon expanding the compiler's preprocessor output and going through some trial and error, I discovered that cause seems to be the use of decltype in the parameter list of a nested lambda function declared in the method of a class or struct. Below follows a minimal example using the standard library.

#include <iostream>
#include <type_traits>

template <class Iterator, class Func>
void for_each(const Iterator first, const Iterator last, Func func)
{
        for (Iterator it = first; it != last; ++it) {
                func(*it);
        }
}

template <class T>
class helper
{
        typedef typename T::size_type type;
};

template <class T>
class helper<T&>
{
        typedef typename T::size_type type;
};

template <class T>
class helper<T*>
{
        typedef typename T::size_type type;
};      

struct bar
{
        struct foo
        {
                typedef int size_type;
        } foo_;

        void test()
        {
                int arr[] = { 1, 2, 3 };
                for_each(arr, arr + 3, [&](int i) {
                        /*
                        ** XXX: The "typename ... type" segfaults g++!
                        */
                        for_each(arr, arr + 3, [&](typename helper<decltype(foo_)>::type j) {

                        });
                });
        }
};

int main()
{
        return 0;
}

Compiler Output:

$ g++ -Wall -std=c++0x nested_lambda.cpp
nested_lambda.cpp: In lambda function:
nested_lambda.cpp:42:56: internal compiler error: Segmentation fault
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-4.6/README.Bugs> for instructions.
Preprocessed source stored into /tmp/ccqYohFA.out file, please attach this to your bugreport.

I initially opted to use decltype because an object is passed to a macro, and I need to extract the object's type. From the object's type, (T, T&, or T*), I'd use a traits class to pull T::size_type. size_type would then be the type of the lambda function parameters.

How can I circumvent this issue without having to use a typedef to declare the type of the lambda function parameter in advance? If you can think of some other solution that could easily be implemented in a macro (i.e. copied and pasted repeatedly in the parameter list of a lambda function), that would work too.

like image 804
void-pointer Avatar asked Jan 27 '12 06:01

void-pointer


1 Answers

As a very rough workaround for those who may be experiencing similar issues, the best standard solution I could come up with involved having the macro declare a typedef in advance, concatenating GUID-like prefix (I personally recommend _qki_zbeu26_w92b27bqy_r62zf91j2n_s0a02_) and __LINE__ to generate some warbled nonsense for the typedef name. With all luck, this name will not clash with any other definitions.

To ensure that the same __LINE__ gets concatenated even when the warbled name is used for the lambda function parameter types, the warbled name will need to be generated by a macro that is initially passed a macro parameter, as in the code sample below.

#define _foo_GUID \
    _qki_zbeu26_w92b27bqy_r62zf91j2n_s0a02_

#define _foo_MANGLE_IMPL2(a, b) \
    a ## b

#define _foo_MANGLE_IMPL(a, b) \
    _foo_MANGLE_IMPL2(a, b)

#define _foo_MANGLE(a) \
    _foo_MANGLE_IMPL(_foo_GUID, a)

When passing _foo_MANGLE(__LINE__) as a macro parameter, please ensure that there is an extra level of indirection so that _foo_MANGLE(__LINE__) gets evaluated before it is used.

like image 62
void-pointer Avatar answered Nov 05 '22 19:11

void-pointer