Files
swift-mirror/lib/ClangImporter/SwiftLookupTable.cpp
Doug Gregor 6d20d22269 Clang importer: allow subsequent macro definitions to replace earlier ones.
This better matches the behavior we had before enabling the Swift name
lookup tables. Tests will be forthcoming, but this is expected to fix
a failure on Linux.
2015-12-21 12:05:16 -08:00

714 lines
23 KiB
C++

//===--- SwiftLookupTable.cpp - Swift Lookup Table ------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements support for Swift name lookup tables stored in Clang
// modules.
//
//===----------------------------------------------------------------------===//
#include "SwiftLookupTable.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/Version.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Serialization/ASTBitCodes.h"
#include "clang/Serialization/ASTReader.h"
#include "clang/Serialization/ASTWriter.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitcode/BitstreamReader.h"
#include "llvm/Bitcode/BitstreamWriter.h"
#include "llvm/Bitcode/RecordLayout.h"
#include "llvm/Support/OnDiskHashTable.h"
using namespace swift;
using namespace llvm::support;
/// Determine whether the new declarations matches an existing declaration.
static bool matchesExistingDecl(clang::Decl *decl, clang::Decl *existingDecl) {
// If the canonical declarations are equivalent, we have a match.
if (decl->getCanonicalDecl() == existingDecl->getCanonicalDecl()) {
return true;
}
return false;
}
bool SwiftLookupTable::contextRequiresName(ContextKind kind) {
switch (kind) {
case ContextKind::ObjCClass:
case ContextKind::ObjCProtocol:
case ContextKind::Tag:
return true;
case ContextKind::TranslationUnit:
return false;
}
}
Optional<std::pair<SwiftLookupTable::ContextKind, StringRef>>
SwiftLookupTable::translateContext(clang::DeclContext *context) {
// Translation unit context.
if (context->isTranslationUnit())
return std::make_pair(ContextKind::TranslationUnit, StringRef());
// Tag declaration context.
if (auto tag = dyn_cast<clang::TagDecl>(context)) {
if (tag->getIdentifier())
return std::make_pair(ContextKind::Tag, tag->getName());
if (auto typedefDecl = tag->getTypedefNameForAnonDecl())
return std::make_pair(ContextKind::Tag, typedefDecl->getName());
return None;
}
// Objective-C class context.
if (auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(context))
return std::make_pair(ContextKind::ObjCClass, objcClass->getName());
// Objective-C protocol context.
if (auto objcProtocol = dyn_cast<clang::ObjCProtocolDecl>(context))
return std::make_pair(ContextKind::ObjCProtocol, objcProtocol->getName());
return None;
}
void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry,
clang::DeclContext *effectiveContext) {
assert(!Reader && "Cannot modify a lookup table stored on disk");
// Translate the context.
auto contextOpt = translateContext(effectiveContext);
if (!contextOpt) return;
auto context = *contextOpt;
// Find the list of entries for this base name.
auto &entries = LookupTable[name.getBaseName().str()];
auto decl = newEntry.dyn_cast<clang::NamedDecl *>();
auto macro = newEntry.dyn_cast<clang::MacroInfo *>();
for (auto &entry : entries) {
if (entry.Context == context) {
// We have entries for this context.
// Check whether this entry matches any existing entry.
for (auto &existingEntry : entry.DeclsOrMacros) {
// If it matches an existing declaration, there's nothing to do.
if (decl && isDeclEntry(existingEntry) &&
matchesExistingDecl(decl, mapStoredDecl(existingEntry)))
return;
// If it matches an existing macro, overwrite the existing entry.
if (macro && isMacroEntry(existingEntry)) {
existingEntry = encodeEntry(macro);
return;
}
}
// Add an entry to this context.
if (decl)
entry.DeclsOrMacros.push_back(encodeEntry(decl));
else
entry.DeclsOrMacros.push_back(encodeEntry(macro));
return;
}
}
// This is a new context for this name. Add it.
FullTableEntry entry;
entry.Context = context;
if (decl)
entry.DeclsOrMacros.push_back(encodeEntry(decl));
else
entry.DeclsOrMacros.push_back(encodeEntry(macro));
entries.push_back(entry);
}
auto SwiftLookupTable::findOrCreate(StringRef baseName)
-> llvm::DenseMap<StringRef, SmallVector<FullTableEntry, 2>>::iterator {
// If there is no base name, there is nothing to find.
if (baseName.empty()) return LookupTable.end();
// Find entries for this base name.
auto known = LookupTable.find(baseName);
// If we found somthing, we're done.
if (known != LookupTable.end()) return known;
// If there's no reader, we've found all there is to find.
if (!Reader) return known;
// Add an entry to the table so we don't look again.
known = LookupTable.insert({ baseName, { } }).first;
// Lookup this base name in the module file.
(void)Reader->lookup(baseName, known->second);
return known;
}
SmallVector<SwiftLookupTable::SingleEntry, 4>
SwiftLookupTable::lookup(StringRef baseName,
clang::DeclContext *searchContext) {
SmallVector<SwiftLookupTable::SingleEntry, 4> result;
// Find the lookup table entry for this base name.
auto known = findOrCreate(baseName);
if (known == LookupTable.end()) return result;
// Translate context.
Optional<std::pair<SwiftLookupTable::ContextKind, StringRef>> context;
if (searchContext) {
context = translateContext(searchContext);
if (!context) return result;
}
// Walk each of the entries.
for (auto &entry : known->second) {
// If we're looking in a particular context and it doesn't match the
// entry context, we're done.
if (context && *context != entry.Context) continue;
// Map each of the declarations.
for (auto &stored : entry.DeclsOrMacros)
result.push_back(mapStored(stored));
}
return result;
}
SmallVector<StringRef, 4> SwiftLookupTable::allBaseNames() {
// If we have a reader, enumerate its base names.
if (Reader) return Reader->getBaseNames();
// Otherwise, walk the lookup table.
SmallVector<StringRef, 4> result;
for (const auto &entry : LookupTable) {
result.push_back(entry.first);
}
return result;
}
SmallVector<clang::NamedDecl *, 4>
SwiftLookupTable::lookupObjCMembers(StringRef baseName) {
SmallVector<clang::NamedDecl *, 4> result;
// Find the lookup table entry for this base name.
auto known = findOrCreate(baseName);
if (known == LookupTable.end()) return result;
// Walk each of the entries.
for (auto &entry : known->second) {
// If we're looking in a particular context and it doesn't match the
// entry context, we're done.
switch (entry.Context.first) {
case ContextKind::TranslationUnit:
case ContextKind::Tag:
continue;
case ContextKind::ObjCClass:
case ContextKind::ObjCProtocol:
break;
}
// Map each of the declarations.
for (auto &stored : entry.DeclsOrMacros) {
assert(isDeclEntry(stored) && "Not a declaration?");
result.push_back(mapStoredDecl(stored));
}
}
return result;
}
static void printName(clang::NamedDecl *named, llvm::raw_ostream &out) {
// If there is a name, print it.
if (!named->getDeclName().isEmpty()) {
// If we have an Objective-C method, print the class name along
// with '+'/'-'.
if (auto objcMethod = dyn_cast<clang::ObjCMethodDecl>(named)) {
out << (objcMethod->isInstanceMethod() ? '-' : '+') << '[';
if (auto classDecl = objcMethod->getClassInterface()) {
classDecl->printName(out);
out << ' ';
} else if (auto proto = dyn_cast<clang::ObjCProtocolDecl>(
objcMethod->getDeclContext())) {
proto->printName(out);
out << ' ';
}
named->printName(out);
out << ']';
return;
}
// If we have an Objective-C property, print the class name along
// with the property name.
if (auto objcProperty = dyn_cast<clang::ObjCPropertyDecl>(named)) {
auto dc = objcProperty->getDeclContext();
if (auto classDecl = dyn_cast<clang::ObjCInterfaceDecl>(dc)) {
classDecl->printName(out);
out << '.';
} else if (auto categoryDecl = dyn_cast<clang::ObjCCategoryDecl>(dc)) {
categoryDecl->getClassInterface()->printName(out);
out << '.';
} else if (auto proto = dyn_cast<clang::ObjCProtocolDecl>(dc)) {
proto->printName(out);
out << '.';
}
named->printName(out);
return;
}
named->printName(out);
return;
}
// If this is an anonymous tag declaration with a typedef name, use that.
if (auto tag = dyn_cast<clang::TagDecl>(named)) {
if (auto typedefName = tag->getTypedefNameForAnonDecl()) {
printName(typedefName, out);
return;
}
}
}
void SwiftLookupTable::deserializeAll() {
if (!Reader) return;
for (auto baseName : Reader->getBaseNames()) {
(void)lookup(baseName, nullptr);
}
}
void SwiftLookupTable::dump() const {
// Dump the base name -> full table entry mappings.
SmallVector<StringRef, 4> baseNames;
for (const auto &entry : LookupTable) {
baseNames.push_back(entry.first);
}
llvm::array_pod_sort(baseNames.begin(), baseNames.end());
llvm::errs() << "Base name -> entry mappings:\n";
for (auto baseName : baseNames) {
llvm::errs() << " " << baseName << ":\n";
const auto &entries = LookupTable.find(baseName)->second;
for (const auto &entry : entries) {
llvm::errs() << " ";
switch (entry.Context.first) {
case ContextKind::TranslationUnit:
llvm::errs() << "TU";
break;
case ContextKind::Tag:
case ContextKind::ObjCClass:
case ContextKind::ObjCProtocol:
llvm::errs() << entry.Context.second;
}
llvm::errs() << ": ";
interleave(entry.DeclsOrMacros.begin(), entry.DeclsOrMacros.end(),
[this](uintptr_t entry) {
if (isSerializationIDEntry(entry)) {
llvm::errs() << (isMacroEntry(entry) ? "macro" : "decl")
<< " ID #" << getSerializationID(entry);
} else if (isMacroEntry(entry)) {
llvm::errs() << "Macro";
} else {
auto decl = const_cast<SwiftLookupTable *>(this)
->mapStoredDecl(entry);
printName(decl, llvm::errs());
}
},
[] {
llvm::errs() << ", ";
});
llvm::errs() << "\n";
}
}
}
// ---------------------------------------------------------------------------
// Serialization
// ---------------------------------------------------------------------------
using llvm::Fixnum;
using llvm::BCArray;
using llvm::BCBlob;
using llvm::BCFixed;
using llvm::BCGenericRecordLayout;
using llvm::BCRecordLayout;
using llvm::BCVBR;
namespace {
enum RecordTypes {
/// Record that contains the mapping from base names to entities with that
/// name.
BASE_NAME_TO_ENTITIES_RECORD_ID
= clang::serialization::FIRST_EXTENSION_RECORD_ID,
};
using BaseNameToEntitiesTableRecordLayout
= BCRecordLayout<BASE_NAME_TO_ENTITIES_RECORD_ID, BCVBR<16>, BCBlob>;
/// Trait used to write the on-disk hash table for the base name -> entities
/// mapping.
class BaseNameToEntitiesTableWriterInfo {
SwiftLookupTable &Table;
clang::ASTWriter &Writer;
public:
using key_type = StringRef;
using key_type_ref = key_type;
using data_type = SmallVector<SwiftLookupTable::FullTableEntry, 2>;
using data_type_ref = data_type &;
using hash_value_type = uint32_t;
using offset_type = unsigned;
BaseNameToEntitiesTableWriterInfo(SwiftLookupTable &table,
clang::ASTWriter &writer)
: Table(table), Writer(writer)
{
}
hash_value_type ComputeHash(key_type_ref key) {
return llvm::HashString(key);
}
std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
key_type_ref key,
data_type_ref data) {
// The length of the key.
uint32_t keyLength = key.size();
// # of entries
uint32_t dataLength = sizeof(uint16_t);
// Storage per entry.
for (const auto &entry : data) {
// Context info.
dataLength += 1;
if (SwiftLookupTable::contextRequiresName(entry.Context.first)) {
dataLength += sizeof(uint16_t) + entry.Context.second.size();
}
// # of entries.
dataLength += sizeof(uint16_t);
// Actual entries.
dataLength += (sizeof(clang::serialization::DeclID) *
entry.DeclsOrMacros.size());
}
endian::Writer<little> writer(out);
writer.write<uint16_t>(keyLength);
writer.write<uint16_t>(dataLength);
return { keyLength, dataLength };
}
void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
out << key;
}
void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
unsigned len) {
endian::Writer<little> writer(out);
// # of entries
writer.write<uint16_t>(data.size());
for (auto &fullEntry : data) {
// Context.
writer.write<uint8_t>(static_cast<uint8_t>(fullEntry.Context.first));
if (SwiftLookupTable::contextRequiresName(fullEntry.Context.first)) {
writer.write<uint16_t>(fullEntry.Context.second.size());
out << fullEntry.Context.second;
}
// # of entries.
writer.write<uint16_t>(fullEntry.DeclsOrMacros.size());
// Write the declarations and macros.
for (auto &entry : fullEntry.DeclsOrMacros) {
uint32_t id;
if (SwiftLookupTable::isDeclEntry(entry)) {
auto decl = Table.mapStoredDecl(entry);
id = (Writer.getDeclID(decl) << 2) | 0x02;
} else {
auto macro = Table.mapStoredMacro(entry);
id = (Writer.getMacroID(macro) << 2) | 0x02 | 0x01;
}
writer.write<uint32_t>(id);
}
}
}
};
}
void SwiftLookupTableWriter::writeExtensionContents(
clang::Sema &sema,
llvm::BitstreamWriter &stream) {
// Populate the lookup table.
SwiftLookupTable table(nullptr);
PopulateTable(sema, table);
SmallVector<uint64_t, 64> ScratchRecord;
// First, gather the sorted list of base names.
SmallVector<StringRef, 2> baseNames;
for (const auto &entry : table.LookupTable)
baseNames.push_back(entry.first);
llvm::array_pod_sort(baseNames.begin(), baseNames.end());
// Form the mapping from base names to entities with their context.
{
llvm::SmallString<4096> hashTableBlob;
uint32_t tableOffset;
{
llvm::OnDiskChainedHashTableGenerator<BaseNameToEntitiesTableWriterInfo>
generator;
BaseNameToEntitiesTableWriterInfo info(table, Writer);
for (auto baseName : baseNames)
generator.insert(baseName, table.LookupTable[baseName], info);
llvm::raw_svector_ostream blobStream(hashTableBlob);
// Make sure that no bucket is at offset 0
endian::Writer<little>(blobStream).write<uint32_t>(0);
tableOffset = generator.Emit(blobStream, info);
}
BaseNameToEntitiesTableRecordLayout layout(stream);
layout.emit(ScratchRecord, tableOffset, hashTableBlob);
}
}
namespace {
/// Used to deserialize the on-disk base name -> entities table.
class BaseNameToEntitiesTableReaderInfo {
public:
using internal_key_type = StringRef;
using external_key_type = internal_key_type;
using data_type = SmallVector<SwiftLookupTable::FullTableEntry, 2>;
using hash_value_type = uint32_t;
using offset_type = unsigned;
internal_key_type GetInternalKey(external_key_type key) {
return key;
}
external_key_type GetExternalKey(internal_key_type key) {
return key;
}
hash_value_type ComputeHash(internal_key_type key) {
return llvm::HashString(key);
}
static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
return lhs == rhs;
}
static std::pair<unsigned, unsigned>
ReadKeyDataLength(const uint8_t *&data) {
unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
return { keyLength, dataLength };
}
static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
return StringRef((const char *)data, length);
}
static data_type ReadData(internal_key_type key, const uint8_t *data,
unsigned length) {
data_type result;
// # of entries.
unsigned numEntries = endian::readNext<uint16_t, little, unaligned>(data);
result.reserve(numEntries);
// Read all of the entries.
while (numEntries--) {
SwiftLookupTable::FullTableEntry entry;
// Read the context.
entry.Context.first =
static_cast<SwiftLookupTable::ContextKind>(
endian::readNext<uint8_t, little, unaligned>(data));
if (SwiftLookupTable::contextRequiresName(entry.Context.first)) {
uint16_t length = endian::readNext<uint16_t, little, unaligned>(data);
entry.Context.second = StringRef((const char *)data, length);
data += length;
}
// Read the declarations and macros.
unsigned numDeclsOrMacros =
endian::readNext<uint16_t, little, unaligned>(data);
while (numDeclsOrMacros--) {
auto id = endian::readNext<uint32_t, little, unaligned>(data);
entry.DeclsOrMacros.push_back(id);
}
result.push_back(entry);
}
return result;
}
};
}
namespace swift {
using SerializedBaseNameToEntitiesTable =
llvm::OnDiskIterableChainedHashTable<BaseNameToEntitiesTableReaderInfo>;
}
clang::NamedDecl *SwiftLookupTable::mapStoredDecl(uintptr_t &entry) {
assert(isDeclEntry(entry) && "Not a declaration entry");
// If we have an AST node here, just cast it.
if (isASTNodeEntry(entry)) {
return static_cast<clang::NamedDecl *>(getPointerFromEntry(entry));
}
// Otherwise, resolve the declaration.
assert(Reader && "Cannot resolve the declaration without a reader");
clang::serialization::DeclID declID = getSerializationID(entry);
auto decl = cast_or_null<clang::NamedDecl>(
Reader->getASTReader().GetLocalDecl(Reader->getModuleFile(),
declID));
// Update the entry now that we've resolved the declaration.
entry = encodeEntry(decl);
return decl;
}
clang::MacroInfo *SwiftLookupTable::mapStoredMacro(uintptr_t &entry) {
assert(isMacroEntry(entry) && "Not a macro entry");
// If we have an AST node here, just cast it.
if (isASTNodeEntry(entry)) {
return static_cast<clang::MacroInfo *>(getPointerFromEntry(entry));
}
// Otherwise, resolve the macro.
assert(Reader && "Cannot resolve the macro without a reader");
clang::serialization::MacroID macroID = getSerializationID(entry);
auto macro = cast_or_null<clang::MacroInfo>(
Reader->getASTReader().getMacro(
Reader->getASTReader().getGlobalMacroID(
Reader->getModuleFile(),
macroID)));
// Update the entry now that we've resolved the macro.
entry = encodeEntry(macro);
return macro;
}
SwiftLookupTable::SingleEntry SwiftLookupTable::mapStored(uintptr_t &entry) {
if (isDeclEntry(entry))
return mapStoredDecl(entry);
return mapStoredMacro(entry);
}
SwiftLookupTableReader::~SwiftLookupTableReader() {
OnRemove();
delete static_cast<SerializedBaseNameToEntitiesTable *>(SerializedTable);
}
std::unique_ptr<SwiftLookupTableReader>
SwiftLookupTableReader::create(clang::ModuleFileExtension *extension,
clang::ASTReader &reader,
clang::serialization::ModuleFile &moduleFile,
std::function<void()> onRemove,
const llvm::BitstreamCursor &stream)
{
// Look for the base name -> entities table record.
SmallVector<uint64_t, 64> scratch;
auto cursor = stream;
auto next = cursor.advance();
std::unique_ptr<SerializedBaseNameToEntitiesTable> serializedTable;
while (next.Kind != llvm::BitstreamEntry::EndBlock) {
if (next.Kind == llvm::BitstreamEntry::Error)
return nullptr;
if (next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (cursor.SkipBlock())
return nullptr;
next = cursor.advance();
continue;
}
scratch.clear();
StringRef blobData;
unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
switch (kind) {
case BASE_NAME_TO_ENTITIES_RECORD_ID: {
// Already saw base name -> entities table.
if (serializedTable)
return nullptr;
uint32_t tableOffset;
BaseNameToEntitiesTableRecordLayout::readRecord(scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
serializedTable.reset(
SerializedBaseNameToEntitiesTable::Create(base + tableOffset,
base + sizeof(uint32_t),
base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
next = cursor.advance();
}
if (!serializedTable) return nullptr;
// Create the reader.
return std::unique_ptr<SwiftLookupTableReader>(
new SwiftLookupTableReader(extension, reader, moduleFile, onRemove,
serializedTable.release()));
}
SmallVector<StringRef, 4> SwiftLookupTableReader::getBaseNames() {
auto table = static_cast<SerializedBaseNameToEntitiesTable*>(SerializedTable);
SmallVector<StringRef, 4> results;
for (auto key : table->keys()) {
results.push_back(key);
}
return results;
}
/// Retrieve the set of entries associated with the given base name.
///
/// \returns true if we found anything, false otherwise.
bool SwiftLookupTableReader::lookup(
StringRef baseName,
SmallVectorImpl<SwiftLookupTable::FullTableEntry> &entries) {
auto table = static_cast<SerializedBaseNameToEntitiesTable*>(SerializedTable);
// Look for an entry with this base name.
auto known = table->find(baseName);
if (known == table->end()) return false;
// Grab the results.
entries = std::move(*known);
return true;
}