Skip to content

Commit 23686c5

Browse files
committed
Materialize recursive_wrapper<T> instead of T when possible
1 parent 6e4a46f commit 23686c5

6 files changed

Lines changed: 70 additions & 20 deletions

File tree

include/iris/x4/core/detail/parse_into_container.hpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct parse_into_container_base_impl
4141
{
4242
// Parser has attribute (synthesize; Attribute is a container)
4343
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
44-
requires (!parser_accepts_container_v<Parser, Attr>)
44+
requires (!parser_accepts_container_v<Parser, unwrap_recursive_type<Attr>>)
4545
[[nodiscard]] static constexpr bool
4646
call_synthesize(
4747
Parser const& parser, It& first, Se const& last,
@@ -50,17 +50,18 @@ struct parse_into_container_base_impl
5050
{
5151
static_assert(!std::same_as<std::remove_const_t<Attr>, unused_container_type>);
5252

53-
using value_type = traits::container_value_t<Attr>;
53+
using value_type = traits::container_value_t<unwrap_recursive_type<Attr>>;
5454
value_type val; // default-initialize
5555

56-
static_assert(Parsable<Parser, It, Se, Context, value_type>);
56+
//static_assert(Parsable<Parser, It, Se, Context, value_type>);
5757
if (!parser.parse(first, last, ctx, val)) return false;
5858

5959
// push the parsed value into our attribute
60-
traits::push_back(attr, std::move(val));
60+
traits::push_back(unwrap_recursive(attr), std::move(val));
6161
return true;
6262
}
6363

64+
// unused_container_type
6465
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
6566
requires (!parser_accepts_container_v<Parser, unused_container_type>)
6667
[[nodiscard]] static constexpr bool
@@ -69,23 +70,25 @@ struct parse_into_container_base_impl
6970
Context const& ctx, unused_container_type const&
7071
) noexcept(is_nothrow_parsable_v<Parser, It, Se, Context, unused_type>)
7172
{
72-
static_assert(Parsable<Parser, It, Se, Context, unused_type>);
73+
//static_assert(Parsable<Parser, It, Se, Context, unused_type>);
7374
return parser.parse(first, last, ctx, unused);
7475
}
7576

7677
// Parser has attribute (synthesize; Attribute is a container)
7778
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
78-
requires parser_accepts_container_v<Parser, Attr>
79+
requires parser_accepts_container_v<Parser, unwrap_recursive_type<Attr>>
7980
[[nodiscard]] static constexpr bool
8081
call_synthesize(
8182
Parser const& parser, It& first, Se const& last,
8283
Context const& ctx, Attr& attr
83-
) noexcept(is_nothrow_parsable_v<Parser, It, Se, Context, Attr>)
84+
) noexcept(is_nothrow_parsable_v<Parser, It, Se, Context, unwrap_recursive_type<Attr>>)
8485
{
85-
static_assert(Parsable<Parser, It, Se, Context, Attr>);
86+
//static_assert(Parsable<Parser, It, Se, Context, unwrap_recursive_type<Attr>>);
8687
return parser.parse(first, last, ctx, attr);
8788
}
8889

90+
// ------------------------------------------------------
91+
8992
// Parser has attribute && it is NOT fusion sequence
9093
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
9194
requires

include/iris/x4/core/move_to.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,23 @@ template<traits::NonUnusedAttr T>
4646
constexpr void move_to(T const&& src, T& dest)
4747
noexcept(std::is_nothrow_assignable_v<T&, T const&&>)
4848
{
49+
static_assert(std::is_assignable_v<T&, T const>);
4950
dest = std::move(src);
5051
}
5152

5253
template<traits::NonUnusedAttr T>
5354
constexpr void move_to(T&& src, T& dest)
5455
noexcept(std::is_nothrow_assignable_v<T&, T&&>)
5556
{
57+
static_assert(std::is_assignable_v<T&, T>);
5658
dest = std::forward<T>(src);
5759
}
5860

5961
template<traits::NonUnusedAttr T>
6062
constexpr void move_to(T const& src, T& dest)
6163
noexcept(std::is_nothrow_copy_assignable_v<T>)
6264
{
65+
static_assert(std::is_assignable_v<T&, T const&>);
6366
dest = src;
6467
}
6568

@@ -120,6 +123,7 @@ move_to(Source&& src, Dest& dest)
120123
noexcept(noexcept(dest = std::forward_like<Source>(boost::fusion::front(std::forward<Source>(src)))))
121124
{
122125
static_assert(!std::same_as<std::remove_cvref_t<Source>, Dest>, "[BUG] This call should instead resolve to the overload handling identical types");
126+
// TODO: preliminarily invoke static_assert to check if the assignment is valid
123127
dest = std::forward_like<Source>(boost::fusion::front(std::forward<Source>(src)));
124128
}
125129

@@ -130,7 +134,7 @@ move_to(Source&& src, Dest& dest)
130134
noexcept(std::is_nothrow_assignable_v<Dest&, Source&&>)
131135
{
132136
static_assert(!std::same_as<std::remove_cvref_t<Source>, Dest>, "[BUG] This call should instead resolve to the overload handling identical types");
133-
static_assert(std::is_assignable_v<Dest&, Source&&>);
137+
static_assert(std::is_assignable_v<Dest&, Source>);
134138
dest = std::forward<Source>(src);
135139
}
136140

