From e3eaff10b29d8bb85a0d09e6bb72fbdb7cc9c3ea Mon Sep 17 00:00:00 2001 From: Yevgeny Rouban Date: Fri, 21 May 2021 14:04:43 +0700 Subject: [PATCH] Allow incomplete template types in unique_function arguments We can't declare unique_function that has in its arguments a reference to a template type with an incomplete argument. For instance, we can't declare unique_function&)> when A is forward declared. This is because SFINAE will trigger a hard error in this case, when instantiating IsSizeLessThanThresholdT with the incomplete type. This patch specialize AdjustedParamT for references to remove this error. Committed on behalf of: @math-fehr (Fehr Mathieu) Reviewed By: DaniilSuchkov, yrouban --- llvm/include/llvm/ADT/FunctionExtras.h | 23 +++++++++++++++++------ llvm/unittests/ADT/FunctionExtrasTest.cpp | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h index 9f8c7a5c052f..e67ef7377c88 100644 --- a/llvm/include/llvm/ADT/FunctionExtras.h +++ b/llvm/include/llvm/ADT/FunctionExtras.h @@ -90,13 +90,24 @@ protected: // The heuristic used is related to common ABI register passing conventions. // It doesn't have to be exact though, and in one way it is more strict // because we want to still be able to observe either moves *or* copies. + template struct AdjustedParamTBase { + static_assert(!std::is_reference::value, + "references should be handled by template specialization"); + using type = typename std::conditional< + llvm::is_trivially_copy_constructible::value && + llvm::is_trivially_move_constructible::value && + IsSizeLessThanThresholdT::value, + T, T &>::type; + }; + + // This specialization ensures that 'AdjustedParam&>' or + // 'AdjustedParam&&>' does not trigger a compile-time error when 'T' is + // an incomplete type and V a templated type. + template struct AdjustedParamTBase { using type = T &; }; + template struct AdjustedParamTBase { using type = T &; }; + template - using AdjustedParamT = typename std::conditional< - !std::is_reference::value && - llvm::is_trivially_copy_constructible::value && - llvm::is_trivially_move_constructible::value && - IsSizeLessThanThresholdT::value, - T, T &>::type; + using AdjustedParamT = typename AdjustedParamTBase::type; // The type of the erased function pointer we use as a callback to dispatch to // the stored callable when it is trivial to move and destroy. diff --git a/llvm/unittests/ADT/FunctionExtrasTest.cpp b/llvm/unittests/ADT/FunctionExtrasTest.cpp index 8eb0d9be7d93..3c6f179c190f 100644 --- a/llvm/unittests/ADT/FunctionExtrasTest.cpp +++ b/llvm/unittests/ADT/FunctionExtrasTest.cpp @@ -273,4 +273,22 @@ TEST(UniqueFunctionTest, SFINAE) { EXPECT_EQ("string", returns([] { return "hello"; })); } +// A forward declared type, and a templated type. +class Incomplete; +template class Templated { T A; }; + +// Check that we can define unique_function that have references to +// incomplete types, even if those types are templated over an +// incomplete type. +TEST(UniqueFunctionTest, IncompleteTypes) { + unique_function &&)> + IncompleteArgumentRValueReference; + unique_function &)> + IncompleteArgumentLValueReference; + unique_function *)> IncompleteArgumentPointer; + unique_function &()> IncompleteResultLValueReference; + unique_function && ()> IncompleteResultRValueReference2; + unique_function *()> IncompleteResultPointer; +} + } // anonymous namespace