When compiling my program using a recent version of OpenVDB library by Clang in C++20 mode, I get the error:
error: use of the 'assume' attribute is a C++23 extension
I see that it is due to the macro OPENVDB_ASSUME conditionally defined as (simplified):
#ifdef __has_cpp_attribute
#if __has_cpp_attribute(assume) >= 202207L
#define OPENVDB_ASSUME(...) [[assume(__VA_ARGS__)]]
#endif
#endif
#ifndef OPENVDB_ASSUME
#define OPENVDB_ASSUME(...) __builtin_assume(__VA_ARGS__)
#endif
Despite the condition __has_cpp_attribute(assume) >= 202207L, the macro still expands in [[assume]] in C++20 mode. Online demo: https://gcc.godbolt.org/z/fEY5GWc9P
How one has to modify the condition to prevent it, and get C++20 compatible __builtin_assume(__VA_ARGS__) expansion instead?
It is really unfortunate that Clang decided to put users in this position. Since the intent of
#if __has_cpp_attribute(attr)
Was that if that returns non-zero, that you can... actually use the attribute. Warning after a check means that you suddenly need far more information to do a proper check.
Now, this answer suggests writing:
#if __has_cpp_attribute(assume) && __cplusplus >= __has_cpp_attribute(assume)
This is actually incorrect. The problem here is that the value that __has_cpp_attribute(attr) returns isn't fixed. It might change. For instance, [[nodiscard]] was an attribute that was added in C++17, initially with the value 201603. Then, during C++20, you were allowed to add a reason to it, so it now uses the value 201907.
Imagine I applied the above check to [[nodiscard]]:
#if __has_cpp_attribute(nodiscard) && __cplusplus >= __has_cpp_attribute(nodiscard)
# define NODISCARD [[nodiscard]]
#else
# define NODISCARD
#endif
If I'm compiling with clang 9 in c++17 mode, I would get [[nodiscard]]. But with clang 10 in c++17 mode, I would suddenly get nothing — because clang 10 implemented the added [[nodiscard]] functionality, so now our check fails. And we don't get any [[nodiscard]] at all! For example:
#if __has_cpp_attribute(nodiscard) && __cplusplus >= __has_cpp_attribute(nodiscard)
# define NODISCARD [[nodiscard]]
#else
# define NODISCARD
#endif
NODISCARD int f() { return 0; }
int main() {
f(); // warns with clang 9, not with clang 10+
}
Instead, you just have to know.
That is, the specific check you need to write for [[nodiscard]] is this:
#if __has_cpp_attribute(nodiscard) && __cplusplus >= 201603
And likewise the specific check you have to write for assume is
#if __has_cpp_attribute(assume) && __cplusplus >= 202207
Thankfully, you can at least easily find these values in the feature-test document (also via the link wg21.link/sd6). But it would have been nice to not have to do this.