Skip to content

Commit c151225

Browse files
cor3ntinAaronBallman
authored andcommitted
[C++2b] Implement multidimentional subscript operator
Implement P2128R6 in C++23 mode. Unlike GCC's implementation, this doesn't try to recover when a user meant to use a comma expression. Because the syntax changes meaning in C++23, the patch is *NOT* implemented as an extension. Instead, declaring an array with not exactly 1 parameter is an error in older languages modes. There is an off-by-default extension warning in C++23 mode. Unlike the standard, we supports default arguments; Ie, we assume, based on conversations in WG21, that the proposed resolution to CWG2507 will be accepted. We allow arrays OpenMP sections and C++23 multidimensional array to coexist: [a , b] multi dimensional array [a : b] open mp section [a, b: c] // error The rest of the patch is relatively straight forward: we take care to support an arbitrary number of arguments everywhere.
1 parent caf7f05 commit c151225

17 files changed

+493
-194
lines changed

clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,8 @@ static bool isAliasDecl(ASTContext *Context, const Decl *TheDecl,
398398
if (OpCall->getOperator() == OO_Star)
399399
return isDereferenceOfOpCall(OpCall, IndexVar);
400400
if (OpCall->getOperator() == OO_Subscript) {
401-
assert(OpCall->getNumArgs() == 2);
402-
return isIndexInSubscriptExpr(OpCall->getArg(1), IndexVar);
401+
return OpCall->getNumArgs() == 2 &&
402+
isIndexInSubscriptExpr(OpCall->getArg(1), IndexVar);
403403
}
404404
break;
405405
}

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ C++20 Feature Support
9292
C++2b Feature Support
9393
^^^^^^^^^^^^^^^^^^^^^
9494

95+
- Implemented `P2128R6: Multidimensional subscript operator <https://wg21.link/P2128R6>`_.
96+
9597
CUDA Language Changes in Clang
9698
------------------------------
9799

clang/include/clang/Basic/DiagnosticSemaKinds.td

+10-1
Original file line numberDiff line numberDiff line change
@@ -4666,6 +4666,8 @@ def err_ovl_no_viable_object_call : Error<
46664666
"no matching function for call to object of type %0">;
46674667
def err_ovl_ambiguous_object_call : Error<
46684668
"call to object of type %0 is ambiguous">;
4669+
def err_ovl_ambiguous_subscript_call : Error<
4670+
"call to subscript operator of type %0 is ambiguous">;
46694671
def err_ovl_deleted_object_call : Error<
46704672
"call to deleted function call operator in type %0">;
46714673
def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">;
@@ -6571,7 +6573,8 @@ def err_arithmetic_nonfragile_interface : Error<
65716573
"this architecture and platform">;
65726574

65736575
def warn_deprecated_comma_subscript : Warning<
6574-
"top-level comma expression in array subscript is deprecated">,
6576+
"top-level comma expression in array subscript is deprecated "
6577+
"in C++20 and unsupported in C++2b">,
65756578
InGroup<DeprecatedCommaSubscript>;
65766579

65776580
def ext_subscript_non_lvalue : Extension<
@@ -8972,6 +8975,12 @@ def err_operator_overload_static : Error<
89728975
"overloaded %0 cannot be a static member function">;
89738976
def err_operator_overload_default_arg : Error<
89748977
"parameter of overloaded %0 cannot have a default argument">;
8978+
8979+
def ext_subscript_overload : ExtWarn<
8980+
"overloaded %0 with %select{no|a defaulted|more than one}1 parameter is a C++2b extension">, InGroup<CXXPre2bCompat>, DefaultIgnore;
8981+
def error_subscript_overload : Error<
8982+
"overloaded %0 cannot have %select{no|a defaulted|more than one}1 parameter before C++2b">;
8983+
89758984
def err_operator_overload_must_be : Error<
89768985
"overloaded %0 must be a %select{unary|binary|unary or binary}2 operator "
89778986
"(has %1 parameter%s1)">;

