[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.
This commit is contained in:
Corentin Jabot 2022-02-08 12:09:03 -05:00 committed by Aaron Ballman
parent caf7f05c1c
commit c151225096
17 changed files with 493 additions and 194 deletions

View File

@ -398,8 +398,8 @@ static bool isAliasDecl(ASTContext *Context, const Decl *TheDecl,
if (OpCall->getOperator() == OO_Star)
return isDereferenceOfOpCall(OpCall, IndexVar);
if (OpCall->getOperator() == OO_Subscript) {
assert(OpCall->getNumArgs() == 2);
return isIndexInSubscriptExpr(OpCall->getArg(1), IndexVar);
return OpCall->getNumArgs() == 2 &&
isIndexInSubscriptExpr(OpCall->getArg(1), IndexVar);
}
break;
}

View File

@ -92,6 +92,8 @@ C++20 Feature Support
C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Implemented `P2128R6: Multidimensional subscript operator <https://wg21.link/P2128R6>`_.
CUDA Language Changes in Clang
------------------------------

View File

@ -4666,6 +4666,8 @@ def err_ovl_no_viable_object_call : Error<
"no matching function for call to object of type %0">;
def err_ovl_ambiguous_object_call : Error<
"call to object of type %0 is ambiguous">;
def err_ovl_ambiguous_subscript_call : Error<
"call to subscript operator of type %0 is ambiguous">;
def err_ovl_deleted_object_call : Error<
"call to deleted function call operator in type %0">;
def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">;
@ -6571,7 +6573,8 @@ def err_arithmetic_nonfragile_interface : Error<
"this architecture and platform">;
def warn_deprecated_comma_subscript : Warning<
"top-level comma expression in array subscript is deprecated">,
"top-level comma expression in array subscript is deprecated "
"in C++20 and unsupported in C++2b">,
InGroup<DeprecatedCommaSubscript>;
def ext_subscript_non_lvalue : Extension<
@ -8972,6 +8975,12 @@ def err_operator_overload_static : Error<
"overloaded %0 cannot be a static member function">;
def err_operator_overload_default_arg : Error<
"parameter of overloaded %0 cannot have a default argument">;
def ext_subscript_overload : ExtWarn<
"overloaded %0 with %select{no|a defaulted|more than one}1 parameter is a C++2b extension">, InGroup<CXXPre2bCompat>, DefaultIgnore;
def error_subscript_overload : Error<
"overloaded %0 cannot have %select{no|a defaulted|more than one}1 parameter before C++2b">;
def err_operator_overload_must_be : Error<
"overloaded %0 must be a %select{unary|binary|unary or binary}2 operator "
"(has %1 parameter%s1)">;

View File

@ -3943,8 +3943,8 @@ public:
FunctionDecl *DefaultedFn);
ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
SourceLocation RLoc,
Expr *Base,Expr *Idx);
SourceLocation RLoc, Expr *Base,
MultiExprArg Args);
ExprResult BuildCallToMemberFunction(Scope *S, Expr *MemExpr,
SourceLocation LParenLoc,
@ -5386,7 +5386,8 @@ public:
tok::TokenKind Kind, Expr *Input);
ExprResult ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc,
Expr *Idx, SourceLocation RLoc);
MultiExprArg ArgExprs,
SourceLocation RLoc);
ExprResult CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
Expr *Idx, SourceLocation RLoc);
@ -7434,10 +7435,16 @@ public:
CheckStructuredBindingMemberAccess(SourceLocation UseLoc,
CXXRecordDecl *DecomposedClass,
DeclAccessPair Field);
AccessResult CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr,
const SourceRange &,
DeclAccessPair FoundDecl);
AccessResult CheckMemberOperatorAccess(SourceLocation Loc,
Expr *ObjectExpr,
Expr *ArgExpr,
DeclAccessPair FoundDecl);
AccessResult CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr,
ArrayRef<Expr *> ArgExprs,
DeclAccessPair FoundDecl);
AccessResult CheckAddressOfMemberAccess(Expr *OvlExpr,
DeclAccessPair FoundDecl);
AccessResult CheckBaseClassAccess(SourceLocation AccessLoc,

View File

@ -1736,21 +1736,16 @@ void StmtPrinter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *Node) {
}
} else if (Kind == OO_Arrow) {
PrintExpr(Node->getArg(0));
} else if (Kind == OO_Call) {
} else if (Kind == OO_Call || Kind == OO_Subscript) {
PrintExpr(Node->getArg(0));
OS << '(';
OS << (Kind == OO_Call ? '(' : '[');
for (unsigned ArgIdx = 1; ArgIdx < Node->getNumArgs(); ++ArgIdx) {
if (ArgIdx > 1)
OS << ", ";
if (!isa<CXXDefaultArgExpr>(Node->getArg(ArgIdx)))
PrintExpr(Node->getArg(ArgIdx));
}
OS << ')';
} else if (Kind == OO_Subscript) {
PrintExpr(Node->getArg(0));
OS << '[';
PrintExpr(Node->getArg(1));
OS << ']';
OS << (Kind == OO_Call ? ')' : ']');
} else if (Node->getNumArgs() == 1) {
OS << getOperatorSpelling(Kind) << ' ';
PrintExpr(Node->getArg(0));

View File

@ -642,6 +642,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_implicit_move", "202011L");
Builder.defineMacro("__cpp_size_t_suffix", "202011L");
Builder.defineMacro("__cpp_if_consteval", "202106L");
Builder.defineMacro("__cpp_­multidimensional_­subscript", "202110L");
}
if (LangOpts.Char8)
Builder.defineMacro("__cpp_char8_t", "201811L");

