Skip to content

[Sema] Fix __array_rank queried type at template instantiation #124491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,8 @@ Bug Fixes to C++ Support
- Fixed assertions or false compiler diagnostics in the case of C++ modules for
lambda functions or inline friend functions defined inside templates (#GH122493).
- Clang now rejects declaring an alias template with the same name as its template parameter. (#GH123423)
- Fix type of expression when calling a template which returns an ``__array_rank`` querying a type depending on a
template parameter. Now, such expression can be used with ``static_assert`` and ``constexpr``. (#GH123498)
- Correctly determine the implicit constexprness of lambdas in dependent contexts. (#GH97958) (#GH114234)

Bug Fixes to AST Handling
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -2847,8 +2847,8 @@ class TypeTraitExpr final
///
/// Example:
/// \code
/// __array_rank(int[10][20]) == 2
/// __array_extent(int, 1) == 20
/// __array_rank(int[10][20]) == 2
/// __array_extent(int[10][20], 1) == 20
/// \endcode
class ArrayTypeTraitExpr : public Expr {
/// The trait. An ArrayTypeTrait enum in MSVC compat unsigned.
Expand Down
3 changes: 0 additions & 3 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -14947,9 +14947,6 @@ TreeTransform<Derived>::TransformArrayTypeTraitExpr(ArrayTypeTraitExpr *E) {
SubExpr = getDerived().TransformExpr(E->getDimensionExpression());
if (SubExpr.isInvalid())
return ExprError();

if (!getDerived().AlwaysRebuild() && SubExpr.get() == E->getDimensionExpression())
return E;
}

return getDerived().RebuildArrayTypeTrait(E->getTrait(), E->getBeginLoc(), T,
Expand Down
129 changes: 129 additions & 0 deletions clang/test/SemaCXX/array-type-trait-with-template.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// RUN: %clang_cc1 -fsyntax-only %s
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -DWITH_AUTO_FUNCTION_PARAMETER=1 %s

// When __array_rank is used with a template type parameter, this test
// ensures clang considers the final expression could be used with
// static_assert/constexpr.
//
// Although array_extent was handled well, we add it as a precaution.

template <typename T>
using remove_reference_t = __remove_reference_t(T);

template <typename T, int N>
constexpr int array_rank(T (&lhs)[N]) {
return __array_rank(T[N]);
}

template <int I, typename T, int N>
constexpr int array_extent(T (&lhs)[N]) {
return __array_extent(T[N], I);
}

template <typename T>
struct Rank {
using ArrayT = remove_reference_t<T>;

template <int N>
static constexpr int call(ArrayT (&lhs)[N]) {
return __array_rank(ArrayT[N]);
}
};

template <typename T>
struct Extent {
using ArrayT = remove_reference_t<T>;

template <int I, int N>
static constexpr int call(ArrayT (&lhs)[N]) {
return __array_extent(ArrayT[N], I);
}
};

#ifdef WITH_AUTO_FUNCTION_PARAMETER
template <int N>
constexpr int array_rank_auto(auto (&lhs)[N]) {
return __array_rank(remove_reference_t<decltype(lhs[0])>[N]);
}

template <int I, int N>
constexpr int array_extent_auto(auto (&lhs)[N]) {
return __array_extent(remove_reference_t<decltype(lhs[0])>[N], I);
}
#endif

template <int N>
constexpr int array_rank_int(const int (&lhs)[N]) {
return __array_rank(const int[N]);
}

template <int I, int N>
constexpr int array_extent_int(const int (&lhs)[N]) {
return __array_extent(const int[N], I);
}

template <int M, int N>
constexpr int array_rank_int(const int (&lhs)[M][N]) {
return __array_rank(const int[M][N]);
}

template <int I, int M, int N>
constexpr int array_extent_int(const int (&lhs)[M][N]) {
return __array_extent(const int[M][N], I);
}

int main() {
constexpr int vec[] = {0, 1, 2, 1};
constexpr int mat[4][4] = {
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}
};

#define ATT_TESTS_WITH_ASSERT(ATT_ASSERT) \
{ ATT_ASSERT(RANK(vec) == 1); } \
{ ATT_ASSERT(RANK(mat) == 2); } \
{ ATT_ASSERT(EXTENT(vec, 0) == 4); } \
{ ATT_ASSERT(EXTENT(vec, 1) == 0); } \
{ ATT_ASSERT(EXTENT(mat, 1) == 4); }

#define ATT_TESTS() \
ATT_TESTS_WITH_ASSERT( constexpr bool cst = ) \
ATT_TESTS_WITH_ASSERT( (void) ) \
ATT_TESTS_WITH_ASSERT( static_assert )

{
#define RANK(lhs) array_rank(lhs)
#define EXTENT(lhs, i) array_extent<i>(lhs)
ATT_TESTS();
#undef RANK
#undef EXTENT
}

{
#define RANK(lhs) Rank<decltype(lhs[0])>::call(lhs)
#define EXTENT(lhs, i) Extent<decltype(lhs[0])>::call<i>(lhs)
ATT_TESTS();
#undef RANK
#undef EXTENT
}

#ifdef WITH_AUTO_FUNCTION_PARAMETER
{
#define RANK(lhs) array_rank_auto(lhs)
#define EXTENT(lhs, i) array_extent_auto<i>(lhs)
ATT_TESTS();
#undef RANK
#undef EXTENT
}
#endif

{
#define RANK(lhs) array_rank_int(lhs)
#define EXTENT(lhs, i) array_extent_int<i>(lhs)
ATT_TESTS();
#undef RANK
#undef EXTENT
}
}
Loading