diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -354,6 +354,7 @@ ^^^^^^^^^^^^^^^^^^^^^ - Support label at end of compound statement (`P2324 `_). +- Implemented `P1169R4: static operator() `_. CUDA/HIP Language Changes in Clang ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -998,7 +998,8 @@ "lambda expressions are incompatible with C++98">, InGroup, DefaultIgnore; def err_lambda_decl_specifier_repeated : Error< - "%select{'mutable'|'constexpr'|'consteval'}0 cannot appear multiple times in a lambda declarator">; + "%select{'mutable'|'static'|'constexpr'|'consteval'}0 cannot " + "appear multiple times in a lambda declarator">; def err_lambda_capture_misplaced_ellipsis : Error< "ellipsis in pack %select{|init-}0capture must appear %select{after|before}0 " "the name of the capture">; @@ -1037,6 +1038,18 @@ def err_lambda_template_parameter_list_empty : Error< "lambda template parameter list cannot be empty">; +// C++2b static lambdas +def err_static_lambda: Error< + "static lambdas are only supported for C++2b onwards">; +def warn_cxx20_compat_static_lambda: Warning< + "static lambdas are incompatible with C++ standards before C++2b">, + InGroup, DefaultIgnore; +def err_static_mutable_lambda : Error< + "lambda cannot be both mutable and static">; +def err_static_lambda_captures : Error< + "a static lambda cannot have any captures">; +def note_lambda_captures : Note<"captures declared here">; + // Availability attribute def err_expected_version : Error< "expected a version of the form 'major[.minor[.subminor]]'">; 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 @@ -9076,6 +9076,11 @@ "or enumeration type">; def err_operator_overload_variadic : Error<"overloaded %0 cannot be variadic">; +def ext_operator_overload_static : ExtWarn< + "declaring overloaded %0 as 'static' is a C++2b extension">, + InGroup, DefaultIgnore; +def err_call_operator_overload_static : Error< + "overloaded %0 cannot be declared static before C++2b">; def err_operator_overload_static : Error< "overloaded %0 cannot be a static member function">; def err_operator_overload_default_arg : Error< diff --git a/clang/include/clang/Basic/OperatorKinds.def b/clang/include/clang/Basic/OperatorKinds.def --- a/clang/include/clang/Basic/OperatorKinds.def +++ b/clang/include/clang/Basic/OperatorKinds.def @@ -38,8 +38,8 @@ /// "operator*") can be both unary and binary. /// /// MemberOnly: True if this operator can only be declared as a -/// non-static member function. False if the operator can be both a -/// non-member function and a non-static member function. +/// member function. False if the operator can be both a +/// non-member function and a member function. /// /// OVERLOADED_OPERATOR_MULTI is used to enumerate the multi-token /// overloaded operator names, e.g., "operator delete []". The macro diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -2748,6 +2748,10 @@ LambdaIntroducer() : Default(LCD_None) {} + bool hasLambdaCapture() const { + return Captures.size() > 0 || Default != LCD_None; + } + /// Append a capture in a lambda introducer. void addCapture(LambdaCaptureKind Kind, SourceLocation Loc, 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 @@ -7005,13 +7005,12 @@ LambdaCaptureDefault CaptureDefault); /// Start the definition of a lambda expression. - CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class, - SourceRange IntroducerRange, - TypeSourceInfo *MethodType, - SourceLocation EndLoc, - ArrayRef Params, - ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause); + CXXMethodDecl * + startLambdaDefinition(CXXRecordDecl *Class, SourceRange IntroducerRange, + TypeSourceInfo *MethodType, SourceLocation EndLoc, + ArrayRef Params, + ConstexprSpecKind ConstexprKind, StorageClass SC, + Expr *TrailingRequiresClause); /// Number lambda for linkage purposes if necessary. void handleLambdaNumbering( 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 @@ -695,6 +695,7 @@ Builder.defineMacro("__cpp_size_t_suffix", "202011L"); Builder.defineMacro("__cpp_if_consteval", "202106L"); Builder.defineMacro("__cpp_multidimensional_subscript", "202110L"); + Builder.defineMacro("__cpp_static_call_operator", "202207L"); } if (LangOpts.Char8) Builder.defineMacro("__cpp_char8_t", "201811L"); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1158,51 +1158,66 @@ static void tryConsumeLambdaSpecifierToken(Parser &P, SourceLocation &MutableLoc, + SourceLocation &StaticLoc, SourceLocation &ConstexprLoc, SourceLocation &ConstevalLoc, SourceLocation &DeclEndLoc) { assert(MutableLoc.isInvalid()); + assert(StaticLoc.isInvalid()); assert(ConstexprLoc.isInvalid()); + assert(ConstevalLoc.isInvalid()); // Consume constexpr-opt mutable-opt in any sequence, and set the DeclEndLoc // to the final of those locations. Emit an error if we have multiple // copies of those keywords and recover. + auto ConsumeLocation = [&P, &DeclEndLoc](SourceLocation &SpecifierLoc, + int diag_index) { + if (SpecifierLoc.isValid()) { + P.Diag(P.getCurToken().getLocation(), + diag::err_lambda_decl_specifier_repeated) + << diag_index + << FixItHint::CreateRemoval(P.getCurToken().getLocation()); + } + SpecifierLoc = P.ConsumeToken(); + DeclEndLoc = SpecifierLoc; + }; + while (true) { switch (P.getCurToken().getKind()) { - case tok::kw_mutable: { - if (MutableLoc.isValid()) { - P.Diag(P.getCurToken().getLocation(), - diag::err_lambda_decl_specifier_repeated) - << 0 << FixItHint::CreateRemoval(P.getCurToken().getLocation()); - } - MutableLoc = P.ConsumeToken(); - DeclEndLoc = MutableLoc; - break /*switch*/; - } + case tok::kw_mutable: + ConsumeLocation(MutableLoc, 0); + break; + case tok::kw_static: + ConsumeLocation(StaticLoc, 1); + break; case tok::kw_constexpr: - if (ConstexprLoc.isValid()) { - P.Diag(P.getCurToken().getLocation(), - diag::err_lambda_decl_specifier_repeated) - << 1 << FixItHint::CreateRemoval(P.getCurToken().getLocation()); - } - ConstexprLoc = P.ConsumeToken(); - DeclEndLoc = ConstexprLoc; - break /*switch*/; + ConsumeLocation(ConstexprLoc, 2); + break; case tok::kw_consteval: - if (ConstevalLoc.isValid()) { - P.Diag(P.getCurToken().getLocation(), - diag::err_lambda_decl_specifier_repeated) - << 2 << FixItHint::CreateRemoval(P.getCurToken().getLocation()); - } - ConstevalLoc = P.ConsumeToken(); - DeclEndLoc = ConstevalLoc; - break /*switch*/; + ConsumeLocation(ConstevalLoc, 3); + break; default: return; } } } +static void addStaticToLambdaDeclSpecifier(Parser &P, SourceLocation StaticLoc, + DeclSpec &DS) { + if (StaticLoc.isValid()) { + P.Diag(StaticLoc, !P.getLangOpts().CPlusPlus2b + ? diag::err_static_lambda + : diag::warn_cxx20_compat_static_lambda); + const char *PrevSpec = nullptr; + unsigned DiagID = 0; + DS.SetStorageClassSpec(P.getActions(), DeclSpec::SCS_static, StaticLoc, + PrevSpec, DiagID, + P.getActions().getASTContext().getPrintingPolicy()); + assert(PrevSpec == nullptr && DiagID == 0 && + "Static cannot have been set previously!"); + } +} + static void addConstexprToLambdaDeclSpecifier(Parser &P, SourceLocation ConstexprLoc, DeclSpec &DS) { @@ -1233,6 +1248,27 @@ } } +static void DiagnoseStaticSpecifierRestrictions(Parser &P, + SourceLocation StaticLoc, + SourceLocation MutableLoc, + const LambdaIntroducer &Intro) { + if (StaticLoc.isInvalid()) + return; + + // [expr.prim.lambda.general] p4 + // The lambda-specifier-seq shall not contain both mutable and static. + // If the lambda-specifier-seq contains static, there shall be no + // lambda-capture. + if (MutableLoc.isValid()) + P.Diag(StaticLoc, diag::err_static_mutable_lambda); + if (Intro.hasLambdaCapture()) { + P.Diag(StaticLoc, diag::err_static_lambda_captures); + P.Diag(Intro.DefaultLoc.isValid() ? Intro.DefaultLoc + : Intro.Captures[0].Loc, + diag::note_lambda_captures); + } +} + /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda /// expression. ExprResult Parser::ParseLambdaExpressionAfterIntroducer( @@ -1332,14 +1368,18 @@ // the mutable specifier to be compatible with MSVC. MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, Attr); - // Parse mutable-opt and/or constexpr-opt or consteval-opt, and update - // the DeclEndLoc. + // Parse lambda specifiers and update the DeclEndLoc. SourceLocation MutableLoc; + SourceLocation StaticLoc; SourceLocation ConstexprLoc; SourceLocation ConstevalLoc; - tryConsumeLambdaSpecifierToken(*this, MutableLoc, ConstexprLoc, - ConstevalLoc, DeclEndLoc); + tryConsumeLambdaSpecifierToken(*this, MutableLoc, StaticLoc, + ConstexprLoc, ConstevalLoc, DeclEndLoc); + + DiagnoseStaticSpecifierRestrictions(*this, StaticLoc, MutableLoc, + Intro); + addStaticToLambdaDeclSpecifier(*this, StaticLoc, DS); addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS); addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS); // Parse exception-specification[opt]. @@ -1435,7 +1475,7 @@ if (Tok.is(tok::kw_requires)) ParseTrailingRequiresClause(D); } else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute, - tok::kw_constexpr, tok::kw_consteval, + tok::kw_constexpr, tok::kw_consteval, tok::kw_static, tok::kw___private, tok::kw___global, tok::kw___local, tok::kw___constant, tok::kw___generic, tok::kw_requires, tok::kw_noexcept) || diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -5853,14 +5853,14 @@ unsigned NumArgs = TheCall->getNumArgs(); Expr *ImplicitThis = nullptr; - if (IsMemberOperatorCall) { - // If this is a call to a member operator, hide the first argument - // from checkCall. + if (IsMemberOperatorCall && !FDecl->isStatic()) { + // If this is a call to a non-static member operator, hide the first + // argument from checkCall. // FIXME: Our choice of AST representation here is less than ideal. ImplicitThis = Args[0]; ++Args; --NumArgs; - } else if (IsMemberFunction) + } else if (IsMemberFunction && !FDecl->isStatic()) ImplicitThis = cast(TheCall)->getImplicitObjectArgument(); 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 @@ -15338,7 +15338,8 @@ CXXRecordDecl *Lambda = Conv->getParent(); FunctionDecl *CallOp = Lambda->getLambdaCallOperator(); - FunctionDecl *Invoker = Lambda->getLambdaStaticInvoker(CC); + FunctionDecl *Invoker = + CallOp->isStatic() ? CallOp : Lambda->getLambdaStaticInvoker(CC); if (auto *TemplateArgs = Conv->getTemplateSpecializationArgs()) { CallOp = InstantiateFunctionDeclaration( @@ -15346,10 +15347,13 @@ if (!CallOp) return; - Invoker = InstantiateFunctionDeclaration( - Invoker->getDescribedFunctionTemplate(), TemplateArgs, CurrentLocation); - if (!Invoker) - return; + if (CallOp != Invoker) { + Invoker = InstantiateFunctionDeclaration( + Invoker->getDescribedFunctionTemplate(), TemplateArgs, + CurrentLocation); + if (!Invoker) + return; + } } if (CallOp->isInvalidDecl()) @@ -15362,17 +15366,19 @@ // to the PendingInstantiations. MarkFunctionReferenced(CurrentLocation, CallOp); - // Fill in the __invoke function with a dummy implementation. IR generation - // will fill in the actual details. Update its type in case it contained - // an 'auto'. - Invoker->markUsed(Context); - Invoker->setReferenced(); - Invoker->setType(Conv->getReturnType()->getPointeeType()); - Invoker->setBody(new (Context) CompoundStmt(Conv->getLocation())); + if (Invoker != CallOp) { + // Fill in the __invoke function with a dummy implementation. IR generation + // will fill in the actual details. Update its type in case it contained + // an 'auto'. + Invoker->markUsed(Context); + Invoker->setReferenced(); + Invoker->setType(Conv->getReturnType()->getPointeeType()); + Invoker->setBody(new (Context) CompoundStmt(Conv->getLocation())); + } // Construct the body of the conversion function { return __invoke; }. - Expr *FunctionRef = BuildDeclRefExpr(Invoker, Invoker->getType(), - VK_LValue, Conv->getLocation()); + Expr *FunctionRef = BuildDeclRefExpr(Invoker, Invoker->getType(), VK_LValue, + Conv->getLocation()); assert(FunctionRef && "Can't refer to __invoke function?"); Stmt *Return = BuildReturnStmt(Conv->getLocation(), FunctionRef).get(); Conv->setBody(CompoundStmt::Create(Context, Return, FPOptionsOverride(), @@ -15382,16 +15388,14 @@ if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Conv); - L->CompletedImplicitDefinition(Invoker); + if (Invoker != CallOp) { + L->CompletedImplicitDefinition(Invoker); + } } } - - void Sema::DefineImplicitLambdaToBlockPointerConversion( - SourceLocation CurrentLocation, - CXXConversionDecl *Conv) -{ + SourceLocation CurrentLocation, CXXConversionDecl *Conv) { assert(!Conv->getParent()->isGenericLambda()); SynthesizedFunctionScope Scope(*this, Conv); @@ -15923,15 +15927,23 @@ if (Op == OO_New || Op == OO_Array_New) return CheckOperatorNewDeclaration(*this, FnDecl); - // C++ [over.oper]p6: - // An operator function shall either be a non-static member - // function or be a non-member function and have at least one - // parameter whose type is a class, a reference to a class, an - // enumeration, or a reference to an enumeration. + // C++ [over.oper]p7: + // An operator function shall either be a member function or + // be a non-member function and have at least one parameter + // whose type is a class, a reference to a class, an enumeration, + // or a reference to an enumeration. + // Note: Before C++23, a member function also has to not be static. if (CXXMethodDecl *MethodDecl = dyn_cast(FnDecl)) { - if (MethodDecl->isStatic()) - return Diag(FnDecl->getLocation(), - diag::err_operator_overload_static) << FnDecl->getDeclName(); + if (MethodDecl->isStatic()) { + if (Op == OO_Call) + Diag(FnDecl->getLocation(), + (LangOpts.CPlusPlus2b ? diag::ext_operator_overload_static + : diag::err_call_operator_overload_static)) + << FnDecl; + else + return Diag(FnDecl->getLocation(), diag::err_operator_overload_static) + << FnDecl; + } } else { bool ClassOrEnumParam = false; for (auto *Param : FnDecl->parameters()) { @@ -16030,7 +16042,7 @@ << FnDecl->getDeclName(); } - // Some operators must be non-static member functions. + // Some operators must be member functions. if (MustBeMemberOperator && !isa(FnDecl)) { return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be_member) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -354,13 +354,11 @@ llvm_unreachable("unexpected context"); } -CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class, - SourceRange IntroducerRange, - TypeSourceInfo *MethodTypeInfo, - SourceLocation EndLoc, - ArrayRef Params, - ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause) { +CXXMethodDecl *Sema::startLambdaDefinition( + CXXRecordDecl *Class, SourceRange IntroducerRange, + TypeSourceInfo *MethodTypeInfo, SourceLocation EndLoc, + ArrayRef Params, ConstexprSpecKind ConstexprKind, + StorageClass SC, Expr *TrailingRequiresClause) { QualType MethodType = MethodTypeInfo->getType(); TemplateParameterList *TemplateParams = getGenericLambdaTemplateParameterList(getCurLambda(), *this); @@ -390,7 +388,7 @@ Context, Class, EndLoc, DeclarationNameInfo(MethodName, IntroducerRange.getBegin(), MethodNameLoc), - MethodType, MethodTypeInfo, SC_None, getCurFPFeatures().isFPConstrained(), + MethodType, MethodTypeInfo, SC, getCurFPFeatures().isFPConstrained(), /*isInline=*/true, ConstexprKind, EndLoc, TrailingRequiresClause); Method->setAccess(AS_public); if (!TemplateParams) @@ -917,6 +915,15 @@ bool ContainsUnexpandedParameterPack = false; SourceLocation EndLoc; SmallVector Params; + + assert(ParamInfo.getDeclSpec().getStorageClassSpec() == + DeclSpec::SCS_unspecified || + ParamInfo.getDeclSpec().getStorageClassSpec() == + DeclSpec::SCS_static && + "Unexpected storage specifier"); + bool IsLambdaStatic = + ParamInfo.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static; + if (ParamInfo.getNumTypeObjects() == 0) { // C++11 [expr.prim.lambda]p4: // If a lambda-expression does not include a lambda-declarator, it is as @@ -953,7 +960,7 @@ // This function call operator is declared const (9.3.1) if and only if // the lambda-expression's parameter-declaration-clause is not followed // by mutable. It is neither virtual nor declared volatile. [...] - if (!FTI.hasMutableQualifier()) { + if (!FTI.hasMutableQualifier() && !IsLambdaStatic) { FTI.getOrCreateMethodQualifiers().SetTypeQual(DeclSpec::TQ_const, SourceLocation()); } @@ -981,6 +988,7 @@ CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, ParamInfo.getDeclSpec().getConstexprSpecifier(), + IsLambdaStatic ? SC_Static : SC_None, ParamInfo.getTrailingRequiresClause()); if (ExplicitParams) CheckCXXDefaultArguments(Method); @@ -1479,45 +1487,50 @@ Class->addDecl(ConversionTemplate); } else Class->addDecl(Conversion); - // Add a non-static member function that will be the result of - // the conversion with a certain unique ID. - DeclarationName InvokerName = &S.Context.Idents.get( - getLambdaStaticInvokerName()); - // FIXME: Instead of passing in the CallOperator->getTypeSourceInfo() - // we should get a prebuilt TrivialTypeSourceInfo from Context - // using FunctionTy & Loc and get its TypeLoc as a FunctionProtoTypeLoc - // then rewire the parameters accordingly, by hoisting up the InvokeParams - // loop below and then use its Params to set Invoke->setParams(...) below. - // This would avoid the 'const' qualifier of the calloperator from - // contaminating the type of the invoker, which is currently adjusted - // in SemaTemplateDeduction.cpp:DeduceTemplateArguments. Fixing the - // trailing return type of the invoker would require a visitor to rebuild - // the trailing return type and adjusting all back DeclRefExpr's to refer - // to the new static invoker parameters - not the call operator's. - CXXMethodDecl *Invoke = CXXMethodDecl::Create( - S.Context, Class, Loc, DeclarationNameInfo(InvokerName, Loc), - InvokerFunctionTy, CallOperator->getTypeSourceInfo(), SC_Static, - S.getCurFPFeatures().isFPConstrained(), - /*isInline=*/true, ConstexprSpecKind::Unspecified, - CallOperator->getBody()->getEndLoc()); - for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) - InvokerParams[I]->setOwningFunction(Invoke); - Invoke->setParams(InvokerParams); - Invoke->setAccess(AS_private); - Invoke->setImplicit(true); - if (Class->isGenericLambda()) { - FunctionTemplateDecl *TemplateCallOperator = - CallOperator->getDescribedFunctionTemplate(); - FunctionTemplateDecl *StaticInvokerTemplate = FunctionTemplateDecl::Create( - S.Context, Class, Loc, InvokerName, - TemplateCallOperator->getTemplateParameters(), - Invoke); - StaticInvokerTemplate->setAccess(AS_private); - StaticInvokerTemplate->setImplicit(true); - Invoke->setDescribedFunctionTemplate(StaticInvokerTemplate); - Class->addDecl(StaticInvokerTemplate); - } else - Class->addDecl(Invoke); + + // If the lambda is not static, we need to add a static member + // function that will be the result of the conversion with a + // certain unique ID. + // When it is static we just return the static call operator instead. + if (CallOperator->isInstance()) { + DeclarationName InvokerName = + &S.Context.Idents.get(getLambdaStaticInvokerName()); + // FIXME: Instead of passing in the CallOperator->getTypeSourceInfo() + // we should get a prebuilt TrivialTypeSourceInfo from Context + // using FunctionTy & Loc and get its TypeLoc as a FunctionProtoTypeLoc + // then rewire the parameters accordingly, by hoisting up the InvokeParams + // loop below and then use its Params to set Invoke->setParams(...) below. + // This would avoid the 'const' qualifier of the calloperator from + // contaminating the type of the invoker, which is currently adjusted + // in SemaTemplateDeduction.cpp:DeduceTemplateArguments. Fixing the + // trailing return type of the invoker would require a visitor to rebuild + // the trailing return type and adjusting all back DeclRefExpr's to refer + // to the new static invoker parameters - not the call operator's. + CXXMethodDecl *Invoke = CXXMethodDecl::Create( + S.Context, Class, Loc, DeclarationNameInfo(InvokerName, Loc), + InvokerFunctionTy, CallOperator->getTypeSourceInfo(), SC_Static, + S.getCurFPFeatures().isFPConstrained(), + /*isInline=*/true, ConstexprSpecKind::Unspecified, + CallOperator->getBody()->getEndLoc()); + for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) + InvokerParams[I]->setOwningFunction(Invoke); + Invoke->setParams(InvokerParams); + Invoke->setAccess(AS_private); + Invoke->setImplicit(true); + if (Class->isGenericLambda()) { + FunctionTemplateDecl *TemplateCallOperator = + CallOperator->getDescribedFunctionTemplate(); + FunctionTemplateDecl *StaticInvokerTemplate = + FunctionTemplateDecl::Create( + S.Context, Class, Loc, InvokerName, + TemplateCallOperator->getTemplateParameters(), Invoke); + StaticInvokerTemplate->setAccess(AS_private); + StaticInvokerTemplate->setImplicit(true); + Invoke->setDescribedFunctionTemplate(StaticInvokerTemplate); + Class->addDecl(StaticInvokerTemplate); + } else + Class->addDecl(Invoke); + } } /// Add a lambda's conversion to function pointers, as described in 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 @@ -7009,8 +7009,10 @@ Candidate.Viable = true; - if (Method->isStatic() || ObjectType.isNull()) - // The implicit object argument is ignored. + // The implicit object argument is ignored in <= C++20 for static member + // functions. Since C++23 it is used to break ties between static call + // operators and implicit conversions to function pointers (See P1169R4). + if ((!LangOpts.CPlusPlus2b && Method->isStatic()) || ObjectType.isNull()) Candidate.IgnoreObjectArgument = true; else { unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0; @@ -9783,7 +9785,7 @@ } } - // C++ [over.match.best]p1: + // C++ [over.match.best]p1: (Changed in C++2b) // // -- if F is a static member function, ICS1(F) is defined such // that ICS1(F) is neither better nor worse than ICS1(G) for @@ -14890,15 +14892,18 @@ bool IsError = false; - // Initialize the implicit object parameter. - ExprResult ObjRes = - PerformObjectArgumentInitialization(Object.get(), /*Qualifier=*/nullptr, - Best->FoundDecl, Method); - if (ObjRes.isInvalid()) - IsError = true; - else - Object = ObjRes; - MethodArgs.push_back(Object.get()); + // Initialize the implicit object parameter if needed. + // Since C++2b, this could also be a call to a static call operator + // which we emit as a regular CallExpr. + if (Method->isInstance()) { + ExprResult ObjRes = PerformObjectArgumentInitialization( + Object.get(), /*Qualifier=*/nullptr, Best->FoundDecl, Method); + if (ObjRes.isInvalid()) + IsError = true; + else + Object = ObjRes; + MethodArgs.push_back(Object.get()); + } IsError |= PrepareArgumentsForCallToObjectOfClassType( *this, MethodArgs, Method, Args, LParenLoc); @@ -14924,9 +14929,14 @@ ExprValueKind VK = Expr::getValueKindForType(ResultTy); ResultTy = ResultTy.getNonLValueExprType(Context); - CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create( - Context, OO_Call, NewFn.get(), MethodArgs, ResultTy, VK, RParenLoc, - CurFPFeatureOverrides()); + CallExpr *TheCall; + if (Method->isInstance()) + TheCall = CXXOperatorCallExpr::Create(Context, OO_Call, NewFn.get(), + MethodArgs, ResultTy, VK, RParenLoc, + CurFPFeatureOverrides()); + else + TheCall = CallExpr::Create(Context, NewFn.get(), MethodArgs, ResultTy, VK, + RParenLoc, CurFPFeatureOverrides()); if (CheckCallReturnType(Method->getReturnType(), LParenLoc, TheCall, Method)) return true; 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 @@ -13111,6 +13111,7 @@ E->getCallOperator()->getEndLoc(), NewCallOpTSI->getTypeLoc().castAs().getParams(), E->getCallOperator()->getConstexprKind(), + E->getCallOperator()->getStorageClass(), E->getCallOperator()->getTrailingRequiresClause()); LSI->CallOperator = NewCallOperator; diff --git a/clang/test/CXX/over/over.match/over.match.best/over.best.ics/p6.cpp b/clang/test/CXX/over/over.match/over.match.best/over.best.ics/p6.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/over/over.match/over.match.best/over.best.ics/p6.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=cxx2b -std=c++2b %s +// RUN: %clang_cc1 -fsyntax-only -verify=cxx20 -std=c++20 %s +// cxx2b-no-diagnostics + +struct __unique { + static constexpr auto operator()() { return 4; }; // cxx20-error {{cannot be declared static}} // cxx20-note {{candidate}} + + using P = int(); + constexpr operator P*() { return operator(); } // cxx20-note {{conversion candidate}} +}; + +__unique four{}; + +int test_four() { + // Checks that overload resolution works. + return four(); // cxx20-error {{call to object of type '__unique' is ambiguous}} +} diff --git a/clang/test/CXX/over/over.oper/p7.cpp b/clang/test/CXX/over/over.oper/p7.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/over/over.oper/p7.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11 +// RUN: %clang_cc1 -std=c++2b %s -verify=expected,cxx2b +// RUN: %clang_cc1 -std=c++2b -Wpre-c++2b-compat %s -verify=expected,precxx2b + + +struct Functor { + static int operator()(int a, int b); + // cxx11-error@-1 {{cannot be declared static}} + // precxx2b-warning@-2 {{declaring overloaded 'operator()' as 'static' is a C++2b extension}} +}; + +struct InvalidParsing1 { + extern int operator()(int a, int b); // expected-error {{storage class specified}} +}; + +struct InvalidParsing2 { + extern static int operator()(int a, int b); // expected-error {{storage class specified}} // expected-error {{cannot combine with previous 'extern' declaration specifier}} +}; diff --git a/clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp b/clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp @@ -0,0 +1,129 @@ +// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -triple x86_64-linux -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -triple x86_64-windows-msvc -o - | FileCheck %s + +struct Functor { + static int operator()(int x, int y) { + return x + y; + } +}; + +auto GetALambda() { + return [](int x, int y) static { + return x + y; + }; +} + +void CallsTheLambda() { + GetALambda()(1, 2); +} + +// CHECK: define {{.*}}CallsTheLambda{{.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: %call = call noundef i32 {{.*}}(i32 noundef 1, i32 noundef 2) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void call_static_call_operator() { + Functor f; + f(101, 102); + f.operator()(201, 202); + Functor{}(301, 302); +} + +// CHECK: define {{.*}}call_static_call_operator{{.*}} +// CHECK-NEXT: entry: +// CHECK: {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 101, i32 noundef 102) +// CHECK-NEXT: {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 201, i32 noundef 202) +// CHECK-NEXT: {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 301, i32 noundef 302) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +struct FunctorConsteval { + consteval static int operator()(int x, int y) { + return x + y; + } +}; + +struct FunctorConstexpr { + constexpr static int operator()(int x, int y) { + return x + y; + } +}; + +constexpr auto my_lambda = []() constexpr { + return 3; +}; + +void test_consteval_constexpr() { + int x = 0; + int y = FunctorConstexpr{}(x, 2); + constexpr int z1 = FunctorConsteval{}(2, 2); + constexpr int z2 = FunctorConstexpr{}(2, 2); + + static_assert(z1 == 4); + static_assert(z2 == 4); + + constexpr auto my_lambda = []() constexpr static { + return 3; + }; + constexpr int (*f)(void) = my_lambda; + constexpr int k = f(); + static_assert(k == 3); +} + +template +struct DepFunctor { + static int operator()(T t) { + return int(t); + } +}; + +template +auto dep_lambda1() { + return [](T t) static -> int { + return t; + }; +} + +auto dep_lambda2() { + return [](auto t) static -> int { + return t; + }; +} + +void test_dep_functors() { + int x = DepFunctor{}(1.0f); + int y = DepFunctor{}(true); + + int a = dep_lambda1()(1.0f); + int b = dep_lambda1()(true); + + int h = dep_lambda2()(1.0f); + int i = dep_lambda2()(true); +} + +// CHECK: define {{.*}}test_dep_functors{{.*}} +// CHECK-NEXT: entry: +// CHECK: %call = call noundef i32 {{.*}}DepFunctor{{.*}}(float noundef 1.000000e+00) +// CHECK: %call1 = call noundef i32 {{.*}}DepFunctor{{.*}}(i1 noundef zeroext true) +// CHECK: %call2 = call noundef i32 {{.*}}dep_lambda1{{.*}}(float noundef 1.000000e+00) +// CHECK: %call3 = call noundef i32 {{.*}}dep_lambda1{{.*}}(i1 noundef zeroext true) +// CHECK: %call4 = call noundef i32 {{.*}}dep_lambda2{{.*}}(float noundef 1.000000e+00) +// CHECK: %call5 = call noundef i32 {{.*}}dep_lambda2{{.*}}(i1 noundef zeroext true) +// CHECK: ret void +// CHECK-NEXT: } + + +struct __unique { + static constexpr auto operator()() { return 4; }; + + using P = int(); + constexpr operator P*() { return operator(); } +}; + +__unique four{}; + +int test_four() { + // Checks that overload resolution works. + return four(); +} diff --git a/clang/test/Parser/cxx2b-lambdas-ext-warns.cpp b/clang/test/Parser/cxx2b-lambdas-ext-warns.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/cxx2b-lambdas-ext-warns.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx20 +// RUN: %clang_cc1 -std=c++2b %s -verify=expected,cxx2b +// RUN: %clang_cc1 -std=c++2b -Wpre-c++2b-compat %s -verify=expected,precxx2b + +//cxx2b-no-diagnostics + +auto L1 = [] constexpr {}; +// cxx20-warning@-1 {{lambda without a parameter clause is a C++2b extension}} +auto L2 = []() static {}; +// cxx20-error@-1 {{static lambdas are only supported for C++2b onwards}} +// precxx2b-warning@-2 {{static lambdas are incompatible with C++ standards before C++2b}} +auto L3 = [] static {}; +// cxx20-warning@-1 {{lambda without a parameter clause is a C++2b extension}} +// cxx20-error@-2 {{static lambdas are only supported for C++2b onwards}} +// precxx2b-warning@-3 {{static lambdas are incompatible with C++ standards before C++2b}} diff --git a/clang/test/Parser/cxx2b-lambdas.cpp b/clang/test/Parser/cxx2b-lambdas.cpp --- a/clang/test/Parser/cxx2b-lambdas.cpp +++ b/clang/test/Parser/cxx2b-lambdas.cpp @@ -38,3 +38,31 @@ auto XL4 = [] requires true {}; // expected-error{{expected body}} auto XL5 = [] requires true requires true {}; // expected-error{{expected body}} auto XL6 = [] requires true noexcept requires true {}; // expected-error{{expected body}} + +auto XL7 = []() static static {}; // expected-error {{cannot appear multiple times}} +auto XL8 = []() static mutable {}; // expected-error {{cannot be both mutable and static}} +auto XL9 = []() static consteval {}; +auto XL10 = []() static constexpr {}; + +auto XL11 = [] static {}; +auto XL12 = []() static {}; +auto XL13 = []() static extern {}; // expected-error {{expected body of lambda expression}} +auto XL14 = []() extern {}; // expected-error {{expected body of lambda expression}} + + +void static_captures() { + int x; + auto SC1 = [&]() static {}; // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + auto SC4 = [x]() static {}; // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + auto SC2 = [&x]() static {}; // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + auto SC3 = [y=x]() static {}; // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + auto SC5 = [&y = x]() static {}; // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + auto SC6 = [=]() static {}; // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + struct X { + int z; + void f() { + [this]() static {}(); // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + [*this]() static {}(); // expected-error {{a static lambda cannot have any captures}} // expected-note {{captures declared here}} + } + }; +} diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp --- a/clang/test/SemaCXX/lambda-unevaluated.cpp +++ b/clang/test/SemaCXX/lambda-unevaluated.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++20 %s -verify +// RUN: %clang_cc1 -std=c++2b %s -verify template struct Nothing {}; @@ -120,3 +121,28 @@ void foo(decltype(+[](T) {}) lambda, T param); static_assert(!__is_same(decltype(foo), void)); } // namespace GH51641 + +#if __cplusplus > 202002L +namespace StaticLambdas { +template struct Nothing {}; +Nothing<[]() static { return 0; }()> nothing; + +template struct NothingT {}; +Nothing<[]() static { return 0; }> nothingT; + +template +concept True = [] static { return true; }(); +static_assert(True); + +static_assert(sizeof([] static { return 0; })); +static_assert(sizeof([] static { return 0; }())); + +void f() noexcept(noexcept([] static { return 0; }())); + +using a = decltype([] static { return 0; }); +using b = decltype([] static { return 0; }()); +using c = decltype([]() static noexcept(noexcept([] { return 0; }())) { return 0; }); +using d = decltype(sizeof([] static { return 0; })); + +} +#endif diff --git a/clang/test/SemaCXX/overloaded-operator-decl.cpp b/clang/test/SemaCXX/overloaded-operator-decl.cpp --- a/clang/test/SemaCXX/overloaded-operator-decl.cpp +++ b/clang/test/SemaCXX/overloaded-operator-decl.cpp @@ -51,7 +51,7 @@ namespace PR14120 { struct A { - static void operator()(int& i) { ++i; } // expected-error{{overloaded 'operator()' cannot be a static member function}} + static void operator()(int& i) { ++i; } // expected-error{{overloaded 'operator()' cannot be declared static}} }; void f() { int i = 0; 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 @@ -1481,7 +1481,7 @@ static operator() P1169R4 - No + Clang 16 Extended floating-point types and standard names