Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@ class CXXRecordDecl : public RecordDecl {
/// the declaration context suffices.
Decl *getLambdaContextDecl() const;

/// Set the context declaration for a lambda class.
void setLambdaContextDecl(Decl *ContextDecl);

/// Retrieve the index of this lambda within the context declaration returned
/// by getLambdaContextDecl().
unsigned getLambdaIndexInContext() const {
Expand All @@ -1800,21 +1803,19 @@ class CXXRecordDecl : public RecordDecl {

/// Information about how a lambda is numbered within its context.
struct LambdaNumbering {
Decl *ContextDecl = nullptr;
unsigned IndexInContext = 0;
unsigned ManglingNumber = 0;
unsigned DeviceManglingNumber = 0;
bool HasKnownInternalLinkage = false;
};

/// Set the mangling numbers and context declaration for a lambda class.
/// Set the mangling numbers for a lambda class.
void setLambdaNumbering(LambdaNumbering Numbering);

// Get the mangling numbers and context declaration for a lambda class.
// Get the mangling numbers for a lambda class.
LambdaNumbering getLambdaNumbering() const {
return {getLambdaContextDecl(), getLambdaIndexInContext(),
getLambdaManglingNumber(), getDeviceLambdaManglingNumber(),
hasKnownLambdaInternalLinkage()};
return {getLambdaIndexInContext(), getLambdaManglingNumber(),
getDeviceLambdaManglingNumber(), hasKnownLambdaInternalLinkage()};
}

/// Retrieve the device side mangling number.
Expand Down
10 changes: 6 additions & 4 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3444,12 +3444,14 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
DC, *TInfoOrErr, Loc, DCXX->getLambdaDependencyKind(),
DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
return D2CXX;
CXXRecordDecl::LambdaNumbering Numbering = DCXX->getLambdaNumbering();
ExpectedDecl CDeclOrErr = import(Numbering.ContextDecl);
Decl *ContextDecl = DCXX->getLambdaContextDecl();
ExpectedDecl CDeclOrErr = import(ContextDecl);
if (!CDeclOrErr)
return CDeclOrErr.takeError();
Numbering.ContextDecl = *CDeclOrErr;
D2CXX->setLambdaNumbering(Numbering);
if (ContextDecl != nullptr) {
D2CXX->setLambdaContextDecl(*CDeclOrErr);
}
D2CXX->setLambdaNumbering(DCXX->getLambdaNumbering());
} else {
if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
D->getTagKind(), DC, *BeginLocOrErr, Loc,
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1837,14 +1837,18 @@ Decl *CXXRecordDecl::getLambdaContextDecl() const {
return getLambdaData().ContextDecl.get(Source);
}

void CXXRecordDecl::setLambdaContextDecl(Decl *ContextDecl) {
assert(isLambda() && "Not a lambda closure type!");
getLambdaData().ContextDecl = ContextDecl;
}

void CXXRecordDecl::setLambdaNumbering(LambdaNumbering Numbering) {
assert(isLambda() && "Not a lambda closure type!");
getLambdaData().ManglingNumber = Numbering.ManglingNumber;
if (Numbering.DeviceManglingNumber)
getASTContext().DeviceLambdaManglingNumbers[this] =
Numbering.DeviceManglingNumber;
getLambdaData().IndexInContext = Numbering.IndexInContext;
getLambdaData().ContextDecl = Numbering.ContextDecl;
getLambdaData().HasKnownInternalLinkage = Numbering.HasKnownInternalLinkage;
}

Expand Down
38 changes: 32 additions & 6 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,19 @@ NamespaceDecl *ItaniumMangleContextImpl::getStdNamespace() {
return StdNamespace;
}

/// Retrieve the lambda associated with an init-capture variable.
static const CXXRecordDecl *getLambdaForInitCapture(const VarDecl *VD) {
if (!VD || !VD->isInitCapture())
return nullptr;

const auto *Method = cast<CXXMethodDecl>(VD->getDeclContext());
const auto *Lambda = dyn_cast<CXXRecordDecl>(Method->getParent());
if (!Lambda || !Lambda->isLambda())
return nullptr;

return Lambda;
}

/// Retrieve the declaration context that should be used when mangling the given
/// declaration.
const DeclContext *
Expand Down Expand Up @@ -674,9 +687,18 @@ ItaniumMangleContextImpl::getEffectiveDeclContext(const Decl *D) {
return getEffectiveDeclContext(cast<Decl>(DC));
}

if (const auto *VD = dyn_cast<VarDecl>(D))
if (const auto *VD = dyn_cast<VarDecl>(D)) {
if (const CXXRecordDecl *Lambda = getLambdaForInitCapture(VD)) {
const DeclContext *ParentDC = getEffectiveParentContext(Lambda);
// Init-captures in local lambdas are mangled relative to the enclosing
// local context rather than operator() to avoid recursive local-name
// encoding through the call operator type.
if (isLocalContainerContext(ParentDC))
return ParentDC;
}
if (VD->isExternC())
return getASTContext().getTranslationUnitDecl();
}

if (const auto *FD = getASTContext().getLangOpts().getClangABICompat() >
LangOptions::ClangABI::Ver19
Expand Down Expand Up @@ -1873,12 +1895,13 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD,
{
AbiTagState LocalAbiTags(AbiTags);

if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC))
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC)) {
mangleObjCMethodName(MD);
else if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC))
} else if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC)) {
mangleBlockForPrefix(BD);
else
} else {
mangleFunctionEncoding(getParentOfLocalEntity(DC));
}

