Evaluating contiguous_iterator on an iterator that satisfies all the
constraints except the to_address constraint and doesn't have
operator-> defined results in a hard error. This is because
instantiating to_address ends up instantiating templates
dependent on the given type which might lead to a hard error even
in a SFINAE context.
Details
- Reviewers
ldionne huixie90 - Group Reviewers
Restricted Project - Commits
- rG52d4c5016c4f: [libc++] Fix a hard error in `contiguous_iterator<NoOperatorArrowIter>`.
Diff Detail
- Repository
- rG LLVM Github Monorepo
Unit Tests
Event Timeline
libcxx/include/__memory/pointer_traits.h | ||
---|---|---|
232 | I'm not sure this is the right fix, but I can't think of too many alternatives (another one is to add a constraint to contiguous_iterator for the type to have operator-> defined). | |
libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.random.access/contiguous_iterator.compile.pass.cpp | ||
226 | What do you think of this approach? I think it could be useful for other types that follow a similar pattern (*almost* support a certain constraint). |
libcxx/include/__memory/pointer_traits.h | ||
---|---|---|
197 | This is more to start a discussion. Would it be easier to list what the _Pointer could be rather than what it should *not* be? Are there any other alternatives rather than a class that we would accept here? | |
197 | _And is necessary because enable_if doesn't short-circuit, so it would still try to evaluate _IsFancyPointer on e.g. an array, leading to a hard error when trying to instantiate pointer_traits<array-type>. |
This mostly LGTM, but I'd like to see it again just to see how it turns out. Please ping me when my comments have been applied. Thanks and good find!
libcxx/include/__memory/pointer_traits.h | ||
---|---|---|
179 | I think you need to call pointer_traits<_Pointer>::to_address(...), otherwise it could be a function taking no arguments, or an overloaded function (and we can't take the address of those), etc. It could even be a static data member. | |
197 | Here's a refactoring suggestion: template <class _Pointer, class = __enable_if_t< _And<is_class<_Pointer>, _HasToAddress<_Pointer> >::value > > _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR decay_t<decltype(pointer_traits<_Pointer>::to_address(declval<const _Pointer&>()))> __to_address(const _Pointer& __p) _NOEXCEPT { return pointer_traits<_Pointer>::to_address(__p); } template <class _Pointer, class = __enable_if_t< _And<is_class<_Pointer>, _HasArrow<_Pointer>, _Not<_HasToAddress<_Pointer> > >::value > > _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR decltype(std::__to_address(declval<const _Pointer&>().operator->())) __to_address(const _Pointer& __p) _NOEXCEPT { return std::__to_address(__p.operator->()); } Something like that. And then we could remove __to_address_helper altogether -- it feels like it's kind of unnecessary. | |
libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.random.access/contiguous_iterator.compile.pass.cpp | ||
226 | I like it, however I can see how it becomes hard to understand if we add too many bool template parameters. I think the fact that you add C-style comments whenever you instantiate no_operator_arrow kind of covers up for it. | |
252–253 | I guess we should also remove the default values in the template parameter. IMO that's more verbose but also more readable, otherwise I definitely have to re-read the template declaration almost every time I see an instantiation. |
LGTM after Louis's comments are addressed
libcxx/include/__memory/pointer_traits.h | ||
---|---|---|
176 | I think your fix and Louis's suggestion read better. just out of curiosity, does it work with using _Lazy and _And ? | |
libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.random.access/contiguous_iterator.compile.pass.cpp | ||
237–238 | does it work without template <bool B1, bool B2> and just use no_operator_arrow |
libcxx/include/__memory/pointer_traits.h | ||
---|---|---|
179 | Thanks, good point. This is also consistent with the check below in __to_address_helper. | |
197 | Hmm, it looks like the compiler is having trouble with decltype(std::__to_address(declval<const _Pointer&>().operator->())) when the type returned by _Pointer::operator-> is another fancy pointer with operator-> defined -- looks like the recursion is causing the issue. decltype(auto) works but we can't use it because of the C++03 mode. I wonder if that's the reason the helper struct was added in the first place? | |
libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.random.access/contiguous_iterator.compile.pass.cpp | ||
237–238 | This is to prevent a GCC warning ("friend declaration declares a non-template function"). Added a comment. |
clang-format not found in user’s local PATH; not linting file.