Skip to content

Commit 6778426

Browse files
committed
Implement LWG-4074
1 parent 5714983 commit 6778426

File tree

7 files changed

+174
-54
lines changed

7 files changed

+174
-54
lines changed

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"`LWG4060 <https://wg21.link/LWG4060>`__","``submdspan`` preconditions do not forbid creating invalid pointer","2024-06 (St. Louis)","","",""
6767
"`LWG4061 <https://wg21.link/LWG4061>`__","Should ``std::basic_format_context`` be default-constructible/copyable/movable?","2024-06 (St. Louis)","|Complete|","19",""
6868
"`LWG4071 <https://wg21.link/LWG4071>`__","``reference_wrapper`` comparisons are not SFINAE-friendly","2024-06 (St. Louis)","|Complete|","19",""
69-
"`LWG4074 <https://wg21.link/LWG4074>`__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","","",""
69+
"`LWG4074 <https://wg21.link/LWG4074>`__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","|Complete|","20",""
7070
"`LWG4076 <https://wg21.link/LWG4076>`__","``concat_view`` should be freestanding","2024-06 (St. Louis)","","",""
7171
"`LWG4079 <https://wg21.link/LWG4079>`__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","","",""
7272
"`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","","",""

libcxx/include/__ranges/concepts.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#ifndef _LIBCPP___RANGES_CONCEPTS_H
1111
#define _LIBCPP___RANGES_CONCEPTS_H
1212

13+
#include <__concepts/common_reference_with.h>
1314
#include <__concepts/constructible.h>
15+
#include <__concepts/convertible_to.h>
1416
#include <__concepts/movable.h>
1517
#include <__concepts/same_as.h>
1618
#include <__config>
@@ -25,6 +27,8 @@
2527
#include <__ranges/enable_view.h>
2628
#include <__ranges/size.h>
2729
#include <__type_traits/add_pointer.h>
30+
#include <__type_traits/common_reference.h>
31+
#include <__type_traits/common_type.h>
2832
#include <__type_traits/is_reference.h>
2933
#include <__type_traits/remove_cvref.h>
3034
#include <__type_traits/remove_reference.h>
@@ -133,6 +137,42 @@ concept viewable_range =
133137
(is_lvalue_reference_v<_Tp> ||
134138
(movable<remove_reference_t<_Tp>> && !__is_std_initializer_list<remove_cvref_t<_Tp>>))));
135139

140+
# if _LIBCPP_STD_VER >= 23
141+
142+
template <class... _Rs>
143+
using __concat_reference_t = common_reference_t<range_reference_t<_Rs>...>;
144+
145+
template <class... _Rs>
146+
using __concat_value_t = common_type_t<range_value_t<_Rs>...>;
147+
148+
template <class... _Rs>
149+
using __concat_rvalue_reference_t = common_reference_t<range_rvalue_reference_t<_Rs>...>;
150+
151+
template <class _Ref, class _RRef, class _It>
152+
concept __concat_indirectly_readable_impl = requires(const _It __it) {
153+
{ *__it } -> convertible_to<_Ref>;
154+
{ ranges::iter_move(__it) } -> convertible_to<_RRef>;
155+
};
156+
157+
template <class... _Rs>
158+
concept __concat_indirectly_readable =
159+
common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&> &&
160+
common_reference_with<__concat_reference_t<_Rs...>&&, __concat_rvalue_reference_t<_Rs...>&&> &&
161+
common_reference_with<__concat_rvalue_reference_t<_Rs...>&&, const __concat_value_t<_Rs...>&> &&
162+
(__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>,
163+
__concat_rvalue_reference_t<_Rs...>,
164+
iterator_t<_Rs>> &&
165+
...);
166+
167+
template <class... _Rs>
168+
concept __concatable = requires {
169+
typename __concat_reference_t<_Rs...>;
170+
typename __concat_value_t<_Rs...>;
171+
typename __concat_rvalue_reference_t<_Rs...>;
172+
} && __concat_indirectly_readable<_Rs...>;
173+
174+
# endif // _LIBCPP_STD_VER >= 23
175+
136176
} // namespace ranges
137177

138178
#endif // _LIBCPP_STD_VER >= 20

libcxx/include/__ranges/join_with_view.h

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
5454
#if _LIBCPP_STD_VER >= 23
5555

