Skip to content

Commit c3ba6f3

Browse files
authored
[Modules] Delay deserialization of preferred_name attribute at r… (llvm#122726)
…ecord level. This fixes the incorrect diagnostic emitted when compiling the following snippet ``` // string_view.h template<class _CharT> class basic_string_view; typedef basic_string_view<char> string_view; template<class _CharT> class __attribute__((__preferred_name__(string_view))) basic_string_view { public: basic_string_view() { } }; inline basic_string_view<char> foo() { return basic_string_view<char>(); } // A.cppm module; #include "string_view.h" export module A; // Use.cppm module; #include "string_view.h" export module Use; import A; ``` The diagnostic is ``` string_view.h:11:5: error: 'basic_string_view<char>::basic_string_view' from module 'A.<global>' is not present in definition of 'string_view' provided earlier ``` The underlying issue is that deserialization of the `preferred_name` attribute triggers deserialization of `basic_string_view<char>`, which triggers the deserialization of the `preferred_name` attribute again (since it's attached to the `basic_string_view` template). The deserialization logic is implemented in a way that prevents it from going on a loop in a literal sense (it detects early on that it has already seen the `string_view` typedef when trying to start its deserialization for the second time), but leaves the typedef deserialization in an unfinished state. Subsequently, the `string_view` typedef from the deserialized module cannot be merged with the same typedef from `string_view.h`, resulting in the above diagnostic. This PR resolves the problem by delaying the deserialization of the `preferred_name` attribute until the deserialization of the `basic_string_view` template is completed. As a result of deferring, the deserialization of the `preferred_name` attribute doesn't need to go on a loop since the type of the `string_view` typedef is already known when it's deserialized.
1 parent 1274bca commit c3ba6f3

File tree

9 files changed

+156
-17
lines changed

9 files changed

+156
-17
lines changed

clang/include/clang/AST/Attr.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class Attr : public AttributeCommonInfo {
6060
unsigned IsLateParsed : 1;
6161
LLVM_PREFERRED_TYPE(bool)
6262
unsigned InheritEvenIfAlreadyPresent : 1;
63+
LLVM_PREFERRED_TYPE(bool)
64+
unsigned DeferDeserialization : 1;
6365

6466
void *operator new(size_t bytes) noexcept {
6567
llvm_unreachable("Attrs cannot be allocated with regular 'new'.");
@@ -80,10 +82,11 @@ class Attr : public AttributeCommonInfo {
8082

8183
protected:
8284
Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
83-
attr::Kind AK, bool IsLateParsed)
85+
attr::Kind AK, bool IsLateParsed, bool DeferDeserialization = false)
8486
: AttributeCommonInfo(CommonInfo), AttrKind(AK), Inherited(false),
8587
IsPackExpansion(false), Implicit(false), IsLateParsed(IsLateParsed),
86-
InheritEvenIfAlreadyPresent(false) {}
88+
InheritEvenIfAlreadyPresent(false),
89+
DeferDeserialization(DeferDeserialization) {}
8790

8891
public:
8992
attr::Kind getKind() const { return static_cast<attr::Kind>(AttrKind); }
@@ -105,6 +108,8 @@ class Attr : public AttributeCommonInfo {
105108
void setPackExpansion(bool PE) { IsPackExpansion = PE; }
106109
bool isPackExpansion() const { return IsPackExpansion; }
107110

111+
bool shouldDeferDeserialization() const { return DeferDeserialization; }
112+
108113
// Clone this attribute.
109114
Attr *clone(ASTContext &C) const;
110115

@@ -146,8 +151,9 @@ class InheritableAttr : public Attr {
146151
protected:
147152
InheritableAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
148153
attr::Kind AK, bool IsLateParsed,
149-
bool InheritEvenIfAlreadyPresent)
150-
: Attr(Context, CommonInfo, AK, IsLateParsed) {
154+
bool InheritEvenIfAlreadyPresent,
155+
bool DeferDeserialization = false)
156+
: Attr(Context, CommonInfo, AK, IsLateParsed, DeferDeserialization) {
151157
this->InheritEvenIfAlreadyPresent = InheritEvenIfAlreadyPresent;
152158
}
153159

clang/include/clang/Basic/Attr.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,12 @@ class Attr {
713713
// attribute may be documented under multiple categories, more than one
714714
// Documentation entry may be listed.
715715
list<Documentation> Documentation;
716+
// Set to true if deserialization of this attribute must be deferred until
717+
// the parent Decl is fully deserialized (during header module file
718+
// deserialization). E.g., this is the case for the preferred_name attribute,
719+
// since its type deserialization depends on its target Decl type.
720+
// (See https://github.com/llvm/llvm-project/issues/56490 for details).
721+
bit DeferDeserialization = 0;
716722
}
717723

718724
/// Used to define a set of mutually exclusive attributes.
@@ -3254,6 +3260,11 @@ def PreferredName : InheritableAttr {
32543260
let InheritEvenIfAlreadyPresent = 1;
32553261
let MeaningfulToClassTemplateDefinition = 1;
32563262
let TemplateDependent = 1;
3263+
// Type of this attribute depends on the target Decl type.
3264+
// Therefore, its deserialization must be deferred until
3265+
// deserialization of the target Decl is complete
3266+
// (for header modules).
3267+
let DeferDeserialization = 1;
32573268
}
32583269

32593270
def PreserveMost : DeclOrTypeAttr {

clang/include/clang/Serialization/ASTReader.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,24 @@ class ASTReader
12211221
/// been completed.
12221222
std::deque<PendingDeclContextInfo> PendingDeclContextInfos;
12231223

1224+
/// Deserialization of some attributes must be deferred since they refer
1225+
/// to themselves in their type (e.g., preferred_name attribute refers to the
1226+
/// typedef that refers back to the template specialization of the template
1227+
/// that the attribute is attached to).
1228+
/// More attributes that store TypeSourceInfo might be potentially affected,
1229+
/// see https://github.com/llvm/llvm-project/issues/56490 for details.
1230+
struct DeferredAttribute {
1231+
// Index of the deferred attribute in the Record of the TargetedDecl.
1232+
uint64_t RecordIdx;
1233+
// Decl to attach a deferred attribute to.
1234+
Decl *TargetedDecl;
1235+
};
1236+
1237+
/// The collection of Decls that have been loaded but some of their attributes
1238+
/// have been deferred, paired with the index inside the record pointing
1239+
/// at the skipped attribute.
1240+
SmallVector<DeferredAttribute> PendingDeferredAttributes;
1241+
12241242
template <typename DeclTy>
12251243
using DuplicateObjCDecls = std::pair<DeclTy *, DeclTy *>;
12261244

@@ -1570,6 +1588,7 @@ class ASTReader
15701588
void loadPendingDeclChain(Decl *D, uint64_t LocalOffset);
15711589
void loadObjCCategories(GlobalDeclID ID, ObjCInterfaceDecl *D,
15721590
unsigned PreviousGeneration = 0);
1591+
void loadDeferredAttribute(const DeferredAttribute &DA);
15731592

15741593
RecordLocation getLocalBitOffset(uint64_t GlobalOffset);
15751594
uint64_t getGlobalBitOffset(ModuleFile &M, uint64_t LocalOffset);

clang/include/clang/Serialization/ASTRecordReader.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ class ASTRecordReader
8383
/// Returns the current value in this record, without advancing.
8484
uint64_t peekInt() { return Record[Idx]; }
8585

86+
/// Returns the next N values in this record, without advancing.
87+
uint64_t peekInts(unsigned N) { return Record[Idx + N]; }
88+
89+
/// Skips the current value.
90+
void skipInt() { Idx += 1; }
91+
8692
/// Skips the specified number of values.
8793
void skipInts(unsigned N) { Idx += N; }
8894

@@ -335,7 +341,12 @@ class ASTRecordReader
335341
Attr *readAttr();
336342

337343
/// Reads attributes from the current stream position, advancing Idx.
338-
void readAttributes(AttrVec &Attrs);
344+
/// For some attributes (where type depends on itself recursively), defer
345+
/// reading the attribute until the type has been read.
346+
void readAttributes(AttrVec &Attrs, Decl *D = nullptr);
347+
348+
/// Reads one attribute from the current stream position, advancing Idx.
349+
Attr *readOrDeferAttrFor(Decl *D);
339350

340351
/// Read an BTFTypeTagAttr object.
341352
BTFTypeTagAttr *readBTFTypeTagAttr() {

clang/lib/Serialization/ASTReader.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10180,6 +10180,11 @@ void ASTReader::finishPendingActions() {
1018010180
}
1018110181
PendingDeducedVarTypes.clear();
1018210182

10183+
// Load the delayed preferred name attributes.
10184+
for (unsigned I = 0; I != PendingDeferredAttributes.size(); ++I)
10185+
loadDeferredAttribute(PendingDeferredAttributes[I]);
10186+
PendingDeferredAttributes.clear();
10187+
1018310188
// For each decl chain that we wanted to complete while deserializing, mark
1018410189
// it as "still needs to be completed".
1018510190
for (unsigned I = 0; I != PendingIncompleteDeclChains.size(); ++I) {

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ void ASTDeclReader::VisitDecl(Decl *D) {
612612

613613
if (HasAttrs) {
614614
AttrVec Attrs;
615-
Record.readAttributes(Attrs);
615+
Record.readAttributes(Attrs, D);
616616
// Avoid calling setAttrs() directly because it uses Decl::getASTContext()
617617
// internally which is unsafe during derialization.
618618
D->setAttrsImpl(Attrs, Reader.getContext());
@@ -3093,6 +3093,8 @@ class AttrReader {
30933093
return Reader.readInt();
30943094
}
30953095

3096+
uint64_t peekInts(unsigned N) { return Reader.peekInts(N); }
3097+
30963098
bool readBool() { return Reader.readBool(); }
30973099

30983100
SourceRange readSourceRange() {
@@ -3123,18 +3125,29 @@ class AttrReader {
31233125
return Reader.readVersionTuple();
31243126
}
31253127

3128+
void skipInt() { Reader.skipInts(1); }
3129+
3130+
void skipInts(unsigned N) { Reader.skipInts(N); }
3131+
3132+
unsigned getCurrentIdx() { return Reader.getIdx(); }
3133+
31263134
OMPTraitInfo *readOMPTraitInfo() { return Reader.readOMPTraitInfo(); }
31273135

31283136
template <typename T> T *readDeclAs() { return Reader.readDeclAs<T>(); }
31293137
};
31303138
}
31313139

3140+
/// Reads one attribute from the current stream position, advancing Idx.
31323141
Attr *ASTRecordReader::readAttr() {
31333142
AttrReader Record(*this);
31343143
auto V = Record.readInt();
31353144
if (!V)
31363145
return nullptr;
31373146

3147+
// Read and ignore the skip count, since attribute deserialization is not
3148+
// deferred on this pass.
3149+
Record.skipInt();
3150+
31383151
Attr *New = nullptr;
31393152
// Kind is stored as a 1-based integer because 0 is used to indicate a null
31403153
// Attr pointer.
@@ -3164,13 +3177,28 @@ Attr *ASTRecordReader::readAttr() {
31643177
return New;
31653178
}
31663179

3167-
/// Reads attributes from the current stream position.
3168-
void ASTRecordReader::readAttributes(AttrVec &Attrs) {
3180+
/// Reads attributes from the current stream position, advancing Idx.
3181+
/// For some attributes (where type depends on itself recursively), defer
3182+
/// reading the attribute until the type has been read.
3183+
void ASTRecordReader::readAttributes(AttrVec &Attrs, Decl *D) {
31693184
for (unsigned I = 0, E = readInt(); I != E; ++I)
3170-
if (auto *A = readAttr())
3185+
if (auto *A = readOrDeferAttrFor(D))
31713186
Attrs.push_back(A);
31723187
}
31733188

3189+
/// Reads one attribute from the current stream position, advancing Idx.
3190+
/// For some attributes (where type depends on itself recursively), defer
3191+
/// reading the attribute until the type has been read.
3192+
Attr *ASTRecordReader::readOrDeferAttrFor(Decl *D) {
3193+
AttrReader Record(*this);
3194+
unsigned SkipCount = Record.peekInts(1);
3195+
if (!SkipCount)
3196+
return readAttr();
3197+
Reader->PendingDeferredAttributes.push_back({Record.getCurrentIdx(), D});
3198+
Record.skipInts(SkipCount);
3199+
return nullptr;
3200+
}
3201+
31743202
//===----------------------------------------------------------------------===//
31753203
// ASTReader Implementation
31763204
//===----------------------------------------------------------------------===//
@@ -4459,6 +4487,49 @@ void ASTReader::loadPendingDeclChain(Decl *FirstLocal, uint64_t LocalOffset) {
44594487
ASTDeclReader::attachLatestDecl(CanonDecl, MostRecent);
44604488
}
44614489

4490+
void ASTReader::loadDeferredAttribute(const DeferredAttribute &DA) {
4491+
Decl *D = DA.TargetedDecl;
4492+
ModuleFile *M = getOwningModuleFile(D);
4493+
4494+
unsigned LocalDeclIndex = D->getGlobalID().getLocalDeclIndex();
4495+
const DeclOffset &DOffs = M->DeclOffsets[LocalDeclIndex];
4496+
RecordLocation Loc(M, DOffs.getBitOffset(M->DeclsBlockStartOffset));
4497+
4498+
llvm::BitstreamCursor &Cursor = Loc.F->DeclsCursor;
4499+
SavedStreamPosition SavedPosition(Cursor);
4500+
if (llvm::Error Err = Cursor.JumpToBit(Loc.Offset)) {
4501+
Error(std::move(Err));
4502+
}
4503+
4504+
Expected<unsigned> MaybeCode = Cursor.ReadCode();
4505+
if (!MaybeCode) {
4506+
llvm::report_fatal_error(
4507+
Twine("ASTReader::loadPreferredNameAttribute failed reading code: ") +
4508+
toString(MaybeCode.takeError()));
4509+
}
4510+
unsigned Code = MaybeCode.get();
4511+
4512+
ASTRecordReader Record(*this, *Loc.F);
4513+
Expected<unsigned> MaybeRecCode = Record.readRecord(Cursor, Code);
4514+
if (!MaybeRecCode) {
4515+
llvm::report_fatal_error(
4516+
Twine(
4517+
"ASTReader::loadPreferredNameAttribute failed reading rec code: ") +
4518+
toString(MaybeCode.takeError()));
4519+
}
4520+
unsigned RecCode = MaybeRecCode.get();
4521+
if (RecCode < DECL_TYPEDEF || RecCode > DECL_LAST) {
4522+
llvm::report_fatal_error(
4523+
Twine("ASTReader::loadPreferredNameAttribute failed reading rec code: "
4524+
"expected valid DeclCode") +
4525+
toString(MaybeCode.takeError()));
4526+
}
4527+
4528+
Record.skipInts(DA.RecordIdx);
4529+
Attr *A = Record.readAttr();
4530+
getContext().getDeclAttrs(D).push_back(A);
4531+
}
4532+
44624533
namespace {
44634534

44644535
/// Given an ObjC interface, goes through the modules and links to the

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "clang/AST/Type.h"
3838
#include "clang/AST/TypeLoc.h"
3939
#include "clang/AST/TypeLocVisitor.h"
40+
#include "clang/Basic/AttrKinds.h"
4041
#include "clang/Basic/Diagnostic.h"
4142
#include "clang/Basic/DiagnosticOptions.h"
4243
#include "clang/Basic/FileEntry.h"
@@ -5067,15 +5068,14 @@ void ASTWriter::WriteModuleFileExtension(Sema &SemaRef,
50675068

50685069
void ASTRecordWriter::AddAttr(const Attr *A) {
50695070
auto &Record = *this;
5070-
// FIXME: Clang can't handle the serialization/deserialization of
5071-
// preferred_name properly now. See
5072-
// https://github.com/llvm/llvm-project/issues/56490 for example.
5073-
if (!A || (isa<PreferredNameAttr>(A) &&
5074-
Writer->isWritingStdCXXNamedModules()))
5071+
if (!A)
50755072
return Record.push_back(0);
50765073

50775074
Record.push_back(A->getKind() + 1); // FIXME: stable encoding, target attrs
50785075

5076+
auto SkipIdx = Record.size();
5077+
// Add placeholder for the size of deferred attribute.
5078+
Record.push_back(0);
50795079
Record.AddIdentifierRef(A->getAttrName());
50805080
Record.AddIdentifierRef(A->getScopeName());
50815081
Record.AddSourceRange(A->getRange());
@@ -5086,6 +5086,12 @@ void ASTRecordWriter::AddAttr(const Attr *A) {
50865086
Record.push_back(A->isRegularKeywordAttribute());
50875087

50885088
#include "clang/Serialization/AttrPCHWrite.inc"
5089+
5090+
if (A->shouldDeferDeserialization()) {
5091+
// Record the actual size of deferred attribute (+ 1 to count the attribute
5092+
// kind).
5093+
Record[SkipIdx] = Record.size() - SkipIdx + 1;
5094+
}
50895095
}
50905096

50915097
/// Emit the list of attributes to the specified record.

clang/test/Modules/preferred_name.cppm

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,16 @@ import A;
5353
export using ::foo_templ;
5454

5555
//--- Use1.cpp
56-
import A; // expected-[email protected]:8 {{attribute declaration must precede definition}}
57-
#include "foo.h" // [email protected]:9 {{previous definition is here}}
58-
56+
// expected-no-diagnostics
57+
import A;
58+
#include "foo.h"
5959
//--- Use2.cpp
6060
// expected-no-diagnostics
6161
#include "foo.h"
6262
import A;
63+
64+
//--- Use3.cpp
65+
#include "foo.h"
66+
import A;
67+
foo test;
68+
int size = test.size(); // expected-error {{no member named 'size' in 'foo'}}

clang/utils/TableGen/ClangAttrEmitter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3043,6 +3043,10 @@ static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS,
30433043
<< (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true"
30443044
: "false");
30453045
}
3046+
if (R.getValueAsBit("DeferDeserialization")) {
3047+
OS << ", "
3048+
<< "/*DeferDeserialization=*/true";
3049+
}
30463050
OS << ")\n";
30473051

30483052
for (auto const &ai : Args) {

0 commit comments

Comments
 (0)