Implements 'P1169R4: static operator()' from C++2b.
Reviewed By: #clang-language-wg, aaron.ballman
Differential Revision: https://reviews.llvm.org/D133659
^^^^^^^^^^^^^^^^^^^^^
- 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
----------------------------------
"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">;
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]]'">;
"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<
/// "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
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,
/// 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,
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;
unsigned getKindRank() const {
switch (getKind()) {
case StandardConversion:
+ case StaticObjectArgumentConversion:
return 0;
case UserDefinedConversion:
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; }
}
void setStandard() { setKind(StandardConversion); }
+ void setStaticObjectArgument() { setKind(StaticObjectArgumentConversion); }
void setEllipsis() { setKind(EllipsisConversion); }
void setUserDefined() { setKind(UserDefinedConversion); }
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(
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");
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) {
}
}
+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(
// 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].
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) ||
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();
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(
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())
// 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(),
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);
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()) {
<< 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)
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 =
SCS = &ICS.UserDefined.After;
break;
case ImplicitConversionSequence::AmbiguousConversion:
+ case ImplicitConversionSequence::StaticObjectArgumentConversion:
case ImplicitConversionSequence::EllipsisConversion:
case ImplicitConversionSequence::BadConversion:
return;
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);
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)
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
// 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());
}
CXXMethodDecl *Method =
startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params,
ParamInfo.getDeclSpec().getConstexprSpecifier(),
+ IsLambdaStatic ? SC_Static : SC_None,
ParamInfo.getTrailingRequiresClause());
if (ExplicitParams)
CheckCXXDefaultArguments(Method);
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
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.
case ImplicitConversionSequence::BadConversion:
case ImplicitConversionSequence::AmbiguousConversion:
case ImplicitConversionSequence::EllipsisConversion:
+ case ImplicitConversionSequence::StaticObjectArgumentConversion:
break;
case ImplicitConversionSequence::UserDefinedConversion:
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;
}
}
- // 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
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);
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;
E->getCallOperator()->getEndLoc(),
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
E->getCallOperator()->getConstexprKind(),
+ E->getCallOperator()->getStorageClass(),
E->getCallOperator()->getTrailingRequiresClause());
LSI->CallOperator = NewCallOperator;
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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@-1 {{is a C++2b extension}}
+ // 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}}
+};
--- /dev/null
+// 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();
+}
#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
--- /dev/null
+// 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@-1 {{lambda without a parameter clause is a C++2b extension}}
+auto L2 = []() static {};
+// cxx20-warning@-1 {{static lambdas are a C++2b extension}}
+// 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-warning@-2 {{static lambdas are a C++2b extension}}
+// precxx2b-warning@-3 {{static lambdas are incompatible with C++ standards before C++2b}}
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}}
+ }
+ };
+}
-// 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 {};
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; }));
+
+}
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;
<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>