Skip to content

Commit c40ff94

Browse files
zygoloidcor3ntin
authored andcommitted
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. Differential Revision: https://reviews.llvm.org/D145737
1 parent 5cf549e commit c40ff94

27 files changed

+581
-231
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ Bug Fixes in This Version
163163
(`#60268 <https://github.com/llvm/llvm-project/issues/60268>`_)
164164
- Fix crash when taking the address of a consteval lambda call operator.
165165
(`#57682 <https://github.com/llvm/llvm-project/issues/57682>`_)
166+
- Fix incorrect merging of lambdas across modules.
167+
(`#60985 <https://github.com/llvm/llvm-project/issues/60985>`_)
166168

167169
Bug Fixes to Compiler Builtins
168170
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/Decl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ struct EvaluatedStmt {
902902
bool HasICEInit : 1;
903903
bool CheckedForICEInit : 1;
904904

905-
Stmt *Value;
905+
LazyDeclStmtPtr Value;
906906
APValue Evaluated;
907907

908908
EvaluatedStmt()

clang/include/clang/AST/DeclCXX.h

Lines changed: 15 additions & 11 deletions
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:
@@ -1763,18 +1767,18 @@ class CXXRecordDecl : public RecordDecl {
17631767
/// the declaration context suffices.
17641768
Decl *getLambdaContextDecl() const;
17651769

1766-
/// Set the mangling number and context declaration for a lambda
1767-
/// class.
1768-
void setLambdaMangling(unsigned ManglingNumber, Decl *ContextDecl,
1769-
bool HasKnownInternalLinkage = false) {
1770+
/// Retrieve the index of this lambda within the context declaration returned
1771+
/// by getLambdaContextDecl().
1772+
unsigned getLambdaIndexInContext() const {
17701773
assert(isLambda() && "Not a lambda closure type!");
1771-
getLambdaData().ManglingNumber = ManglingNumber;
1772-
getLambdaData().ContextDecl = ContextDecl;
1773-
getLambdaData().HasKnownInternalLinkage = HasKnownInternalLinkage;
1774+
return getLambdaData().IndexInContext;
17741775
}
17751776

1776-
/// Set the device side mangling number.
1777-
void setDeviceLambdaManglingNumber(unsigned Num) const;
1777+
/// Set the mangling numbers and context declaration for a lambda class.
1778+
void setLambdaNumbering(Decl *ContextDecl, unsigned IndexInContext,
1779+
unsigned ManglingNumber,
1780+
unsigned DeviceManglingNumber,
1781+
bool HasKnownInternalLinkage);
17781782

17791783
/// Retrieve the device side mangling number.
17801784
unsigned getDeviceLambdaManglingNumber() const;

clang/include/clang/AST/ExternalASTSource.h

Lines changed: 9 additions & 1 deletion
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+
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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 25 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7082,11 +7082,19 @@ class Sema final {
70827082
ConstexprSpecKind ConstexprKind, StorageClass SC,
70837083
Expr *TrailingRequiresClause);
70847084

7085+
/// Information about how a lambda is numbered within its context.
7086+
struct LambdaNumbering {
7087+
Decl *ContextDecl;
7088+
unsigned IndexInContext;
7089+
unsigned ManglingNumber;
7090+
unsigned DeviceManglingNumber;
7091+
bool HasKnownInternalLinkage;
7092+
};
7093+
70857094
/// Number lambda for linkage purposes if necessary.
70867095
void handleLambdaNumbering(
70877096
CXXRecordDecl *Class, CXXMethodDecl *Method,
7088-
std::optional<std::tuple<bool, unsigned, unsigned, Decl *>> Mangling =
7089-
std::nullopt);
7097+
std::optional<LambdaNumbering> Numbering = std::nullopt);
70907098

70917099
/// Endow the lambda scope info with the relevant properties.
70927100
void buildLambdaScope(sema::LambdaScopeInfo *LSI,

clang/include/clang/Serialization/ASTReader.h

Lines changed: 18 additions & 1 deletion
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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6719,6 +6719,11 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
67196719
if (const auto *VarX = dyn_cast<VarDecl>(X)) {
67206720
const auto *VarY = cast<VarDecl>(Y);
67216721
if (VarX->getLinkageInternal() == VarY->getLinkageInternal()) {
6722+
// During deserialization, we might compare variables before we load
6723+
// their types. Assume the types will end up being the same.
6724+
if (VarX->getType().isNull() || VarY->getType().isNull())
6725+
return true;
6726+
67226727
if (hasSameType(VarX->getType(), VarY->getType()))
67236728
return true;
67246729

clang/lib/AST/ASTImporter.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2913,10 +2913,10 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
29132913
ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
29142914
if (!CDeclOrErr)
29152915
return CDeclOrErr.takeError();
2916-
D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr,
2917-
DCXX->hasKnownLambdaInternalLinkage());
2918-
D2CXX->setDeviceLambdaManglingNumber(
2919-
DCXX->getDeviceLambdaManglingNumber());
2916+
D2CXX->setLambdaNumbering(*CDeclOrErr, DCXX->getLambdaIndexInContext(),
2917+
DCXX->getLambdaManglingNumber(),
2918+
DCXX->getDeviceLambdaManglingNumber(),
2919+
DCXX->hasKnownLambdaInternalLinkage());
29202920
} else if (DCXX->isInjectedClassName()) {
29212921
// We have to be careful to do a similar dance to the one in
29222922
// Sema::ActOnStartCXXMemberDeclarations

clang/lib/AST/Decl.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,12 +2339,15 @@ Expr *VarDecl::getInit() {
23392339
if (auto *S = Init.dyn_cast<Stmt *>())
23402340
return cast<Expr>(S);
23412341

2342-
return cast_or_null<Expr>(Init.get<EvaluatedStmt *>()->Value);
2342+
auto *Eval = getEvaluatedStmt();
2343+
return cast<Expr>(Eval->Value.isOffset()
2344+
? Eval->Value.get(getASTContext().getExternalSource())
2345+
: Eval->Value.get(nullptr));
23432346
}
23442347

23452348
Stmt **VarDecl::getInitAddress() {
23462349
if (auto *ES = Init.dyn_cast<EvaluatedStmt *>())
2347-
return &ES->Value;
2350+
return ES->Value.getAddressOfPointer(getASTContext().getExternalSource());
23482351

23492352
return Init.getAddrOfPtr1();
23502353
}
@@ -2481,7 +2484,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
24812484
bool IsConstantInitialization) const {
24822485
EvaluatedStmt *Eval = ensureEvaluatedStmt();
24832486

2484-
const auto *Init = cast<Expr>(Eval->Value);
2487+
const auto *Init = getInit();
24852488
assert(!Init->isValueDependent());
24862489

24872490
// We only produce notes indicating why an initializer is non-constant the
@@ -2565,7 +2568,7 @@ bool VarDecl::checkForConstantInitialization(
25652568
"already evaluated var value before checking for constant init");
25662569
assert(getASTContext().getLangOpts().CPlusPlus && "only meaningful in C++");
25672570

2568-
assert(!cast<Expr>(Eval->Value)->isValueDependent());
2571+
assert(!getInit()->isValueDependent());
25692572

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

clang/lib/AST/DeclCXX.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,10 +1646,18 @@ Decl *CXXRecordDecl::getLambdaContextDecl() const {
16461646
return getLambdaData().ContextDecl.get(Source);
16471647
}
16481648

1649-
void CXXRecordDecl::setDeviceLambdaManglingNumber(unsigned Num) const {
1649+
void CXXRecordDecl::setLambdaNumbering(Decl *ContextDecl,
1650+
unsigned IndexInContext,
1651+
unsigned ManglingNumber,
1652+
unsigned DeviceManglingNumber,
1653+
bool HasKnownInternalLinkage) {
16501654
assert(isLambda() && "Not a lambda closure type!");
1651-
if (Num)
1652-
getASTContext().DeviceLambdaManglingNumbers[this] = Num;
1655+
getLambdaData().ManglingNumber = ManglingNumber;
1656+
if (DeviceManglingNumber)
1657+
getASTContext().DeviceLambdaManglingNumbers[this] = DeviceManglingNumber;
1658+
getLambdaData().IndexInContext = IndexInContext;
1659+
getLambdaData().ContextDecl = ContextDecl;
1660+
getLambdaData().HasKnownInternalLinkage = HasKnownInternalLinkage;
16531661
}
16541662

16551663
unsigned CXXRecordDecl::getDeviceLambdaManglingNumber() const {

clang/lib/AST/ODRDiagsEmitter.cpp

Lines changed: 1 addition & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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+
}

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8239,12 +8239,12 @@ static inline bool VariableCanNeverBeAConstantExpression(VarDecl *Var,
82398239
const VarDecl *DefVD = nullptr;
82408240

82418241
// If there is no initializer - this can not be a constant expression.
8242-
if (!Var->getAnyInitializer(DefVD)) return true;
8242+
const Expr *Init = Var->getAnyInitializer(DefVD);
8243+
if (!Init)
8244+
return true;
82438245
assert(DefVD);
8244-
if (DefVD->isWeak()) return false;
8245-
EvaluatedStmt *Eval = DefVD->ensureEvaluatedStmt();
8246-
8247-
Expr *Init = cast<Expr>(Eval->Value);
8246+
if (DefVD->isWeak())
8247+
return false;
82488248

82498249
if (Var->getType()->isDependentType() || Init->isValueDependent()) {
82508250
// FIXME: Teach the constant evaluator to deal with the non-dependent parts

0 commit comments

Comments
 (0)