Skip to content

Commit 6acc699

Browse files
authored
[STLForwardCompat] Improve category handling in transformOptional (#149539)
The old version would prefer the "const &" overload over the "&&" one unless the former was not allowed in the given situation. In particular, if the function passed was "[](auto &&)" the argument would be "const &" even if the value passed to transformOptional was an rvalue reference. This version improves the handling of expression categories, and the lambda argument category will reflect the argument category in the above scenario.
1 parent 10b0dee commit 6acc699

File tree

2 files changed

+32
-15
lines changed

2 files changed

+32
-15
lines changed

llvm/include/llvm/ADT/STLForwardCompat.h

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,13 @@ using type_identity_t // NOLINT(readability-identifier-naming)
5555

5656
// TODO: Remove this in favor of std::optional<T>::transform once we switch to
5757
// C++23.
58-
template <typename T, typename Function>
59-
auto transformOptional(const std::optional<T> &O, const Function &F)
60-
-> std::optional<decltype(F(*O))> {
61-
if (O)
62-
return F(*O);
63-
return std::nullopt;
64-
}
65-
66-
// TODO: Remove this in favor of std::optional<T>::transform once we switch to
67-
// C++23.
68-
template <typename T, typename Function>
69-
auto transformOptional(std::optional<T> &&O, const Function &F)
70-
-> std::optional<decltype(F(*std::move(O)))> {
71-
if (O)
72-
return F(*std::move(O));
58+
template <typename Optional, typename Function,
59+
typename Value = typename llvm::remove_cvref_t<Optional>::value_type>
60+
std::optional<std::invoke_result_t<Function, Value>>
61+
transformOptional(Optional &&O, Function &&F) {
62+
if (O) {
63+
return F(*std::forward<Optional>(O));
64+
}
7365
return std::nullopt;
7466
}
7567

llvm/unittests/ADT/STLForwardCompatTest.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
#include "CountCopyAndMove.h"
1111
#include "gtest/gtest.h"
1212

13+
#include <optional>
14+
#include <tuple>
15+
#include <type_traits>
16+
#include <utility>
17+
1318
namespace {
1419

1520
template <typename T>
@@ -142,6 +147,26 @@ TEST(TransformTest, MoveTransformLlvm) {
142147
EXPECT_EQ(0, CountCopyAndMove::Destructions);
143148
}
144149

150+
TEST(TransformTest, TransformCategory) {
151+
struct StructA {
152+
int x;
153+
};
154+
struct StructB : StructA {
155+
StructB(StructA &&A) : StructA(std::move(A)) {}
156+
};
157+
158+
std::optional<StructA> A{StructA{}};
159+
llvm::transformOptional(A, [](auto &&s) {
160+
EXPECT_FALSE(std::is_rvalue_reference_v<decltype(s)>);
161+
return StructB{std::move(s)};
162+
});
163+
164+
llvm::transformOptional(std::move(A), [](auto &&s) {
165+
EXPECT_TRUE(std::is_rvalue_reference_v<decltype(s)>);
166+
return StructB{std::move(s)};
167+
});
168+
}
169+
145170
TEST(TransformTest, ToUnderlying) {
146171
enum E { A1 = 0, B1 = -1 };
147172
static_assert(llvm::to_underlying(A1) == 0);

0 commit comments

Comments
 (0)