View File

@ -1835,6 +1835,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
/// primary-expression
/// postfix-expression '[' expression ']'
/// postfix-expression '[' braced-init-list ']'
/// postfix-expression '[' expression-list [opt] ']' [C++2b 12.4.5]
/// postfix-expression '(' argument-expression-list[opt] ')'
/// postfix-expression '.' identifier
/// postfix-expression '->' identifier
@ -1898,30 +1899,58 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
(void)Actions.CorrectDelayedTyposInExpr(LHS);
return ExprError();
}
BalancedDelimiterTracker T(*this, tok::l_square);
T.consumeOpen();
Loc = T.getOpenLocation();
ExprResult Idx, Length, Stride;
ExprResult Length, Stride;
SourceLocation ColonLocFirst, ColonLocSecond;
ExprVector ArgExprs;
bool HasError = false;
PreferredType.enterSubscript(Actions, Tok.getLocation(), LHS.get());
if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
Idx = ParseBraceInitializer();
} else if (getLangOpts().OpenMP) {
ColonProtectionRAIIObject RAII(*this);
// Parse [: or [ expr or [ expr :
if (!Tok.is(tok::colon)) {
// [ expr
Idx = ParseExpression();
// We try to parse a list of indexes in all language mode first
// and, in we find 0 or one index, we try to parse an OpenMP array
// section. This allow us to support C++2b multi dimensional subscript and
// OpenMp sections in the same language mode.
if (!getLangOpts().OpenMP || Tok.isNot(tok::colon)) {
if (!getLangOpts().CPlusPlus2b) {
ExprResult Idx;
if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
Idx = ParseBraceInitializer();
} else {
Idx = ParseExpression(); // May be a comma expression
}
LHS = Actions.CorrectDelayedTyposInExpr(LHS);
Idx = Actions.CorrectDelayedTyposInExpr(Idx);
if (Idx.isInvalid()) {
HasError = true;
} else {
ArgExprs.push_back(Idx.get());
}
} else if (Tok.isNot(tok::r_square)) {
CommaLocsTy CommaLocs;
if (ParseExpressionList(ArgExprs, CommaLocs)) {
LHS = Actions.CorrectDelayedTyposInExpr(LHS);
HasError = true;
}
assert(
(ArgExprs.empty() || ArgExprs.size() == CommaLocs.size() + 1) &&
"Unexpected number of commas!");
}
}
if (ArgExprs.size() <= 1 && getLangOpts().OpenMP) {
ColonProtectionRAIIObject RAII(*this);
if (Tok.is(tok::colon)) {
// Consume ':'
ColonLocFirst = ConsumeToken();
if (Tok.isNot(tok::r_square) &&
(getLangOpts().OpenMP < 50 ||
((Tok.isNot(tok::colon) && getLangOpts().OpenMP >= 50))))
((Tok.isNot(tok::colon) && getLangOpts().OpenMP >= 50)))) {
Length = ParseExpression();
Length = Actions.CorrectDelayedTyposInExpr(Length);
}
}
if (getLangOpts().OpenMP >= 50 &&
(OMPClauseKind == llvm::omp::Clause::OMPC_to ||
@ -1933,27 +1962,23 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
Stride = ParseExpression();
}
}
} else
Idx = ParseExpression();
}
SourceLocation RLoc = Tok.getLocation();
LHS = Actions.CorrectDelayedTyposInExpr(LHS);
Idx = Actions.CorrectDelayedTyposInExpr(Idx);
Length = Actions.CorrectDelayedTyposInExpr(Length);
if (!LHS.isInvalid() && !Idx.isInvalid() && !Length.isInvalid() &&
if (!LHS.isInvalid() && !HasError && !Length.isInvalid() &&
!Stride.isInvalid() && Tok.is(tok::r_square)) {
if (ColonLocFirst.isValid() || ColonLocSecond.isValid()) {
LHS = Actions.ActOnOMPArraySectionExpr(
LHS.get(), Loc, Idx.get(), ColonLocFirst, ColonLocSecond,
Length.get(), Stride.get(), RLoc);
LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0],
ColonLocFirst, ColonLocSecond, Length.get(), Stride.get(), RLoc);
} else {
LHS = Actions.ActOnArraySubscriptExpr(getCurScope(), LHS.get(), Loc,
Idx.get(), RLoc);
ArgExprs, RLoc);
}
} else {
LHS = ExprError();
Idx = ExprError();
}
// Match the ']'.

View File

