diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2016-11-19 18:51:44 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2016-11-19 18:51:44 +0200 |
commit | 3db0756adc641e0a63c4c9f194c4f73cceddd90c (patch) | |
tree | 186b3eadaa746228e8d593870684f2f3f91a7090 | |
parent | 6b7075adc71104c5f6ad652b99fb753565eb67d8 (diff) |
Implement workaround for GCC bug (#62052) in function machinery
-rw-r--r-- | build2/function | 99 |
1 files changed, 99 insertions, 0 deletions
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 <typename L, typename R, typename... A> + struct function_cast_lamb + { + struct data + { + value (*const thunk) (vector_view<value>, const void*); + R (L::*const impl) (A...) const; + }; + + static value + thunk (vector_view<value> args, const void* d) + { + return thunk (move (args), + static_cast<const data*> (d)->impl, + std::index_sequence_for<A...> ()); + } + + template <size_t... i> + static value + thunk (vector_view<value> args, + R (L::*impl) (A...) const, + std::index_sequence<i...>) + { + const L* l (nullptr); // Undefined behavior. + + return value ( + (l->*impl) ( + function_arg<A>::cast ( + i < args.size () ? &args[i] : nullptr)...)); + } + }; + + template <typename L, typename... A> + struct function_cast_lamb<L, void, A...> + { + struct data + { + value (*const thunk) (vector_view<value>, const void*); + void (L::*const impl) (A...) const; + }; + + static value + thunk (vector_view<value> args, const void* d) + { + thunk (move (args), + static_cast<const data*> (d)->impl, + std::index_sequence_for<A...> ()); + return value (nullptr); + } + + template <size_t... i> + static void + thunk (vector_view<value> args, + void (L::*impl) (A...) const, + std::index_sequence<i...>) + { + const L* l (nullptr); + (l->*impl) ( + function_arg<A>::cast ( + i < args.size () ? &args[i] : nullptr)...); + } + }; +#endif + // Customization for member functions. // template <typename R, typename T> @@ -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 <typename L> + void + operator= (const L&) && + { + move (*this).coerce_lambda (&L::operator()); + } + + template <typename L, typename R, typename... A> + void + coerce_lambda (R (L::*op) (A...) const) && + { + using args = function_args<A...>; + using cast = function_cast_lamb<L, R, A...>; + + 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 <typename L> void operator= (const L& l) && @@ -475,6 +573,7 @@ namespace build2 { return static_cast<R (*) (A...)> (l); } +#endif // Support for assigning a pointer to member function (e.g. an accessor). // |