-
Notifications
You must be signed in to change notification settings - Fork 14.2k
[ADT] Fix llvm::concat_iterator for ValueT == common_base_class *
#144744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[ADT] Fix llvm::concat_iterator for ValueT == common_base_class *
#144744
Conversation
@llvm/pr-subscribers-llvm-adt Author: Javier Lopez-Gomez (jalopezg-git) ChangesFix In particular, the case below was not working before this patch, but I see no particular reason why it shouldn't be supported.
Full diff: https://github.com/llvm/llvm-project/pull/144744.diff 2 Files Affected:
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index eea06cfb99ba2..951da522a8aa2 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -1030,14 +1030,15 @@ class concat_iterator
std::forward_iterator_tag, ValueT> {
using BaseT = typename concat_iterator::iterator_facade_base;
- static constexpr bool ReturnsByValue =
- !(std::is_reference_v<decltype(*std::declval<IterTs>())> && ...);
+ static constexpr bool ReturnsValueOrPointer =
+ !(std::is_reference_v<decltype(*std::declval<IterTs>())> && ...)
+ || (std::is_pointer_v<IterTs> && ...);
using reference_type =
- typename std::conditional_t<ReturnsByValue, ValueT, ValueT &>;
+ typename std::conditional_t<ReturnsValueOrPointer, ValueT, ValueT &>;
using handle_type =
- typename std::conditional_t<ReturnsByValue, std::optional<ValueT>,
+ typename std::conditional_t<ReturnsValueOrPointer, std::optional<ValueT>,
ValueT *>;
/// We store both the current and end iterators for each concatenated
@@ -1088,7 +1089,7 @@ class concat_iterator
if (Begin == End)
return {};
- if constexpr (ReturnsByValue)
+ if constexpr (ReturnsValueOrPointer)
return *Begin;
else
return &*Begin;
diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp
index 286cfa745fd14..0e6b040a08f4a 100644
--- a/llvm/unittests/ADT/STLExtrasTest.cpp
+++ b/llvm/unittests/ADT/STLExtrasTest.cpp
@@ -398,6 +398,9 @@ struct some_struct {
std::string swap_val;
};
+struct derives_from_some_struct : some_struct {
+};
+
std::vector<int>::const_iterator begin(const some_struct &s) {
return s.data.begin();
}
@@ -532,6 +535,19 @@ TEST(STLExtrasTest, ConcatRangeADL) {
EXPECT_THAT(concat<const int>(S0, S1), ElementsAre(1, 2, 3, 4));
}
+TEST(STLExtrasTest, ConcatRangePtrToDerivedClass) {
+ auto S0 = std::make_unique<some_namespace::some_struct>();
+ auto S1 = std::make_unique<some_namespace::derives_from_some_struct>();
+ SmallVector<some_namespace::some_struct *> V0{S0.get()};
+ SmallVector<some_namespace::derives_from_some_struct *> V1{S1.get(), S1.get()};
+
+ // Use concat over ranges of pointers to different (but related) types.
+ EXPECT_THAT(concat<some_namespace::some_struct *>(V0, V1),
+ ElementsAre(S0.get(),
+ static_cast<some_namespace::some_struct *>(S1.get()),
+ static_cast<some_namespace::some_struct *>(S1.get())));
+}
+
TEST(STLExtrasTest, MakeFirstSecondRangeADL) {
// Make sure that we use the `begin`/`end` functions from `some_namespace`,
// using ADL.
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
ebdc2a3
to
560fccc
Compare
3d31e30
to
74331ed
Compare
74331ed
to
ca4ae15
Compare
ca4ae15
to
55726cd
Compare
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.
55726cd
to
6af2cf5
Compare
static constexpr bool ReturnsConvertiblePointer = | ||
std::is_pointer_v<ValueT> && | ||
(std::is_convertible_v<decltype(*std::declval<IterTs>()), ValueT> && ...); | ||
|
||
using reference_type = | ||
typename std::conditional_t<ReturnsByValue, ValueT, ValueT &>; | ||
typename std::conditional_t<ReturnsByValue || ReturnsConvertiblePointer, | ||
ValueT, ValueT &>; | ||
|
||
using handle_type = | ||
typename std::conditional_t<ReturnsByValue, std::optional<ValueT>, | ||
ValueT *>; | ||
using handle_type = typename std::conditional_t< | ||
ReturnsConvertiblePointer, ValueT, | ||
std::conditional_t<ReturnsByValue, std::optional<ValueT>, ValueT *>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is getting complicated with the need for a nested condition -- could we move this to a new type trait struct or find some other way to simplify the code?
Fix
llvm::concat_iterator
for the case ofValueT
being a pointer to a common base class to which the result of dereferencing any iterator inItersT
can be casted to. In particular, the case below was not working before this patch, but I see no particular reason why it shouldn't be supported.Per https://en.cppreference.com/w/cpp/language/implicit_conversion.html, Sec. Pointer conversions,
I.e. conversion shall be possible, but the former implementation of
llvm::concat_iterator
forbids that, given thathandle_type
is pointer-to-pointer type, which is not convertible.