@ -1761,14 +1761,11 @@ Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc,
return CheckAccess(*this, UseLoc, Entity);
}
/// Checks access to an overloaded member operator, including
/// conversion operators.
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
Expr *ArgExpr,
const SourceRange &Range,
DeclAccessPair Found) {
if (!getLangOpts().AccessControl ||
Found.getAccess() == AS_public)
if (!getLangOpts().AccessControl || Found.getAccess() == AS_public)
return AR_accessible;
const RecordType *RT = ObjectExpr->getType()->castAs<RecordType>();
@ -1776,13 +1773,35 @@ Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
ObjectExpr->getType());
Entity.setDiag(diag::err_access)
<< ObjectExpr->getSourceRange()
<< (ArgExpr ? ArgExpr->getSourceRange() : SourceRange());
Entity.setDiag(diag::err_access) << ObjectExpr->getSourceRange() << Range;
return CheckAccess(*this, OpLoc, Entity);
}
/// Checks access to an overloaded member operator, including
/// conversion operators.
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
Expr *ArgExpr,
DeclAccessPair Found) {
return CheckMemberOperatorAccess(
OpLoc, ObjectExpr, ArgExpr ? ArgExpr->getSourceRange() : SourceRange(),
Found);
}
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
ArrayRef<Expr *> ArgExprs,
DeclAccessPair FoundDecl) {
SourceRange R;
if (!ArgExprs.empty()) {
R = SourceRange(ArgExprs.front()->getBeginLoc(),
ArgExprs.back()->getEndLoc());
}
return CheckMemberOperatorAccess(OpLoc, ObjectExpr, R, FoundDecl);
}
/// Checks access to the target of a friend declaration.
Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) {
assert(isa<CXXMethodDecl>(target->getAsFunction()));

View File

@ -15865,14 +15865,29 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
// An operator function cannot have default arguments (8.3.6),
// except where explicitly stated below.
//
// Only the function-call operator allows default arguments
// (C++ [over.call]p1).
// Only the function-call operator (C++ [over.call]p1) and the subscript
// operator (CWG2507) allow default arguments.
if (Op != OO_Call) {
ParmVarDecl *FirstDefaultedParam = nullptr;
for (auto Param : FnDecl->parameters()) {
if (Param->hasDefaultArg())
return Diag(Param->getLocation(),
if (Param->hasDefaultArg()) {
FirstDefaultedParam = Param;
break;
}
}
if (FirstDefaultedParam) {
if (Op == OO_Subscript) {
Diag(FnDecl->getLocation(), LangOpts.CPlusPlus2b
? diag::ext_subscript_overload
: diag::error_subscript_overload)
<< FnDecl->getDeclName() << 1
<< FirstDefaultedParam->getDefaultArgRange();
} else {
return Diag(FirstDefaultedParam->getLocation(),
diag::err_operator_overload_default_arg)
<< FnDecl->getDeclName() << Param->getDefaultArgRange();
<< FnDecl->getDeclName()
<< FirstDefaultedParam->getDefaultArgRange();
}
}
}
@ -15893,10 +15908,10 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
// described in the rest of this subclause.
unsigned NumParams = FnDecl->getNumParams()
+ (isa<CXXMethodDecl>(FnDecl)? 1 : 0);
if (Op != OO_Call &&
if (Op != OO_Call && Op != OO_Subscript &&
((NumParams == 1 && !CanBeUnaryOperator) ||
(NumParams == 2 && !CanBeBinaryOperator) ||
(NumParams < 1) || (NumParams > 2))) {
(NumParams == 2 && !CanBeBinaryOperator) || (NumParams < 1) ||
(NumParams > 2))) {
// We have the wrong number of parameters.
unsigned ErrorKind;
if (CanBeUnaryOperator && CanBeBinaryOperator) {
@ -15908,16 +15923,23 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
"All non-call overloaded operators are unary or binary!");
ErrorKind = 1; // 1 -> binary
}
return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be)
<< FnDecl->getDeclName() << NumParams << ErrorKind;
}
// Overloaded operators other than operator() cannot be variadic.
if (Op == OO_Subscript && NumParams != 2) {
Diag(FnDecl->getLocation(), LangOpts.CPlusPlus2b
? diag::ext_subscript_overload
: diag::error_subscript_overload)
<< FnDecl->getDeclName() << (NumParams == 1 ? 0 : 2);
}
// Overloaded operators other than operator() and operator[] cannot be
// variadic.
if (Op != OO_Call &&
FnDecl->getType()->castAs<FunctionProtoType>()->isVariadic()) {
return Diag(FnDecl->getLocation(), diag::err_operator_overload_variadic)
<< FnDecl->getDeclName();
<< FnDecl->getDeclName();
}
// Some operators must be non-static member functions.

View File

