Skip to content

Commit 7522b99

Browse files
committed
unignorable colors
1 parent d8f6f55 commit 7522b99

6 files changed

Lines changed: 135 additions & 63 deletions

File tree

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,8 @@
4141
*.dwo
4242

4343
**/CMakeUserPresets.json
44-
**/build/
44+
**/build/
45+
**/metadata/
46+
47+
**/.*cache/
48+
compile_commands.json

CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ install(TARGETS chroma EXPORT chroma-targets)
3030
install(DIRECTORY include/ DESTINATION include)
3131

3232
install(EXPORT chroma-targets
33-
FILE chroma-targets.cmake
34-
DESTINATION lib/cmake/chroma
35-
)
33+
FILE chroma-targets.cmake
34+
DESTINATION lib/cmake/chroma)
35+
36+
option(BUILD_DUMMY "Build dummy" ON)
37+
if (BUILD_DUMMY)
38+
add_executable(chroma-dummy dummy.cpp)
39+
target_link_libraries(chroma-dummy PRIVATE chroma)
40+
endif()

dummy.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <chroma>
2+
3+
int main() {}

include/chroma

Lines changed: 87 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@
77
#include <string_view>
88
#include <vector>
99

10-
template <typename T>
11-
struct callable_from;
12-
13-
namespace _chroma_impl {
10+
namespace chroma {
11+
namespace _chroma_util {
1412
template <char const* V>
1513
struct error {
1614
constexpr static auto cond = V == nullptr;
@@ -26,6 +24,26 @@ consteval void panic(T const& message) {
2624
(void)is_complete_type(compile_error); // force instantiation
2725
}
2826

27+
consteval bool is_specialization(std::meta::info type, std::meta::info templ) {
28+
if (not is_type(type)) {
29+
throw "not a type";
30+
}
31+
return has_template_arguments(type) &&
32+
template_of(type) == (is_template(templ) ? templ : template_of(templ));
33+
}
34+
35+
constexpr struct {
36+
template <typename F>
37+
decltype(auto) operator->*(F&& fnc) const {
38+
if constexpr (std::is_void_v<std::invoke_result_t<F>>) {
39+
std::forward<F>(fnc)();
40+
} else {
41+
return std::forward<F>(fnc)();
42+
}
43+
}
44+
} eval_rhs;
45+
} // namespace _chroma_util
46+
2947
struct unchecked_context {
3048
template <typename F>
3149
static auto operator()(F&& f) {
@@ -36,23 +54,38 @@ struct unchecked_context {
3654
{ return std::forward<F>(f)(std::forward<Ts>(args)...); };
3755
}
3856
};
57+
constexpr unchecked_context unchecked{};
3958

4059
template <typename... Ts>
4160
struct assumed_colors {};
4261

43-
consteval bool is_specialization(std::meta::info type, std::meta::info templ) {
44-
if (not is_type(type)) {
45-
throw "not a type";
46-
}
47-
return has_template_arguments(type) &&
48-
template_of(type) == (is_template(templ) ? templ : template_of(templ));
49-
}
62+
template <typename... Ts>
63+
constexpr assumed_colors<Ts...> assume_color{};
5064

51-
enum struct Viability { accepted, rejected, ignored };
65+
template <typename T>
66+
struct color {
67+
color(color const&) = delete;
68+
color& operator=(color const&) = delete;
69+
color(color&&) = default;
70+
color& operator=(color&&) = default;
71+
~color() = default;
5272

53-
consteval Viability viability_of(std::meta::info fnc, std::meta::info type) {
54-
using enum Viability;
55-
if (fnc == std::meta::info()) {
73+
protected:
74+
constexpr color() = default;
75+
friend T;
76+
};
77+
78+
template <typename T>
79+
struct callable_from;
80+
81+
template <typename Color>
82+
concept ignorable = not requires { Color::ignorable; } || Color::ignorable;
83+
84+
enum struct viability { accepted, rejected, ignored };
85+
86+
consteval viability viability_of(std::meta::info fnc, std::meta::info type) {
87+
using enum viability;
88+
if (fnc == std::meta::info() and extract<bool>(substitute(^^chroma::ignorable, {type}))) {
5689
return ignored;
5790
}
5891

@@ -62,58 +95,61 @@ consteval Viability viability_of(std::meta::info fnc, std::meta::info type) {
6295
if (annotation == ^^unchecked_context) {
6396
return ignored;
6497
}
65-
if (is_specialization(annotation, ^^assumed_colors) and
98+
if (_chroma_util::is_specialization(annotation, ^^assumed_colors) and
6699
std::ranges::any_of(template_arguments_of(annotation), is_convertible)) {
67100
return accepted;
68101
}
69102
}
70103

71104
for (auto parameter : parameters_of(fnc) | std::views::transform(std::meta::type_of)) {
72-
if (is_specialization(parameter, ^^callable_from) and
105+
if (_chroma_util::is_specialization(parameter, ^^callable_from) and
73106
is_convertible(template_arguments_of(parameter)[0])) {
74107
return accepted;
75108
}
76109
}
77110
return rejected;
78111
}
79-
} // namespace _chroma_impl
80-
81-
constexpr _chroma_impl::unchecked_context unchecked{};
82112

83-
template <typename... Ts>
84-
constexpr _chroma_impl::assumed_colors<Ts...> assume_color{};
113+
consteval std::vector<std::meta::info> colors_of(std::meta::info fnc) {
114+
std::vector<std::meta::info> result;
115+
for (auto annotation : annotations_of(fnc) | std::views::transform(std::meta::type_of)) {
116+
if (_chroma_util::is_specialization(annotation, ^^assumed_colors)) {
117+
for (auto arg : template_arguments_of(annotation)) {
118+
result.push_back(arg);
119+
}
120+
}
121+
}
122+
for (auto parameter : parameters_of(fnc) | std::views::transform(std::meta::type_of)) {
123+
if (_chroma_util::is_specialization(parameter, ^^callable_from)) {
124+
result.push_back(template_arguments_of(parameter)[0]);
125+
}
126+
}
127+
return result;
128+
}
85129

86-
namespace chroma {
87130
template <typename T>
88-
struct color {
89-
color(color const&) = delete;
90-
color& operator=(color const&) = delete;
91-
color(color&&) = default;
92-
color& operator=(color&&) = default;
131+
struct callable_from : color<T> {
132+
constexpr explicit(false) callable_from(color<T> const&) {}
93133

94-
protected:
95-
constexpr color() = default;
96-
friend T;
97-
};
98-
} // namespace chroma
134+
constexpr explicit(false) callable_from(unchecked_context const&)
135+
requires ignorable<T>
136+
{}
99137

100-
template <typename T>
101-
struct callable_from : chroma::color<T> {
102-
consteval explicit(false) callable_from(chroma::color<T>) {}
103-
consteval explicit(false) callable_from(_chroma_impl::unchecked_context) {}
138+
constexpr explicit(false) callable_from(unchecked_context const&)
139+
requires(not ignorable<T>)
140+
= delete ("unchecked access to unignorable color");
104141

105142
consteval explicit(false)
106143
callable_from(std::meta::access_context ctx = std::meta::access_context::current()) {
107-
using enum _chroma_impl::Viability;
108-
if (_chroma_impl::viability_of(ctx.scope(), ^^T) == rejected) {
109-
_chroma_impl::panic(T::error_message);
144+
if (viability_of(ctx.scope(), ^^T) == viability::rejected) {
145+
_chroma_util::panic(T::error_message);
110146
}
111147
}
112148

113149
template <typename U>
114150
requires std::convertible_to<T, U>
115151
operator callable_from<U>() const {
116-
return unchecked;
152+
return chroma::color<U>{};
117153
}
118154

119155
callable_from(callable_from const&) = delete;
@@ -125,13 +161,12 @@ struct callable_from : chroma::color<T> {
125161

126162
template <typename T>
127163
struct not_callable_from {
128-
consteval explicit(false) not_callable_from(_chroma_impl::unchecked_context) {}
164+
constexpr explicit(false) not_callable_from(unchecked_context) {}
129165

130166
consteval explicit(false)
131167
not_callable_from(std::meta::access_context ctx = std::meta::access_context::current()) {
132-
using enum _chroma_impl::Viability;
133-
if (_chroma_impl::viability_of(ctx.scope(), ^^T) == accepted) {
134-
_chroma_impl::panic(T::error_message);
168+
if (viability_of(ctx.scope(), ^^T) == viability::accepted) {
169+
_chroma_util::panic(T::error_message);
135170
}
136171
}
137172

@@ -141,19 +176,12 @@ struct not_callable_from {
141176
not_callable_from& operator=(not_callable_from&&) = default;
142177
constexpr ~not_callable_from() = default;
143178
};
179+
} // namespace chroma
144180

145-
namespace _chroma_impl {
146-
constexpr struct {
147-
template <typename F>
148-
decltype(auto) operator->*(F&& fnc) const {
149-
if constexpr (std::is_void_v<std::invoke_result_t<F>>) {
150-
std::forward<F>(fnc)();
151-
} else {
152-
return std::forward<F>(fnc)();
153-
}
154-
}
155-
} eval_rhs;
156-
} // namespace _chroma_impl
181+
using chroma::assume_color;
182+
using chroma::callable_from;
183+
using chroma::not_callable_from;
184+
using chroma::unchecked;
157185

158-
#define $unchecked ::_chroma_impl::eval_rhs->*[&][[= unchecked]]
159-
#define $with(...) ::_chroma_impl::eval_rhs->*[&][[= assume_color<__VA_ARGS__>]]
186+
#define $unchecked ::chroma::_chroma_util::eval_rhs->*[&][[= unchecked]]
187+
#define $with(...) ::chroma::_chroma_util::eval_rhs->*[&][[= assume_color<__VA_ARGS__>]]

test/unignorable.pass.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %compile
2+
3+
#include <chroma>
4+
5+
struct A {
6+
static constexpr auto error_message = "not in A";
7+
static constexpr auto ignorable = false;
8+
9+
chroma::color<A> make_context() & { return {}; }
10+
};
11+
12+
void fnc1(callable_from<A> = {}) {}
13+
14+
void test() {
15+
auto a = A();
16+
fnc1(a.make_context());
17+
}

test/unignorable.verify.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %verify
2+
3+
#include <chroma>
4+
5+
struct A {
6+
static constexpr auto error_message = "not in A";
7+
static constexpr auto ignorable = false;
8+
};
9+
10+
void fnc1(callable_from<A> = {}) {}
11+
12+
void test() {
13+
// ERROR_HERE: {{.*}}unchecked access to unignorable color
14+
fnc1(unchecked);
15+
}

0 commit comments

Comments
 (0)