Skip to content

Commit 17229ae

Browse files
committed
[ADT] Fix llvm::concat_iterator for ValueT == common_base_class *
Fix llvm::concat_iterator for the case of `ValueT` being a pointer to a common base class to which the result of dereferencing any iterator in `ItersT` can be casted to.
1 parent d46e7f8 commit 17229ae

File tree

2 files changed

+66
-3
lines changed

2 files changed

+66
-3
lines changed

llvm/include/llvm/ADT/STLExtras.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ using is_one_of = std::disjunction<std::is_same<T, Ts>...>;
114114
template <typename T, typename... Ts>
115115
using are_base_of = std::conjunction<std::is_base_of<T, Ts>...>;
116116

117+
/// traits class for checking whether type `T` is same as all other types in
118+
/// `Ts`.
119+
template <typename T = void, typename... Ts>
120+
using all_types_equal = std::conjunction<std::is_same<T, Ts>...>;
121+
template <typename T = void, typename... Ts>
122+
constexpr bool all_types_equal_v = all_types_equal<T, Ts...>::value;
123+
117124
/// Determine if all types in Ts are distinct.
118125
///
119126
/// Useful to statically assert when Ts is intended to describe a non-multi set
@@ -996,9 +1003,17 @@ class concat_iterator
9961003

9971004
static constexpr bool ReturnsByValue =
9981005
!(std::is_reference_v<decltype(*std::declval<IterTs>())> && ...);
999-
1006+
static constexpr bool ReturnsConvertibleType =
1007+
!all_types_equal_v<
1008+
std::remove_cv_t<ValueT>,
1009+
remove_cvref_t<decltype(*std::declval<IterTs>())>...> &&
1010+
(std::is_convertible_v<decltype(*std::declval<IterTs>()), ValueT> && ...);
1011+
1012+
// Cannot return a reference type if a conversion takes place, provided that
1013+
// the result of dereferencing all `IterTs...` is convertible to `ValueT`.
10001014
using reference_type =
1001-
typename std::conditional_t<ReturnsByValue, ValueT, ValueT &>;
1015+
std::conditional_t<ReturnsByValue || ReturnsConvertibleType, ValueT,
1016+
ValueT &>;
10021017

10031018
/// We store both the current and end iterators for each concatenated
10041019
/// sequence in a tuple of pairs.
@@ -1031,7 +1046,7 @@ class concat_iterator
10311046

10321047
/// Dereferences the `Index`-th iterator and returns the resulting reference.
10331048
/// If `Index` is at end, recurse over iterators in `Others...`.
1034-
template <size_t Index, size_t... Others> handle_type getImpl() const {
1049+
template <size_t Index, size_t... Others> reference_type getImpl() const {
10351050
auto &Begin = std::get<Index>(Begins);
10361051
auto &End = std::get<Index>(Ends);
10371052
if (Begin == End) {

llvm/unittests/ADT/STLExtrasTest.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ struct some_struct {
398398
std::string swap_val;
399399
};
400400

401+
struct derives_from_some_struct : some_struct {};
402+
401403
std::vector<int>::const_iterator begin(const some_struct &s) {
402404
return s.data.begin();
403405
}
@@ -500,6 +502,15 @@ TEST(STLExtrasTest, ToVector) {
500502
}
501503
}
502504

505+
TEST(STLExtrasTest, AllTypesEqual) {
506+
static_assert(all_types_equal_v<>);
507+
static_assert(all_types_equal_v<int>);
508+
static_assert(all_types_equal_v<int, int, int>);
509+
510+
static_assert(!all_types_equal_v<int, int, unsigned int>);
511+
static_assert(!all_types_equal_v<int, int, float>);
512+
}
513+
503514
TEST(STLExtrasTest, ConcatRange) {
504515
std::vector<int> Expected = {1, 2, 3, 4, 5, 6, 7, 8};
505516
std::vector<int> Test;
@@ -532,6 +543,43 @@ TEST(STLExtrasTest, ConcatRangeADL) {
532543
EXPECT_THAT(concat<const int>(S0, S1), ElementsAre(1, 2, 3, 4));
533544
}
534545

546+
TEST(STLExtrasTest, ConcatRangePtrToSameClass) {
547+
some_namespace::some_struct S0{};
548+
some_namespace::some_struct S1{};
549+
SmallVector<some_namespace::some_struct *> V0{&S0};
550+
SmallVector<some_namespace::some_struct *> V1{&S1, &S1};
551+
552+
// Dereferencing all iterators yields `some_namespace::some_struct *&`; no
553+
// conversion takes place, `reference_type` is
554+
// `some_namespace::some_struct *&`.
555+
auto C = concat<some_namespace::some_struct *>(V0, V1);
556+
static_assert(
557+
std::is_same_v<decltype(*C.begin()), some_namespace::some_struct *&>);
558+
EXPECT_THAT(C, ElementsAre(&S0, &S1, &S1));
559+
// `reference_type` should still allow container modification.
560+
for (auto &i : C)
561+
if (i == &S0)
562+
i = nullptr;
563+
EXPECT_THAT(C, ElementsAre(nullptr, &S1, &S1));
564+
}
565+
566+
TEST(STLExtrasTest, ConcatRangePtrToDerivedClass) {
567+
some_namespace::some_struct S0{};
568+
some_namespace::derives_from_some_struct S1{};
569+
SmallVector<some_namespace::some_struct *> V0{&S0};
570+
SmallVector<some_namespace::derives_from_some_struct *> V1{&S1, &S1};
571+
572+
// Dereferencing all iterators yields different (but convertible types);
573+
// conversion takes place, `reference_type` is
574+
// `some_namespace::some_struct *`.
575+
auto C = concat<some_namespace::some_struct *>(V0, V1);
576+
static_assert(
577+
std::is_same_v<decltype(*C.begin()), some_namespace::some_struct *>);
578+
EXPECT_THAT(C,
579+
ElementsAre(&S0, static_cast<some_namespace::some_struct *>(&S1),
580+
static_cast<some_namespace::some_struct *>(&S1)));
581+
}
582+
535583
TEST(STLExtrasTest, MakeFirstSecondRangeADL) {
536584
// Make sure that we use the `begin`/`end` functions from `some_namespace`,
537585
// using ADL.

0 commit comments

Comments
 (0)