Skip to content

Commit ec809e4

Browse files
author
Erich Keane
committed
PR47372: Fix Lambda invoker calling conventions
As mentioned in the defect, the lambda static invoker does not follow the calling convention of the lambda itself, which seems wrong. This patch ensures that the calling convention of operator() is passed onto the invoker and conversion-operator type. This is accomplished by extracting the calling-convention determination code out into a separate function in order to better reflect the 'thiscall' work, as well as somewhat better support the future implementation of https://devblogs.microsoft.com/oldnewthing/20150220-00/?p=44623 For any target (basically just win32) that has a different free and static function calling convention, this generates BOTH alternatives. This required some work to get the Windows mangler to work correctly for this, as well as some tie-breaking for the unary operators. Differential Revision: https://reviews.llvm.org/D89559
1 parent 316593c commit ec809e4

File tree

10 files changed

+433
-48
lines changed

10 files changed

+433
-48
lines changed

clang/include/clang/AST/DeclCXX.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,8 +1008,13 @@ class CXXRecordDecl : public RecordDecl {
10081008

10091009
/// Retrieve the lambda static invoker, the address of which
10101010
/// is returned by the conversion operator, and the body of which
1011-
/// is forwarded to the lambda call operator.
1011+
/// is forwarded to the lambda call operator. The version that does not
1012+
/// take a calling convention uses the 'default' calling convention for free
1013+
/// functions if the Lambda's calling convention was not modified via
1014+
/// attribute. Otherwise, it will return the calling convention specified for
1015+
/// the lambda.
10121016
CXXMethodDecl *getLambdaStaticInvoker() const;
1017+
CXXMethodDecl *getLambdaStaticInvoker(CallingConv CC) const;
10131018

10141019
/// Retrieve the generic lambda's template parameter list.
10151020
/// Returns null if the class does not represent a lambda or a generic

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6597,7 +6597,8 @@ class Sema final {
65976597
/// Get the return type to use for a lambda's conversion function(s) to
65986598
/// function pointer type, given the type of the call operator.
65996599
QualType
6600-
getLambdaConversionFunctionResultType(const FunctionProtoType *CallOpType);
6600+
getLambdaConversionFunctionResultType(const FunctionProtoType *CallOpType,
6601+
CallingConv CC);
66016602

66026603
/// Define the "body" of the conversion from a lambda object to a
66036604
/// function pointer.

clang/lib/AST/DeclCXX.cpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,18 +1507,38 @@ CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const {
15071507
}
15081508

15091509
CXXMethodDecl* CXXRecordDecl::getLambdaStaticInvoker() const {
1510-
if (!isLambda()) return nullptr;
1510+
CXXMethodDecl *CallOp = getLambdaCallOperator();
1511+
CallingConv CC = CallOp->getType()->getAs<FunctionType>()->getCallConv();
1512+
return getLambdaStaticInvoker(CC);
1513+
}
1514+
1515+
static DeclContext::lookup_result
1516+
getLambdaStaticInvokers(const CXXRecordDecl &RD) {
1517+
assert(RD.isLambda() && "Must be a lambda");
15111518
DeclarationName Name =
1512-
&getASTContext().Idents.get(getLambdaStaticInvokerName());
1513-
DeclContext::lookup_result Invoker = lookup(Name);
1514-
if (Invoker.empty()) return nullptr;
1515-
assert(allLookupResultsAreTheSame(Invoker) &&
1516-
"More than one static invoker operator!");
1517-
NamedDecl *InvokerFun = Invoker.front();
1518-
if (const auto *InvokerTemplate = dyn_cast<FunctionTemplateDecl>(InvokerFun))
1519+
&RD.getASTContext().Idents.get(getLambdaStaticInvokerName());
1520+
return RD.lookup(Name);
1521+
}
1522+
1523+
static CXXMethodDecl *getInvokerAsMethod(NamedDecl *ND) {
1524+
if (const auto *InvokerTemplate = dyn_cast<FunctionTemplateDecl>(ND))
15191525
return cast<CXXMethodDecl>(InvokerTemplate->getTemplatedDecl());
1526+
return cast<CXXMethodDecl>(ND);
1527+
}
1528+
1529+
CXXMethodDecl *CXXRecordDecl::getLambdaStaticInvoker(CallingConv CC) const {
1530+
if (!isLambda())
1531+
return nullptr;
1532+
DeclContext::lookup_result Invoker = getLambdaStaticInvokers(*this);
1533+
1534+
for (NamedDecl *ND : Invoker) {
1535+
const FunctionType *FTy =
1536+
cast<ValueDecl>(ND->getAsFunction())->getType()->getAs<FunctionType>();
1537+
if (FTy->getCallConv() == CC)
1538+
return getInvokerAsMethod(ND);
1539+
}
15201540

1521-
return cast<CXXMethodDecl>(InvokerFun);
1541+
return nullptr;
15221542
}
15231543

15241544
void CXXRecordDecl::getCaptureFields(
@@ -2459,14 +2479,8 @@ bool CXXMethodDecl::hasInlineBody() const {
24592479

24602480
bool CXXMethodDecl::isLambdaStaticInvoker() const {
24612481
const CXXRecordDecl *P = getParent();
2462-
if (P->isLambda()) {
2463-
if (const CXXMethodDecl *StaticInvoker = P->getLambdaStaticInvoker()) {
2464-
if (StaticInvoker == this) return true;
2465-
if (P->isGenericLambda() && this->isFunctionTemplateSpecialization())
2466-
return StaticInvoker == this->getPrimaryTemplate()->getTemplatedDecl();
2467-
}
2468-
}
2469-
return false;
2482+
return P->isLambda() && getDeclName().isIdentifier() &&
2483+
getName() == getLambdaStaticInvokerName();
24702484
}
24712485

24722486
CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context,

clang/lib/AST/MicrosoftMangle.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,10 +2260,20 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
22602260
return;
22612261
}
22622262
Out << '@';
2263+
} else if (IsInLambda && D && isa<CXXConversionDecl>(D)) {
2264+
// The only lambda conversion operators are to function pointers, which
2265+
// can differ by their calling convention and are typically deduced. So
2266+
// we make sure that this type gets mangled properly.
2267+
mangleType(T->getReturnType(), Range, QMM_Result);
22632268
} else {
22642269
QualType ResultType = T->getReturnType();
2265-
if (const auto *AT =
2266-
dyn_cast_or_null<AutoType>(ResultType->getContainedAutoType())) {
2270+
if (IsInLambda && isa<CXXConversionDecl>(D)) {
2271+
// The only lambda conversion operators are to function pointers, which
2272+
// can differ by their calling convention and are typically deduced. So
2273+
// we make sure that this type gets mangled properly.
2274+
mangleType(ResultType, Range, QMM_Result);
2275+
} else if (const auto *AT = dyn_cast_or_null<AutoType>(
2276+
ResultType->getContainedAutoType())) {
22672277
Out << '?';
22682278
mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false);
22692279
Out << '?';

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14811,9 +14811,13 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
1481114811
SynthesizedFunctionScope Scope(*this, Conv);
1481214812
assert(!Conv->getReturnType()->isUndeducedType());
1481314813

14814+
QualType ConvRT = Conv->getType()->getAs<FunctionType>()->getReturnType();
14815+
CallingConv CC =
14816+
ConvRT->getPointeeType()->getAs<FunctionType>()->getCallConv();
14817+
1481414818
CXXRecordDecl *Lambda = Conv->getParent();
1481514819
FunctionDecl *CallOp = Lambda->getLambdaCallOperator();
14816-
FunctionDecl *Invoker = Lambda->getLambdaStaticInvoker();
14820+
FunctionDecl *Invoker = Lambda->getLambdaStaticInvoker(CC);
1481714821

1481814822
if (auto *TemplateArgs = Conv->getTemplateSpecializationArgs()) {
1481914823
CallOp = InstantiateFunctionDeclaration(

clang/lib/Sema/SemaLambda.cpp

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,30 +1263,61 @@ void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope,
12631263
PopFunctionScopeInfo();
12641264
}
12651265

1266+
template <typename Func>
1267+
static void repeatForLambdaConversionFunctionCallingConvs(
1268+
Sema &S, const FunctionProtoType &CallOpProto, Func F) {
1269+
CallingConv DefaultFree = S.Context.getDefaultCallingConvention(
1270+
CallOpProto.isVariadic(), /*IsCXXMethod=*/false);
1271+
CallingConv DefaultMember = S.Context.getDefaultCallingConvention(
1272+
CallOpProto.isVariadic(), /*IsCXXMethod=*/true);
1273+
CallingConv CallOpCC = CallOpProto.getCallConv();
1274+
1275+
if (CallOpCC == DefaultMember && DefaultMember != DefaultFree) {
1276+
F(DefaultFree);
1277+
F(DefaultMember);
1278+
} else {
1279+
F(CallOpCC);
1280+
}
1281+
}
1282+
1283+
// Returns the 'standard' calling convention to be used for the lambda
1284+
// conversion function, that is, the 'free' function calling convention unless
1285+
// it is overridden by a non-default calling convention attribute.
1286+
static CallingConv
1287+
getLambdaConversionFunctionCallConv(Sema &S,
1288+
const FunctionProtoType *CallOpProto) {
1289+
CallingConv DefaultFree = S.Context.getDefaultCallingConvention(
1290+
CallOpProto->isVariadic(), /*IsCXXMethod=*/false);
1291+
CallingConv DefaultMember = S.Context.getDefaultCallingConvention(
1292+
CallOpProto->isVariadic(), /*IsCXXMethod=*/true);
1293+
CallingConv CallOpCC = CallOpProto->getCallConv();
1294+
1295+
// If the call-operator hasn't been changed, return both the 'free' and
1296+
// 'member' function calling convention.
1297+
if (CallOpCC == DefaultMember && DefaultMember != DefaultFree)
1298+
return DefaultFree;
1299+
return CallOpCC;
1300+
}
1301+
12661302
QualType Sema::getLambdaConversionFunctionResultType(
1267-
const FunctionProtoType *CallOpProto) {
1268-
// The function type inside the pointer type is the same as the call
1269-
// operator with some tweaks. The calling convention is the default free
1270-
// function convention, and the type qualifications are lost.
1303+
const FunctionProtoType *CallOpProto, CallingConv CC) {
12711304
const FunctionProtoType::ExtProtoInfo CallOpExtInfo =
12721305
CallOpProto->getExtProtoInfo();
12731306
FunctionProtoType::ExtProtoInfo InvokerExtInfo = CallOpExtInfo;
1274-
CallingConv CC = Context.getDefaultCallingConvention(
1275-
CallOpProto->isVariadic(), /*IsCXXMethod=*/false);
12761307
InvokerExtInfo.ExtInfo = InvokerExtInfo.ExtInfo.withCallingConv(CC);
12771308
InvokerExtInfo.TypeQuals = Qualifiers();
12781309
assert(InvokerExtInfo.RefQualifier == RQ_None &&
1279-
"Lambda's call operator should not have a reference qualifier");
1310+
"Lambda's call operator should not have a reference qualifier");
12801311
return Context.getFunctionType(CallOpProto->getReturnType(),
12811312
CallOpProto->getParamTypes(), InvokerExtInfo);
12821313
}
12831314

12841315
/// Add a lambda's conversion to function pointer, as described in
12851316
/// C++11 [expr.prim.lambda]p6.
1286-
static void addFunctionPointerConversion(Sema &S,
1287-
SourceRange IntroducerRange,
1317+
static void addFunctionPointerConversion(Sema &S, SourceRange IntroducerRange,
12881318
CXXRecordDecl *Class,
1289-
CXXMethodDecl *CallOperator) {
1319+
CXXMethodDecl *CallOperator,
1320+
QualType InvokerFunctionTy) {
12901321
// This conversion is explicitly disabled if the lambda's function has
12911322
// pass_object_size attributes on any of its parameters.
12921323
auto HasPassObjectSizeAttr = [](const ParmVarDecl *P) {
@@ -1296,8 +1327,6 @@ static void addFunctionPointerConversion(Sema &S,
12961327
return;
12971328

12981329
// Add the conversion to function pointer.
1299-
QualType InvokerFunctionTy = S.getLambdaConversionFunctionResultType(
1300-
CallOperator->getType()->castAs<FunctionProtoType>());
13011330
QualType PtrToFunctionTy = S.Context.getPointerType(InvokerFunctionTy);
13021331

13031332
// Create the type of the conversion function.
@@ -1442,13 +1471,37 @@ static void addFunctionPointerConversion(Sema &S,
14421471
Class->addDecl(Invoke);
14431472
}
14441473

1474+
/// Add a lambda's conversion to function pointers, as described in
1475+
/// C++11 [expr.prim.lambda]p6. Note that in most cases, this should emit only a
1476+
/// single pointer conversion. In the event that the default calling convention
1477+
/// for free and member functions is different, it will emit both conventions.
1478+
/// FIXME: Implement emitting a version of the operator for EVERY calling
1479+
/// convention for MSVC, as described here:
1480+
/// https://devblogs.microsoft.com/oldnewthing/20150220-00/?p=44623.
1481+
static void addFunctionPointerConversions(Sema &S, SourceRange IntroducerRange,
1482+
CXXRecordDecl *Class,
1483+
CXXMethodDecl *CallOperator) {
1484+
const FunctionProtoType *CallOpProto =
1485+
CallOperator->getType()->castAs<FunctionProtoType>();
1486+
1487+
repeatForLambdaConversionFunctionCallingConvs(
1488+
S, *CallOpProto, [&](CallingConv CC) {
1489+
QualType InvokerFunctionTy =
1490+
S.getLambdaConversionFunctionResultType(CallOpProto, CC);
1491+
addFunctionPointerConversion(S, IntroducerRange, Class, CallOperator,
1492+
InvokerFunctionTy);
1493+
});
1494+
}
1495+
14451496
/// Add a lambda's conversion to block pointer.
14461497
static void addBlockPointerConversion(Sema &S,
14471498
SourceRange IntroducerRange,
14481499
CXXRecordDecl *Class,
14491500
CXXMethodDecl *CallOperator) {
1501+
const FunctionProtoType *CallOpProto =
1502+
CallOperator->getType()->castAs<FunctionProtoType>();
14501503
QualType FunctionTy = S.getLambdaConversionFunctionResultType(
1451-
CallOperator->getType()->castAs<FunctionProtoType>());
1504+
CallOpProto, getLambdaConversionFunctionCallConv(S, CallOpProto));
14521505
QualType BlockPtrTy = S.Context.getBlockPointerType(FunctionTy);
14531506

14541507
FunctionProtoType::ExtProtoInfo ConversionEPI(
@@ -1795,8 +1848,8 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
17951848
// to pointer to function having the same parameter and return
17961849
// types as the closure type's function call operator.
17971850
if (Captures.empty() && CaptureDefault == LCD_None)
1798-
addFunctionPointerConversion(*this, IntroducerRange, Class,
1799-
CallOperator);
1851+
addFunctionPointerConversions(*this, IntroducerRange, Class,
1852+
CallOperator);
18001853

18011854
// Objective-C++:
18021855
// The closure type for a lambda-expression has a public non-virtual

clang/lib/Sema/SemaOverload.cpp

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3645,13 +3645,32 @@ Sema::DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType) {
36453645
return true;
36463646
}
36473647

3648+
// Helper for compareConversionFunctions that gets the FunctionType that the
3649+
// conversion-operator return value 'points' to, or nullptr.
3650+
static const FunctionType *
3651+
getConversionOpReturnTyAsFunction(CXXConversionDecl *Conv) {
3652+
const FunctionType *ConvFuncTy = Conv->getType()->castAs<FunctionType>();
3653+
const PointerType *RetPtrTy =
3654+
ConvFuncTy->getReturnType()->getAs<PointerType>();
3655+
3656+
if (!RetPtrTy)
3657+
return nullptr;
3658+
3659+
return RetPtrTy->getPointeeType()->getAs<FunctionType>();
3660+
}
3661+
36483662
/// Compare the user-defined conversion functions or constructors
36493663
/// of two user-defined conversion sequences to determine whether any ordering
36503664
/// is possible.
36513665
static ImplicitConversionSequence::CompareKind
36523666
compareConversionFunctions(Sema &S, FunctionDecl *Function1,
36533667
FunctionDecl *Function2) {
3654-
if (!S.getLangOpts().ObjC || !S.getLangOpts().CPlusPlus11)
3668+
CXXConversionDecl *Conv1 = dyn_cast_or_null<CXXConversionDecl>(Function1);
3669+
CXXConversionDecl *Conv2 = dyn_cast_or_null<CXXConversionDecl>(Function2);
3670+
if (!Conv1 || !Conv2)
3671+
return ImplicitConversionSequence::Indistinguishable;
3672+
3673+
if (!Conv1->getParent()->isLambda() || !Conv2->getParent()->isLambda())
36553674
return ImplicitConversionSequence::Indistinguishable;
36563675

36573676
// Objective-C++:
@@ -3660,22 +3679,47 @@ compareConversionFunctions(Sema &S, FunctionDecl *Function1,
36603679
// respectively, always prefer the conversion to a function pointer,
36613680
// because the function pointer is more lightweight and is more likely
36623681
// to keep code working.
3663-
CXXConversionDecl *Conv1 = dyn_cast_or_null<CXXConversionDecl>(Function1);
3664-
if (!Conv1)
3665-
return ImplicitConversionSequence::Indistinguishable;
3666-
3667-
CXXConversionDecl *Conv2 = dyn_cast<CXXConversionDecl>(Function2);
3668-
if (!Conv2)
3669-
return ImplicitConversionSequence::Indistinguishable;
3670-
3671-
if (Conv1->getParent()->isLambda() && Conv2->getParent()->isLambda()) {
3682+
if (S.getLangOpts().ObjC && S.getLangOpts().CPlusPlus11) {
36723683
bool Block1 = Conv1->getConversionType()->isBlockPointerType();
36733684
bool Block2 = Conv2->getConversionType()->isBlockPointerType();
36743685
if (Block1 != Block2)
36753686
return Block1 ? ImplicitConversionSequence::Worse
36763687
: ImplicitConversionSequence::Better;
36773688
}
36783689

3690+
// In order to support multiple calling conventions for the lambda conversion
3691+
// operator (such as when the free and member function calling convention is
3692+
// different), prefer the 'free' mechanism, followed by the calling-convention
3693+
// of operator(). The latter is in place to support the MSVC-like solution of
3694+
// defining ALL of the possible conversions in regards to calling-convention.
3695+
const FunctionType *Conv1FuncRet = getConversionOpReturnTyAsFunction(Conv1);
3696+
const FunctionType *Conv2FuncRet = getConversionOpReturnTyAsFunction(Conv2);
3697+
3698+
if (Conv1FuncRet && Conv2FuncRet &&
3699+
Conv1FuncRet->getCallConv() != Conv2FuncRet->getCallConv()) {
3700+
CallingConv Conv1CC = Conv1FuncRet->getCallConv();
3701+
CallingConv Conv2CC = Conv2FuncRet->getCallConv();
3702+
3703+
CXXMethodDecl *CallOp = Conv2->getParent()->getLambdaCallOperator();
3704+
const FunctionProtoType *CallOpProto =
3705+
CallOp->getType()->getAs<FunctionProtoType>();
3706+
3707+
CallingConv CallOpCC =
3708+
CallOp->getType()->getAs<FunctionType>()->getCallConv();
3709+
CallingConv DefaultFree = S.Context.getDefaultCallingConvention(
3710+
CallOpProto->isVariadic(), /*IsCXXMethod=*/false);
3711+
CallingConv DefaultMember = S.Context.getDefaultCallingConvention(
3712+
CallOpProto->isVariadic(), /*IsCXXMethod=*/true);
3713+
3714+
CallingConv PrefOrder[] = {DefaultFree, DefaultMember, CallOpCC};
3715+
for (CallingConv CC : PrefOrder) {
3716+
if (Conv1CC == CC)
3717+
return ImplicitConversionSequence::Better;
3718+
if (Conv2CC == CC)
3719+
return ImplicitConversionSequence::Worse;
3720+
}
3721+
}
3722+
36793723
return ImplicitConversionSequence::Indistinguishable;
36803724
}
36813725

@@ -10180,6 +10224,22 @@ void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
1018010224
if (Fn->isMultiVersion() && Fn->hasAttr<TargetAttr>() &&
1018110225
!Fn->getAttr<TargetAttr>()->isDefaultVersion())
1018210226
return;
10227+
if (isa<CXXConversionDecl>(Fn) &&
10228+
cast<CXXRecordDecl>(Fn->getParent())->isLambda()) {
10229+
// Don't print candidates other than the one that matches the calling
10230+
// convention of the call operator, since that is guaranteed to exist.
10231+
const auto *RD = cast<CXXRecordDecl>(Fn->getParent());
10232+
CXXMethodDecl *CallOp = RD->getLambdaCallOperator();
10233+
CallingConv CallOpCC =
10234+
CallOp->getType()->getAs<FunctionType>()->getCallConv();
10235+
CXXConversionDecl *ConvD = cast<CXXConversionDecl>(Fn);
10236+
QualType ConvRTy = ConvD->getType()->getAs<FunctionType>()->getReturnType();
10237+
CallingConv ConvToCC =
10238+
ConvRTy->getPointeeType()->getAs<FunctionType>()->getCallConv();
10239+
10240+
if (ConvToCC != CallOpCC)
10241+
return;
10242+
}
1018310243

1018410244
std::string FnDesc;
1018510245
std::pair<OverloadCandidateKind, OverloadCandidateSelect> KSPair =

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5022,8 +5022,12 @@ bool Sema::DeduceReturnType(FunctionDecl *FD, SourceLocation Loc,
50225022
"failed to deduce lambda return type");
50235023

50245024
// Build the new return type from scratch.
5025+
CallingConv RetTyCC = FD->getReturnType()
5026+
->getPointeeType()
5027+
->castAs<FunctionType>()
5028+
->getCallConv();
50255029
QualType RetType = getLambdaConversionFunctionResultType(
5026-
CallOp->getType()->castAs<FunctionProtoType>());
5030+
CallOp->getType()->castAs<FunctionProtoType>(), RetTyCC);
50275031
if (FD->getReturnType()->getAs<PointerType>())
50285032
RetType = Context.getPointerType(RetType);
50295033
else {

0 commit comments

Comments
 (0)