Skip to content

Commit c86f8e7

Browse files
authored
[Serialization] Improve extensions of nested types with the same name (swiftlang#7397)
Previously looking up an extension would result in all extensions for types with the same name (nested or not) being deserialized; this could even bring in base types that had not been deserialized yet. Add in a string to distinguish an extension's base type; in the top-level case this is just a module name, but for nested types it's a full mangled name. This is a little heavier than I'd like it to be, since it means we mangle names and then throw them away, and since it means there's a whole bunch of extra string data in the module just for uniquely identifying a declaration. But it's correct, and does less work than before, and fixes a circularity issue with a nested type A.B.A that apparently used to work. https://bugs.swift.org/browse/SR-3915
1 parent 542638d commit c86f8e7

File tree

11 files changed

+301
-12
lines changed

11 files changed

+301
-12
lines changed

include/swift/Serialization/ModuleFile.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ class ModuleFile : public LazyMemberLoader {
312312
using SerializedDeclTable =
313313
llvm::OnDiskIterableChainedHashTable<DeclTableInfo>;
314314

315+
class ExtensionTableInfo;
316+
using SerializedExtensionTable =
317+
llvm::OnDiskIterableChainedHashTable<ExtensionTableInfo>;
318+
315319
class LocalDeclTableInfo;
316320
using SerializedLocalDeclTable =
317321
llvm::OnDiskIterableChainedHashTable<LocalDeclTableInfo>;
@@ -323,9 +327,9 @@ class ModuleFile : public LazyMemberLoader {
323327
std::unique_ptr<SerializedDeclTable> TopLevelDecls;
324328
std::unique_ptr<SerializedDeclTable> OperatorDecls;
325329
std::unique_ptr<SerializedDeclTable> PrecedenceGroupDecls;
326-
std::unique_ptr<SerializedDeclTable> ExtensionDecls;
327330
std::unique_ptr<SerializedDeclTable> ClassMembersByName;
328331
std::unique_ptr<SerializedDeclTable> OperatorMethodDecls;
332+
std::unique_ptr<SerializedExtensionTable> ExtensionDecls;
329333
std::unique_ptr<SerializedLocalDeclTable> LocalTypeDecls;
330334
std::unique_ptr<SerializedNestedTypeDeclsTable> NestedTypeDecls;
331335

@@ -446,6 +450,11 @@ class ModuleFile : public LazyMemberLoader {
446450
std::unique_ptr<ModuleFile::SerializedObjCMethodTable>
447451
readObjCMethodTable(ArrayRef<uint64_t> fields, StringRef blobData);
448452

453+
/// Read an on-disk local decl hash table stored in
454+
/// index_block::ExtensionTableLayout format.
455+
std::unique_ptr<SerializedExtensionTable>
456+
readExtensionTable(ArrayRef<uint64_t> fields, StringRef blobData);
457+
449458
/// Read an on-disk local decl hash table stored in
450459
/// index_block::NestedTypeDeclsLayout format.
451460
std::unique_ptr<SerializedNestedTypeDeclsTable>

include/swift/Serialization/ModuleFormat.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
5454
/// in source control, you should also update the comment to briefly
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
57-
const uint16_t VERSION_MINOR = 314; // Last change: change synthetic substitution serialization
57+
const uint16_t VERSION_MINOR = 315; // Last change: uniquely identify extensions
5858

5959
using DeclID = PointerEmbeddedInt<unsigned, 31>;
6060
using DeclIDField = BCFixed<31>;
@@ -1476,6 +1476,12 @@ namespace index_block {
14761476
BCBlob // actual names
14771477
>;
14781478

1479+
using ExtensionTableLayout = BCRecordLayout<
1480+
EXTENSIONS, // record ID
1481+
BCVBR<16>, // table offset within the blob (see below)
1482+
BCBlob // map from identifier strings to decl kinds / decl IDs
1483+
>;
1484+
14791485
using ObjCMethodTableLayout = BCRecordLayout<
14801486
OBJC_METHODS, // record ID
14811487
BCVBR<16>, // table offset within the blob (see below)

lib/Serialization/Deserialization.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
#include "swift/Parse/Parser.h"
2424
#include "swift/Serialization/BCReadingExtras.h"
2525
#include "swift/Serialization/SerializedModuleLoader.h"
26+
#include "swift/Basic/Defer.h"
2627
#include "llvm/ADT/Statistic.h"
2728
#include "llvm/Support/Compiler.h"
2829
#include "llvm/Support/raw_ostream.h"
2930

3031
#define DEBUG_TYPE "Serialization"
3132

33+
STATISTIC(NumDeclsLoaded, "# of decls deserialized");
3234
STATISTIC(NumMemberListsLoaded,
3335
"# of nominals/extensions whose members were loaded");
3436
STATISTIC(NumNestedTypeShortcuts,
@@ -2163,6 +2165,7 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
21632165
if (declOrOffset.isComplete())
21642166
return declOrOffset;
21652167

2168+
++NumDeclsLoaded;
21662169
BCOffsetRAII restoreOffset(DeclTypeCursor);
21672170
DeclTypeCursor.JumpToBit(declOrOffset);
21682171
auto entry = DeclTypeCursor.advance();

lib/Serialization/ModuleFile.cpp

+92-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/Serialization/ModuleFormat.h"
1515
#include "swift/Subsystems.h"
1616
#include "swift/AST/AST.h"
17+
#include "swift/AST/ASTMangler.h"
1718
#include "swift/AST/ModuleLoader.h"
1819
#include "swift/AST/NameLookup.h"
1920
#include "swift/AST/USRGeneration.h"
@@ -339,6 +340,66 @@ class ModuleFile::DeclTableInfo {
339340
}
340341
};
341342

343+
/// Used to deserialize entries in the on-disk decl hash table.
344+
class ModuleFile::ExtensionTableInfo {
345+
ModuleFile &File;
346+
public:
347+
using internal_key_type = StringRef;
348+
using external_key_type = Identifier;
349+
using data_type = SmallVector<std::pair<StringRef, DeclID>, 8>;
350+
using hash_value_type = uint32_t;
351+
using offset_type = unsigned;
352+
353+
internal_key_type GetInternalKey(external_key_type ID) {
354+
return ID.str();
355+
}
356+
357+
hash_value_type ComputeHash(internal_key_type key) {
358+
return llvm::HashString(key);
359+
}
360+
361+
static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
362+
return lhs == rhs;
363+
}
364+
365+
static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&data) {
366+
unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
367+
unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
368+
return { keyLength, dataLength };
369+
}
370+
371+
static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
372+
return StringRef(reinterpret_cast<const char *>(data), length);
373+
}
374+
375+
data_type ReadData(internal_key_type key, const uint8_t *data,
376+
unsigned length) {
377+
data_type result;
378+
const uint8_t *limit = data + length;
379+
while (data < limit) {
380+
DeclID offset = endian::readNext<uint32_t, little, unaligned>(data);
381+
382+
int32_t nameIDOrLength =
383+
endian::readNext<int32_t, little, unaligned>(data);
384+
StringRef moduleNameOrMangledBase;
385+
if (nameIDOrLength < 0) {
386+
const ModuleDecl *module = File.getModule(-nameIDOrLength);
387+
moduleNameOrMangledBase = module->getName().str();
388+
} else {
389+
moduleNameOrMangledBase =
390+
StringRef(reinterpret_cast<const char *>(data), nameIDOrLength);
391+
data += nameIDOrLength;
392+
}
393+
394+
result.push_back({ moduleNameOrMangledBase, offset });
395+
}
396+
397+
return result;
398+
}
399+
400+
explicit ExtensionTableInfo(ModuleFile &file) : File(file) {}
401+
};
402+
342403
/// Used to deserialize entries in the on-disk decl hash table.
343404
class ModuleFile::LocalDeclTableInfo {
344405
public:
@@ -432,6 +493,17 @@ ModuleFile::readDeclTable(ArrayRef<uint64_t> fields, StringRef blobData) {
432493
base + sizeof(uint32_t), base));
433494
}
434495

496+
std::unique_ptr<ModuleFile::SerializedExtensionTable>
497+
ModuleFile::readExtensionTable(ArrayRef<uint64_t> fields, StringRef blobData) {
498+
uint32_t tableOffset;
499+
index_block::DeclListLayout::readRecord(fields, tableOffset);
500+
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
501+
502+
using OwnedTable = std::unique_ptr<SerializedExtensionTable>;
503+
return OwnedTable(SerializedExtensionTable::Create(base + tableOffset,
504+
base + sizeof(uint32_t), base, ExtensionTableInfo(*this)));
505+
}
506+
435507
std::unique_ptr<ModuleFile::SerializedLocalDeclTable>
436508
ModuleFile::readLocalDeclTable(ArrayRef<uint64_t> fields, StringRef blobData) {
437509
uint32_t tableOffset;
@@ -569,7 +641,7 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) {
569641
PrecedenceGroupDecls = readDeclTable(scratch, blobData);
570642
break;
571643
case index_block::EXTENSIONS:
572-
ExtensionDecls = readDeclTable(scratch, blobData);
644+
ExtensionDecls = readExtensionTable(scratch, blobData);
573645
break;
574646
case index_block::CLASS_MEMBERS:
575647
ClassMembersByName = readDeclTable(scratch, blobData);
@@ -1437,9 +1509,25 @@ void ModuleFile::loadExtensions(NominalTypeDecl *nominal) {
14371509
if (iter == ExtensionDecls->end())
14381510
return;
14391511

1440-
for (auto item : *iter) {
1441-
if (item.first == getKindForTable(nominal))
1442-
(void)getDecl(item.second);
1512+
if (nominal->hasAccessibility() &&
1513+
nominal->getEffectiveAccess() < Accessibility::Internal) {
1514+
if (nominal->getModuleScopeContext() != getFile())
1515+
return;
1516+
}
1517+
1518+
if (nominal->getParent()->isModuleScopeContext()) {
1519+
Identifier moduleName = nominal->getParentModule()->getName();
1520+
for (auto item : *iter) {
1521+
if (item.first == moduleName.str())
1522+
(void)getDecl(item.second);
1523+
}
1524+
} else {
1525+
std::string mangledName =
1526+
NewMangling::ASTMangler().mangleNominalType(nominal);
1527+
for (auto item : *iter) {
1528+
if (item.first == mangledName)
1529+
(void)getDecl(item.second);
1530+
}
14431531
}
14441532
}
14451533

lib/Serialization/Serialization.cpp

+103-4
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
#include "Serialization.h"
1414
#include "SILFormat.h"
1515
#include "swift/AST/AST.h"
16+
#include "swift/AST/ASTMangler.h"
1617
#include "swift/AST/ASTWalker.h"
1718
#include "swift/AST/DiagnosticsCommon.h"
1819
#include "swift/AST/ForeignErrorConvention.h"
1920
#include "swift/AST/GenericEnvironment.h"
2021
#include "swift/AST/Initializer.h"
2122
#include "swift/AST/LinkLibrary.h"
22-
#include "swift/AST/Mangle.h"
2323
#include "swift/AST/ProtocolConformance.h"
2424
#include "swift/AST/ASTMangler.h"
2525
#include "swift/AST/RawComment.h"
@@ -112,6 +112,74 @@ namespace {
112112
}
113113
};
114114

115+
class ExtensionTableInfo {
116+
serialization::Serializer &Serializer;
117+
llvm::SmallDenseMap<const NominalTypeDecl *,std::string,4> MangledNameCache;
118+
119+
public:
120+
explicit ExtensionTableInfo(serialization::Serializer &serializer)
121+
: Serializer(serializer) {}
122+
123+
using key_type = Identifier;
124+
using key_type_ref = key_type;
125+
using data_type = Serializer::ExtensionTableData;
126+
using data_type_ref = const data_type &;
127+
using hash_value_type = uint32_t;
128+
using offset_type = unsigned;
129+
130+
hash_value_type ComputeHash(key_type_ref key) {
131+
assert(!key.empty());
132+
return llvm::HashString(key.str());
133+
}
134+
135+
int32_t getNameDataForBase(const NominalTypeDecl *nominal,
136+
StringRef *dataToWrite = nullptr) {
137+
if (nominal->getDeclContext()->isModuleScopeContext())
138+
return -Serializer.addModuleRef(nominal->getParentModule());
139+
140+
auto &mangledName = MangledNameCache[nominal];
141+
if (mangledName.empty())
142+
mangledName = NewMangling::ASTMangler().mangleNominalType(nominal);
143+
144+
assert(llvm::isUInt<31>(mangledName.size()));
145+
if (dataToWrite)
146+
*dataToWrite = mangledName;
147+
return mangledName.size();
148+
}
149+
150+
std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
151+
key_type_ref key,
152+
data_type_ref data) {
153+
uint32_t keyLength = key.str().size();
154+
uint32_t dataLength = (sizeof(uint32_t) * 2) * data.size();
155+
for (auto dataPair : data) {
156+
int32_t nameData = getNameDataForBase(dataPair.first);
157+
if (nameData > 0)
158+
dataLength += nameData;
159+
}
160+
endian::Writer<little> writer(out);
161+
writer.write<uint16_t>(keyLength);
162+
writer.write<uint16_t>(dataLength);
163+
return { keyLength, dataLength };
164+
}
165+
166+
void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
167+
out << key.str();
168+
}
169+
170+
void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
171+
unsigned len) {
172+
static_assert(declIDFitsIn32Bits(), "DeclID too large");
173+
endian::Writer<little> writer(out);
174+
for (auto entry : data) {
175+
StringRef dataToWrite;
176+
writer.write<uint32_t>(entry.second);
177+
writer.write<int32_t>(getNameDataForBase(entry.first, &dataToWrite));
178+
out << dataToWrite;
179+
}
180+
}
181+
};
182+
115183
class LocalDeclTableInfo {
116184
public:
117185
using key_type = std::string;
@@ -3592,6 +3660,32 @@ static void writeDeclTable(const index_block::DeclListLayout &DeclList,
35923660
DeclList.emit(scratch, kind, tableOffset, hashTableBlob);
35933661
}
35943662

3663+
static void
3664+
writeExtensionTable(const index_block::ExtensionTableLayout &ExtensionTable,
3665+
const Serializer::ExtensionTable &table,
3666+
Serializer &serializer) {
3667+
if (table.empty())
3668+
return;
3669+
3670+
SmallVector<uint64_t, 8> scratch;
3671+
llvm::SmallString<4096> hashTableBlob;
3672+
uint32_t tableOffset;
3673+
{
3674+
llvm::OnDiskChainedHashTableGenerator<ExtensionTableInfo> generator;
3675+
ExtensionTableInfo info{serializer};
3676+
for (auto &entry : table) {
3677+
generator.insert(entry.first, entry.second, info);
3678+
}
3679+
3680+
llvm::raw_svector_ostream blobStream(hashTableBlob);
3681+
// Make sure that no bucket is at offset 0
3682+
endian::Writer<little>(blobStream).write<uint32_t>(0);
3683+
tableOffset = generator.Emit(blobStream, info);
3684+
}
3685+
3686+
ExtensionTable.emit(scratch, tableOffset, hashTableBlob);
3687+
}
3688+
35953689
static void writeLocalDeclTable(const index_block::DeclListLayout &DeclList,
35963690
index_block::RecordKind kind,
35973691
LocalTypeHashTableGenerator &generator) {
@@ -4158,11 +4252,12 @@ static void collectInterestingNestedDeclarations(
41584252

41594253
void Serializer::writeAST(ModuleOrSourceFile DC,
41604254
bool enableNestedTypeLookupTable) {
4161-
DeclTable topLevelDecls, extensionDecls, operatorDecls, operatorMethodDecls;
4255+
DeclTable topLevelDecls, operatorDecls, operatorMethodDecls;
41624256
DeclTable precedenceGroupDecls;
41634257
ObjCMethodTable objcMethods;
41644258
NestedTypeDeclsTable nestedTypeDecls;
41654259
LocalTypeHashTableGenerator localTypeGenerator;
4260+
ExtensionTable extensionDecls;
41664261
bool hasLocalTypes = false;
41674262

41684263
Optional<DeclID> entryPointClassID;
@@ -4196,7 +4291,7 @@ void Serializer::writeAST(ModuleOrSourceFile DC,
41964291
Type extendedTy = ED->getExtendedType();
41974292
const NominalTypeDecl *extendedNominal = extendedTy->getAnyNominal();
41984293
extensionDecls[extendedNominal->getName()]
4199-
.push_back({ getKindForTable(extendedNominal), addDeclRef(D) });
4294+
.push_back({ extendedNominal, addDeclRef(D) });
42004295
} else if (auto OD = dyn_cast<OperatorDecl>(D)) {
42014296
operatorDecls[OD->getName()]
42024297
.push_back({ getStableFixity(OD->getKind()), addDeclRef(D) });
@@ -4264,13 +4359,17 @@ void Serializer::writeAST(ModuleOrSourceFile DC,
42644359
writeDeclTable(DeclList, index_block::TOP_LEVEL_DECLS, topLevelDecls);
42654360
writeDeclTable(DeclList, index_block::OPERATORS, operatorDecls);
42664361
writeDeclTable(DeclList, index_block::PRECEDENCE_GROUPS, precedenceGroupDecls);
4267-
writeDeclTable(DeclList, index_block::EXTENSIONS, extensionDecls);
42684362
writeDeclTable(DeclList, index_block::CLASS_MEMBERS, ClassMembersByName);
42694363
writeDeclTable(DeclList, index_block::OPERATOR_METHODS, operatorMethodDecls);
42704364
if (hasLocalTypes)
42714365
writeLocalDeclTable(DeclList, index_block::LOCAL_TYPE_DECLS,
42724366
localTypeGenerator);
42734367

4368+
if (!extensionDecls.empty()) {
4369+
index_block::ExtensionTableLayout ExtensionTable(Out);
4370+
writeExtensionTable(ExtensionTable, extensionDecls, *this);
4371+
}
4372+
42744373
index_block::ObjCMethodTableLayout ObjCMethodTable(Out);
42754374
writeObjCMethodTable(ObjCMethodTable, objcMethods);
42764375

lib/Serialization/Serialization.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,16 @@ class Serializer {
128128

129129
// In-memory representation of what will eventually be an on-disk
130130
// hash table of all defined Objective-C methods.
131-
using ObjCMethodTable = llvm::DenseMap<ObjCSelector, ObjCMethodTableData>;
131+
using ObjCMethodTable = llvm::MapVector<ObjCSelector, ObjCMethodTableData>;
132132

133133
using NestedTypeDeclsData = SmallVector<std::pair<DeclID, DeclID>, 4>;
134134
// In-memory representation of what will eventually be an on-disk
135135
// hash table of all defined Objective-C methods.
136-
using NestedTypeDeclsTable = llvm::DenseMap<Identifier, NestedTypeDeclsData>;
136+
using NestedTypeDeclsTable = llvm::MapVector<Identifier, NestedTypeDeclsData>;
137+
138+
using ExtensionTableData =
139+
SmallVector<std::pair<const NominalTypeDecl *, DeclID>, 4>;
140+
using ExtensionTable = llvm::MapVector<Identifier, ExtensionTableData>;
137141

138142
private:
139143
/// A map from identifiers to methods and properties with the given name.

0 commit comments

Comments
 (0)