@ -4681,19 +4681,24 @@ static QualType getDependentArraySubscriptType(Expr *LHS, Expr *RHS,
return Result->isDependentType() ? Result : Ctx.DependentTy;
}
ExprResult
Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
Expr *idx, SourceLocation rbLoc) {
static bool checkArgsForPlaceholders(Sema &S, MultiExprArg args);
ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
SourceLocation lbLoc,
MultiExprArg ArgExprs,
SourceLocation rbLoc) {
if (base && !base->getType().isNull() &&
base->hasPlaceholderType(BuiltinType::OMPArraySection))
return ActOnOMPArraySectionExpr(base, lbLoc, idx, SourceLocation(),
return ActOnOMPArraySectionExpr(base, lbLoc, ArgExprs.front(), SourceLocation(),
SourceLocation(), /*Length*/ nullptr,
/*Stride=*/nullptr, rbLoc);
// Since this might be a postfix expression, get rid of ParenListExprs.
if (isa<ParenListExpr>(base)) {
ExprResult result = MaybeConvertParenListExprToParenExpr(S, base);
if (result.isInvalid()) return ExprError();
if (result.isInvalid())
return ExprError();
base = result.get();
}
@ -4721,13 +4726,15 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
// MatrixSubscriptExpr.
auto *matSubscriptE = dyn_cast<MatrixSubscriptExpr>(base);
if (matSubscriptE) {
if (CheckAndReportCommaError(idx))
assert(ArgExprs.size() == 1);
if (CheckAndReportCommaError(ArgExprs.front()))
return ExprError();
assert(matSubscriptE->isIncomplete() &&
"base has to be an incomplete matrix subscript");
return CreateBuiltinMatrixSubscriptExpr(
matSubscriptE->getBase(), matSubscriptE->getRowIdx(), idx, rbLoc);
return CreateBuiltinMatrixSubscriptExpr(matSubscriptE->getBase(),
matSubscriptE->getRowIdx(),
ArgExprs.front(), rbLoc);
}
// Handle any non-overload placeholder types in the base and index
@ -4748,32 +4755,42 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
// If the base is a matrix type, try to create a new MatrixSubscriptExpr.
if (base->getType()->isMatrixType()) {
if (CheckAndReportCommaError(idx))
assert(ArgExprs.size() == 1);
if (CheckAndReportCommaError(ArgExprs.front()))
return ExprError();
return CreateBuiltinMatrixSubscriptExpr(base, idx, nullptr, rbLoc);
return CreateBuiltinMatrixSubscriptExpr(base, ArgExprs.front(), nullptr,
rbLoc);
}
// A comma-expression as the index is deprecated in C++2a onwards.
if (getLangOpts().CPlusPlus20 &&
((isa<BinaryOperator>(idx) && cast<BinaryOperator>(idx)->isCommaOp()) ||
(isa<CXXOperatorCallExpr>(idx) &&
cast<CXXOperatorCallExpr>(idx)->getOperator() == OO_Comma))) {
Diag(idx->getExprLoc(), diag::warn_deprecated_comma_subscript)
<< SourceRange(base->getBeginLoc(), rbLoc);
if (ArgExprs.size() == 1 && getLangOpts().CPlusPlus20) {
Expr *idx = ArgExprs[0];
if ((isa<BinaryOperator>(idx) && cast<BinaryOperator>(idx)->isCommaOp()) ||
(isa<CXXOperatorCallExpr>(idx) &&
cast<CXXOperatorCallExpr>(idx)->getOperator() == OO_Comma)) {
Diag(idx->getExprLoc(), diag::warn_deprecated_comma_subscript)
<< SourceRange(base->getBeginLoc(), rbLoc);
}
}
if (idx->getType()->isNonOverloadPlaceholderType()) {
ExprResult result = CheckPlaceholderExpr(idx);
if (result.isInvalid()) return ExprError();
idx = result.get();
if (ArgExprs.size() == 1 &&
ArgExprs[0]->getType()->isNonOverloadPlaceholderType()) {
ExprResult result = CheckPlaceholderExpr(ArgExprs[0]);
if (result.isInvalid())
return ExprError();
ArgExprs[0] = result.get();
} else {
if (checkArgsForPlaceholders(*this, ArgExprs))
return ExprError();
}
// Build an unanalyzed expression if either operand is type-dependent.
if (getLangOpts().CPlusPlus &&
(base->isTypeDependent() || idx->isTypeDependent())) {
if (getLangOpts().CPlusPlus && ArgExprs.size() == 1 &&
(base->isTypeDependent() ||
Expr::hasAnyTypeDependentArguments(ArgExprs))) {
return new (Context) ArraySubscriptExpr(
base, idx, getDependentArraySubscriptType(base, idx, getASTContext()),
base, ArgExprs.front(),
getDependentArraySubscriptType(base, ArgExprs.front(), getASTContext()),
VK_LValue, OK_Ordinary, rbLoc);
}
@ -4786,10 +4803,12 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
// 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);
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, idx, Context.PseudoObjectTy, VK_LValue, OK_Ordinary, rbLoc);
return new (Context)
MSPropertySubscriptExpr(base, ArgExprs.front(), Context.PseudoObjectTy,
VK_LValue, OK_Ordinary, rbLoc);
}
// Use C++ overloaded-operator rules if either operand has record
@ -4800,14 +4819,14 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
//
// ObjC pointers have their own subscripting logic that is not tied
// to overload resolution and so should not take this path.
if (getLangOpts().CPlusPlus &&
(base->getType()->isRecordType() ||
(!base->getType()->isObjCObjectPointerType() &&
idx->getType()->isRecordType()))) {
return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, idx);
if (getLangOpts().CPlusPlus && !base->getType()->isObjCObjectPointerType() &&
((base->getType()->isRecordType() ||
(ArgExprs.size() != 1 || ArgExprs[0]->getType()->isRecordType())))) {
return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, ArgExprs);
}
ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc);
ExprResult Res =
CreateBuiltinArraySubscriptExpr(base, lbLoc, ArgExprs.front(), rbLoc);
if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get()))
CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get()));
@ -6582,9 +6601,9 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
auto ArgAS = ArgPtTy.getAddressSpace();
// Add address space cast if target address spaces are different
bool NeedImplicitASC =
bool NeedImplicitASC =
ParamAS != LangAS::Default && // Pointer params in generic AS don't need special handling.
( ArgAS == LangAS::Default || // We do allow implicit conversion from generic AS
( ArgAS == LangAS::Default || // We do allow implicit conversion from generic AS
// or from specific AS which has target AS matching that of Param.
getASTContext().getTargetAddressSpace(ArgAS) == getASTContext().getTargetAddressSpace(ParamAS));
if (!NeedImplicitASC)

View File

@ -49,11 +49,10 @@ static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) {
}
/// A convenience routine for creating a decayed reference to a function.
static ExprResult
CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl,
const Expr *Base, bool HadMultipleCandidates,
SourceLocation Loc = SourceLocation(),
const DeclarationNameLoc &LocInfo = DeclarationNameLoc()){
static ExprResult CreateFunctionRefExpr(
Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, const Expr *Base,
bool HadMultipleCandidates, SourceLocation Loc = SourceLocation(),
const DeclarationNameLoc &LocInfo = DeclarationNameLoc()) {
if (S.DiagnoseUseOfDecl(FoundDecl, Loc))
return ExprError();
// If FoundDecl is different from Fn (such as if one is a template
@ -984,8 +983,7 @@ checkPlaceholderForOverload(Sema &S, Expr *&E,
/// checkArgPlaceholdersForOverload - Check a set of call operands for
/// placeholders.
static bool checkArgPlaceholdersForOverload(Sema &S,
MultiExprArg Args,
static bool checkArgPlaceholdersForOverload(Sema &S, MultiExprArg Args,
UnbridgedCastsSet &unbridged) {
for (unsigned i = 0, e = Args.size(); i != e; ++i)
if (checkPlaceholderForOverload(S, Args[i], &unbridged))
@ -9352,7 +9350,8 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
break;
case OO_Subscript:
OpBuilder.addSubscriptOverloads();
if (Args.size() == 2)
OpBuilder.addSubscriptOverloads();
break;
case OO_ArrowStar:
@ -11617,7 +11616,9 @@ CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
// Conversion 0 is 'this', which doesn't have a corresponding parameter.
ConvIdx = 1;
if (CSK == OverloadCandidateSet::CSK_Operator &&
Cand->Function->getDeclName().getCXXOverloadedOperator() != OO_Call)
Cand->Function->getDeclName().getCXXOverloadedOperator() != OO_Call &&
Cand->Function->getDeclName().getCXXOverloadedOperator() !=
OO_Subscript)
// Argument 0 is 'this', which doesn't have a corresponding parameter.
ArgIdx = 1;
}
@ -14055,17 +14056,65 @@ ExprResult Sema::BuildSynthesizedThreeWayComparison(
return PseudoObjectExpr::Create(Context, SyntacticForm, SemanticForm, 2);
}
ExprResult
Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
SourceLocation RLoc,
Expr *Base, Expr *Idx) {
Expr *Args[2] = { Base, Idx };
static bool PrepareArgumentsForCallToObjectOfClassType(
Sema &S, SmallVectorImpl<Expr *> &MethodArgs, CXXMethodDecl *Method,
MultiExprArg Args, SourceLocation LParenLoc) {
const auto *Proto = Method->getType()->castAs<FunctionProtoType>();
unsigned NumParams = Proto->getNumParams();
unsigned NumArgsSlots =
MethodArgs.size() + std::max<unsigned>(Args.size(), NumParams);
// Build the full argument list for the method call (the implicit object
// parameter is placed at the beginning of the list).
MethodArgs.reserve(MethodArgs.size() + NumArgsSlots);
bool IsError = false;
// Initialize the implicit object parameter.
// Check the argument types.
for (unsigned i = 0; i != NumParams; i++) {
Expr *Arg;
if (i < Args.size()) {
Arg = Args[i];
ExprResult InputInit =
S.PerformCopyInitialization(InitializedEntity::InitializeParameter(
S.Context, Method->getParamDecl(i)),
SourceLocation(), Arg);
IsError |= InputInit.isInvalid();
Arg = InputInit.getAs<Expr>();
} else {
ExprResult DefArg =
S.BuildCXXDefaultArgExpr(LParenLoc, Method, Method->getParamDecl(i));
if (DefArg.isInvalid()) {
IsError = true;
break;
}
Arg = DefArg.getAs<Expr>();
}
MethodArgs.push_back(Arg);
}
return IsError;
}
ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
SourceLocation RLoc,
Expr *Base,
MultiExprArg ArgExpr) {
SmallVector<Expr *, 2> Args;
Args.push_back(Base);
for (auto e : ArgExpr) {
Args.push_back(e);
}
DeclarationName OpName =
Context.DeclarationNames.getCXXOperatorName(OO_Subscript);
SourceRange Range = ArgExpr.empty()
? SourceRange{}
: SourceRange(ArgExpr.front()->getBeginLoc(),
ArgExpr.back()->getEndLoc());
// If either side is type-dependent, create an appropriate dependent
// expression.
if (Args[0]->isTypeDependent() || Args[1]->isTypeDependent()) {
if (Expr::hasAnyTypeDependentArguments(Args)) {
CXXRecordDecl *NamingClass = nullptr; // lookup ignores member operators
// CHECKME: no 'operator' keyword?
@ -14082,12 +14131,11 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
CurFPFeatureOverrides());
}
// Handle placeholders on both operands.
if (checkPlaceholderForOverload(*this, Args[0]))
// Handle placeholders
UnbridgedCastsSet UnbridgedCasts;
if (checkArgPlaceholdersForOverload(*this, Args, UnbridgedCasts)) {
return ExprError();
if (checkPlaceholderForOverload(*this, Args[1]))
return ExprError();
}
// Build an empty overload set.
OverloadCandidateSet CandidateSet(LLoc, OverloadCandidateSet::CSK_Operator);
@ -14097,7 +14145,8 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
AddMemberOperatorCandidates(OO_Subscript, LLoc, Args, CandidateSet);
// Add builtin operator candidates.
AddBuiltinOperatorCandidates(OO_Subscript, LLoc, Args, CandidateSet);
if (Args.size() == 2)
AddBuiltinOperatorCandidates(OO_Subscript, LLoc, Args, CandidateSet);
bool HadMultipleCandidates = (CandidateSet.size() > 1);
@ -14112,38 +14161,28 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
// We matched an overloaded operator. Build a call to that
// operator.
CheckMemberOperatorAccess(LLoc, Args[0], Args[1], Best->FoundDecl);
CheckMemberOperatorAccess(LLoc, Args[0], ArgExpr, Best->FoundDecl);
// Convert the arguments.
CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl);
ExprResult Arg0 =
PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr,
Best->FoundDecl, Method);
SmallVector<Expr *, 2> MethodArgs;
ExprResult Arg0 = PerformObjectArgumentInitialization(
Args[0], /*Qualifier=*/nullptr, Best->FoundDecl, Method);
if (Arg0.isInvalid())
return ExprError();
Args[0] = Arg0.get();
// Convert the arguments.
ExprResult InputInit
= PerformCopyInitialization(InitializedEntity::InitializeParameter(
Context,
FnDecl->getParamDecl(0)),
SourceLocation(),
Args[1]);
if (InputInit.isInvalid())
MethodArgs.push_back(Arg0.get());
bool IsError = PrepareArgumentsForCallToObjectOfClassType(
*this, MethodArgs, Method, ArgExpr, LLoc);
if (IsError)
return ExprError();
Args[1] = InputInit.getAs<Expr>();
// Build the actual expression node.
DeclarationNameInfo OpLocInfo(OpName, LLoc);
OpLocInfo.setCXXOperatorNameRange(SourceRange(LLoc, RLoc));
ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl,
Best->FoundDecl,
Base,
HadMultipleCandidates,
OpLocInfo.getLoc(),
OpLocInfo.getInfo());
ExprResult FnExpr = CreateFunctionRefExpr(
*this, FnDecl, Best->FoundDecl, Base, HadMultipleCandidates,
OpLocInfo.getLoc(), OpLocInfo.getInfo());
if (FnExpr.isInvalid())
return ExprError();
@ -14153,7 +14192,7 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
ResultTy = ResultTy.getNonLValueExprType(Context);
CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create(
Context, OO_Subscript, FnExpr.get(), Args, ResultTy, VK, RLoc,
Context, OO_Subscript, FnExpr.get(), MethodArgs, ResultTy, VK, RLoc,
CurFPFeatureOverrides());
if (CheckCallReturnType(FnDecl->getReturnType(), LLoc, TheCall, FnDecl))
return ExprError();
@ -14187,33 +14226,41 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
}
case OR_No_Viable_Function: {
PartialDiagnostic PD = CandidateSet.empty()
? (PDiag(diag::err_ovl_no_oper)
<< Args[0]->getType() << /*subscript*/ 0
<< Args[0]->getSourceRange() << Args[1]->getSourceRange())
: (PDiag(diag::err_ovl_no_viable_subscript)
<< Args[0]->getType() << Args[0]->getSourceRange()
<< Args[1]->getSourceRange());
PartialDiagnostic PD =
CandidateSet.empty()
? (PDiag(diag::err_ovl_no_oper)
<< Args[0]->getType() << /*subscript*/ 0
<< Args[0]->getSourceRange() << Range)
: (PDiag(diag::err_ovl_no_viable_subscript)
<< Args[0]->getType() << Args[0]->getSourceRange() << Range);
CandidateSet.NoteCandidates(PartialDiagnosticAt(LLoc, PD), *this,
OCD_AllCandidates, Args, "[]", LLoc);
OCD_AllCandidates, ArgExpr, "[]", LLoc);
return ExprError();
}
case OR_Ambiguous:
CandidateSet.NoteCandidates(
PartialDiagnosticAt(LLoc, PDiag(diag::err_ovl_ambiguous_oper_binary)
<< "[]" << Args[0]->getType()
<< Args[1]->getType()
<< Args[0]->getSourceRange()
<< Args[1]->getSourceRange()),
*this, OCD_AmbiguousCandidates, Args, "[]", LLoc);
if (Args.size() == 2) {
CandidateSet.NoteCandidates(
PartialDiagnosticAt(
LLoc, PDiag(diag::err_ovl_ambiguous_oper_binary)
<< "[]" << Args[0]->getType() << Args[1]->getType()
<< Args[0]->getSourceRange() << Range),
*this, OCD_AmbiguousCandidates, Args, "[]", LLoc);
} else {
CandidateSet.NoteCandidates(
PartialDiagnosticAt(LLoc,
PDiag(diag::err_ovl_ambiguous_subscript_call)
<< Args[0]->getType()
<< Args[0]->getSourceRange() << Range),
*this, OCD_AmbiguousCandidates, Args, "[]", LLoc);
}
return ExprError();
case OR_Deleted:
CandidateSet.NoteCandidates(
PartialDiagnosticAt(LLoc, PDiag(diag::err_ovl_deleted_oper)
<< "[]" << Args[0]->getSourceRange()
<< Args[1]->getSourceRange()),
<< Range),
*this, OCD_AllCandidates, Args, "[]", LLoc);
return ExprError();
}
@ -14717,14 +14764,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
if (NewFn.isInvalid())
return true;
// The number of argument slots to allocate in the call. If we have default
// arguments we need to allocate space for them as well. We additionally
// need one more slot for the object parameter.
unsigned NumArgsSlots = 1 + std::max<unsigned>(Args.size(), NumParams);
// Build the full argument list for the method call (the implicit object
// parameter is placed at the beginning of the list).
SmallVector<Expr *, 8> MethodArgs(NumArgsSlots);
SmallVector<Expr *, 8> MethodArgs;
MethodArgs.reserve(NumParams + 1);
bool IsError = false;
@ -14736,37 +14777,10 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
IsError = true;
else
Object = ObjRes;
MethodArgs[0] = Object.get();
MethodArgs.push_back(Object.get());
// Check the argument types.
for (unsigned i = 0; i != NumParams; i++) {
Expr *Arg;
if (i < Args.size()) {
Arg = Args[i];
// Pass the argument.
ExprResult InputInit
= PerformCopyInitialization(InitializedEntity::InitializeParameter(
Context,
Method->getParamDecl(i)),
SourceLocation(), Arg);
IsError |= InputInit.isInvalid();
Arg = InputInit.getAs<Expr>();
} else {
ExprResult DefArg
= BuildCXXDefaultArgExpr(LParenLoc, Method, Method->getParamDecl(i));
if (DefArg.isInvalid()) {
IsError = true;
break;
}
Arg = DefArg.getAs<Expr>();
}
MethodArgs[i + 1] = Arg;
}
IsError |= PrepareArgumentsForCallToObjectOfClassType(
*this, MethodArgs, Method, Args, LParenLoc);
// If this is a variadic call, handle args passed through "...".
if (Proto->isVariadic()) {
@ -14775,7 +14789,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod,
nullptr);
IsError |= Arg.isInvalid();
MethodArgs[i + 1] = Arg.get();
MethodArgs.push_back(Arg.get());
}
}