// Implicit ABI tags (from namespace) are not available in the following
// entity; reset to actually emitted tags, which are available.
Expand Down Expand Up @@ -2277,15 +2300,18 @@ const NamedDecl *CXXNameMangler::getClosurePrefix(const Decl *ND) {
const NamedDecl *Context = nullptr;
if (auto *Block = dyn_cast<BlockDecl>(ND)) {
Context = dyn_cast_or_null<NamedDecl>(Block->getBlockManglingContextDecl());
} else if (auto *VD = dyn_cast<VarDecl>(ND)) {
if (const CXXRecordDecl *Lambda = getLambdaForInitCapture(VD))
Context = dyn_cast_or_null<NamedDecl>(Lambda->getLambdaContextDecl());
} else if (auto *RD = dyn_cast<CXXRecordDecl>(ND)) {
if (RD->isLambda())
Context = dyn_cast_or_null<NamedDecl>(RD->getLambdaContextDecl());
}
if (!Context)
return nullptr;

// Only lambdas within the initializer of a non-local variable or non-static
// data member get a <closure-prefix>.
// Only entities associated with lambdas within the initializer of a
// non-local variable or non-static data member get a <closure-prefix>.
if ((isa<VarDecl>(Context) && cast<VarDecl>(Context)->hasGlobalStorage()) ||
isa<FieldDecl>(Context))
return Context;
Expand Down
24 changes: 16 additions & 8 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,11 +478,6 @@ bool Sema::DiagnoseInvalidExplicitObjectParameterInLambda(
void Sema::handleLambdaNumbering(
CXXRecordDecl *Class, CXXMethodDecl *Method,
std::optional<CXXRecordDecl::LambdaNumbering> NumberingOverride) {
if (NumberingOverride) {
Class->setLambdaNumbering(*NumberingOverride);
return;
}

ContextRAII ManglingContext(*this, Class->getDeclContext());

auto getMangleNumberingContext =
Expand All @@ -499,10 +494,23 @@ void Sema::handleLambdaNumbering(
return &Context.getManglingNumberContext(DC);
};

CXXRecordDecl::LambdaNumbering Numbering;
MangleNumberingContext *MCtx;
std::tie(MCtx, Numbering.ContextDecl) =
Decl *ContextDecl;
std::tie(MCtx, ContextDecl) =
getCurrentMangleNumberContext(Class->getDeclContext());
// getManglingNumber(Method) below may trigger mangling of dependent types
// that reference init-captures. Publish the lambda context declaration early
// so such mangling can resolve the surrounding context without recursing
// through the lambda call operator. This avoids publishing provisional
// numbering state before final numbering is assigned below.
if (ContextDecl)
Class->setLambdaContextDecl(ContextDecl);
if (NumberingOverride) {
Class->setLambdaNumbering(*NumberingOverride);
return;
}

CXXRecordDecl::LambdaNumbering Numbering;
if (!MCtx && (getLangOpts().CUDA || getLangOpts().SYCLIsDevice ||
getLangOpts().SYCLIsHost)) {
// Force lambda numbering in CUDA/HIP as we need to name lambdas following
Expand All @@ -512,7 +520,7 @@ void Sema::handleLambdaNumbering(
// Also force for SYCL, since we need this for the
// __builtin_sycl_unique_stable_name implementation, which depends on lambda
// mangling.
MCtx = getMangleNumberingContext(Class, Numbering.ContextDecl);
MCtx = getMangleNumberingContext(Class, ContextDecl);
assert(MCtx && "Retrieving mangle numbering context failed!");
Numbering.HasKnownInternalLinkage = true;
}
Expand Down
66 changes: 66 additions & 0 deletions clang/test/CodeGenCXX/mangle-lambdas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,70 @@ void test_StaticInlineMember() {
StaticInlineMember::x();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the Itanium ABI specify how to mangle these cases? Does gcc produce the same mangling? (I think at least some of these cases are externally visible.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this still aligns to the Itanium ABI spec. The 5.1.8 rule "If the context is the body of a function (inline and/or template), the closure type is encoded like any other local entity", and some relevant statements in both 5.1.7 and 5.1.8.

Basically, I think this is a recursion bug in Clang’s mangling path. This fix is to break recursive re-entry by resolving/publishing lambda context early. I do not invent a new recursive-type encoding.

For GCC, it produces the same output of all the test files. (except the existing differences of how we mangle lambda, e.g. main::$_0 vs main::{lambda()#1})

template <typename L>
auto pr63271foo(L l_) {
return [l = l_](decltype(l)) -> void {};
}

// CHECK-LABEL: define{{.*}} @_Z10pr63271usev
// CHECK: call void @_ZZ10pr63271fooIiEDaT_ENKUliE_clEi
void pr63271use() {
pr63271foo(0)(0);
}

template <typename T>
struct pr139089_vlambda {
pr139089_vlambda() {
[&m = m_args](decltype(m) args) { (void)args; }(m_args);
}
int m_args = 0;
};

// CHECK-LABEL: define{{.*}} @_Z11pr139089usev
// CHECK: call void @_ZN16pr139089_vlambdaIiEC1Ev
void pr139089use() {
(void)pr139089_vlambda<int>{};
}


template <class RET> struct pr86240_context {
using Ptr = pr86240_context<RET> *;
};

template <typename Callable>
void pr86240_schedule_coro(Callable &&coro_function) {
[coro_function{coro_function}](
typename pr86240_context<decltype(coro_function())>::Ptr ctx) -> int {
return ctx != nullptr;
}(nullptr);
}

// CHECK-LABEL: define{{.*}} @_Z10pr86240usev
// CHECK: call noundef i32 @"_ZZ21pr86240_schedule_coroIZ10pr86240usevE3$_0EvOT_ENKUlP15pr86240_contextIiEE_clES5_"
void pr86240use() {
pr86240_schedule_coro([] { return 0; });
}

template <typename T>
auto nonLocalVarTemplate = [x = T{}](decltype(x) y) { return y; };

// CHECK-LABEL: define{{.*}} @_Z22nonLocalVarTemplateUsev
// CHECK: call noundef i32 @_ZNK19nonLocalVarTemplateIiEMUliE_clEi
int nonLocalVarTemplateUse() {
return nonLocalVarTemplate<int>(2);
}

template <typename T>
struct nonLocalStaticInlineMember {
static inline auto l = [x = T{}](decltype(x) y) { return y; };
};

// CHECK-LABEL: define{{.*}} @_Z29nonLocalStaticInlineMemberUsev
// CHECK: call noundef i32 @_ZNK26nonLocalStaticInlineMemberIiE1lMUliE_clEi
int nonLocalStaticInlineMemberUse() {
return nonLocalStaticInlineMember<int>::l(2);
}

// Check linkage of the various lambdas.
// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ11inline_funciENKUlvE_clEv
// CHECK: ret i32 1
Expand Down Expand Up @@ -331,6 +395,8 @@ void test_StaticInlineMember() {

// CHECK-LABEL: define linkonce_odr void @_Z1fIZZNK23TestNestedInstantiationclEvENKUlvE_clEvEUlvE_EvT_

// CHECK-LABEL: define linkonce_odr void @_ZZN16pr139089_vlambdaIiEC1EvENKUlRiE_clES1_


namespace PR12808 {
template <typename> struct B {
Expand Down