Skip to content

Commit bc73ef0

Browse files
committed
PR60985: Fix merging of lambda closure types across modules.
Previously, distinct lambdas would get merged, and multiple definitions of the same lambda would not get merged, because we attempted to identify lambdas by their ordinal position within their lexical DeclContext. This failed for lambdas within namespace-scope variables and within variable templates, where the lexical position in the context containing the variable didn't uniquely identify the lambda. In this patch, we instead identify lambda closure types by index within their context declaration, which does uniquely identify them in a way that's consistent across modules. This change causes a deserialization cycle between the type of a variable with deduced type and a lambda appearing as the initializer of the variable -- reading the variable's type requires reading and merging the lambda, and reading the lambda requires reading and merging the variable. This is addressed by deferring loading the deduced type of a variable until after we finish recursive deserialization. This also exposes a pre-existing subtle issue where loading a variable declaration would trigger immediate loading of its initializer, which could recursively refer back to properties of the variable. This particularly causes problems if the initializer contains a lambda-expression, but can be problematic in general. That is addressed by switching to lazily loading the initializers of variables rather than always loading them with the variable declaration. As well as fixing a deserialization cycle, that should improve laziness of deserialization in general. LambdaDefinitionData had 63 spare bits in it, presumably caused by an off-by-one-error in some previous change. This change claims 32 of those bits as a counter for the lambda within its context. We could probably move the numbering to separate storage, like we do for the device-side mangling number, to optimize the likely-common case where all three numbers (host-side mangling number, device-side mangling number, and index within the context declaration) are zero, but that's not done in this change. Fixes llvm#60985. Reviewed By: #clang-language-wg, aaron.ballman Differential Revision: https://reviews.llvm.org/D145737
1 parent 72c662a commit bc73ef0

27 files changed

+593
-248
lines changed

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ Bug Fixes in This Version
257257
- Fix handling of comments in function like macros so they are ignored in -CC
258258
mode.
259259
(`#60887 <https://github.com/llvm/llvm-project/issues/60887>`_)
260+
- Fix incorrect merging of lambdas across modules.
261+
(`#60985 <https://github.com/llvm/llvm-project/issues/60985>`_)
260262

261263

262264
Bug Fixes to Compiler Builtins