clang/include/clang/Sema/Sema.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -3943,8 +3943,8 @@ class Sema final {
39433943
FunctionDecl *DefaultedFn);
39443944

39453945
ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
3946-
SourceLocation RLoc,
3947-
Expr *Base,Expr *Idx);
3946+
SourceLocation RLoc, Expr *Base,
3947+
MultiExprArg Args);
39483948

39493949
ExprResult BuildCallToMemberFunction(Scope *S, Expr *MemExpr,
39503950
SourceLocation LParenLoc,
@@ -5386,7 +5386,8 @@ class Sema final {
53865386
tok::TokenKind Kind, Expr *Input);
53875387

53885388
ExprResult ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc,
5389-
Expr *Idx, SourceLocation RLoc);
5389+
MultiExprArg ArgExprs,
5390+
SourceLocation RLoc);
53905391
ExprResult CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
53915392
Expr *Idx, SourceLocation RLoc);
53925393

@@ -7434,10 +7435,16 @@ class Sema final {
74347435
CheckStructuredBindingMemberAccess(SourceLocation UseLoc,
74357436
CXXRecordDecl *DecomposedClass,
74367437
DeclAccessPair Field);
7438+
AccessResult CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr,
7439+
const SourceRange &,
7440+
DeclAccessPair FoundDecl);
74377441
AccessResult CheckMemberOperatorAccess(SourceLocation Loc,
74387442
Expr *ObjectExpr,
74397443
Expr *ArgExpr,
74407444
DeclAccessPair FoundDecl);
7445+
AccessResult CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr,
7446+
ArrayRef<Expr *> ArgExprs,
7447+
DeclAccessPair FoundDecl);
74417448
AccessResult CheckAddressOfMemberAccess(Expr *OvlExpr,
74427449
DeclAccessPair FoundDecl);
74437450
AccessResult CheckBaseClassAccess(SourceLocation AccessLoc,

clang/lib/AST/StmtPrinter.cpp

+3-8
Original file line numberDiff line numberDiff line change
@@ -1736,21 +1736,16 @@ void StmtPrinter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *Node) {
17361736
}
17371737
} else if (Kind == OO_Arrow) {
17381738
PrintExpr(Node->getArg(0));
1739-
} else if (Kind == OO_Call) {
1739+
} else if (Kind == OO_Call || Kind == OO_Subscript) {
17401740
PrintExpr(Node->getArg(0));
1741-
OS << '(';
1741+
OS << (Kind == OO_Call ? '(' : '[');
17421742
for (unsigned ArgIdx = 1; ArgIdx < Node->getNumArgs(); ++ArgIdx) {
17431743
if (ArgIdx > 1)
17441744
OS << ", ";
17451745
if (!isa<CXXDefaultArgExpr>(Node->getArg(ArgIdx)))
17461746
PrintExpr(Node->getArg(ArgIdx));
17471747
}
1748-
OS << ')';
1749-
} else if (Kind == OO_Subscript) {
1750-
PrintExpr(Node->getArg(0));
1751-
OS << '[';
1752-
PrintExpr(Node->getArg(1));
1753-
OS << ']';
1748+
OS << (Kind == OO_Call ? ')' : ']');
17541749
} else if (Node->getNumArgs() == 1) {
17551750
OS << getOperatorSpelling(Kind) << ' ';
17561751
PrintExpr(Node->getArg(0));

clang/lib/Frontend/InitPreprocessor.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
642642
Builder.defineMacro("__cpp_implicit_move", "202011L");
643643
Builder.defineMacro("__cpp_size_t_suffix", "202011L");
644644
Builder.defineMacro("__cpp_if_consteval", "202106L");
645+
Builder.defineMacro("__cpp_­multidimensional_­subscript", "202110L");
645646
}
646647
if (LangOpts.Char8)
647648
Builder.defineMacro("__cpp_char8_t", "201811L");

clang/lib/Parse/ParseExpr.cpp

