Skip to content

Commit 827ed79

Browse files
committed
Enable UFCS in initializers of global variables, closes #314
The `[&]` introducer is required for the UFCS lambda in case it's invoked in a deeply nested context, but then the `[&]` is illegal when it's outside a function (initializing a namespace scope object)
1 parent 5cc3326 commit 827ed79

File tree

5 files changed

+79
-3
lines changed

5 files changed

+79
-3
lines changed

include/cpp2util.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,45 @@ class out {
672672
}(PARAM1)
673673

674674

675+
// But for non-local lambdas [&] is not allowed
676+
677+
#define CPP2_UFCS_NONLOCAL(FUNCNAME,PARAM1,...) \
678+
[](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
679+
if constexpr (requires{ std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); }) { \
680+
return std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); \
681+
} else { \
682+
return FUNCNAME(std::forward<decltype(obj)>(obj), std::forward<decltype(params)>(params)...); \
683+
} \
684+
}(PARAM1, __VA_ARGS__)
685+
686+
#define CPP2_UFCS_0_NONLOCAL(FUNCNAME,PARAM1) \
687+
[](auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
688+
if constexpr (requires{ std::forward<decltype(obj)>(obj).FUNCNAME(); }) { \
689+
return std::forward<decltype(obj)>(obj).FUNCNAME(); \
690+
} else { \
691+
return FUNCNAME(std::forward<decltype(obj)>(obj)); \
692+
} \
693+
}(PARAM1)
694+
695+
#define CPP2_UFCS_TEMPLATE_NONLOCAL(FUNCNAME,TEMPARGS,PARAM1,...) \
696+
[](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
697+
if constexpr (requires{ std::forward<decltype(obj)>(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (std::forward<decltype(params)>(params)...); }) { \
698+
return std::forward<decltype(obj)>(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (std::forward<decltype(params)>(params)...); \
699+
} else { \
700+
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (std::forward<decltype(obj)>(obj), std::forward<decltype(params)>(params)...); \
701+
} \
702+
}(PARAM1, __VA_ARGS__)
703+
704+
#define CPP2_UFCS_TEMPLATE_0_NONLOCAL(FUNCNAME,TEMPARGS,PARAM1) \
705+
[](auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
706+
if constexpr (requires{ std::forward<decltype(obj)>(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); }) { \
707+
return std::forward<decltype(obj)>(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); \
708+
} else { \
709+
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (std::forward<decltype(obj)>(obj)); \
710+
} \
711+
}(PARAM1)
712+
713+
675714
//-----------------------------------------------------------------------
676715
//
677716
// is and as

regression-tests/pure2-ufcs-member-access-and-chaining.cpp2

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,8 @@ fun: () -> (i:int) = {
3030

3131
get_i: (r:_) -> int = {
3232
return r.i;
33-
}
33+
}
34+
35+
// And a test for non-local UFCS, which shouldn't do a [&] capture
36+
f: (x)->int = 0;
37+
y: int = 0.f();

regression-tests/test-results/pure2-ufcs-member-access-and-chaining.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ struct fun__ret {
2424
#line 31 "pure2-ufcs-member-access-and-chaining.cpp2"
2525
[[nodiscard]] auto get_i(auto const& r) -> int;
2626

27+
#line 35 "pure2-ufcs-member-access-and-chaining.cpp2"
28+
// And a test for non-local UFCS, which shouldn't do a [&] capture
29+
[[nodiscard]] auto f(auto const& x) -> int;
30+
extern int y;
2731
//=== Cpp2 function definitions =================================================
2832

2933
#line 1 "pure2-ufcs-member-access-and-chaining.cpp2"
@@ -63,3 +67,7 @@ struct fun__ret {
6367
return r.i;
6468
}
6569

70+
#line 36 "pure2-ufcs-member-access-and-chaining.cpp2"
71+
[[nodiscard]] auto f(auto const& x) -> int { return 0; }
72+
int y {CPP2_UFCS_0_NONLOCAL(f, 0)};
73+

source/cppfront.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2250,9 +2250,11 @@ class cppfront
22502250
if (
22512251
!current_functions.empty()
22522252
&& current_functions.back().decl->is_function_with_this()
2253+
&& !current_functions.back().decl->parent_is_namespace()
22532254
)
22542255
{
2255-
// Note: & is needed because a nested UFCS might be viewed as trying to capture 'this'
2256+
// Note: & is needed (when allowed, not at namespace scope) because a
2257+
// nested UFCS might be viewed as trying to capture 'this'
22562258
lambda_intro += "&";
22572259
++num_captures;
22582260
}
@@ -2737,8 +2739,11 @@ class cppfront
27372739
{
27382740
auto funcname = print_to_string(*i->id_expr);
27392741

2742+
// First, build the UFCS macro name
2743+
27402744
auto ufcs_string = std::string("CPP2_UFCS");
27412745

2746+
// If there are template arguments, use the _TEMPLATE version
27422747
if (i->id_expr->template_args_count() > 0) {
27432748
ufcs_string += "_TEMPLATE";
27442749
// we need to replace "fun<int,long,double>" to "fun, (<int,long,double>)" to be able to generate
@@ -2748,11 +2753,31 @@ class cppfront
27482753
assert(funcname.back() == '>');
27492754
funcname += ')';
27502755
}
2756+
27512757
// If there are no additional arguments, use the _0 version
27522758
if (args.value().text_chunks.empty()) {
27532759
ufcs_string += "_0";
27542760
}
27552761

2762+
// If we're in an object declaration (i.e., initializer)
2763+
// at namespace scope, use the _NONLOCAL version
2764+
//
2765+
// Note: If there are other cases where code could execute
2766+
// in a non-local scope where a capture-default for the UFCS
2767+
// lambda would not be allowed, then add them here
2768+
if (
2769+
current_declarations.back()->is_namespace()
2770+
|| (
2771+
current_declarations.back()->is_object()
2772+
&& current_declarations.back()->parent_is_namespace()
2773+
)
2774+
)
2775+
{
2776+
ufcs_string += "_NONLOCAL";
2777+
}
2778+
2779+
// Second, emit the UFCS argument list
2780+
27562781
prefix.emplace_back(ufcs_string + "(" + funcname + ", ", args.value().open_pos );
27572782
suffix.emplace_back(")", args.value().close_pos );
27582783
if (!args.value().text_chunks.empty()) {

source/parse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,7 @@ struct declaration_node
17141714
auto parent_is_namespace() const -> bool
17151715
{ return !parent_declaration || parent_declaration->type.index() == a_namespace; }
17161716
auto parent_is_alias () const -> bool
1717-
{ return !parent_declaration || parent_declaration->type.index() == an_alias; }
1717+
{ return parent_declaration && parent_declaration->type.index() == an_alias; }
17181718

17191719
enum which {
17201720
functions = 1,

0 commit comments

Comments
 (0)