Skip to content

Commit 33c940e

Browse files
committed
fix: constrain uses of nontype values as constants
1 parent bdb0987 commit 33c940e

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

include/std23/function_ref.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,10 @@ class function_ref<Sig, R(Args...)> // freestanding
145145

146146
template<auto f>
147147
constexpr function_ref(nontype_t<f>) noexcept
148-
requires is_invocable_using<decltype(f)>
148+
requires is_invocable_using<decltype((f))>
149149
: fptr_(
150-
[](storage, _param_t<Args>... args) noexcept(noex) -> R {
150+
[](storage, _param_t<Args>... args) noexcept(noex) -> R
151+
{
151152
return std23::invoke_r<R>(
152153
f, static_cast<decltype(args)>(args)...);
153154
})
@@ -160,7 +161,7 @@ class function_ref<Sig, R(Args...)> // freestanding
160161
template<auto f, class U, class T = std::remove_reference_t<U>>
161162
constexpr function_ref(nontype_t<f>, U &&obj) noexcept
162163
requires(not std::is_rvalue_reference_v<U &&> and
163-
is_invocable_using<decltype(f), cvref<T>>)
164+
is_invocable_using<decltype((f)), cvref<T>>)
164165
: fptr_(
165166
[](storage this_, _param_t<Args>... args) noexcept(noex) -> R
166167
{
@@ -177,7 +178,7 @@ class function_ref<Sig, R(Args...)> // freestanding
177178

178179
template<auto f, class T>
179180
constexpr function_ref(nontype_t<f>, cv<T> *obj) noexcept
180-
requires is_invocable_using<decltype(f), decltype(obj)>
181+
requires is_invocable_using<decltype((f)), decltype(obj)>
181182
: fptr_(
182183
[](storage this_, _param_t<Args>... args) noexcept(noex) -> R
183184
{

tests/function_ref/test_nontype.cpp

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,17 @@ suite nttp_callable = []
4848
expect(call({nontype<&A::data>, &a}) == 99_i);
4949
};
5050

51-
when("binding free function to object") = [&] {
52-
expect(call({nontype<h>, a}) == free_function);
53-
};
51+
when("binding free function to object") = [&]
52+
{ expect(call({nontype<h>, a}) == free_function); };
5453

55-
when("binding closure to object") = [&] {
54+
when("binding closure to object") = [&]
55+
{
5656
expect(call({nontype<[](A p) { return BODYN(p.data); }>, a}) ==
5757
99_i);
5858
};
5959

60-
when("binding closure to pointer") = [&] {
60+
when("binding closure to pointer") = [&]
61+
{
6162
expect(call({nontype<[](A *p) { return BODYN(p->data); }>,
6263
&a}) == 99_i);
6364
};
@@ -71,3 +72,35 @@ suite nttp_callable = []
7172
};
7273
};
7374
};
75+
76+
struct C_mut
77+
{
78+
int operator()() { return BODYN(non_const); }
79+
};
80+
81+
struct C_mut_A
82+
{
83+
int operator()(A const &) { return 0; }
84+
};
85+
86+
struct C_mut_pA
87+
{
88+
int operator()(A *) { return 0; }
89+
};
90+
91+
using T = function_ref<int()>;
92+
93+
static_assert(std::is_constructible_v<T, nontype_t<C{}>>,
94+
"nontype callable may have operator() overloaded on const");
95+
static_assert(not std::is_constructible_v<T, nontype_t<C_mut{}>>,
96+
"nontype callable is called as a constant");
97+
98+
static_assert(
99+
std::is_constructible_v<T, nontype_t<[](A const &) { return 0; }>, A &>);
100+
static_assert(not std::is_constructible_v<T, nontype_t<C_mut_A{}>, A &>,
101+
"nontype callable cannot potentially mutate its own state");
102+
static_assert(
103+
std::is_constructible_v<T, nontype_t<[](A *) { return 0; }>, A *>);
104+
static_assert(
105+
not std::is_constructible_v<T, nontype_t<C_mut_pA{}>, A *>,
106+
"nontype callable cannot potentially mutate its own state (pointer form)");

0 commit comments

Comments
 (0)