+47-22
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
18351835
/// primary-expression
18361836
/// postfix-expression '[' expression ']'
18371837
/// postfix-expression '[' braced-init-list ']'
1838+
/// postfix-expression '[' expression-list [opt] ']' [C++2b 12.4.5]
18381839
/// postfix-expression '(' argument-expression-list[opt] ')'
18391840
/// postfix-expression '.' identifier
18401841
/// postfix-expression '->' identifier
@@ -1898,30 +1899,58 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
18981899
(void)Actions.CorrectDelayedTyposInExpr(LHS);
18991900
return ExprError();
19001901
}
1901-
19021902
BalancedDelimiterTracker T(*this, tok::l_square);
19031903
T.consumeOpen();
19041904
Loc = T.getOpenLocation();
1905-
ExprResult Idx, Length, Stride;
1905+
ExprResult Length, Stride;
19061906
SourceLocation ColonLocFirst, ColonLocSecond;
1907+
ExprVector ArgExprs;
1908+
bool HasError = false;
19071909
PreferredType.enterSubscript(Actions, Tok.getLocation(), LHS.get());
1908-
if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
1909-
Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
1910-
Idx = ParseBraceInitializer();
1911-
} else if (getLangOpts().OpenMP) {
1912-
ColonProtectionRAIIObject RAII(*this);
1913-
// Parse [: or [ expr or [ expr :
1914-
if (!Tok.is(tok::colon)) {
1915-
// [ expr
1916-
Idx = ParseExpression();
1910+
1911+
// We try to parse a list of indexes in all language mode first
1912+
// and, in we find 0 or one index, we try to parse an OpenMP array
1913+
// section. This allow us to support C++2b multi dimensional subscript and
1914+
// OpenMp sections in the same language mode.
1915+
if (!getLangOpts().OpenMP || Tok.isNot(tok::colon)) {
1916+
if (!getLangOpts().CPlusPlus2b) {
1917+
ExprResult Idx;
1918+
if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
1919+
Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
1920+
Idx = ParseBraceInitializer();
1921+
} else {
1922+
Idx = ParseExpression(); // May be a comma expression
1923+
}
1924+
LHS = Actions.CorrectDelayedTyposInExpr(LHS);
1925+
Idx = Actions.CorrectDelayedTyposInExpr(Idx);
1926+
if (Idx.isInvalid()) {
1927+
HasError = true;
1928+
} else {
1929+
ArgExprs.push_back(Idx.get());
1930+
}
1931+
} else if (Tok.isNot(tok::r_square)) {
1932+
CommaLocsTy CommaLocs;
1933+
if (ParseExpressionList(ArgExprs, CommaLocs)) {
1934+
LHS = Actions.CorrectDelayedTyposInExpr(LHS);
1935+
HasError = true;
1936+
}
1937+
assert(
1938+
(ArgExprs.empty() || ArgExprs.size() == CommaLocs.size() + 1) &&
1939+
"Unexpected number of commas!");
19171940
}
1941+
}
1942+
1943+
if (ArgExprs.size() <= 1 && getLangOpts().OpenMP) {
1944+
ColonProtectionRAIIObject RAII(*this);
19181945
if (Tok.is(tok::colon)) {
19191946
// Consume ':'
19201947
ColonLocFirst = ConsumeToken();
19211948
if (Tok.isNot(tok::r_square) &&
19221949
(getLangOpts().OpenMP < 50 ||
1923-
((Tok.isNot(tok::colon) && getLangOpts().OpenMP >= 50))))
1950+
((Tok.isNot(tok::colon) && getLangOpts().OpenMP >= 50)))) {
19241951
Length = ParseExpression();
1952+
Length = Actions.CorrectDelayedTyposInExpr(Length);
1953+
}
19251954
}
19261955
if (getLangOpts().OpenMP >= 50 &&
19271956
(OMPClauseKind == llvm::omp::Clause::OMPC_to ||
@@ -1933,27 +1962,23 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
19331962
Stride = ParseExpression();
19341963
}
19351964
}
1936-
} else
1937-
Idx = ParseExpression();
1965+
}
19381966