View File

@ -2629,6 +2629,13 @@ public:
/*Scope=*/nullptr, Callee, LParenLoc, Args, RParenLoc, ExecConfig);
}
ExprResult RebuildCxxSubscriptExpr(Expr *Callee, SourceLocation LParenLoc,
MultiExprArg Args,
SourceLocation RParenLoc) {
return getSema().ActOnArraySubscriptExpr(
/*Scope=*/nullptr, Callee, LParenLoc, Args, RParenLoc);
}
/// Build a new member access expression.
///
/// By default, performs semantic analysis to build the new expression.
@ -11507,6 +11514,7 @@ TreeTransform<Derived>::TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
case OO_Array_Delete:
llvm_unreachable("new and delete operators cannot use CXXOperatorCallExpr");
case OO_Subscript:
case OO_Call: {
// This is a call to an object's operator().
assert(E->getNumArgs() >= 1 && "Object call is missing arguments");
@ -11526,17 +11534,20 @@ TreeTransform<Derived>::TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
Args))
return ExprError();
if (E->getOperator() == OO_Subscript)
return getDerived().RebuildCxxSubscriptExpr(Object.get(), FakeLParenLoc,
Args, E->getEndLoc());
return getDerived().RebuildCallExpr(Object.get(), FakeLParenLoc, Args,
E->getEndLoc());
}
#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \
case OO_##Name:
#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
case OO_##Name: \
break;
#define OVERLOADED_OPERATOR_MULTI(Name,Spelling,Unary,Binary,MemberOnly)
#include "clang/Basic/OperatorKinds.def"
case OO_Subscript:
// Handled below.
break;
case OO_Conditional:
llvm_unreachable("conditional operator is not actually overloadable");

