diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp @@ -398,8 +398,8 @@ 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; } diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -92,6 +92,8 @@ C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Implemented `P2128R6: Multidimensional subscript operator `_. + CUDA Language Changes in Clang ------------------------------ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4666,6 +4666,8 @@ "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 @@ "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; def ext_subscript_non_lvalue : Extension< @@ -8972,6 +8975,12 @@ "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, 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)">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3943,8 +3943,8 @@ 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 @@ 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 @@ 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 ArgExprs, + DeclAccessPair FoundDecl); AccessResult CheckAddressOfMemberAccess(Expr *OvlExpr, DeclAccessPair FoundDecl); AccessResult CheckBaseClassAccess(SourceLocation AccessLoc, diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1736,21 +1736,16 @@ } } 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(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)); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -642,6 +642,7 @@ 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"); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1835,6 +1835,7 @@ /// 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 @@ (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 @@ 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 ']'. diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -1761,14 +1761,11 @@ 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(); @@ -1776,13 +1773,35 @@ 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 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(target->getAsFunction())); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -15865,14 +15865,29 @@ // 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 @@ // described in the rest of this subclause. unsigned NumParams = FnDecl->getNumParams() + (isa(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 @@ "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()->isVariadic()) { return Diag(FnDecl->getLocation(), diag::err_operator_overload_variadic) - << FnDecl->getDeclName(); + << FnDecl->getDeclName(); } // Some operators must be non-static member functions. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4681,19 +4681,24 @@ 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(base)) { ExprResult result = MaybeConvertParenListExprToParenExpr(S, base); - if (result.isInvalid()) return ExprError(); + if (result.isInvalid()) + return ExprError(); base = result.get(); } @@ -4721,13 +4726,15 @@ // MatrixSubscriptExpr. auto *matSubscriptE = dyn_cast(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 @@ // 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(idx) && cast(idx)->isCommaOp()) || - (isa(idx) && - cast(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(idx) && cast(idx)->isCommaOp()) || + (isa(idx) && + cast(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 @@ // 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 @@ // // 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(Res.get())) CheckSubscriptAccessOfNoDeref(cast(Res.get())); @@ -6582,9 +6601,9 @@ 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) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -49,11 +49,10 @@ } /// 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 @@ /// 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 @@ break; case OO_Subscript: - OpBuilder.addSubscriptOverloads(); + if (Args.size() == 2) + OpBuilder.addSubscriptOverloads(); break; case OO_ArrowStar: @@ -11617,7 +11616,9 @@ // 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 @@ 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 &MethodArgs, CXXMethodDecl *Method, + MultiExprArg Args, SourceLocation LParenLoc) { + + const auto *Proto = Method->getType()->castAs(); + unsigned NumParams = Proto->getNumParams(); + unsigned NumArgsSlots = + MethodArgs.size() + std::max(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(); + } else { + ExprResult DefArg = + S.BuildCXXDefaultArgExpr(LParenLoc, Method, Method->getParamDecl(i)); + if (DefArg.isInvalid()) { + IsError = true; + break; + } + Arg = DefArg.getAs(); + } + + MethodArgs.push_back(Arg); + } + return IsError; +} + +ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, + SourceLocation RLoc, + Expr *Base, + MultiExprArg ArgExpr) { + SmallVector 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 @@ CurFPFeatureOverrides()); } - // Handle placeholders on both operands. - if (checkPlaceholderForOverload(*this, Args[0])) - return ExprError(); - if (checkPlaceholderForOverload(*this, Args[1])) + // Handle placeholders + UnbridgedCastsSet UnbridgedCasts; + if (checkArgPlaceholdersForOverload(*this, Args, UnbridgedCasts)) { return ExprError(); - + } // Build an empty overload set. OverloadCandidateSet CandidateSet(LLoc, OverloadCandidateSet::CSK_Operator); @@ -14097,7 +14145,8 @@ 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 @@ // 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(FnDecl); - ExprResult Arg0 = - PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr, - Best->FoundDecl, Method); + SmallVector 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(); - // 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 @@ 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 @@ } 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 @@ 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(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 MethodArgs(NumArgsSlots); + SmallVector MethodArgs; + MethodArgs.reserve(NumParams + 1); bool IsError = false; @@ -14736,37 +14777,10 @@ IsError = true; else Object = ObjRes; - MethodArgs[0] = 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(); - } else { - ExprResult DefArg - = BuildCXXDefaultArgExpr(LParenLoc, Method, Method->getParamDecl(i)); - if (DefArg.isInvalid()) { - IsError = true; - break; - } + MethodArgs.push_back(Object.get()); - Arg = DefArg.getAs(); - } - - 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 @@ ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, nullptr); IsError |= Arg.isInvalid(); - MethodArgs[i + 1] = Arg.get(); + MethodArgs.push_back(Arg.get()); } } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2629,6 +2629,13 @@ /*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 @@ 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 @@ 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"); diff --git a/clang/test/OpenMP/target_update_messages.cpp b/clang/test/OpenMP/target_update_messages.cpp --- a/clang/test/OpenMP/target_update_messages.cpp +++ b/clang/test/OpenMP/target_update_messages.cpp @@ -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 @@ 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 diff --git a/clang/test/Parser/cxx2b-subscript.cpp b/clang/test/Parser/cxx2b-subscript.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/cxx2b-subscript.cpp @@ -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 +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 t12; // cxx20-note {{requested here}} +T1 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}} +} diff --git a/clang/test/SemaCXX/cxx2b-overloaded-operator.cpp b/clang/test/SemaCXX/cxx2b-overloaded-operator.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/cxx2b-overloaded-operator.cpp @@ -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 +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{}[1] == 2); +static_assert(T1{}[1, 1] == 3); +static_assert(T1{}[1] == 3); // expected-error {{no viable overloaded operator[] for type 'T1'}} + +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); diff --git a/clang/test/SemaTemplate/instantiate-subscript.cpp b/clang/test/SemaTemplate/instantiate-subscript.cpp --- a/clang/test/SemaTemplate/instantiate-subscript.cpp +++ b/clang/test/SemaTemplate/instantiate-subscript.cpp @@ -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); diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1356,7 +1356,7 @@ Multidimensional subscript operator P2128R6 - No + Clang 15 Non-literal variables (and labels and gotos) in constexpr functions