19391967
SourceLocation RLoc = Tok.getLocation();
1940-
19411968
LHS = Actions.CorrectDelayedTyposInExpr(LHS);
1942-
Idx = Actions.CorrectDelayedTyposInExpr(Idx);
1943-
Length = Actions.CorrectDelayedTyposInExpr(Length);
1944-
if (!LHS.isInvalid() && !Idx.isInvalid() && !Length.isInvalid() &&
1969+
1970+
if (!LHS.isInvalid() && !HasError && !Length.isInvalid() &&
19451971
!Stride.isInvalid() && Tok.is(tok::r_square)) {
19461972
if (ColonLocFirst.isValid() || ColonLocSecond.isValid()) {
19471973
LHS = Actions.ActOnOMPArraySectionExpr(
1948-
LHS.get(), Loc, Idx.get(), ColonLocFirst, ColonLocSecond,
1949-
Length.get(), Stride.get(), RLoc);
1974+
LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0],
1975+
ColonLocFirst, ColonLocSecond, Length.get(), Stride.get(), RLoc);
19501976
} else {
19511977
LHS = Actions.ActOnArraySubscriptExpr(getCurScope(), LHS.get(), Loc,
1952-
Idx.get(), RLoc);
1978+
ArgExprs, RLoc);
19531979
}
19541980
} else {
19551981
LHS = ExprError();
1956-
Idx = ExprError();
19571982
}
19581983

19591984
// Match the ']'.

clang/lib/Sema/SemaAccess.cpp

+27-8
Original file line numberDiff line numberDiff line change
@@ -1761,28 +1761,47 @@ Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc,
17611761
return CheckAccess(*this, UseLoc, Entity);
17621762
}
17631763

1764-
/// Checks access to an overloaded member operator, including
1765-
/// conversion operators.
17661764
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
17671765
Expr *ObjectExpr,
1768-
Expr *ArgExpr,
1766+
const SourceRange &Range,
17691767
DeclAccessPair Found) {
1770-
if (!getLangOpts().AccessControl ||
1771-
Found.getAccess() == AS_public)
1768+
if (!getLangOpts().AccessControl || Found.getAccess() == AS_public)
17721769
return AR_accessible;
17731770

17741771
const RecordType *RT = ObjectExpr->getType()->castAs<RecordType>();
17751772
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
17761773

17771774
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
17781775
ObjectExpr->getType());
1779-
Entity.setDiag(diag::err_access)
1780-
<< ObjectExpr->getSourceRange()
1781-
<< (ArgExpr ? ArgExpr->getSourceRange() : SourceRange());
1776+
Entity.setDiag(diag::err_access) << ObjectExpr->getSourceRange() << Range;
17821777

17831778
return CheckAccess(*this, OpLoc, Entity);
17841779
}
17851780