View File

@ -6,6 +6,8 @@
// RUN: %clang_cc1 -verify=expected,ge50,lt51 -fopenmp-simd -fopenmp-version=50 -ferror-limit 100 -o - -std=c++11 %s -Wuninitialized
// RUN: %clang_cc1 -verify=expected,ge50,ge51 -fopenmp-simd -fopenmp-version=51 -ferror-limit 100 -o - -std=c++11 %s -Wuninitialized
// RUN: %clang_cc1 -verify=expected,ge50,ge51,cxx2b -fopenmp -fopenmp-simd -fopenmp-version=51 -x c++ -std=c++2b %s -Wuninitialized
void xxx(int argc) {
int x; // expected-note {{initialize the variable 'x' to silence this warning}}
#pragma omp target update to(x)
@ -209,3 +211,43 @@ struct bar {
foo();
}
};
#if defined(__cplusplus) && __cplusplus >= 202101L
namespace cxx2b {
struct S {
int operator[](auto...);
};
void f() {
int test[10];
#pragma omp target update to(test[1])
#pragma omp target update to(test[1, 2]) // cxx2b-error {{type 'int[10]' does not provide a subscript operator}} \
// cxx2b-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])
#pragma omp target update to(test [1, 2:1:1]) // cxx2b-error {{expected ']'}} // expected-note {{'['}} \
// cxx2b-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(test [1, 2:]) // cxx2b-error {{expected ']'}} // expected-note {{'['}} \
// cxx2b-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(test[1, 2 ::]) // cxx2b-error {{expected ']'}} // expected-note {{'['}} \
// cxx2b-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(test[]) // cxx2b-error {{type 'int[10]' does not provide a subscript operator}} \
// cxx2b-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
S s;
(void)s[0];
(void)s[];
(void)s[1, 2];
}
}
#endif