clang/include/clang/AST/Decl.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ struct EvaluatedStmt {
900900
bool HasICEInit : 1;
901901
bool CheckedForICEInit : 1;
902902

903-
Stmt *Value;
903+
LazyDeclStmtPtr Value;
904904
APValue Evaluated;
905905

906906
EvaluatedStmt()

clang/include/clang/AST/DeclCXX.h

+28-11
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ class CXXRecordDecl : public RecordDecl {
395395
unsigned NumCaptures : 15;
396396

397397
/// The number of explicit captures in this lambda.
398-
unsigned NumExplicitCaptures : 13;
398+
unsigned NumExplicitCaptures : 12;
399399

400400
/// Has known `internal` linkage.
401401
unsigned HasKnownInternalLinkage : 1;
@@ -404,6 +404,10 @@ class CXXRecordDecl : public RecordDecl {
404404
/// mangling in the Itanium C++ ABI.
405405
unsigned ManglingNumber : 31;
406406

407+
/// The index of this lambda within its context declaration. This is not in
408+
/// general the same as the mangling number.
409+
unsigned IndexInContext;
410+
407411
/// The declaration that provides context for this lambda, if the
408412
/// actual DeclContext does not suffice. This is used for lambdas that
409413
/// occur within default arguments of function parameters within the class
@@ -424,7 +428,7 @@ class CXXRecordDecl : public RecordDecl {
424428
: DefinitionData(D), DependencyKind(DK), IsGenericLambda(IsGeneric),
425429
CaptureDefault(CaptureDefault), NumCaptures(0),
426430
NumExplicitCaptures(0), HasKnownInternalLinkage(0), ManglingNumber(0),
427-
MethodTyInfo(Info) {
431+
IndexInContext(0), MethodTyInfo(Info) {
428432
IsLambda = true;
429433

430434
// C++1z [expr.prim.lambda]p4:
@@ -1772,18 +1776,31 @@ class CXXRecordDecl : public RecordDecl {
17721776
/// the declaration context suffices.
17731777
Decl *getLambdaContextDecl() const;
17741778

1775-
/// Set the mangling number and context declaration for a lambda
1776-
/// class.
1777-
void setLambdaMangling(unsigned ManglingNumber, Decl *ContextDecl,
1778-
bool HasKnownInternalLinkage = false) {
1779+
/// Retrieve the index of this lambda within the context declaration returned
1780+
/// by getLambdaContextDecl().
1781+
unsigned getLambdaIndexInContext() const {
17791782
assert(isLambda() && "Not a lambda closure type!");
1780-
getLambdaData().ManglingNumber = ManglingNumber;
1781-
getLambdaData().ContextDecl = ContextDecl;
1782-
getLambdaData().HasKnownInternalLinkage = HasKnownInternalLinkage;
1783+
return getLambdaData().IndexInContext;
17831784
}
17841785

1785-
/// Set the device side mangling number.
1786-
void setDeviceLambdaManglingNumber(unsigned Num) const;
1786+
/// Information about how a lambda is numbered within its context.
1787+
struct LambdaNumbering {
1788+
Decl *ContextDecl = nullptr;
1789+
unsigned IndexInContext = 0;
1790+
unsigned ManglingNumber = 0;
1791+
unsigned DeviceManglingNumber = 0;
1792+
bool HasKnownInternalLinkage = false;
1793+
};
1794+
1795+
/// Set the mangling numbers and context declaration for a lambda class.
1796+
void setLambdaNumbering(LambdaNumbering Numbering);
1797+
1798+
// Get the mangling numbers and context declaration for a lambda class.
1799+
LambdaNumbering getLambdaNumbering() const {
1800+
return {getLambdaContextDecl(), getLambdaIndexInContext(),
1801+
getLambdaManglingNumber(), getDeviceLambdaManglingNumber(),
1802+
hasKnownLambdaInternalLinkage()};
1803+
}
17871804

17881805
/// Retrieve the device side mangling number.
17891806
unsigned getDeviceLambdaManglingNumber() const;

clang/include/clang/AST/ExternalASTSource.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,22 @@ struct LazyOffsetPtr {
371371
/// \param Source the external AST source.
372372
///
373373
/// \returns a pointer to the AST node.
374-
T* get(ExternalASTSource *Source) const {
374+
T *get(ExternalASTSource *Source) const {
375375
if (isOffset()) {
376376
assert(Source &&
377377
"Cannot deserialize a lazy pointer without an AST source");
378378
Ptr = reinterpret_cast<uint64_t>((Source->*Get)(Ptr >> 1));
379379
}
380380
return reinterpret_cast<T*>(Ptr);
381381
}
382+
383+
/// Retrieve the address of the AST node pointer. Deserializes the pointee if
384+
/// necessary.
385+
T **getAddressOfPointer(ExternalASTSource *Source) const {
386+
// Ensure the integer is in pointer form.
387+
(void)get(Source);
388+
return reinterpret_cast<T**>(&Ptr);
389+
}
382390
};
383391

384392
/// A lazy value (of type T) that is within an AST node of type Owner,

clang/include/clang/AST/MangleNumberingContext.h

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class VarDecl;
2727
/// Keeps track of the mangled names of lambda expressions and block
2828
/// literals within a particular context.
2929
class MangleNumberingContext {
30+
// The index of the next lambda we encounter in this context.
31+
unsigned LambdaIndex = 0;
32+
3033
public:
3134
virtual ~MangleNumberingContext() {}
3235

@@ -55,6 +58,11 @@ class MangleNumberingContext {
5558
/// given call operator within the device context. No device number is
5659
/// assigned if there's no device numbering context is associated.
5760
virtual unsigned getDeviceManglingNumber(const CXXMethodDecl *) { return 0; }
61+
62+
// Retrieve the index of the next lambda appearing in this context, which is
63+
// used for deduplicating lambdas across modules. Note that this is a simple
64+
// sequence number and is not ABI-dependent.
65+
unsigned getNextLambdaIndex() { return LambdaIndex++; }
5866
};
5967

6068
} // end namespace clang

clang/include/clang/AST/Stmt.h

+25
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,11 @@ class IfStmt final
20922092
: nullptr;
20932093
}
20942094

2095+
void setConditionVariableDeclStmt(DeclStmt *CondVar) {
2096+
assert(hasVarStorage());
2097+
getTrailingObjects<Stmt *>()[varOffset()] = CondVar;
2098+
}
2099+
20952100
Stmt *getInit() {
20962101
return hasInitStorage() ? getTrailingObjects<Stmt *>()[initOffset()]
20972102
: nullptr;
@@ -2324,6 +2329,11 @@ class SwitchStmt final : public Stmt,
23242329
: nullptr;
23252330
}
23262331

2332+
void setConditionVariableDeclStmt(DeclStmt *CondVar) {
2333+
assert(hasVarStorage());
2334+
getTrailingObjects<Stmt *>()[varOffset()] = CondVar;
2335+
}
2336+
23272337
SwitchCase *getSwitchCaseList() { return FirstCase; }
23282338
const SwitchCase *getSwitchCaseList() const { return FirstCase; }
23292339
void setSwitchCaseList(SwitchCase *SC) { FirstCase = SC; }
@@ -2487,6 +2497,11 @@ class WhileStmt final : public Stmt,
24872497
: nullptr;
24882498
}
24892499

2500+
void setConditionVariableDeclStmt(DeclStmt *CondVar) {
2501+
assert(hasVarStorage());
2502+
getTrailingObjects<Stmt *>()[varOffset()] = CondVar;
2503+
}
2504+
24902505
SourceLocation getWhileLoc() const { return WhileStmtBits.WhileLoc; }
24912506
void setWhileLoc(SourceLocation L) { WhileStmtBits.WhileLoc = L; }
24922507

@@ -2576,6 +2591,8 @@ class DoStmt : public Stmt {
25762591
/// the init/cond/inc parts of the ForStmt will be null if they were not
25772592
/// specified in the source.
25782593
class ForStmt : public Stmt {
2594+
friend class ASTStmtReader;
2595+
25792596
enum { INIT, CONDVAR, COND, INC, BODY, END_EXPR };
25802597
Stmt* SubExprs[END_EXPR]; // SubExprs[INIT] is an expression or declstmt.
25812598
SourceLocation LParenLoc, RParenLoc;
@@ -2603,10 +2620,18 @@ class ForStmt : public Stmt {
26032620

26042621
/// If this ForStmt has a condition variable, return the faux DeclStmt
26052622
/// associated with the creation of that condition variable.
2623+
DeclStmt *getConditionVariableDeclStmt() {
2624+
return reinterpret_cast<DeclStmt*>(SubExprs[CONDVAR]);
2625+
}
2626+
26062627
const DeclStmt *getConditionVariableDeclStmt() const {
26072628
return reinterpret_cast<DeclStmt*>(SubExprs[CONDVAR]);
26082629
}
26092630

2631+
void setConditionVariableDeclStmt(DeclStmt *CondVar) {
2632+
SubExprs[CONDVAR] = CondVar;
2633+
}
2634+
26102635
Expr *getCond() { return reinterpret_cast<Expr*>(SubExprs[COND]); }
26112636
Expr *getInc() { return reinterpret_cast<Expr*>(SubExprs[INC]); }
26122637
Stmt *getBody() { return SubExprs[BODY]; }

clang/include/clang/Sema/ExternalSemaSource.h

+5
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ class ExternalSemaSource : public ExternalASTSource {
230230
return false;
231231
}
232232

233+
/// Notify the external source that a lambda was assigned a mangling number.
234+
/// This enables the external source to track the correspondence between
235+
/// lambdas and mangling numbers if necessary.
236+
virtual void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) {}
237+
233238
/// LLVM-style RTTI.
234239
/// \{
235240
bool isA(const void *ClassID) const override {

clang/include/clang/Sema/MultiplexExternalSemaSource.h

+3
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
360360
bool MaybeDiagnoseMissingCompleteType(SourceLocation Loc,
361361
QualType T) override;
362362

363+
// Inform all attached sources that a mangling number was assigned.
364+
void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) override;
365+
363366
/// LLVM-style RTTI.
364367
/// \{
365368
bool isA(const void *ClassID) const override {

clang/include/clang/Sema/Sema.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -7108,10 +7108,9 @@ class Sema final {
71087108
Expr *TrailingRequiresClause);
71097109

71107110
/// Number lambda for linkage purposes if necessary.
7111-
void handleLambdaNumbering(
7112-
CXXRecordDecl *Class, CXXMethodDecl *Method,
7113-
std::optional<std::tuple<bool, unsigned, unsigned, Decl *>> Mangling =
7114-
std::nullopt);
7111+
void handleLambdaNumbering(CXXRecordDecl *Class, CXXMethodDecl *Method,
7112+
std::optional<CXXRecordDecl::LambdaNumbering>
7113+
NumberingOverride = std::nullopt);
71157114

71167115
/// Endow the lambda scope info with the relevant properties.
71177116
void buildLambdaScope(sema::LambdaScopeInfo *LSI, CXXMethodDecl *CallOperator,

clang/include/clang/Serialization/ASTReader.h

+18-1
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,10 @@ class ASTReader
560560
llvm::DenseMap<Decl*, llvm::SmallVector<NamedDecl*, 2>>
561561
AnonymousDeclarationsForMerging;
562562

563+
/// Map from numbering information for lambdas to the corresponding lambdas.
564+
llvm::DenseMap<std::pair<const Decl *, unsigned>, NamedDecl *>
565+
LambdaDeclarationsForMerging;
566+
563567
/// Key used to identify LifetimeExtendedTemporaryDecl for merging,
564568
/// containing the lifetime-extending declaration and the mangling number.
565569
using LETemporaryKey = std::pair<Decl *, unsigned>;
@@ -1101,7 +1105,13 @@ class ASTReader
11011105
/// they might contain a deduced return type that refers to a local type
11021106
/// declared within the function.
11031107
SmallVector<std::pair<FunctionDecl *, serialization::TypeID>, 16>
1104-
PendingFunctionTypes;
1108+
PendingDeducedFunctionTypes;
1109+
1110+
/// The list of deduced variable types that we have not yet read, because
1111+
/// they might contain a deduced type that refers to a local type declared
1112+
/// within the variable.
1113+
SmallVector<std::pair<VarDecl *, serialization::TypeID>, 16>
1114+
PendingDeducedVarTypes;
11051115

11061116
/// The list of redeclaration chains that still need to be
11071117
/// reconstructed, and the local offset to the corresponding list
@@ -1139,6 +1149,11 @@ class ASTReader
11391149
2>
11401150
PendingObjCExtensionIvarRedeclarations;
11411151

1152+
/// Members that have been added to classes, for which the class has not yet
1153+
/// been notified. CXXRecordDecl::addedMember will be called for each of
1154+
/// these once recursive deserialization is complete.
1155+
SmallVector<std::pair<CXXRecordDecl*, Decl*>, 4> PendingAddedClassMembers;
1156+
11421157
/// The set of NamedDecls that have been loaded, but are members of a
11431158
/// context that has been merged into another context where the corresponding
11441159
/// declaration is either missing or has not yet been loaded.
@@ -2082,6 +2097,8 @@ class ASTReader
20822097
llvm::MapVector<const FunctionDecl *, std::unique_ptr<LateParsedTemplate>>
20832098
&LPTMap) override;
20842099

2100+
void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) override;
2101+
20852102
/// Load a selector from disk, registering its ID if it exists.
20862103
void LoadSelector(Selector Sel);
20872104

clang/lib/AST/ASTContext.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -6622,6 +6622,10 @@ bool ASTContext::FriendsDifferByConstraints(const FunctionDecl *X,
66226622
}
66236623

66246624
bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
6625+
// Caution: this function is called by the AST reader during deserialization,
6626+
// so it cannot rely on AST invariants being met. Non-trivial accessors
6627+
// should be avoided, along with any traversal of redeclaration chains.
6628+
66256629
if (X == Y)
66266630
return true;
66276631

@@ -6757,6 +6761,11 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
67576761
if (const auto *VarX = dyn_cast<VarDecl>(X)) {
67586762
const auto *VarY = cast<VarDecl>(Y);
67596763
if (VarX->getLinkageInternal() == VarY->getLinkageInternal()) {
6764+
// During deserialization, we might compare variables before we load
6765+
// their types. Assume the types will end up being the same.
6766+
if (VarX->getType().isNull() || VarY->getType().isNull())
6767+
return true;
6768+
67606769
if (hasSameType(VarX->getType(), VarY->getType()))
67616770
return true;
67626771

clang/lib/AST/ASTImporter.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -2919,13 +2919,12 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
29192919
DC, *TInfoOrErr, Loc, DCXX->getLambdaDependencyKind(),
29202920
DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
29212921
return D2CXX;
2922-
ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
2922+
CXXRecordDecl::LambdaNumbering Numbering = DCXX->getLambdaNumbering();
2923+
ExpectedDecl CDeclOrErr = import(Numbering.ContextDecl);
29232924
if (!CDeclOrErr)
29242925
return CDeclOrErr.takeError();
2925-
D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr,
2926-
DCXX->hasKnownLambdaInternalLinkage());
2927-
D2CXX->setDeviceLambdaManglingNumber(
2928-
DCXX->getDeviceLambdaManglingNumber());
2926+
Numbering.ContextDecl = *CDeclOrErr;
2927+
D2CXX->setLambdaNumbering(Numbering);
29292928
} else if (DCXX->isInjectedClassName()) {
29302929
// We have to be careful to do a similar dance to the one in
29312930
// Sema::ActOnStartCXXMemberDeclarations

clang/lib/AST/Decl.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -2356,12 +2356,15 @@ Expr *VarDecl::getInit() {
23562356
if (auto *S = Init.dyn_cast<Stmt *>())
23572357
return cast<Expr>(S);
23582358

2359-
return cast_or_null<Expr>(Init.get<EvaluatedStmt *>()->Value);
2359+
auto *Eval = getEvaluatedStmt();
2360+
return cast<Expr>(Eval->Value.isOffset()
2361+
? Eval->Value.get(getASTContext().getExternalSource())
2362+
: Eval->Value.get(nullptr));
23602363
}
23612364

23622365
Stmt **VarDecl::getInitAddress() {
23632366
if (auto *ES = Init.dyn_cast<EvaluatedStmt *>())
2364-
return &ES->Value;
2367+
return ES->Value.getAddressOfPointer(getASTContext().getExternalSource());
23652368

23662369
return Init.getAddrOfPtr1();
23672370
}
@@ -2498,7 +2501,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
24982501
bool IsConstantInitialization) const {
24992502
EvaluatedStmt *Eval = ensureEvaluatedStmt();
25002503

2501-
const auto *Init = cast<Expr>(Eval->Value);
2504+
const auto *Init = getInit();
25022505
assert(!Init->isValueDependent());
25032506

25042507
// We only produce notes indicating why an initializer is non-constant the
@@ -2582,7 +2585,7 @@ bool VarDecl::checkForConstantInitialization(
25822585
"already evaluated var value before checking for constant init");
25832586
assert(getASTContext().getLangOpts().CPlusPlus && "only meaningful in C++");
25842587

2585-
assert(!cast<Expr>(Eval->Value)->isValueDependent());
2588+
assert(!getInit()->isValueDependent());
25862589

25872590
// Evaluate the initializer to check whether it's a constant expression.
25882591
Eval->HasConstantInitialization =

clang/lib/AST/DeclCXX.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -1646,10 +1646,15 @@ Decl *CXXRecordDecl::getLambdaContextDecl() const {
16461646
return getLambdaData().ContextDecl.get(Source);
16471647
}
16481648

1649-
void CXXRecordDecl::setDeviceLambdaManglingNumber(unsigned Num) const {
1649+
void CXXRecordDecl::setLambdaNumbering(LambdaNumbering Numbering) {
16501650
assert(isLambda() && "Not a lambda closure type!");
1651-
if (Num)
1652-
getASTContext().DeviceLambdaManglingNumbers[this] = Num;
1651+
getLambdaData().ManglingNumber = Numbering.ManglingNumber;
1652+
if (Numbering.DeviceManglingNumber)
1653+
getASTContext().DeviceLambdaManglingNumbers[this] =
1654+
Numbering.DeviceManglingNumber;
1655+
getLambdaData().IndexInContext = Numbering.IndexInContext;
1656+
getLambdaData().ContextDecl = Numbering.ContextDecl;
1657+
getLambdaData().HasKnownInternalLinkage = Numbering.HasKnownInternalLinkage;
16531658
}
16541659

16551660
unsigned CXXRecordDecl::getDeviceLambdaManglingNumber() const {

clang/lib/AST/ODRDiagsEmitter.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(
17421742
return true;
17431743
}
17441744

1745+
// Note, these calls can trigger deserialization.
17451746
const Expr *FirstInit = FirstParam->getInit();
17461747
const Expr *SecondInit = SecondParam->getInit();
17471748
if ((FirstInit == nullptr) != (SecondInit == nullptr)) {

clang/lib/Sema/MultiplexExternalSemaSource.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,9 @@ bool MultiplexExternalSemaSource::MaybeDiagnoseMissingCompleteType(
341341
}
342342
return false;
343343
}
344+
345+
void MultiplexExternalSemaSource::AssignedLambdaNumbering(
346+
const CXXRecordDecl *Lambda) {
347+
for (auto *Source : Sources)
348+
Source->AssignedLambdaNumbering(Lambda);
349+
}

0 commit comments

Comments
 (0)