https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/187828
>From 906630f5dcaacdcac69d3976018812126a250b1a Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Sat, 21 Mar 2026 01:16:51 +0100 Subject: [PATCH 1/3] [Clang] [Sema] Improve handling of multidimensional subscript operator for builtin types --- clang/docs/ReleaseNotes.rst | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/SemaExpr.cpp | 40 +++++++--- clang/test/OpenMP/target_update_messages.cpp | 4 +- .../test/SemaCXX/cxx23-builtin-subscript.cpp | 77 +++++++++++++++++++ .../SemaCXX/cxx2b-overloaded-operator.cpp | 2 +- 6 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 clang/test/SemaCXX/cxx23-builtin-subscript.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b13171f7ecafc..f6bc428d5f30b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -339,6 +339,7 @@ Bug Fixes in This Version - Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping. - Fixed a crash caused by accessing dependent diagnostics of a non-dependent context. - Fixed a crash when substituting into a non-type template parameter that has a type containing an undeduced placeholder type. +- Fixed several crashes and improved diagnostics when a multidimensional subscript operator is applied to a built-in type. (#GH187800) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d4d09a8ecef36..c0527db7da27d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5425,6 +5425,8 @@ def err_ovl_no_viable_subscript : Error<"no viable overloaded operator[] for type %0">; def err_ovl_no_oper : Error<"type %0 does not provide a %select{subscript|call}1 operator">; +def err_ovl_builtin_subscript_expects_single_arg : Error< + "built-in subscript operator for type %0 expects exactly one argument">; def err_ovl_unresolvable : Error< "reference to %select{overloaded|multiversioned}1 function could not be " "resolved; did you mean to call it%select{| with no arguments}0?">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2b0a786302aab..30fd2d91ca343 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5040,8 +5040,11 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, // // Helper to check for comma expressions, which are not allowed as indices for // matrix subscript expressions. - auto CheckAndReportCommaError = [this, base, rbLoc](Expr *E) { - if (isa<BinaryOperator>(E) && cast<BinaryOperator>(E)->isCommaOp()) { + // + // In C++23, we get multiple arguments instead of a comma expression. + auto CheckAndReportCommaError = [&](Expr *E) { + if (ArgExprs.size() > 1 || + (isa<BinaryOperator>(E) && cast<BinaryOperator>(E)->isCommaOp())) { Diag(E->getExprLoc(), diag::err_matrix_subscript_comma) << SourceRange(base->getBeginLoc(), rbLoc); return true; @@ -5061,7 +5064,6 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, // MatrixSubscriptExpr. auto *matSubscriptE = dyn_cast<MatrixSubscriptExpr>(base); if (matSubscriptE) { - assert(ArgExprs.size() == 1); if (CheckAndReportCommaError(ArgExprs.front())) return ExprError(); @@ -5098,7 +5100,6 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, // If the base is a matrix type, try to create a new MatrixSubscriptExpr. if (base->getType()->isMatrixType()) { - assert(ArgExprs.size() == 1); if (CheckAndReportCommaError(ArgExprs.front())) return ExprError(); @@ -5146,13 +5147,20 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, // The above statement indicates that x[] can be used with one or more array // indices. In this case, i=p->x[a][b] will be turned into i=p->GetX(a, b), // and p->x[a][b] = i will be turned into p->PutX(a, b, i); + // + // Since C++23, MSVC accepts multiple arguments; e.g. given + // + // struct S { + // int get_x(int, int); + // __declspec(property(get=get_x)) int x[][]; + // }; + // + // MSVC accepts both 'S().x[1][2]' and 'S().x[1, 2]'. if (IsMSPropertySubscript) { - assert(ArgExprs.size() == 1); - // Build MS property subscript expression if base is MS property reference - // or MS property subscript. - return new (Context) - MSPropertySubscriptExpr(base, ArgExprs.front(), Context.PseudoObjectTy, - VK_LValue, OK_Ordinary, rbLoc); + for (Expr *Arg : ArgExprs) + base = new (Context) MSPropertySubscriptExpr( + base, Arg, Context.PseudoObjectTy, VK_LValue, OK_Ordinary, rbLoc); + return base; } // Use C++ overloaded-operator rules if either operand has record @@ -5163,6 +5171,18 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, // // ObjC pointers have their own subscripting logic that is not tied // to overload resolution and so should not take this path. + // + // Issue a better diagnostic if we tried to pass multiple arguments to + // a builtin subscript operator rather than diagnosing this as a generic + // overload resolution failure. + if (ArgExprs.size() != 1 && + !base->getType()->isRecordType() && + !base->getType()->isObjCObjectPointerType()) { + Diag(base->getExprLoc(), diag::err_ovl_builtin_subscript_expects_single_arg) + << base->getType() << base->getSourceRange(); + return ExprError(); + } + if (getLangOpts().CPlusPlus && !base->getType()->isObjCObjectPointerType() && ((base->getType()->isRecordType() || (ArgExprs.size() != 1 || isa<PackExpansionExpr>(ArgExprs[0]) || diff --git a/clang/test/OpenMP/target_update_messages.cpp b/clang/test/OpenMP/target_update_messages.cpp index 000cc80e513e6..1f6ecdbf344cb 100644 --- a/clang/test/OpenMP/target_update_messages.cpp +++ b/clang/test/OpenMP/target_update_messages.cpp @@ -241,7 +241,7 @@ void f() { #pragma omp target update to(test[1]) -#pragma omp target update to(test[1, 2]) // cxx23-error {{type 'int[10]' does not provide a subscript operator}} \ +#pragma omp target update to(test[1, 2]) // cxx23-error {{built-in subscript operator for type 'int[10]' expects exactly one argument}} \ // cxx23-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update to(test [1:1:1]) @@ -255,7 +255,7 @@ void f() { #pragma omp target update to(test[1, 2 ::]) // cxx23-error {{expected ']'}} // expected-note {{'['}} \ // cxx23-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} -#pragma omp target update to(test[]) // cxx23-error {{type 'int[10]' does not provide a subscript operator}} \ +#pragma omp target update to(test[]) // cxx23-error {{built-in subscript operator for type 'int[10]' expects exactly one argument}} \ // cxx23-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} S s; (void)s[0]; diff --git a/clang/test/SemaCXX/cxx23-builtin-subscript.cpp b/clang/test/SemaCXX/cxx23-builtin-subscript.cpp new file mode 100644 index 0000000000000..e85e43443f314 --- /dev/null +++ b/clang/test/SemaCXX/cxx23-builtin-subscript.cpp @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 -std=c++23 -verify -fenable-matrix -fdeclspec %s + +union U {}; +struct S {}; +enum E {}; +enum class EC {}; + +using vec3 = int __attribute__((ext_vector_type(3))); +using mat3 = int __attribute__((matrix_type(3, 3))); + +void f(int *p) { + int a[4]{}; + vec3 v{}; + mat3 m; + p[1, 2]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + p[1, p]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + 1[1, 2]; // expected-error {{built-in subscript operator for type 'int' expects exactly one argument}} + 1[p, 2]; // expected-error {{built-in subscript operator for type 'int' expects exactly one argument}} + 1[p, 2]; // expected-error {{built-in subscript operator for type 'int' expects exactly one argument}} + p[U{}, U{}]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + p[E{}, 1]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + p[EC{}, 1]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + p[S{}, 1]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + p[1u, 1l]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + p[1, 2, 3]; // expected-error {{built-in subscript operator for type 'int *' expects exactly one argument}} + a[1, 2]; // expected-error {{built-in subscript operator for type 'int[4]' expects exactly one argument}} + a[1, p]; // expected-error {{built-in subscript operator for type 'int[4]' expects exactly one argument}} + a[S{}, p]; // expected-error {{built-in subscript operator for type 'int[4]' expects exactly one argument}} + a[1, 2, 3]; // expected-error {{built-in subscript operator for type 'int[4]' expects exactly one argument}} + v[1, 2]; // expected-error {{built-in subscript operator for type 'vec3' (vector of 3 'int' values) expects exactly one argument}} + v[1, p]; // expected-error {{built-in subscript operator for type 'vec3' (vector of 3 'int' values) expects exactly one argument}} + v[S{}, p]; // expected-error {{built-in subscript operator for type 'vec3' (vector of 3 'int' values) expects exactly one argument}} + v[1, 2, 3]; // expected-error {{built-in subscript operator for type 'vec3' (vector of 3 'int' values) expects exactly one argument}} + E{}[2, 2]; // expected-error {{built-in subscript operator for type 'E' expects exactly one argument}} + EC{}[2, 2]; // expected-error {{built-in subscript operator for type 'EC' expects exactly one argument}} + + m[1][3, 4]; // expected-error {{comma expressions are not allowed as indices in matrix subscript}} + m[1][2, 3]; // expected-error {{comma expressions are not allowed as indices in matrix subscript}} + m[1, 2][3, 4]; // expected-error {{comma expressions are not allowed as indices in matrix subscript}} + + U{}[2, 2]; // expected-error {{type 'U' does not provide a subscript operator}} + S{}[2, 2]; // expected-error {{type 'S' does not provide a subscript operator}} +} + +struct Prop { + int val = 0; + + constexpr int get_two(int a, int b) { return a + b; } + constexpr int get_three(int a, int b, int c) { return a + b + c; } + + constexpr void put_two(int a, int b, int c) { val = a + b + c; } + constexpr void put_three(int a, int b, int c, int d) { val = a + b + c + d; } + + __declspec(property(get = get_two, put = put_two)) int two[][]; + __declspec(property(get = get_three, put = put_three)) int three[][][]; +}; + +static_assert(Prop().two[1, 2] == 3); +static_assert(Prop().three[1, 2, 3] == 6); +static_assert(Prop().three[1, 2][3] == 6); +static_assert(Prop().three[1][2, 3] == 6); + +constexpr int p1() { + Prop p; + p.two[1, 2] = 3; + return p.val; +} + +static_assert(p1() == 6); + +constexpr int p2() { + Prop p; + p.three[1, 2, 3] = 4; + return p.val; +} + +static_assert(p2() == 10); diff --git a/clang/test/SemaCXX/cxx2b-overloaded-operator.cpp b/clang/test/SemaCXX/cxx2b-overloaded-operator.cpp index c734b82987083..2ca72c8d39cc2 100644 --- a/clang/test/SemaCXX/cxx2b-overloaded-operator.cpp +++ b/clang/test/SemaCXX/cxx2b-overloaded-operator.cpp @@ -98,7 +98,7 @@ int cxx_subscript_unexpanded() { template<int... Is> constexpr int c_array() { int arr[] = {1, 2, 3}; - return arr[Is...]; // expected-error 2{{type 'int[3]' does not provide a subscript operator}} + return arr[Is...]; // expected-error 2{{built-in subscript operator for type 'int[3]' expects exactly one argument}} } template<int... Is> >From 95f9aa96f14ea3b701d7ec979446bff4e785bac8 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Sat, 21 Mar 2026 01:28:42 +0100 Subject: [PATCH 2/3] clang-format --- clang/lib/Sema/SemaExpr.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 30fd2d91ca343..344c3d5051c8c 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5175,8 +5175,7 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, // Issue a better diagnostic if we tried to pass multiple arguments to // a builtin subscript operator rather than diagnosing this as a generic // overload resolution failure. - if (ArgExprs.size() != 1 && - !base->getType()->isRecordType() && + if (ArgExprs.size() != 1 && !base->getType()->isRecordType() && !base->getType()->isObjCObjectPointerType()) { Diag(base->getExprLoc(), diag::err_ovl_builtin_subscript_expects_single_arg) << base->getType() << base->getSourceRange(); >From cf3f2582c781ac60bea2ae69a3c70932c5683e3c Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Tue, 24 Mar 2026 19:14:35 +0100 Subject: [PATCH 3/3] Disallow multidimensional property subscripts for now --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/SemaExpr.cpp | 24 ++++++------ .../test/SemaCXX/cxx23-builtin-subscript.cpp | 39 +++++++------------ 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c0527db7da27d..41d806f70e534 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5427,6 +5427,8 @@ def err_ovl_no_oper : Error<"type %0 does not provide a %select{subscript|call}1 operator">; def err_ovl_builtin_subscript_expects_single_arg : Error< "built-in subscript operator for type %0 expects exactly one argument">; +def err_ms_property_subscript_expects_single_arg : Error< + "property subscript expects exactly one argument">; def err_ovl_unresolvable : Error< "reference to %select{overloaded|multiversioned}1 function could not be " "resolved; did you mean to call it%select{| with no arguments}0?">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 344c3d5051c8c..11f0645b74291 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5147,20 +5147,18 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, // The above statement indicates that x[] can be used with one or more array // indices. In this case, i=p->x[a][b] will be turned into i=p->GetX(a, b), // and p->x[a][b] = i will be turned into p->PutX(a, b, i); - // - // Since C++23, MSVC accepts multiple arguments; e.g. given - // - // struct S { - // int get_x(int, int); - // __declspec(property(get=get_x)) int x[][]; - // }; - // - // MSVC accepts both 'S().x[1][2]' and 'S().x[1, 2]'. if (IsMSPropertySubscript) { - for (Expr *Arg : ArgExprs) - base = new (Context) MSPropertySubscriptExpr( - base, Arg, Context.PseudoObjectTy, VK_LValue, OK_Ordinary, rbLoc); - return base; + if (ArgExprs.size() > 1) { + Diag(base->getExprLoc(), + diag::err_ms_property_subscript_expects_single_arg); + return ExprError(); + } + + // Build MS property subscript expression if base is MS property reference + // or MS property subscript. + return new (Context) + MSPropertySubscriptExpr(base, ArgExprs.front(), Context.PseudoObjectTy, + VK_LValue, OK_Ordinary, rbLoc); } // Use C++ overloaded-operator rules if either operand has record diff --git a/clang/test/SemaCXX/cxx23-builtin-subscript.cpp b/clang/test/SemaCXX/cxx23-builtin-subscript.cpp index e85e43443f314..8067663ded676 100644 --- a/clang/test/SemaCXX/cxx23-builtin-subscript.cpp +++ b/clang/test/SemaCXX/cxx23-builtin-subscript.cpp @@ -43,35 +43,22 @@ void f(int *p) { } struct Prop { - int val = 0; - - constexpr int get_two(int a, int b) { return a + b; } - constexpr int get_three(int a, int b, int c) { return a + b + c; } - - constexpr void put_two(int a, int b, int c) { val = a + b + c; } - constexpr void put_three(int a, int b, int c, int d) { val = a + b + c + d; } - + constexpr int get_two(int a, int b); + constexpr int get_three(int a, int b, int c); + constexpr void put_two(int a, int b, int c); + constexpr void put_three(int a, int b, int c, int d); __declspec(property(get = get_two, put = put_two)) int two[][]; __declspec(property(get = get_three, put = put_three)) int three[][][]; }; -static_assert(Prop().two[1, 2] == 3); -static_assert(Prop().three[1, 2, 3] == 6); -static_assert(Prop().three[1, 2][3] == 6); -static_assert(Prop().three[1][2, 3] == 6); - -constexpr int p1() { +void f() { Prop p; - p.two[1, 2] = 3; - return p.val; + p.two[1, 2]; // expected-error {{property subscript expects exactly one argument}} + p.three[1, 2, 3]; // expected-error {{property subscript expects exactly one argument}} + p.three[1, 2][3]; // expected-error {{property subscript expects exactly one argument}} + p.three[1][2, 3]; // expected-error {{property subscript expects exactly one argument}} + p.two[1, 2] = 3; // expected-error {{property subscript expects exactly one argument}} + p.three[1, 2, 3] = 4; // expected-error {{property subscript expects exactly one argument}} + p.three[1, 2][3] = 4; // expected-error {{property subscript expects exactly one argument}} + p.three[1][2, 3] = 4; // expected-error {{property subscript expects exactly one argument}} } - -static_assert(p1() == 6); - -constexpr int p2() { - Prop p; - p.three[1, 2, 3] = 4; - return p.val; -} - -static_assert(p2() == 10); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
