Skip to content

Commit a447396

Browse files
var-consttstellar
authored andcommitted
[libc++] Fix a hard error in contiguous_iterator<NoOperatorArrowIter>.
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. Differential Revision: https://reviews.llvm.org/D130835 (cherry picked from commit 52d4c50)
1 parent 80f03ec commit a447396

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

libcxx/include/__memory/pointer_traits.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,30 @@ _Tp* __to_address(_Tp* __p) _NOEXCEPT {
171171
return __p;
172172
}
173173

174+
template <class _Pointer, class = void>
175+
struct _HasToAddress : false_type {};
176+
177+
template <class _Pointer>
178+
struct _HasToAddress<_Pointer,
179+
decltype((void)pointer_traits<_Pointer>::to_address(declval<const _Pointer&>()))
180+
> : true_type {};
181+
182+
template <class _Pointer, class = void>
183+
struct _HasArrow : false_type {};
184+
185+
template <class _Pointer>
186+
struct _HasArrow<_Pointer,
187+
decltype((void)declval<const _Pointer&>().operator->())
188+
> : true_type {};
189+
190+
template <class _Pointer>
191+
struct _IsFancyPointer {
192+
static const bool value = _HasArrow<_Pointer>::value || _HasToAddress<_Pointer>::value;
193+
};
194+
174195
// enable_if is needed here to avoid instantiating checks for fancy pointers on raw pointers
175196
template <class _Pointer, class = __enable_if_t<
176-
!is_pointer<_Pointer>::value && !is_array<_Pointer>::value && !is_function<_Pointer>::value
197+
_And<is_class<_Pointer>, _IsFancyPointer<_Pointer> >::value
177198
> >
178199
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
179200
typename decay<decltype(__to_address_helper<_Pointer>::__call(declval<const _Pointer&>()))>::type
@@ -208,7 +229,7 @@ auto to_address(_Tp *__p) noexcept {
208229

209230
template <class _Pointer>
210231
inline _LIBCPP_INLINE_VISIBILITY constexpr
211-
auto to_address(const _Pointer& __p) noexcept {
232+
auto to_address(const _Pointer& __p) noexcept -> decltype(std::__to_address(__p)) {
212233
return _VSTD::__to_address(__p);
213234
}
214235
#endif

libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.random.access/contiguous_iterator.compile.pass.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <iterator>
1515
#include <compare>
16+
#include <memory>
1617

1718
#include "test_iterators.h"
1819

@@ -208,3 +209,47 @@ struct template_and_no_element_type {
208209
// Template param is used instead of element_type.
209210
static_assert(std::random_access_iterator<template_and_no_element_type<int>>);
210211
static_assert(std::contiguous_iterator<template_and_no_element_type<int>>);
212+
213+
template <bool DisableArrow, bool DisableToAddress>
214+
struct no_operator_arrow {
215+
typedef std::contiguous_iterator_tag iterator_category;
216+
typedef int value_type;
217+
typedef int element_type;
218+
typedef std::ptrdiff_t difference_type;
219+
typedef int* pointer;
220+
typedef int& reference;
221+
typedef no_operator_arrow self;
222+
223+
no_operator_arrow();
224+
225+
reference operator*() const;
226+
pointer operator->() const requires (!DisableArrow);
227+
auto operator<=>(const self&) const = default;
228+
229+
self& operator++();
230+
self operator++(int);
231+
232+
self& operator--();
233+
self operator--(int);
234+
235+
self& operator+=(difference_type n);
236+
self operator+(difference_type n) const;
237+
// Note: it's a template function to prevent a GCC warning ("friend declaration declares a non-template function").
238+
template <bool B1, bool B2>
239+
friend no_operator_arrow<B1, B2> operator+(difference_type n, no_operator_arrow<B1, B2> x);
240+
241+
self& operator-=(difference_type n);
242+
self operator-(difference_type n) const;
243+
difference_type operator-(const self& n) const;
244+
245+
reference operator[](difference_type n) const;
246+
};
247+
248+
template<>
249+
struct std::pointer_traits<no_operator_arrow</*DisableArrow=*/true, /*DisableToAddress=*/false>> {
250+
static constexpr int *to_address(const no_operator_arrow<true, false>&);
251+
};
252+
253+
static_assert(std::contiguous_iterator<no_operator_arrow</*DisableArrow=*/false, /*DisableToAddress=*/true>>);
254+
static_assert(!std::contiguous_iterator<no_operator_arrow</*DisableArrow=*/true, /*DisableToAddress=*/true>>);
255+
static_assert(std::contiguous_iterator<no_operator_arrow</*DisableArrow=*/true, /*DisableToAddress=*/false>>);

0 commit comments

Comments
 (0)