diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -239,6 +239,8 @@ C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- 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 @@ -988,7 +988,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">; @@ -1027,6 +1028,18 @@ def err_lambda_template_parameter_list_empty : Error< "lambda template parameter list cannot be empty">; +// C++2b static lambdas +def ext_static_lambda: ExtWarn< + "static lambdas is a C++2b extension">, + InGroup; +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 capture">; + // 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 @@ -9071,8 +9071,10 @@ "or enumeration type">; def err_operator_overload_variadic : Error<"overloaded %0 cannot be variadic">; +def ext_operator_overload_static : ExtWarn< + "making overloaded %0 static is a C++2b extension">, InGroup, DefaultIgnore; def err_operator_overload_static : Error< - "overloaded %0 cannot be a static member function">; + "overloaded %0 cannot be a static member function befor C++2b">; def err_operator_overload_default_arg : Error< "parameter of overloaded %0 cannot have a default argument">; 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 @@ -6966,13 +6966,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::ext_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,23 @@ } } +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); +} + /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda /// expression. ExprResult Parser::ParseLambdaExpressionAfterIntroducer( @@ -1332,14 +1364,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 +1471,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 @@ -5836,14 +5836,14 @@ unsigned NumArgs = TheCall->getNumArgs(); Expr *ImplicitThis = nullptr; - if (IsMemberOperatorCall) { - // If this is a call to a member operator, hide the first argument + if (IsMemberOperatorCall && !FDecl->isStatic()) { + // If this is a call to an instance 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 @@ -15920,15 +15920,18 @@ 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(); + (LangOpts.CPlusPlus2b ? diag::ext_operator_overload_static + : diag::err_operator_overload_static)) + << FnDecl->getDeclName(); } else { bool ClassOrEnumParam = false; for (auto *Param : FnDecl->parameters()) { @@ -16027,7 +16030,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) @@ -953,7 +951,8 @@ // 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() && + ParamInfo.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) { FTI.getOrCreateMethodQualifiers().SetTypeQual(DeclSpec::TQ_const, SourceLocation()); } @@ -978,10 +977,13 @@ CXXRecordDecl *Class = createLambdaClosureType( Intro.Range, MethodTyInfo, LambdaDependencyKind, Intro.Default); - CXXMethodDecl *Method = - startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, - ParamInfo.getDeclSpec().getConstexprSpecifier(), - ParamInfo.getTrailingRequiresClause()); + CXXMethodDecl *Method = startLambdaDefinition( + Class, Intro.Range, MethodTyInfo, EndLoc, Params, + ParamInfo.getDeclSpec().getConstexprSpecifier(), + ParamInfo.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static + ? SC_Static + : SC_None, + ParamInfo.getTrailingRequiresClause()); if (ExplicitParams) CheckCXXDefaultArguments(Method); @@ -1479,45 +1481,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 @@ -6993,8 +6993,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; @@ -9766,7 +9768,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 @@ -14872,15 +14874,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); @@ -14906,9 +14911,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 @@ -13120,7 +13120,7 @@ E->getCallOperator()->getEndLoc(), NewCallOpTSI->getTypeLoc().castAs().getParams(), E->getCallOperator()->getConstexprKind(), - NewTrailingRequiresClause.get()); + E->getCallOperator()->getStorageClass(), NewTrailingRequiresClause.get()); LSI->CallOperator = NewCallOperator; diff --git a/clang/test/CXX/over/over.oper/over.literal/p7.cpp b/clang/test/CXX/over/over.oper/over.literal/p7.cpp --- a/clang/test/CXX/over/over.oper/over.literal/p7.cpp +++ b/clang/test/CXX/over/over.oper/over.literal/p7.cpp @@ -1,5 +1,8 @@ -// RUN: %clang_cc1 -std=c++11 %s -verify -// expected-no-diagnostics +// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11 +// RUN: %clang_cc1 -std=c++2b %s -verify=expected,cxx2b + +//cxx2b-no-diagnostics + constexpr int operator "" _a(const char *c) { return c[0]; @@ -15,3 +18,8 @@ operator "" _puts("foo"); operator "" _puts("bar"); } + + +struct Functor { + static int operator()(int a, int b); // cxx11-error {{cannot be a static member function}} +}; 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,8 @@ +// RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx20 +// RUN: %clang_cc1 -std=c++2b %s -verify=expected,cxx2b + +//cxx2b-no-diagnostics + +auto L1 = [] constexpr {}; // cxx20-warning {{lambda without a parameter clause is a C++2b extension}} +auto L2 = []() static {}; // cxx20-warning {{static lambdas is a C++2b extension}} +auto L3 = [] static {}; // cxx20-warning {{static lambdas is a C++2b extension}} 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,16 @@ 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 {}; +auto XL9 = []() static {}; + +void static_captures() { + int x; + auto SC1 = [&]() static {}; // expected-error {{a static lambda cannot capture}} + auto SC2 = [&x]() static {}; // expected-error {{a static lambda cannot capture}} + auto SC3 = [=]() static {}; // expected-error {{a static lambda cannot capture}} +} \ No newline at end of file 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