mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We want to distinguish the special case of a library built with -sil-serialize-all, from a SIL function that is [fragile] because of an explicitly @_transparent or @inline(__always). For now, NFC.
549 lines
19 KiB
C++
549 lines
19 KiB
C++
//===--- SerializedModuleLoader.cpp - Import Swift modules ----------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Serialization/SerializedModuleLoader.h"
|
|
#include "swift/Serialization/ModuleFile.h"
|
|
#include "swift/Strings.h"
|
|
#include "swift/AST/AST.h"
|
|
#include "swift/AST/DiagnosticsSema.h"
|
|
#include "swift/Basic/STLExtras.h"
|
|
#include "swift/Basic/SourceManager.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include <system_error>
|
|
|
|
using namespace swift;
|
|
|
|
namespace {
|
|
typedef std::pair<Identifier, SourceLoc> AccessPathElem;
|
|
} // end unnamed namespace
|
|
|
|
// Defined out-of-line so that we can see ~ModuleFile.
|
|
SerializedModuleLoader::SerializedModuleLoader(ASTContext &ctx,
|
|
DependencyTracker *tracker)
|
|
: ModuleLoader(tracker), Ctx(ctx) {}
|
|
SerializedModuleLoader::~SerializedModuleLoader() = default;
|
|
|
|
static std::error_code
|
|
openModuleFiles(StringRef DirName, StringRef ModuleFilename,
|
|
StringRef ModuleDocFilename,
|
|
std::unique_ptr<llvm::MemoryBuffer> &ModuleBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> &ModuleDocBuffer,
|
|
llvm::SmallVectorImpl<char> &Scratch) {
|
|
// Try to open the module file first. If we fail, don't even look for the
|
|
// module documentation file.
|
|
Scratch.clear();
|
|
llvm::sys::path::append(Scratch, DirName, ModuleFilename);
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ModuleOrErr =
|
|
llvm::MemoryBuffer::getFile(StringRef(Scratch.data(), Scratch.size()));
|
|
if (!ModuleOrErr)
|
|
return ModuleOrErr.getError();
|
|
|
|
// Try to open the module documentation file. If it does not exist, ignore
|
|
// the error. However, pass though all other errors.
|
|
Scratch.clear();
|
|
llvm::sys::path::append(Scratch, DirName, ModuleDocFilename);
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ModuleDocOrErr =
|
|
llvm::MemoryBuffer::getFile(StringRef(Scratch.data(), Scratch.size()));
|
|
if (!ModuleDocOrErr &&
|
|
ModuleDocOrErr.getError() != std::errc::no_such_file_or_directory) {
|
|
return ModuleDocOrErr.getError();
|
|
}
|
|
ModuleBuffer = std::move(ModuleOrErr.get());
|
|
if (ModuleDocOrErr)
|
|
ModuleDocBuffer = std::move(ModuleDocOrErr.get());
|
|
return std::error_code();
|
|
}
|
|
|
|
static std::error_code
|
|
findModule(ASTContext &ctx, AccessPathElem moduleID,
|
|
std::unique_ptr<llvm::MemoryBuffer> &moduleBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> &moduleDocBuffer,
|
|
bool &isFramework) {
|
|
llvm::SmallString<64> moduleFilename(moduleID.first.str());
|
|
moduleFilename += '.';
|
|
moduleFilename += SERIALIZED_MODULE_EXTENSION;
|
|
|
|
llvm::SmallString<64> moduleDocFilename(moduleID.first.str());
|
|
moduleDocFilename += '.';
|
|
moduleDocFilename += SERIALIZED_MODULE_DOC_EXTENSION;
|
|
|
|
// FIXME: Which name should we be using here? Do we care about CPU subtypes?
|
|
// FIXME: At the very least, don't hardcode "arch".
|
|
llvm::SmallString<16> archFile{
|
|
ctx.LangOpts.getPlatformConditionValue("arch")};
|
|
llvm::SmallString<16> archDocFile{archFile};
|
|
if (!archFile.empty()) {
|
|
archFile += '.';
|
|
archFile += SERIALIZED_MODULE_EXTENSION;
|
|
|
|
archDocFile += '.';
|
|
archDocFile += SERIALIZED_MODULE_DOC_EXTENSION;
|
|
}
|
|
|
|
llvm::SmallString<128> scratch;
|
|
llvm::SmallString<128> currPath;
|
|
|
|
isFramework = false;
|
|
for (auto path : ctx.SearchPathOpts.ImportSearchPaths) {
|
|
auto err = openModuleFiles(path,
|
|
moduleFilename.str(), moduleDocFilename.str(),
|
|
moduleBuffer, moduleDocBuffer,
|
|
scratch);
|
|
if (err == std::errc::is_a_directory) {
|
|
currPath = path;
|
|
llvm::sys::path::append(currPath, moduleFilename.str());
|
|
err = openModuleFiles(currPath,
|
|
archFile.str(), archDocFile.str(),
|
|
moduleBuffer, moduleDocBuffer,
|
|
scratch);
|
|
}
|
|
if (!err || err != std::errc::no_such_file_or_directory)
|
|
return err;
|
|
}
|
|
|
|
{
|
|
llvm::SmallString<64> moduleFramework(moduleID.first.str());
|
|
moduleFramework += ".framework";
|
|
isFramework = true;
|
|
|
|
for (auto path : ctx.SearchPathOpts.FrameworkSearchPaths) {
|
|
currPath = path;
|
|
llvm::sys::path::append(currPath, moduleFramework.str(),
|
|
"Modules", moduleFilename.str());
|
|
auto err = openModuleFiles(currPath,
|
|
archFile.str(), archDocFile.str(),
|
|
moduleBuffer, moduleDocBuffer,
|
|
scratch);
|
|
if (!err || err != std::errc::no_such_file_or_directory)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
// If we're not allowed to look in the runtime library import path, stop.
|
|
if (ctx.SearchPathOpts.SkipRuntimeLibraryImportPath)
|
|
return std::make_error_code(std::errc::no_such_file_or_directory);
|
|
|
|
// Search the runtime import path.
|
|
isFramework = false;
|
|
return openModuleFiles(ctx.SearchPathOpts.RuntimeLibraryImportPath,
|
|
moduleFilename.str(), moduleDocFilename.str(),
|
|
moduleBuffer, moduleDocBuffer, scratch);
|
|
}
|
|
|
|
FileUnit *SerializedModuleLoader::loadAST(
|
|
Module &M, Optional<SourceLoc> diagLoc,
|
|
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
|
|
bool isFramework) {
|
|
assert(moduleInputBuffer);
|
|
|
|
const char *moduleBufferID = moduleInputBuffer->getBufferIdentifier();
|
|
const char *moduleDocBufferID = nullptr;
|
|
if (moduleDocInputBuffer)
|
|
moduleDocBufferID = moduleDocInputBuffer->getBufferIdentifier();
|
|
|
|
if (moduleInputBuffer->getBufferSize() % 4 != 0) {
|
|
if (diagLoc)
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_malformed_module,
|
|
moduleBufferID);
|
|
return nullptr;
|
|
}
|
|
|
|
serialization::ExtendedValidationInfo extendedInfo;
|
|
std::unique_ptr<ModuleFile> loadedModuleFile;
|
|
serialization::Status err = ModuleFile::load(std::move(moduleInputBuffer),
|
|
std::move(moduleDocInputBuffer),
|
|
isFramework, loadedModuleFile,
|
|
&extendedInfo);
|
|
if (err == serialization::Status::Valid) {
|
|
Ctx.bumpGeneration();
|
|
|
|
M.setResilienceStrategy(extendedInfo.getResilienceStrategy());
|
|
|
|
// We've loaded the file. Now try to bring it into the AST.
|
|
auto fileUnit = new (Ctx) SerializedASTFile(M, *loadedModuleFile,
|
|
extendedInfo.isSIB());
|
|
M.addFile(*fileUnit);
|
|
if (extendedInfo.isTestable())
|
|
M.setTestingEnabled();
|
|
|
|
auto diagLocOrInvalid = diagLoc.getValueOr(SourceLoc());
|
|
err = loadedModuleFile->associateWithFileContext(fileUnit,
|
|
diagLocOrInvalid);
|
|
if (err == serialization::Status::Valid) {
|
|
LoadedModuleFiles.emplace_back(std::move(loadedModuleFile),
|
|
Ctx.getCurrentGeneration());
|
|
return fileUnit;
|
|
}
|
|
|
|
M.removeFile(*fileUnit);
|
|
}
|
|
|
|
// This is the failure path. If we have a location, diagnose the issue.
|
|
if (!diagLoc)
|
|
return nullptr;
|
|
|
|
switch (loadedModuleFile->getStatus()) {
|
|
case serialization::Status::Valid:
|
|
llvm_unreachable("At this point we know loading has failed");
|
|
|
|
case serialization::Status::FormatTooNew:
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_module_too_new,
|
|
moduleBufferID);
|
|
break;
|
|
case serialization::Status::FormatTooOld:
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_module_too_old,
|
|
M.getName(), moduleBufferID);
|
|
break;
|
|
case serialization::Status::Malformed:
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_malformed_module,
|
|
moduleBufferID);
|
|
break;
|
|
|
|
case serialization::Status::MalformedDocumentation:
|
|
assert(moduleDocBufferID);
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_malformed_module,
|
|
moduleDocBufferID ? moduleDocBufferID : "");
|
|
break;
|
|
|
|
case serialization::Status::MissingDependency: {
|
|
// Figure out /which/ dependencies are missing.
|
|
// FIXME: Dependencies should be de-duplicated at serialization time,
|
|
// not now.
|
|
llvm::StringMap<bool> duplicates;
|
|
llvm::SmallVector<ModuleFile::Dependency, 4> missing;
|
|
std::copy_if(loadedModuleFile->getDependencies().begin(),
|
|
loadedModuleFile->getDependencies().end(),
|
|
std::back_inserter(missing),
|
|
[&duplicates](const ModuleFile::Dependency &dependency)->bool {
|
|
if (dependency.isLoaded() || dependency.isHeader())
|
|
return false;
|
|
bool &seen = duplicates[dependency.RawPath];
|
|
if (seen)
|
|
return false;
|
|
seen = true;
|
|
return true;
|
|
});
|
|
|
|
// FIXME: only show module part of RawAccessPath
|
|
assert(!missing.empty() && "unknown missing dependency?");
|
|
if (missing.size() == 1) {
|
|
Ctx.Diags.diagnose(*diagLoc,diag::serialization_missing_single_dependency,
|
|
missing.front().getPrettyPrintedPath());
|
|
} else {
|
|
llvm::SmallString<64> missingNames;
|
|
missingNames += '\'';
|
|
interleave(missing,
|
|
[&](const ModuleFile::Dependency &next) {
|
|
missingNames += next.getPrettyPrintedPath();
|
|
},
|
|
[&] { missingNames += "', '"; });
|
|
missingNames += '\'';
|
|
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_missing_dependencies,
|
|
missingNames);
|
|
}
|
|
|
|
if (Ctx.SearchPathOpts.SDKPath.empty() &&
|
|
llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) {
|
|
Ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk);
|
|
Ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case serialization::Status::MissingShadowedModule: {
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_missing_shadowed_module,
|
|
M.getName());
|
|
if (Ctx.SearchPathOpts.SDKPath.empty() &&
|
|
llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) {
|
|
Ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk);
|
|
Ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case serialization::Status::FailedToLoadBridgingHeader:
|
|
// We already emitted a diagnostic about the bridging header. Just emit
|
|
// a generic message here.
|
|
Ctx.Diags.diagnose(*diagLoc, diag::serialization_load_failed, M.getName());
|
|
break;
|
|
|
|
case serialization::Status::NameMismatch: {
|
|
// FIXME: This doesn't handle a non-debugger REPL, which should also treat
|
|
// this as a non-fatal error.
|
|
auto diagKind = diag::serialization_name_mismatch;
|
|
if (Ctx.LangOpts.DebuggerSupport)
|
|
diagKind = diag::serialization_name_mismatch_repl;
|
|
Ctx.Diags.diagnose(*diagLoc, diagKind,
|
|
loadedModuleFile->getModuleName(), M.getName());
|
|
break;
|
|
}
|
|
|
|
case serialization::Status::TargetIncompatible: {
|
|
// FIXME: This doesn't handle a non-debugger REPL, which should also treat
|
|
// this as a non-fatal error.
|
|
auto diagKind = diag::serialization_target_incompatible;
|
|
if (Ctx.LangOpts.DebuggerSupport)
|
|
diagKind = diag::serialization_target_incompatible_repl;
|
|
Ctx.Diags.diagnose(*diagLoc, diagKind,
|
|
loadedModuleFile->getTargetTriple(), moduleBufferID);
|
|
break;
|
|
}
|
|
|
|
case serialization::Status::TargetTooNew: {
|
|
StringRef moduleTargetTriple = loadedModuleFile->getTargetTriple();
|
|
llvm::Triple moduleTarget(llvm::Triple::normalize(moduleTargetTriple));
|
|
|
|
StringRef osName;
|
|
unsigned major, minor, micro;
|
|
if (moduleTarget.isMacOSX()) {
|
|
osName = swift::prettyPlatformString(PlatformKind::OSX);
|
|
moduleTarget.getMacOSXVersion(major, minor, micro);
|
|
} else {
|
|
osName = moduleTarget.getOSName();
|
|
moduleTarget.getOSVersion(major, minor, micro);
|
|
}
|
|
|
|
// FIXME: This doesn't handle a non-debugger REPL, which should also treat
|
|
// this as a non-fatal error.
|
|
auto diagKind = diag::serialization_target_too_new;
|
|
if (Ctx.LangOpts.DebuggerSupport)
|
|
diagKind = diag::serialization_target_too_new_repl;
|
|
Ctx.Diags.diagnose(*diagLoc, diagKind,
|
|
osName, major, minor, micro, moduleBufferID);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Module *SerializedModuleLoader::loadModule(SourceLoc importLoc,
|
|
Module::AccessPathTy path) {
|
|
// FIXME: Swift submodules?
|
|
if (path.size() > 1)
|
|
return nullptr;
|
|
|
|
auto moduleID = path[0];
|
|
bool isFramework = false;
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer;
|
|
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer;
|
|
// First see if we find it in the registered memory buffers.
|
|
if (!MemoryBuffers.empty()) {
|
|
// FIXME: Right now this works only with access paths of length 1.
|
|
// Once submodules are designed, this needs to support suffix
|
|
// matching and a search path.
|
|
auto bufIter = MemoryBuffers.find(moduleID.first.str());
|
|
if (bufIter != MemoryBuffers.end()) {
|
|
moduleInputBuffer = std::move(bufIter->second);
|
|
MemoryBuffers.erase(bufIter);
|
|
}
|
|
}
|
|
|
|
// Otherwise look on disk.
|
|
if (!moduleInputBuffer) {
|
|
if (std::error_code err = findModule(Ctx, moduleID, moduleInputBuffer,
|
|
moduleDocInputBuffer,
|
|
isFramework)) {
|
|
if (err != std::errc::no_such_file_or_directory) {
|
|
Ctx.Diags.diagnose(moduleID.second, diag::sema_opening_import,
|
|
moduleID.first, err.message());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
addDependency(moduleInputBuffer->getBufferIdentifier());
|
|
}
|
|
|
|
assert(moduleInputBuffer);
|
|
|
|
auto M = Module::create(moduleID.first, Ctx);
|
|
Ctx.LoadedModules[moduleID.first] = M;
|
|
|
|
if (!loadAST(*M, moduleID.second, std::move(moduleInputBuffer),
|
|
std::move(moduleDocInputBuffer), isFramework)) {
|
|
M->setFailedToLoad();
|
|
}
|
|
|
|
return M;
|
|
}
|
|
|
|
void SerializedModuleLoader::loadExtensions(NominalTypeDecl *nominal,
|
|
unsigned previousGeneration) {
|
|
for (auto &modulePair : LoadedModuleFiles) {
|
|
if (modulePair.second <= previousGeneration)
|
|
continue;
|
|
modulePair.first->loadExtensions(nominal);
|
|
}
|
|
}
|
|
|
|
void SerializedModuleLoader::loadObjCMethods(
|
|
ClassDecl *classDecl,
|
|
ObjCSelector selector,
|
|
bool isInstanceMethod,
|
|
unsigned previousGeneration,
|
|
llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) {
|
|
for (auto &modulePair : LoadedModuleFiles) {
|
|
if (modulePair.second <= previousGeneration)
|
|
continue;
|
|
modulePair.first->loadObjCMethods(classDecl, selector, isInstanceMethod,
|
|
methods);
|
|
}
|
|
}
|
|
|
|
void SerializedModuleLoader::verifyAllModules() {
|
|
#ifndef NDEBUG
|
|
for (const LoadedModulePair &loaded : LoadedModuleFiles)
|
|
loaded.first->verify();
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// SerializedASTFile implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SerializedASTFile::getImportedModules(
|
|
SmallVectorImpl<Module::ImportedModule> &imports,
|
|
Module::ImportFilter filter) const {
|
|
File.getImportedModules(imports, filter);
|
|
}
|
|
|
|
void SerializedASTFile::collectLinkLibraries(
|
|
Module::LinkLibraryCallback callback) const {
|
|
if (isSIB()) {
|
|
llvm::SmallVector<Module::ImportedModule, 8> Imports;
|
|
File.getImportedModules(Imports, Module::ImportFilter::All);
|
|
|
|
for (auto Import : Imports)
|
|
Import.second->collectLinkLibraries(callback);
|
|
} else {
|
|
File.collectLinkLibraries(callback);
|
|
}
|
|
}
|
|
|
|
bool SerializedASTFile::isSystemModule() const {
|
|
if (auto Mod = File.getShadowedModule()) {
|
|
return Mod->isSystemModule();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SerializedASTFile::lookupValue(Module::AccessPathTy accessPath,
|
|
DeclName name, NLKind lookupKind,
|
|
SmallVectorImpl<ValueDecl*> &results) const{
|
|
if (!Module::matchesAccessPath(accessPath, name))
|
|
return;
|
|
|
|
File.lookupValue(name, results);
|
|
}
|
|
|
|
TypeDecl *SerializedASTFile::lookupLocalType(llvm::StringRef MangledName) const{
|
|
return File.lookupLocalType(MangledName);
|
|
}
|
|
|
|
OperatorDecl *SerializedASTFile::lookupOperator(Identifier name,
|
|
DeclKind fixity) const {
|
|
return File.lookupOperator(name, fixity);
|
|
}
|
|
|
|
void SerializedASTFile::lookupVisibleDecls(Module::AccessPathTy accessPath,
|
|
VisibleDeclConsumer &consumer,
|
|
NLKind lookupKind) const {
|
|
File.lookupVisibleDecls(accessPath, consumer, lookupKind);
|
|
}
|
|
|
|
void SerializedASTFile::lookupClassMembers(Module::AccessPathTy accessPath,
|
|
VisibleDeclConsumer &consumer) const{
|
|
File.lookupClassMembers(accessPath, consumer);
|
|
}
|
|
|
|
void
|
|
SerializedASTFile::lookupClassMember(Module::AccessPathTy accessPath,
|
|
DeclName name,
|
|
SmallVectorImpl<ValueDecl*> &decls) const {
|
|
File.lookupClassMember(accessPath, name, decls);
|
|
}
|
|
|
|
void SerializedASTFile::lookupObjCMethods(
|
|
ObjCSelector selector,
|
|
SmallVectorImpl<AbstractFunctionDecl *> &results) const {
|
|
File.lookupObjCMethods(selector, results);
|
|
}
|
|
|
|
Optional<CommentInfo>
|
|
SerializedASTFile::getCommentForDecl(const Decl *D) const {
|
|
return File.getCommentForDecl(D);
|
|
}
|
|
|
|
Optional<StringRef>
|
|
SerializedASTFile::getGroupNameForDecl(const Decl *D) const {
|
|
return File.getGroupNameForDecl(D);
|
|
}
|
|
|
|
|
|
Optional<StringRef>
|
|
SerializedASTFile::getSourceFileNameForDecl(const Decl *D) const {
|
|
return File.getSourceFileNameForDecl(D);
|
|
}
|
|
|
|
Optional<unsigned>
|
|
SerializedASTFile::getSourceOrderForDecl(const Decl *D) const {
|
|
return File.getSourceOrderForDecl(D);
|
|
}
|
|
|
|
void
|
|
SerializedASTFile::collectAllGroups(std::vector<StringRef> &Names) const {
|
|
File.collectAllGroups(Names);
|
|
};
|
|
|
|
Optional<StringRef>
|
|
SerializedASTFile::getGroupNameByUSR(StringRef USR) const {
|
|
return File.getGroupNameByUSR(USR);
|
|
}
|
|
|
|
void
|
|
SerializedASTFile::getTopLevelDecls(SmallVectorImpl<Decl*> &results) const {
|
|
File.getTopLevelDecls(results);
|
|
}
|
|
|
|
void
|
|
SerializedASTFile::getLocalTypeDecls(SmallVectorImpl<TypeDecl*> &results) const{
|
|
File.getLocalTypeDecls(results);
|
|
}
|
|
|
|
void SerializedASTFile::getDisplayDecls(SmallVectorImpl<Decl*> &results) const {
|
|
File.getDisplayDecls(results);
|
|
}
|
|
|
|
StringRef SerializedASTFile::getFilename() const {
|
|
return File.getModuleFilename();
|
|
}
|
|
|
|
const clang::Module *SerializedASTFile::getUnderlyingClangModule() {
|
|
if (auto *ShadowedModule = File.getShadowedModule())
|
|
return ShadowedModule->findUnderlyingClangModule();
|
|
return nullptr;
|
|
}
|
|
|
|
Identifier
|
|
SerializedASTFile::getDiscriminatorForPrivateValue(const ValueDecl *D) const {
|
|
Identifier discriminator = File.getDiscriminatorForPrivateValue(D);
|
|
assert(!discriminator.empty() && "no discriminator found for value");
|
|
return discriminator;
|
|
}
|