@@ -148,6 +152,8 @@ move_to(Source&& src, Dest& dest)
148152
{
149153
static_assert(!std::same_as<std::remove_cvref_t<Source>, Dest>, "[BUG] This call should instead resolve to the overload handling identical types");
150154

155+
// TODO: preliminarily invoke static_assert to check if the assignment is valid
156+
151157
if constexpr (std::is_rvalue_reference_v<Source&&>) {
152158
boost::fusion::move(std::move(src), dest);
153159
} else {
@@ -165,6 +171,7 @@ move_to(Source&& src, Dest& dest)
165171

166172
// dest is a variant, src is a single element fusion sequence that the variant
167173
// *can* directly hold.
174+
static_assert(std::is_assignable_v<Dest&, Source>);
168175
dest = std::forward<Source>(src);
169176
}
170177

@@ -185,6 +192,7 @@ move_to(Source&& src, Dest& dest)
185192
"Error! The destination variant (Dest) cannot hold the source type (Source)"
186193
);
187194

195+
// TODO: preliminarily invoke static_assert to check if the assignment is valid
188196
dest = std::forward_like<Source>(boost::fusion::front(std::forward<Source>(src)));
189197
}
190198

@@ -195,6 +203,7 @@ move_to(Source&& src, Dest& dest)
195203
noexcept(std::is_nothrow_assignable_v<Dest&, Source&&>)
196204
{
197205
static_assert(!std::same_as<std::remove_cvref_t<Source>, Dest>, "[BUG] This call should instead resolve to the overload handling identical types");
206+
static_assert(std::is_assignable_v<Dest&, Source>);
198207
dest = std::forward<Source>(src);
199208
}
200209

@@ -204,6 +213,7 @@ move_to(Source&& src, Dest& dest)
204213
noexcept(std::is_nothrow_assignable_v<Dest&, Source&&>)
205214
{
206215
static_assert(!std::same_as<std::remove_cvref_t<Source>, Dest>, "[BUG] This call should instead resolve to the overload handling identical types");
216+
static_assert(std::is_assignable_v<Dest&, Source>);
207217
dest = std::forward<Source>(src);
208218
}
209219

include/iris/x4/traits/variant_traits.hpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,18 @@ template<class Attr, class First, class... Rest>
4747
struct variant_find_substitute_impl<Attr, First, Rest...>
4848
{
4949
using type = std::conditional_t<
50-
is_substitute_v<Attr, iris::unwrap_recursive_t<First>>,
50+
is_substitute_v<Attr, iris::unwrap_recursive_type<First>>,
5151

52-
// TODO
5352
// Given some type `T`, when both `T` and `recursive_wrapper<T>` is seen
5453
// during attribute resolution, X4 should ideally materialize the latter
5554
// because:
5655
// - It means that the user has supplied at least one explicit type
57-
// (possibly a rule attribute type) that is `recursive_wrapper<T>`,
58-
// - Constructing `T` and then moving it to `recursive_wrapper<T>`
56+
// (i.e. exposed attribute type, possibly a rule attribute type) that
57+
// is `recursive_wrapper<T>`, and
58+
// - constructing `T` and then moving it to `recursive_wrapper<T>`
5959
// involves copying from stack to heap.
6060
//
61-
// This is to-do because the above optimization is currently not
62-
// implementable in a straightforward way. We need to add
63-
// `unwrap_recursive(attr)` to every places where any parser attempts
64-
// to modify the content.
65-
iris::unwrap_recursive_t<First>,
61+
First, // no need to unwrap due to the reason described above
6662

6763
typename variant_find_substitute_impl<Attr, Rest...>::type
6864
>;

test/x4/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function(x4_define_test test_name)
1212
iris_define_test(x4_${test_name} ${ARGN})
1313
iris_define_test_headers(x4_${test_name} iris_x4_test.hpp)
1414
target_link_libraries(x4_${test_name}_test PRIVATE Iris::X4)
15+
set_target_properties(x4_${test_name}_test PROPERTIES FOLDER "test/x4")
1516

1617
if(MSVC)
1718
# Prevent "Warning: Conflicting <Type> entries detected" error
@@ -28,7 +29,6 @@ endfunction()
2829
function(x4_define_tests)
2930
foreach(test_name IN LISTS ARGV)
3031
x4_define_test(${test_name} ${test_name}.cpp)
31-
set_target_properties(x4_${test_name}_test PROPERTIES FOLDER "test/x4")
3232
endforeach()
3333
endfunction()
3434

@@ -71,6 +71,7 @@ x4_define_tests(
7171
real1
7272
real2
7373
real3
74+
recursive
7475
repeat
7576
rule1
7677
rule2

test/x4/recursive.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*=============================================================================
2+
Copyright (c) 2026 The Iris Project Contributors
3+
4+
Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
=============================================================================*/
7+
8+
#include "iris_x4_test.hpp"
9+
10+
#include <iris/x4/char/char.hpp>
11+
#include <iris/x4/string/string.hpp>
12+
#include <iris/x4/numeric/int.hpp>
13+
#include <iris/x4/operator/list.hpp>
14+
15+
#include <iris/rvariant/recursive_wrapper.hpp>
16+
17+
#include <vector>
18+
19+
TEST_CASE("recursive")
20+
{
21+
using x4::int_;
22+
23+
{
24+
iris::recursive_wrapper<int> val;
25+
REQUIRE(parse("1", int_, val));
26+
CHECK(*val == 1);
27+
}
28+
29+
{
30+
iris::recursive_wrapper<std::vector<int>> ints;
31+
REQUIRE(parse("0,1", int_ % ',', ints));
32+
CHECK(*ints == std::vector{0, 1});
33+
}
34+
35+
{
36+
iris::recursive_wrapper<std::vector<int>> ints;
37+
REQUIRE(parse("0-1-2,3-4-5", (int_ % '-') % ',', ints));
38+
CHECK(*ints == std::vector{0, 1, 2, 3, 4, 5});
39+
}
40+
}

0 commit comments

Comments
 (0)