Closed ldionne closed 5 months ago
@llvm/issue-subscribers-clang-frontend
Note, if you use -pedantic
both gcc and clang will note that zero-sized arrays as an extension e.g.:
<source>:3:42: warning: zero size arrays are an extension [-Wzero-length-array]
static_assert(!std::is_bounded_array<int[0]>::value);
^
<source>:4:44: warning: zero size arrays are an extension [-Wzero-length-array]
static_assert(!std::is_unbounded_array<int[0]>::value);
^
<source>:5:34: warning: zero size arrays are an extension [-Wzero-length-array]
static_assert(!std::is_array<int[0]>::value); // implemented as __is_array on Clang/libc++
I have always been curious why we allowed this outside of flexible array members but last time I asked @zygoloid was not sure why.
GCC says it's false, MSVC says it's true (see Godbolt).
N.B. MSVC changed that in v19.32
Perhaps it makes more sence to treat T[0]
as a bounded array. It seems to me that we can "fix" std::is_array
.
Proof-of-concept implementation (Godbolt link):
#include <cstddef>
#include <type_traits>
template<class T, bool = std::is_const<const T>::value && !std::is_void<T>::value>
struct ext_is_array_impl { // functions, references, and cv void
static constexpr bool is_array = false;
static constexpr bool is_bounded_array = false;
static constexpr bool is_unbounded_array = false;
};
template<class T, std::size_t N>
struct ext_is_array_impl<T[N], true> {
static constexpr bool is_array = true;
static constexpr bool is_bounded_array = true;
static constexpr bool is_unbounded_array = false;
};
template<class T>
struct ext_is_array_impl<T[], true> {
static constexpr bool is_array = true;
static constexpr bool is_bounded_array = false;
static constexpr bool is_unbounded_array = true;
};
template<class>
struct ext_is_array_impl_extraction_helper {};
template<class T>
struct ext_is_array_impl_extraction_helper<void(*)(T)> { using type = T; };
template<class T>
struct ext_is_array_impl<T, true> { // for non-array objects and extension T[0]
static constexpr bool is_array = !std::is_same<
typename std::remove_cv<T>::type,
typename ext_is_array_impl_extraction_helper<void(*)(T)>::type>::value;
static constexpr bool is_bounded_array = is_array;
static constexpr bool is_unbounded_array = false;
};
template<class T>
constexpr bool ext_is_array_v = ext_is_array_impl<T>::is_array;
template<class T>
constexpr bool ext_is_bounded_array_v = ext_is_array_impl<T>::is_bounded_array;
template<class T>
constexpr bool ext_is_unbounded_array_v = ext_is_array_impl<T>::is_unbounded_array;
Clang currently reports that
__is_array(T[0])
is true, which leads tostd::is_array<T[0]>
being true as well.T[0]
is downright ill-formed: http://eel.is/c++draft/dcl.array#1Since it's ill-formed, I guess Clang is technically conforming, however it still leads to code like this surprisingly compiling with Clang:
So it looks like
T[0]
is neither a bounded array nor an unbounded array, but it is an array, which is quite confusing. I would suggest Clang either:T[0]
since it's ill-formed, orT[0]
is an array from its__is_array
builtin (patch provided for this)Also note that Clang will not match a partial specialization like
template <class T, size_t N> struct foo<T[N]> { ... };
when instantiating it withT[0]
, which seems to support the fact that we really don't want to treatT[0]
as an array type.Here's a potential patch that changes
__is_array(T[0])
tofalse
, but I suspect we may want a deeper fix in the compiler.