[clang] 6523814 - [Clang] P1169R4: static operator()
Roy Jacobson via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 29 13:03:36 PDT 2022
Author: Roy Jacobson
Date: 2022-09-29T23:03:26+03:00
New Revision: 6523814c4e38ad70f8fd3fc3c39d089195e099a1
URL: https://github.com/llvm/llvm-project/commit/6523814c4e38ad70f8fd3fc3c39d089195e099a1
DIFF: https://github.com/llvm/llvm-project/commit/6523814c4e38ad70f8fd3fc3c39d089195e099a1.diff
LOG: [Clang] P1169R4: static operator()
Implements 'P1169R4: static operator()' from C++2b.
Reviewed By: #clang-language-wg, aaron.ballman
Differential Revision: https://reviews.llvm.org/D133659
Added:
clang/test/CXX/over/over.match/over.match.best/over.best.ics/p6.cpp
clang/test/CXX/over/over.oper/p7.cpp
clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp
clang/test/Parser/cxx2b-lambdas-ext-warns.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/OperatorKinds.def
clang/include/clang/Sema/DeclSpec.h
clang/include/clang/Sema/Overload.h
clang/include/clang/Sema/Sema.h
clang/lib/Frontend/InitPreprocessor.cpp
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Sema/SemaChecking.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaLambda.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/TreeTransform.h
clang/test/Lexer/cxx-features.cpp
clang/test/Parser/cxx2b-lambdas.cpp
clang/test/SemaCXX/lambda-unevaluated.cpp
clang/test/SemaCXX/overloaded-operator-decl.cpp
clang/www/cxx_status.html
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f464439f7832a..ed5b4a2f031db 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -409,6 +409,7 @@ C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Support label at end of compound statement (`P2324 <https://wg21.link/p2324r2>`_).
+- Implemented `P1169R4: static operator() <https://wg21.link/P1169R4>`_.
CUDA/HIP Language Changes in Clang
----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index b5ef572ab71bc..d3605d9bcba7d 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1004,7 +1004,8 @@ def warn_cxx98_compat_lambda : Warning<
"lambda expressions are incompatible with C++98">,
InGroup<CXX98Compat>, 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">;
@@ -1043,6 +1044,17 @@ def warn_cxx17_compat_lambda_template_parameter_list: Warning<
def err_lambda_template_parameter_list_empty : Error<
"lambda template parameter list cannot be empty">;
+// C++2b static lambdas
+def err_static_lambda: ExtWarn<
+ "static lambdas are a C++2b extension">, InGroup<CXX2b>;
+def warn_cxx20_compat_static_lambda: ExtWarn<
+ "static lambdas are incompatible with C++ standards before C++2b">,
+ InGroup<CXXPre2bCompat>, 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">;
+
// 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
index 62afa66ca320d..a3e20d0d683d4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9087,6 +9087,11 @@ def err_operator_overload_needs_class_or_enum : Error<
"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<CXXPre2bCompat>, DefaultIgnore;
+def err_call_operator_overload_static : ExtWarn<
+ "declaring overloaded %0 as 'static' is a C++2b extension">, InGroup<CXX2b>;
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
index d464db29274e1..fab777349ede7 100644
--- 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
index 463561b2224fc..98f9bec16e607 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -2766,6 +2766,10 @@ struct LambdaIntroducer {
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/Overload.h b/clang/include/clang/Sema/Overload.h
index 6aaa6a481cf84..eb935e0a8cbf3 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -521,8 +521,12 @@ class Sema;
/// specifies that there is no conversion from the source type to
/// the target type. AmbiguousConversion represents the unique
/// ambiguous conversion (C++0x [over.best.ics]p10).
+ /// StaticObjectArgumentConversion represents the conversion rules for
+ /// the synthesized first argument of calls to static member functions
+ /// ([over.best.ics.general]p8).
enum Kind {
StandardConversion = 0,
+ StaticObjectArgumentConversion,
UserDefinedConversion,
AmbiguousConversion,
EllipsisConversion,
@@ -589,6 +593,8 @@ class Sema;
switch (ConversionKind) {
case Uninitialized: break;
case StandardConversion: Standard = Other.Standard; break;
+ case StaticObjectArgumentConversion:
+ break;
case UserDefinedConversion: UserDefined = Other.UserDefined; break;
case AmbiguousConversion: Ambiguous.copyFrom(Other.Ambiguous); break;
case EllipsisConversion: break;
@@ -622,6 +628,7 @@ class Sema;
unsigned getKindRank() const {
switch (getKind()) {
case StandardConversion:
+ case StaticObjectArgumentConversion:
return 0;
case UserDefinedConversion:
@@ -640,6 +647,9 @@ class Sema;
bool isBad() const { return getKind() == BadConversion; }
bool isStandard() const { return getKind() == StandardConversion; }
+ bool isStaticObjectArgument() const {
+ return getKind() == StaticObjectArgumentConversion;
+ }
bool isEllipsis() const { return getKind() == EllipsisConversion; }
bool isAmbiguous() const { return getKind() == AmbiguousConversion; }
bool isUserDefined() const { return getKind() == UserDefinedConversion; }
@@ -665,6 +675,7 @@ class Sema;
}
void setStandard() { setKind(StandardConversion); }
+ void setStaticObjectArgument() { setKind(StaticObjectArgumentConversion); }
void setEllipsis() { setKind(EllipsisConversion); }
void setUserDefined() { setKind(UserDefinedConversion); }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2e0e3d5524035..52b554b9f8b10 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7013,13 +7013,12 @@ class Sema final {
LambdaCaptureDefault CaptureDefault);
/// Start the definition of a lambda expression.
- CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class,
- SourceRange IntroducerRange,
- TypeSourceInfo *MethodType,
- SourceLocation EndLoc,
- ArrayRef<ParmVarDecl *> Params,
- ConstexprSpecKind ConstexprKind,
- Expr *TrailingRequiresClause);
+ CXXMethodDecl *
+ startLambdaDefinition(CXXRecordDecl *Class, SourceRange IntroducerRange,
+ TypeSourceInfo *MethodType, SourceLocation EndLoc,
+ ArrayRef<ParmVarDecl *> 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
index 162cc264f99cc..04ebeea522d65 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -696,6 +696,12 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_if_consteval", "202106L");
Builder.defineMacro("__cpp_multidimensional_subscript", "202110L");
}
+
+ // We provide this as an extension in earlier language modes, so we
+ // also define the macro.
+ if (LangOpts.CPlusPlus11)
+ Builder.defineMacro("__cpp_static_call_operator", "202207L");
+
if (LangOpts.Char8)
Builder.defineMacro("__cpp_char8_t", "201811L");
Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 7fa75d40af331..e77e6a1f2f7c6 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1158,51 +1158,66 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
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 DiagIndex) {
+ if (SpecifierLoc.isValid()) {
+ P.Diag(P.getCurToken().getLocation(),
+ diag::err_lambda_decl_specifier_repeated)
+ << DiagIndex
+ << 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,24 @@ static void addConstevalToLambdaDeclSpecifier(Parser &P,
}
}
+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 +1365,18 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
// 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].
@@ -1434,7 +1471,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
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
index 90eca753f3059..95f86cb6a48e2 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5853,14 +5853,14 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
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<CXXMemberCallExpr>(TheCall)->getImplicitObjectArgument();
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index acc84066c2224..6af546958c4f0 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -15338,7 +15338,8 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
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 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
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 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
// 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,13 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
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 +15926,24 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
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 could not be static. The only member
+ // function allowed to be static is the call operator function.
if (CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(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 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
<< FnDecl->getDeclName();
}
- // Some operators must be non-static member functions.
+ // Some operators must be member functions.
if (MustBeMemberOperator && !isa<CXXMethodDecl>(FnDecl)) {
return Diag(FnDecl->getLocation(),
diag::err_operator_overload_must_be_member)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d6d8a7494cb00..1f827a3ec49ef 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4185,7 +4185,8 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
return ExprError();
case ImplicitConversionSequence::EllipsisConversion:
- llvm_unreachable("Cannot perform an ellipsis conversion");
+ case ImplicitConversionSequence::StaticObjectArgumentConversion:
+ llvm_unreachable("bad conversion");
case ImplicitConversionSequence::BadConversion:
Sema::AssignConvertType ConvTy =
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 8f21cf5cc7d7a..7097b9deb8ed6 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -9882,6 +9882,7 @@ static void DiagnoseNarrowingInInitList(Sema &S,
SCS = &ICS.UserDefined.After;
break;
case ImplicitConversionSequence::AmbiguousConversion:
+ case ImplicitConversionSequence::StaticObjectArgumentConversion:
case ImplicitConversionSequence::EllipsisConversion:
case ImplicitConversionSequence::BadConversion:
return;
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 1cf44bd694c54..e0a9091c4f2b0 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -354,13 +354,11 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
llvm_unreachable("unexpected context");
}
-CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
- SourceRange IntroducerRange,
- TypeSourceInfo *MethodTypeInfo,
- SourceLocation EndLoc,
- ArrayRef<ParmVarDecl *> Params,
- ConstexprSpecKind ConstexprKind,
- Expr *TrailingRequiresClause) {
+CXXMethodDecl *Sema::startLambdaDefinition(
+ CXXRecordDecl *Class, SourceRange IntroducerRange,
+ TypeSourceInfo *MethodTypeInfo, SourceLocation EndLoc,
+ ArrayRef<ParmVarDecl *> Params, ConstexprSpecKind ConstexprKind,
+ StorageClass SC, Expr *TrailingRequiresClause) {
QualType MethodType = MethodTypeInfo->getType();
TemplateParameterList *TemplateParams =
getGenericLambdaTemplateParameterList(getCurLambda(), *this);
@@ -390,7 +388,7 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
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 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
bool ContainsUnexpandedParameterPack = false;
SourceLocation EndLoc;
SmallVector<ParmVarDecl *, 8> 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 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
// 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 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
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 @@ static void addFunctionPointerConversion(Sema &S, SourceRange IntroducerRange,
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
index d9898855e6051..6e328ab3f075d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5739,7 +5739,8 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
return ExprError();
case ImplicitConversionSequence::EllipsisConversion:
- llvm_unreachable("ellipsis conversion in converted constant expression");
+ case ImplicitConversionSequence::StaticObjectArgumentConversion:
+ llvm_unreachable("bad conversion in converted constant expression");
}
// Check that we would only use permitted conversions.
@@ -5923,6 +5924,7 @@ TryContextuallyConvertToObjCPointer(Sema &S, Expr *From) {
case ImplicitConversionSequence::BadConversion:
case ImplicitConversionSequence::AmbiguousConversion:
case ImplicitConversionSequence::EllipsisConversion:
+ case ImplicitConversionSequence::StaticObjectArgumentConversion:
break;
case ImplicitConversionSequence::UserDefinedConversion:
@@ -7009,17 +7011,27 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
Candidate.Viable = true;
- if (Method->isStatic() || ObjectType.isNull())
- // The implicit object argument is ignored.
+ unsigned FirstConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;
+ if (ObjectType.isNull())
Candidate.IgnoreObjectArgument = true;
- else {
- unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;
+ else if (Method->isStatic()) {
+ // [over.best.ics.general]p8
+ // When the parameter is the implicit object parameter of a static member
+ // function, the implicit conversion sequence is a standard conversion
+ // sequence that is neither better nor worse than any other standard
+ // conversion sequence.
+ //
+ // This is a rule that was introduced in C++23 to support static lambdas. We
+ // apply it retroactively because we want to support static lambdas as an
+ // extension and it doesn't hurt previous code.
+ Candidate.Conversions[FirstConvIdx].setStaticObjectArgument();
+ } else {
// Determine the implicit conversion sequence for the object
// parameter.
- Candidate.Conversions[ConvIdx] = TryObjectArgumentInitialization(
+ Candidate.Conversions[FirstConvIdx] = TryObjectArgumentInitialization(
*this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
Method, ActingContext);
- if (Candidate.Conversions[ConvIdx].isBad()) {
+ if (Candidate.Conversions[FirstConvIdx].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
return;
@@ -9783,7 +9795,7 @@ bool clang::isBetterOverloadCandidate(
}
}
- // 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
@@ -14905,15 +14917,18 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
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);
@@ -14939,9 +14954,14 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
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
index e6109d6173bc6..0ae3c77859668 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13117,6 +13117,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->getCallOperator()->getEndLoc(),
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().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
index 0000000000000..d33b1a4b7438b
--- /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-warning {{is a C++2b extension}}
+
+ 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/CXX/over/over.oper/p7.cpp b/clang/test/CXX/over/over.oper/p7.cpp
new file mode 100644
index 0000000000000..c7a169de26cd2
--- /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-warning at -1 {{is a C++2b extension}}
+ // precxx2b-warning at -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
index 0000000000000..e85974d911f7c
--- /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 <class T>
+struct DepFunctor {
+ static int operator()(T t) {
+ return int(t);
+ }
+};
+
+template<class T>
+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<float>{}(1.0f);
+ int y = DepFunctor<bool>{}(true);
+
+ int a = dep_lambda1<float>()(1.0f);
+ int b = dep_lambda1<bool>()(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/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 1963b1dc43a6f..d1c9f0d2b2aee 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -47,7 +47,7 @@
#error "wrong value for __cpp_multidimensional_subscript"
#endif
-#if check(static_call_operator, 0, 0, 0, 0, 0, 0)
+#if check(static_call_operator, 0, 202207, 202207, 202207, 202207, 202207)
#error "wrong value for __cpp_static_call_operator"
#endif
diff --git a/clang/test/Parser/cxx2b-lambdas-ext-warns.cpp b/clang/test/Parser/cxx2b-lambdas-ext-warns.cpp
new file mode 100644
index 0000000000000..bcb5574a2fe10
--- /dev/null
+++ b/clang/test/Parser/cxx2b-lambdas-ext-warns.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20
+// RUN: %clang_cc1 -std=c++2b %s -verify=cxx2b
+// RUN: %clang_cc1 -std=c++2b -Wpre-c++2b-compat %s -verify=precxx2b
+
+//cxx2b-no-diagnostics
+
+auto L1 = [] constexpr {};
+// cxx20-warning at -1 {{lambda without a parameter clause is a C++2b extension}}
+auto L2 = []() static {};
+// cxx20-warning at -1 {{static lambdas are a C++2b extension}}
+// precxx2b-warning at -2 {{static lambdas are incompatible with C++ standards before C++2b}}
+auto L3 = [] static {};
+// cxx20-warning at -1 {{lambda without a parameter clause is a C++2b extension}}
+// cxx20-warning at -2 {{static lambdas are a C++2b extension}}
+// precxx2b-warning at -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
index 19e9bf009ba12..9474dface2e3d 100644
--- a/clang/test/Parser/cxx2b-lambdas.cpp
+++ b/clang/test/Parser/cxx2b-lambdas.cpp
@@ -38,3 +38,31 @@ auto XL3 = []( constexpr mutable constexpr {}; // expected-error{{invalid storag
auto XL4 = [] requires true {}; // expected-error{{expected body}}
auto XL5 = []<auto> requires true requires true {}; // expected-error{{expected body}}
auto XL6 = []<auto> 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}}
+ auto SC4 = [x]() static {}; // expected-error {{a static lambda cannot have any captures}}
+ auto SC2 = [&x]() static {}; // expected-error {{a static lambda cannot have any captures}}
+ auto SC3 = [y=x]() static {}; // expected-error {{a static lambda cannot have any captures}}
+ auto SC5 = [&y = x]() static {}; // expected-error {{a static lambda cannot have any captures}}
+ auto SC6 = [=]() static {}; // expected-error {{a static lambda cannot have any captures}}
+ struct X {
+ int z;
+ void f() {
+ [this]() static {}(); // expected-error {{a static lambda cannot have any captures}}
+ [*this]() static {}(); // expected-error {{a static lambda cannot have any captures}}
+ }
+ };
+}
diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp
index 07a4db78ac311..fb58b3e0f2021 100644
--- 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++20 %s -Wno-c++2b-extensions -verify
+// RUN: %clang_cc1 -std=c++2b %s -verify
template <auto> struct Nothing {};
@@ -120,3 +121,26 @@ template <class T>
void foo(decltype(+[](T) {}) lambda, T param);
static_assert(!__is_same(decltype(foo<int>), void));
} // namespace GH51641
+
+namespace StaticLambdas {
+template <auto> struct Nothing {};
+Nothing<[]() static { return 0; }()> nothing;
+
+template <typename> struct NothingT {};
+Nothing<[]() static { return 0; }> nothingT;
+
+template <typename T>
+concept True = [] static { return true; }();
+static_assert(True<int>);
+
+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; }));
+
+}
diff --git a/clang/test/SemaCXX/overloaded-operator-decl.cpp b/clang/test/SemaCXX/overloaded-operator-decl.cpp
index 972e2deac29ed..a21e13f3fceb8 100644
--- a/clang/test/SemaCXX/overloaded-operator-decl.cpp
+++ b/clang/test/SemaCXX/overloaded-operator-decl.cpp
@@ -51,7 +51,7 @@ struct PR10839 {
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-warning{{is a C++2b extension}}
};
void f() {
int i = 0;
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index d895cb75ec7ac..2d57897c7e8dc 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1477,7 +1477,7 @@ <h2 id="cxx23">C++2b implementation status</h2>
<tr>
<td>static <code>operator()</code></td>
<td><a href="https://wg21.link/P1169R4">P1169R4</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 16</td>
</tr>
<tr>
<td>Extended floating-point types and standard names</td>
More information about the cfe-commits
mailing list