diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -165,6 +165,9 @@ /// /// This class does not own the callable, so it is not in general safe to store /// a function_ref. +/// +/// (As an exception, function_ref(&someFreeFunction) is safe, even though the +/// callable is technically a temporary function pointer). template class function_ref; template @@ -196,6 +199,21 @@ : callback(callback_fn::type>), callable(reinterpret_cast(&callable)) {} + // Overload for function pointers. + // We store the function pointer itself in callback, therefore we don't rely + // on the caller keeping the pointer (which may be a temporary) alive. + template + function_ref( + PRet (*fptr)(PParams...), + // Pointer must be callable and return a suitable type. + std::enable_if_t< + std::is_void::value || + std::is_convertible()( + std::declval()...)), + Ret>::value> * = nullptr) + : callback(callback_fn), + callable(reinterpret_cast(fptr)) {} + Ret operator()(Params ...params) const { return callback(callable, std::forward(params)...); } diff --git a/llvm/unittests/ADT/FunctionRefTest.cpp b/llvm/unittests/ADT/FunctionRefTest.cpp --- a/llvm/unittests/ADT/FunctionRefTest.cpp +++ b/llvm/unittests/ADT/FunctionRefTest.cpp @@ -48,6 +48,36 @@ ASSERT_EQ(1, X()); } +// Ensure that function_ref captures a function pointer by value. +// This means we don't have to keep the original pointer alive. +TEST(FunctionRefTest, FunctionPointer) { + static int X; + void (*Inc)(void) = [] { ++X; }; + void (*Dec)(void) = [] { ++X; }; + function_ref IncRef = Inc; + + X = 0; + IncRef(); + EXPECT_EQ(X, 1); + Inc = Dec; // not nullptr as UB may be optimized. + IncRef(); + EXPECT_EQ(X, 2); +} + +static int square(int X) { return X * X; } + +// Ensure function_ref binds properly to free functions. +// This uses the function-pointer-by-value constructor. +// (Using the "normal" constructor with a function ref caused issues in GCC 5 +// due to too-eager decay: See https://github.com/clangd/clangd/issues/800) +TEST(FunctionRefTest, FreeFunction) { + struct S { + function_ref Call; + }; + S Wrapper = S{square}; + EXPECT_EQ(4, Wrapper.Call(2)); +} + // Test that overloads on function_refs are resolved as expected. std::string returns(StringRef) { return "not a function"; } std::string returns(function_ref F) { return "number"; }