Non-type template argument `members_of(^...).size()` is not a constant expression
Closed this issue · 4 comments
Describe the bug
Inspired by this issue, I implemented function get_enum_members()
. The code still worked yesterday. Nevertheless, it refused to compile sometime today.
Code
template <class E>
concept enumeration = std::is_enum_v<E>;
template <enumeration E>
using enum_member = std::pair<E, std::string_view>; // value: name
template <enumeration E>
consteval auto get_enum_members() {
auto enum_members_info = members_of(^E);
std::array<enum_member<E>, members_of(^E).size()> enum_members;
for (std::size_t i{}; i < enum_members_info.size(); ++i) {
enum_members[i] = {value_of<E>(enum_members_info[i]),
name_of(enum_members_info[i])};
}
return enum_members;
}
Error message
<source>:17:32: error: non-type template argument is not a constant expression
17 | std::array<enum_member<E>, members_of(^E).size()> enum_members;
| ^~~~~~~~~~~~~~~~~~~~~
<source>:46:35: note: in instantiation of function template specialization 'get_enum_members<std::filesystem::copy_options>' requested here
46 | constexpr auto enum_members = get_enum_members<E>();
| ^
/opt/compiler-explorer/clang-bb-p2996-trunk-20240514/bin/../include/c++/v1/experimental/meta:354:12: note: subexpression not valid in a constant expression
354 | return __metafunction(detail::__metafn_get_begin_member_decl_of,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
355 | reflectedEntity, ^sentinel);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-bb-p2996-trunk-20240514/bin/../include/c++/v1/experimental/meta:273:21: note: in call to 'm_front.operator()(^(type))'
273 | , m_currInfoItr{m_front(reflectedEntity)}
| ^~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-bb-p2996-trunk-20240514/bin/../include/c++/v1/experimental/meta:321:7: note: in call to 'iterator(^(type), {})'
321 | : m_first(reflection, pred) , m_last(pred)
| ^~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-bb-p2996-trunk-20240514/bin/../include/c++/v1/experimental/meta:519:14: note: in call to 'range(^(type), {})'
519 | auto rng = range{r, pred};
| ^~~~~~~~~~~~~~
/opt/compiler-explorer/clang-bb-p2996-trunk-20240514/bin/../include/c++/v1/experimental/meta:531:10: note: in call to 'members_of<std::meta::__range_of_infos::always_true_fn>(^(type), {})'
531 | return members_of(r, __range_of_infos::always_true);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:17:32: note: in call to 'members_of(^(type))'
17 | std::array<enum_member<E>, members_of(^E).size()> enum_members;
| ^~~~~~~~~~~~~~
<source>:27:42: error: no matching function for call to 'get_enum_members'
27 | constexpr static auto enum_members = get_enum_members<E>();
| ^~~~~~~~~~~~~~~~~~~
<source>:55:27: note: in instantiation of function template specialization 'get_enum_member_name_consteval<std::filesystem::copy_options>' requested here
55 | constexpr auto name = get_enum_member_name_consteval(E::overwrite_existing);
| ^
<source>:15:16: note: candidate template ignored: substitution failure [with E = std::filesystem::copy_options]
15 | consteval auto get_enum_members() {
| ^
<source>:55:27: error: constexpr variable 'name' must be initialized by a constant expression
55 | constexpr auto name = get_enum_member_name_consteval(E::overwrite_existing);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:39:9: error: no matching function for call to 'get_enum_members'
39 | get_enum_members<E>() | std::ranges::to<std::unordered_map>();
| ^~~~~~~~~~~~~~~~~~~
<source>:58:18: note: in instantiation of function template specialization 'get_enum_member_name_runtime<std::filesystem::copy_options>' requested here
58 | get_enum_member_name_runtime(E::overwrite_existing));
| ^
<source>:15:16: note: candidate template ignored: substitution failure [with E = std::filesystem::copy_options]
15 | consteval auto get_enum_members() {
| ^
4 errors generated.
Compiler returned: 1
Possible causes
The most recent commit adds an additional argument to function __metafunction()
.
libcxx/include/experimental/meta
@@ -783,7 +783,7 @@ consteval auto is_special_member(info r) -> bool {
- return __metafunction(detail::__metafn_reflect_value, r);
+ return __metafunction(detail::__metafn_reflect_value, ^Ty, r);
It seems that only enumerations are affected. The code still works for structures and classes.
Sorry, I forgot function enumerators_of()
and that works. So, it was a bug that function members_of()
used to work for enumerations, which has been fixed?
Hey @Shuangcheng-Ni ! Thanks for opening the issue, and good find.
As you've already figured out, enumerators_of(^Enum)
should be used, and it was a bug for members_of(^Enum)
to be allowed in the first place. It seems that this change is what (quite accidentally) fixed the bug.
What was previously happening was that reflecting on some typename T
within a template was accidentally picking up a "SubstTemplateTypeParmType
" type-node, rather than the "replacement type" (i.e., the template argument that it was instantiated with). I'm surprised that didn't cause more issues in the first place 😅 With the above change, all code that accesses the type reflected by a std::meta::info
will receive a type for which the SubstTemplateTypeParmType
layer has already been "unwrapped".
Let me know if there's anything else I can help with, or if we can mark this one as closed :)
Thanks for the clarification and congratulations on the bug fix! I have no other questions at the moment, so I'm going to close this issue as completed.