Skip to content

Commit 266154a

Browse files
authored
[ADT] Make concat able to handle ranges with iterators that return by value (such as zip) (#112783)
If any iterator in the concatenation returns by value, the result must return by value otherwise it'll produce dangling references. (some context that may or may not be relevant to this part of the code may be in 981ce8f ) An alternative to #112441
1 parent 7e87c2a commit 266154a

File tree

2 files changed

+58
-8
lines changed

2 files changed

+58
-8
lines changed

llvm/include/llvm/ADT/STLExtras.h

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,16 @@ class concat_iterator
10231023
std::forward_iterator_tag, ValueT> {
10241024
using BaseT = typename concat_iterator::iterator_facade_base;
10251025

1026+
static constexpr bool ReturnsByValue =
1027+
!(std::is_reference_v<decltype(*std::declval<IterTs>())> && ...);
1028+
1029+
using reference_type =
1030+
typename std::conditional_t<ReturnsByValue, ValueT, ValueT &>;
1031+
1032+
using handle_type =
1033+
typename std::conditional_t<ReturnsByValue, std::optional<ValueT>,
1034+
ValueT *>;
1035+
10261036
/// We store both the current and end iterators for each concatenated
10271037
/// sequence in a tuple of pairs.
10281038
///
@@ -1065,27 +1075,30 @@ class concat_iterator
10651075
/// Returns null if the specified iterator is at the end. Otherwise,
10661076
/// dereferences the iterator and returns the address of the resulting
10671077
/// reference.
1068-
template <size_t Index> ValueT *getHelper() const {
1078+
template <size_t Index> handle_type getHelper() const {
10691079
auto &Begin = std::get<Index>(Begins);
10701080
auto &End = std::get<Index>(Ends);
10711081
if (Begin == End)
1072-
return nullptr;
1082+
return {};
10731083

1074-
return &*Begin;
1084+
if constexpr (ReturnsByValue)
1085+
return *Begin;
1086+
else
1087+
return &*Begin;
10751088
}
10761089

10771090
/// Finds the first non-end iterator, dereferences, and returns the resulting
10781091
/// reference.
10791092
///
10801093
/// It is an error to call this with all iterators at the end.
1081-
template <size_t... Ns> ValueT &get(std::index_sequence<Ns...>) const {
1094+
template <size_t... Ns> reference_type get(std::index_sequence<Ns...>) const {
10821095
// Build a sequence of functions to get from iterator if possible.
1083-
ValueT *(concat_iterator::*GetHelperFns[])() const = {
1084-
&concat_iterator::getHelper<Ns>...};
1096+
handle_type (concat_iterator::*GetHelperFns[])()
1097+
const = {&concat_iterator::getHelper<Ns>...};
10851098

10861099
// Loop over them, and return the first result we find.
10871100
for (auto &GetHelperFn : GetHelperFns)
1088-
if (ValueT *P = (this->*GetHelperFn)())
1101+
if (auto P = (this->*GetHelperFn)())
10891102
return *P;
10901103

10911104
llvm_unreachable("Attempted to get a pointer from an end concat iterator!");
@@ -1107,7 +1120,7 @@ class concat_iterator
11071120
return *this;
11081121
}
11091122

1110-
ValueT &operator*() const {
1123+
reference_type operator*() const {
11111124
return get(std::index_sequence_for<IterTs...>());
11121125
}
11131126

llvm/unittests/ADT/STLExtrasTest.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,43 @@ TEST(STLExtrasTest, ConcatRange) {
504504
EXPECT_EQ(Expected, Test);
505505
}
506506

507+
template <typename T> struct Iterator {
508+
int i = 0;
509+
T operator*() const { return i; }
510+
Iterator &operator++() {
511+
++i;
512+
return *this;
513+
}
514+
bool operator==(Iterator RHS) const { return i == RHS.i; }
515+
};
516+
517+
template <typename T> struct RangeWithValueType {
518+
int i;
519+
RangeWithValueType(int i) : i(i) {}
520+
Iterator<T> begin() { return Iterator<T>{0}; }
521+
Iterator<T> end() { return Iterator<T>{i}; }
522+
};
523+
524+
TEST(STLExtrasTest, ValueReturn) {
525+
RangeWithValueType<int> R(1);
526+
auto C = concat<int>(R, R);
527+
auto I = C.begin();
528+
ASSERT_NE(I, C.end());
529+
static_assert(std::is_same_v<decltype((*I)), int>);
530+
auto V = *I;
531+
ASSERT_EQ(V, 0);
532+
}
533+
534+
TEST(STLExtrasTest, ReferenceReturn) {
535+
RangeWithValueType<const int&> R(1);
536+
auto C = concat<const int>(R, R);
537+
auto I = C.begin();
538+
ASSERT_NE(I, C.end());
539+
static_assert(std::is_same_v<decltype((*I)), const int &>);
540+
auto V = *I;
541+
ASSERT_EQ(V, 0);
542+
}
543+
507544
TEST(STLExtrasTest, PartitionAdaptor) {
508545
std::vector<int> V = {1, 2, 3, 4, 5, 6, 7, 8};
509546

0 commit comments

Comments
 (0)