From 3db0756adc641e0a63c4c9f194c4f73cceddd90c Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 19 Nov 2016 18:51:44 +0200 Subject: Implement workaround for GCC bug (#62052) in function machinery --- build2/function | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/build2/function b/build2/function index 7a7b43e..bd91117 100644 --- a/build2/function +++ b/build2/function @@ -381,6 +381,73 @@ namespace build2 } }; + // Customization for coerced lambdas (see below). + // +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 6 + template + struct function_cast_lamb + { + struct data + { + value (*const thunk) (vector_view, const void*); + R (L::*const impl) (A...) const; + }; + + static value + thunk (vector_view args, const void* d) + { + return thunk (move (args), + static_cast (d)->impl, + std::index_sequence_for ()); + } + + template + static value + thunk (vector_view args, + R (L::*impl) (A...) const, + std::index_sequence) + { + const L* l (nullptr); // Undefined behavior. + + return value ( + (l->*impl) ( + function_arg::cast ( + i < args.size () ? &args[i] : nullptr)...)); + } + }; + + template + struct function_cast_lamb + { + struct data + { + value (*const thunk) (vector_view, const void*); + void (L::*const impl) (A...) const; + }; + + static value + thunk (vector_view args, const void* d) + { + thunk (move (args), + static_cast (d)->impl, + std::index_sequence_for ()); + return value (nullptr); + } + + template + static void + thunk (vector_view args, + void (L::*impl) (A...) const, + std::index_sequence) + { + const L* l (nullptr); + (l->*impl) ( + function_arg::cast ( + i < args.size () ? &args[i] : nullptr)...); + } + }; +#endif + // Customization for member functions. // template @@ -462,6 +529,37 @@ namespace build2 // Support for assigning a (capture-less) lambda. // + // GCC up until version 6 has a bug (#62052) that is triggered by calling + // a lambda that takes a by-value argument via its "decayed" function + // pointer. To work around this we are not going to decay it and instead + // will call its operator() on NULL pointer; yes, undefined behavior, but + // better than a guaranteed crash. + // +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 6 + template + void + operator= (const L&) && + { + move (*this).coerce_lambda (&L::operator()); + } + + template + void + coerce_lambda (R (L::*op) (A...) const) && + { + using args = function_args; + using cast = function_cast_lamb; + + insert (move (name), + function_overload ( + nullptr, + args::min, + args::max, + function_overload::types (args::types, args::max), + thunk, + typename cast::data {&cast::thunk, op})); + } +#else template void operator= (const L& l) && @@ -475,6 +573,7 @@ namespace build2 { return static_cast (l); } +#endif // Support for assigning a pointer to member function (e.g. an accessor). // -- cgit v1.1