5656
namespace ranges {
57-
template <class _Range, class _Pattern>
58-
concept __compatible_joinable_ranges =
59-
common_with<range_value_t<_Range>, range_value_t<_Pattern>> &&
60-
common_reference_with<range_reference_t<_Range>, range_reference_t<_Pattern>> &&
61-
common_reference_with<range_rvalue_reference_t<_Range>, range_rvalue_reference_t<_Pattern>>;
62-
6357
template <class _Range>
6458
concept __bidirectional_common = bidirectional_range<_Range> && common_range<_Range>;
6559

6660
template <input_range _View, forward_range _Pattern>
6761
requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
68-
__compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
62+
__concatable<range_reference_t<_View>, _Pattern>
6963
class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
7064
using _InnerRng = range_reference_t<_View>;
7165

@@ -121,7 +115,8 @@ class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
121115

122116
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
123117
requires forward_range<const _View> && forward_range<const _Pattern> &&
124-
is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
118+
is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>> &&
119+
__concatable<range_reference_t<const _View>, const _Pattern>
125120
{
126121
return __iterator<true>{*this, ranges::begin(__base_)};
127122
}
@@ -137,7 +132,8 @@ class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
137132

138133
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
139134
requires forward_range<const _View> && forward_range<const _Pattern> &&
140-
is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
135+
is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>> &&
136+
__concatable<range_reference_t<const _View>, const _Pattern>
141137
{
142138
using _InnerConstRng = range_reference_t<const _View>;
143139
if constexpr (forward_range<_InnerConstRng> && common_range<const _View> && common_range<_InnerConstRng>)
@@ -187,7 +183,7 @@ struct __join_with_view_iterator_category<_Base, _PatternBase, _InnerBase> {
187183

188184
template <input_range _View, forward_range _Pattern>
189185
requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
190-
__compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
186+
__concatable<range_reference_t<_View>, _Pattern>
191187
template <bool _Const>
192188
struct join_with_view<_View, _Pattern>::__iterator
193189
: public __join_with_view_iterator_category<__maybe_const<_Const, _View>, __maybe_const<_Const, _Pattern>> {
@@ -395,7 +391,7 @@ struct join_with_view<_View, _Pattern>::__iterator
395391

396392
template <input_range _View, forward_range _Pattern>
397393
requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
398-
__compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
394+
__concatable<range_reference_t<_View>, _Pattern>
399395
template <bool _Const>
400396
struct join_with_view<_View, _Pattern>::__sentinel {
401397
private:

libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ constexpr void test_const_begin() {
240240
using JWV = std::ranges::join_with_view<MaybeSimpleForwardView<true>, MaybeSimplePattern<true>>;
241241
static_assert(std::same_as<std::ranges::iterator_t<JWV&>, std::ranges::iterator_t<const JWV&>>);
242242
}
243+
244+
{ // Check LWG-4074: compatible-joinable-ranges is underconstrained
245+
static_assert(!JoinWithViewHasConstBegin<BasicVectorView<int, ViewProperties{}, forward_iterator>,
246+
lwg4074::PatternWithProxyConstAccess>);
247+
}
243248
}
244249

245250
constexpr bool test() {

libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <vector>
2222

2323
#include "test_iterators.h"
24+
#include "../types.h"
2425

2526
template <class View, class Pattern>
2627
concept CanFormJoinWithView = requires { typename std::ranges::join_with_view<View, Pattern>; };
@@ -133,7 +134,7 @@ static_assert(!CanFormJoinWithView<View, Pattern>);
133134

134135
// join_with_view is not valid when `range_reference_t<View>` and pattern
135136
// does not model together compatible-joinable-ranges
136-
namespace test_when_used_ranges_are_not_compatible_joinable_ranges {
137+
namespace test_when_used_ranges_are_not_concatable {
137138
using std::ranges::range_reference_t;
138139
using std::ranges::range_rvalue_reference_t;
139140
using std::ranges::range_value_t;
@@ -144,15 +145,15 @@ struct View : std::ranges::view_base {
144145
InnerRange* end();
145146
};
146147

147-
namespace no_common_range_value_type {
148+
namespace no_concat_reference_t {
149+
struct ValueType {};
150+
148151
struct InnerRange {
149152
struct It {
150-
using difference_type = ptrdiff_t;
151-
struct value_type {};
152-
153+
using difference_type = std::ptrdiff_t;
154+
using value_type = ValueType;
153155
struct reference {
154156
operator value_type();
155-
operator float();
156157
};
157158

158159
It& operator++();
@@ -165,28 +166,42 @@ struct InnerRange {
165166
};
166167

167168
struct Pattern : std::ranges::view_base {
168-
const float* begin();
169-
const float* end();
169+
struct It {
170+
using difference_type = std::ptrdiff_t;
171+
using value_type = ValueType;
172+
struct reference {
173+
operator value_type();
174+
};
175+
176+
It& operator++();
177+
It operator++(int);
178+
reference operator*() const;
179+
bool operator==(const It&) const;
180+
friend value_type&& iter_move(const It&);
181+
};
182+
183+
It begin();
184+
It end();
170185
};
171186

172187
static_assert(std::ranges::input_range<InnerRange>);
173188
static_assert(std::ranges::forward_range<Pattern>);
174189
static_assert(std::ranges::view<Pattern>);
175-
static_assert(!std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
176-
static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
190+
static_assert(!std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
191+
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
177192
static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
178193
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
179-
} // namespace no_common_range_value_type
180-
181-
namespace no_common_range_reference_type {
182-
struct ValueType {};
194+
} // namespace no_concat_reference_t
183195

196+
namespace no_concat_value_t {
184197
struct InnerRange {
185198
struct It {
186-
using difference_type = ptrdiff_t;
187-
using value_type = ValueType;
199+
using difference_type = std::ptrdiff_t;
200+
struct value_type {};
201+
188202
struct reference {
189203
operator value_type();
204+
operator float();
190205
};
191206

192207
It& operator++();
@@ -199,34 +214,20 @@ struct InnerRange {
199214
};
200215

201216
struct Pattern : std::ranges::view_base {
202-
struct It {
203-
using difference_type = ptrdiff_t;
204-
using value_type = ValueType;
205-
struct reference {
206-
operator value_type();
207-
};
208-
209-
It& operator++();
210-
It operator++(int);
211-
reference operator*() const;
212-
bool operator==(const It&) const;
213-
friend value_type&& iter_move(const It&);
214-
};
215-
216-
It begin();
217-
It end();
217+
const float* begin();
218+
const float* end();
218219
};
219220

220221
static_assert(std::ranges::input_range<InnerRange>);
221222
static_assert(std::ranges::forward_range<Pattern>);
222223
static_assert(std::ranges::view<Pattern>);
223-
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
224-
static_assert(!std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
224+
static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
225+
static_assert(!std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
225226
static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
226227
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
227-
} // namespace no_common_range_reference_type
228+
} // namespace no_concat_value_t
228229

229-
namespace no_common_range_rvalue_reference_type {
230+
namespace no_concat_rvalue_reference_t {
230231
struct InnerRange {
231232
using It = cpp20_input_iterator<int*>;
232233
It begin();
@@ -235,7 +236,7 @@ struct InnerRange {
235236

236237
struct Pattern : std::ranges::view_base {
237238
struct It {
238-
using difference_type = ptrdiff_t;
239+
using difference_type = std::ptrdiff_t;
239240
struct value_type {
240241
operator int() const;
241242
};
@@ -258,9 +259,31 @@ struct Pattern : std::ranges::view_base {
258259
static_assert(std::ranges::input_range<InnerRange>);
259260
static_assert(std::ranges::forward_range<Pattern>);
260261
static_assert(std::ranges::view<Pattern>);
261-
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
262262
static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
263+
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
263264
static_assert(!std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
264265
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
265-
} // namespace no_common_range_rvalue_reference_type
266-
} // namespace test_when_used_ranges_are_not_compatible_joinable_ranges
266+
} // namespace no_concat_rvalue_reference_t
267+
268+
namespace not_concat_indirectly_readable { // Required after LWG-4074 ("compatible-joinable-ranges is underconstrained")
269+
struct InnerRange {
270+
using It = cpp20_input_iterator<int*>;
271+
It begin();
272+
sentinel_wrapper<It> end();
273+
};
274+
275+
struct Pattern : std::ranges::view_base {
276+
lwg4074::Iter begin();
277+
lwg4074::Iter end();
278+
};
279+
280+
static_assert(std::ranges::input_range<InnerRange>);
281+
static_assert(std::ranges::forward_range<Pattern>);
282+
static_assert(std::ranges::view<Pattern>);
283+
static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
284+
static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
285+
static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
286+
LIBCPP_STATIC_ASSERT(!std::ranges::__concat_indirectly_readable<InnerRange, Pattern>);
287+
static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
288+
} // namespace not_concat_indirectly_readable
289+
} // namespace test_when_used_ranges_are_not_concatable

libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ constexpr bool test() {
233233
static_assert(std::same_as<Sentinel, ConstSentinel>);
234234
}
235235

236+
// Check LWG-4074: compatible-joinable-ranges is underconstrained
237+
static_assert(!JoinWithViewHasConstEnd<BasicVectorView<int, ViewProperties{}, forward_iterator>,
238+
lwg4074::PatternWithProxyConstAccess>);
239+
236240
return true;
237241
}
238242

0 commit comments

Comments
 (0)