Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Can you update the description with information on what this is fixing?
I am not sure how to phrase this, the first error was:
/llvm/include/llvm/ADT/iterator.h:299:41: error: binding reference of type 'result_pair<...>' to value of type 'const result_pair<...>' drops 'const' qualifier ReferenceT operator*() const { return *I; } ^~ llvm/unittests/ADT/IteratorTest.cpp:186:26: note: in instantiation of member function 'llvm::iterator_adaptor_base<llvm::filter_iterator_base<llvm::detail::enumerator_iter<int (&)[7]>, (lambda at llvm/unittests/ADT/IteratorTest.cpp:182:16), std::forward_iterator_tag>, llvm::detail::enumerator_iter<int (&)[7]>, std::forward_iterator_tag, llvm::detail::result_pair<int (&)[7]>, long, int *, llvm::detail::result_pair<int (&)[7]> &>::operator*' requested here for (auto IndexedValue : make_filter_range(Enumerate, IsOdd)) ^
The fix in STLExtra is for:
llvm/unittests/ADT/IteratorTest.cpp:187:34: error: member reference base type 'int' is not a structure or union Actual.push_back(IndexedValue.value()); ~~~~~~~~~~~~^~~~~~ In file included from llvm/unittests/ADT/IteratorTest.cpp:10: llvm/include/llvm/ADT/iterator.h:300:35: error: non-const lvalue reference to type 'int' cannot bind to a value of unrelated type 'llvm::detail::enumerator_iter<int (&)[7]>::result_type' (aka 'result_pair<int (&)[7]>') ReferenceT operator*() { return *I; } ^~ llvm/unittests/ADT/IteratorTest.cpp:186:26: note: in instantiation of member function 'llvm::iterator_adaptor_base<llvm::filter_iterator_base<llvm::detail::enumerator_iter<int (&)[7]>, (lambda at llvm/unittests/ADT/IteratorTest.cpp:182:16), std::forward_iterator_tag>, llvm::detail::enumerator_iter<int (&)[7]>, std::forward_iterator_tag, llvm::detail::result_pair<int (&)[7]>, long, int *, int &>::operator*' requested here for (auto IndexedValue : make_filter_range(Enumerate, IsOdd)) ^
It was more of a passing comment, but something as simple as:
* Properly specify reference type in enumerator_iter * Fix constness of iterator_adaptor_base::operator*
This doesn't look correct to me.
Here are two examples that I think this patch breaks:
// Setup: simple wrappers around `int *` and `const int *`. struct const_iterator : public iterator_adaptor_base<const_iterator, const int *> {}; struct iterator : public iterator_adaptor_base<iterator, int *> {}; // `const`-qualified iterators (wrapping `int *const` and `const int *const`). const int &ConstRef = *std::declval<const const_iterator &>(); // (example #1) int &Ref = *std::declval<const iterator &>(); // (example #2)
The const-qualification on iterator_adaptor_base should not affect the reference value:
- non-const iterator: int * (dereferencing returns int &)
- non-const iterator with const: int *const (dereferencing returns int &)
- const iterator: const int * (dereferencing returns const int &)
- const iterator with const: const int *const (dereferencing returns const int &)
I think I found the actual bug; see my inline comments.
llvm/include/llvm/ADT/STLExtras.h | ||
---|---|---|
1920–1921 | This is the actual bug. The const-ness of the iterator shouldn't affect the const-ness of the returned value. The non-const operator*() should be deleted and only a const result_type& should ever be returned. The caller should never be allowed to modify Result.Iter or Result.Index. | |
llvm/include/llvm/ADT/iterator.h | ||
299–300 | If you used std::add_const_t<ReferenceT> then I think (1) would start compiling again but (2) would still fail. I think this code change would "fix" the problem you encountered: ReferenceT operator*() const { return *const_cast<WrappedIteratorT &>(I); } ... but I think the actual bug is in the WrappedIteratorT. |
I was wrong that these would fail, but the reason seems a bit subtle: it appears that const ReferenceT is the same as int& when ReferenceT is int&.
I didn't see the revert here, and wrote https://reviews.llvm.org/D113158 to go on top. There were a few other problems to work through. Happy to rebase that if it makes sense.
This is the actual bug. The const-ness of the iterator shouldn't affect the const-ness of the returned value.
The non-const operator*() should be deleted and only a const result_type& should ever be returned. The caller should never be allowed to modify Result.Iter or Result.Index.