View File

@ -0,0 +1,58 @@
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx2b -std=c++2b %s
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx20 -std=c++20 %s
//cxx2b-no-diagnostics
struct S {
constexpr int operator[](int i) {
return i;
}
constexpr int operator[](int a, int b) { // cxx20-error {{overloaded 'operator[]' cannot have more than one parameter before C++2b}}
return a + b;
}
constexpr int operator[]() { // cxx20-error {{overloaded 'operator[]' cannot have no parameter before C++2b}}
return 42;
}
};
struct Defaults {
constexpr int operator[](int i = 0) { // cxx20-error {{overloaded 'operator[]' cannot have a defaulted parameter before C++2b}}
return 0;
}
constexpr int operator[](int a, int b, int c = 0) { // cxx20-error {{overloaded 'operator[]' cannot have a defaulted parameter before C++2b}}\
// cxx20-error {{cannot have more than one parameter before C++2b}}
return 0;
}
};
template <typename... T>
struct T1 {
constexpr auto operator[](T &&...arg); // cxx20-error {{overloaded 'operator[]' cannot have no parameter before C++2b}} \
// cxx20-error {{overloaded 'operator[]' cannot have more than one parameter before C++2b}}
};
T1<> t10; // cxx20-note {{requested here}}
T1<int, int> t12; // cxx20-note {{requested here}}
T1<int> t11;
struct Variadic {
constexpr int operator[](auto &&...arg) { return 0; }
};
void f() {
S s;
(void)s[0];
(void)s[1, 2]; // cxx20-warning {{left operand of comma operator has no effect}}\
// cxx20-warning {{top-level comma expression in array subscript is deprecated in C++20 and unsupported in C++2b}}
(void)S{}[]; // cxx20-error {{expected expression}}
(void)Defaults{}[1];
(void)Defaults{}[]; // cxx20-error {{expected expression}}
(void)Defaults{}[1, 2]; // cxx20-warning {{left operand of comma operator has no effect}}\
// cxx20-warning {{top-level comma expression in array subscript is deprecated in C++20 and unsupported in C++2b}}
Variadic{}[]; // cxx20-error {{expected expression}}
Variadic{}[1];
Variadic{}[1, 2]; // cxx20-warning {{left operand of comma operator has no effect}}\
// cxx20-warning {{top-level comma expression in array subscript is deprecated in C++20 and unsupported in C++2b}}
}