1781+
/// Checks access to an overloaded member operator, including
1782+
/// conversion operators.
1783+
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
1784+
Expr *ObjectExpr,
1785+
Expr *ArgExpr,
1786+
DeclAccessPair Found) {
1787+
return CheckMemberOperatorAccess(
1788+
OpLoc, ObjectExpr, ArgExpr ? ArgExpr->getSourceRange() : SourceRange(),
1789+
Found);
1790+
}
1791+
1792+
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
1793+
Expr *ObjectExpr,
1794+
ArrayRef<Expr *> ArgExprs,
1795+
DeclAccessPair FoundDecl) {
1796+
SourceRange R;
1797+
if (!ArgExprs.empty()) {
1798+
R = SourceRange(ArgExprs.front()->getBeginLoc(),
1799+
ArgExprs.back()->getEndLoc());
1800+
}
1801+
1802+
return CheckMemberOperatorAccess(OpLoc, ObjectExpr, R, FoundDecl);
1803+
}
1804+
17861805
/// Checks access to the target of a friend declaration.
17871806
Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) {
17881807
assert(isa<CXXMethodDecl>(target->getAsFunction()));

clang/lib/Sema/SemaDeclCXX.cpp

+33-11
Original file line numberDiff line numberDiff line change
@@ -15865,14 +15865,29 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
1586515865
// An operator function cannot have default arguments (8.3.6),
1586615866
// except where explicitly stated below.
1586715867
//
15868-
// Only the function-call operator allows default arguments
15869-
// (C++ [over.call]p1).
15868+
// Only the function-call operator (C++ [over.call]p1) and the subscript
15869+
// operator (CWG2507) allow default arguments.
1587015870
if (Op != OO_Call) {
15871+
ParmVarDecl *FirstDefaultedParam = nullptr;
1587115872
for (auto Param : FnDecl->parameters()) {
15872-
if (Param->hasDefaultArg())
15873-
return Diag(Param->getLocation(),
15873+
if (Param->hasDefaultArg()) {
15874+
FirstDefaultedParam = Param;
15875+
break;
15876+
}
15877+
}
15878+
if (FirstDefaultedParam) {
15879+
if (Op == OO_Subscript) {
15880+
Diag(FnDecl->getLocation(), LangOpts.CPlusPlus2b
15881+
? diag::ext_subscript_overload
15882+
: diag::error_subscript_overload)
15883+
<< FnDecl->getDeclName() << 1
15884+
<< FirstDefaultedParam->getDefaultArgRange();
15885+
} else {
15886+
return Diag(FirstDefaultedParam->getLocation(),
1587415887
diag::err_operator_overload_default_arg)
15875-
<< FnDecl->getDeclName() << Param->getDefaultArgRange();
15888+
<< FnDecl->getDeclName()
15889+
<< FirstDefaultedParam->getDefaultArgRange();
15890+
}
1587615891
}
1587715892
}
1587815893

@@ -15893,10 +15908,10 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
1589315908
// described in the rest of this subclause.
1589415909
unsigned NumParams = FnDecl->getNumParams()
1589515910
+ (isa<CXXMethodDecl>(FnDecl)? 1 : 0);
15896-
if (Op != OO_Call &&
15911+
if (Op != OO_Call && Op != OO_Subscript &&
1589715912
((NumParams == 1 && !CanBeUnaryOperator) ||
15898-
(NumParams == 2 && !CanBeBinaryOperator) ||
15899-
(NumParams < 1) || (NumParams > 2))) {
15913+
(NumParams == 2 && !CanBeBinaryOperator) || (NumParams < 1) ||
15914+
(NumParams > 2))) {
1590015915
// We have the wrong number of parameters.
1590115916
unsigned ErrorKind;
1590215917
if (CanBeUnaryOperator && CanBeBinaryOperator) {
@@ -15908,16 +15923,23 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
1590815923
"All non-call overloaded operators are unary or binary!");
1590915924
ErrorKind = 1; // 1 -> binary
1591015925
}
15911-
1591215926
return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be)
1591315927
<< FnDecl->getDeclName() << NumParams << ErrorKind;
1591415928
}
1591515929

15916-
// Overloaded operators other than operator() cannot be variadic.
15930+
if (Op == OO_Subscript && NumParams != 2) {
15931+
Diag(FnDecl->getLocation(), LangOpts.CPlusPlus2b
15932+
? diag::ext_subscript_overload
15933+
: diag::error_subscript_overload)
15934+
<< FnDecl->getDeclName() << (NumParams == 1 ? 0 : 2);
15935+
}
15936+
15937+
// Overloaded operators other than operator() and operator[] cannot be
15938+
// variadic.
1591715939
if (Op != OO_Call &&
1591815940
FnDecl->getType()->castAs<FunctionProtoType>()->isVariadic()) {
1591915941
return Diag(FnDecl->getLocation(), diag::err_operator_overload_variadic)
15920-
<< FnDecl->getDeclName();
15942+
<< FnDecl->getDeclName();
1592115943
}
1592215944

1592315945
// Some operators must be non-static member functions.

0 commit comments

Comments
 (0)