I have some code that defines a niebloid as such:
#define FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
namespace mylib {
namespace detail {
auto func(struct poison&) = delete;
struct func_function {
constexpr auto operator()(auto&& a) const -> decltype(func(FWD(a))) {
return func(FWD(a));
}
};
}
inline namespace niebloid {
inline constexpr auto func = detail::func_function{};
}
template<typename T>
struct some_type {
template<typename U>
friend constexpr auto func(U&&) -> int {
return 42;
}
};
}
int main() {
return mylib::func(mylib::some_type<int>{});
}
Everything looks good, except for clang 16 specifically.
If we look at this conformance view, we can observe older version of clang down to clang 11 handles this code very well, and clang 17 and up also handles it quite well.
But clang 16 and only 16 seem to not be able to compile this code.
Here's the error message clang 16 outputs:
<source>:21:31: error: redefinition of 'func' as different kind of symbol
friend constexpr auto func(U&&) -> int {
^
<source>:29:24: note: in instantiation of template class 'mylib::some_type<int>' requested here
return mylib::func(mylib::some_type<int>{});
^
<source>:15:31: note: previous definition is here
inline constexpr auto func = detail::func_function{};
^
<source>:29:12: error: no matching function for call to object of type 'const detail::func_function'
return mylib::func(mylib::some_type<int>{});
^~~~~~~~~~~
<source>:8:28: note: candidate template ignored: substitution failure [with a:auto = mylib::some_type<int>]: no matching function for call to 'func'
constexpr auto operator()(auto&& a) const -> decltype(func(FWD(a))) {
^ ~~~~
2 errors generated.
Compiler returned: 1
Making some_type
non template or making the hidden friend non template too seems to not trigger the bug, but both options are not available for my codebase.
Now for the question. Is there a most minimally disruptive workaround I could use to make this code work with clang 16 or this compiler is inherently incompatible with niebloid like patterns?
It seem that playing around with namespaces I got a minimally disruptive workaround that changes very little where types, variable and functions are getting declared.
As with most problem in software engineering, one more indirection layer solves the problem:
namespace niebloid {
inline constexpr auto func = detail::func_function{};
}
inline namespace workaround {
using namespace niebloid;
}