View File

@ -0,0 +1,75 @@
// RUN: %clang_cc1 -verify -std=c++2b %s
namespace N {
void empty() {
struct S {
int operator[](); // expected-note{{not viable: requires 0 arguments, but 1 was provided}}
};
S{}[];
S{}[1]; // expected-error {{no viable overloaded operator[] for type 'S'}}
}
void default_var() {
struct S {
constexpr int operator[](int i = 42) { return i; } // expected-note {{not viable: allows at most single argument 'i'}}
};
static_assert(S{}[] == 42);
static_assert(S{}[1] == 1);
static_assert(S{}[1, 2] == 1); // expected-error {{no viable overloaded operator[] for type 'S'}}
}
struct Variadic {
constexpr int operator[](auto... i) { return (42 + ... + i); }
};
void variadic() {
static_assert(Variadic{}[] == 42);
static_assert(Variadic{}[1] == 43);
static_assert(Variadic{}[1, 2] == 45);
}
void multiple() {
struct S {
constexpr int operator[]() { return 0; }
constexpr int operator[](int) { return 1; };
constexpr int operator[](int, int) { return 2; };
};
static_assert(S{}[] == 0);
static_assert(S{}[1] == 1);
static_assert(S{}[1, 1] == 2);
}
void ambiguous() {
struct S {
constexpr int operator[]() { return 0; } // expected-note{{candidate function}}
constexpr int operator[](int = 0) { return 1; }; // expected-note{{candidate function}}
};
static_assert(S{}[] == 0); // expected-error{{call to subscript operator of type 'S' is ambiguous}}
}
} // namespace N
template <typename... T>
struct T1 {
constexpr auto operator[](T... arg) { // expected-note {{candidate function not viable: requires 2 arguments, but 1 was provided}}
return (1 + ... + arg);
}
};
static_assert(T1<>{}[] == 1);
static_assert(T1<int>{}[1] == 2);
static_assert(T1<int, int>{}[1, 1] == 3);
static_assert(T1<int, int>{}[1] == 3); // expected-error {{no viable overloaded operator[] for type 'T1<int, int>'}}
struct T2 {
constexpr auto operator[](auto... arg) {
return (1 + ... + arg);
}
};
static_assert(T2{}[] == 1);
static_assert(T2{}[1] == 2);
static_assert(T2{}[1, 1] == 3);

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2b %s
struct Sub0 {
int &operator[](int);

View File

@ -1356,7 +1356,7 @@ C++20, informally referred to as C++2b.</p>
<tr>
<td>Multidimensional subscript operator</td>
<td><a href="https://wg21.link/P2128R6">P2128R6</a></td>
<td class="none" align="center">No</td>
<td class="unreleased" align="center">Clang 15</td>
</tr>
<tr>
<td>Non-literal variables (and labels and gotos) in constexpr functions</td>