blob: f4cce1d1d93f2a6965f3e45b9908041e6867921e [file] [log] [blame]
//===--- ReferenceDependencies.cpp - Generates swiftdeps files ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "ReferenceDependencies.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/FileSystem.h"
#include "swift/AST/Module.h"
#include "swift/AST/ModuleLoader.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ReferencedNameTracker.h"
#include "swift/AST/Types.h"
#include "swift/Basic/FileSystem.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/ReferenceDependencyKeys.h"
#include "swift/Frontend/FrontendOptions.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/YAMLParser.h"
using namespace swift;
using namespace reference_dependency_keys;
namespace {
/// Emits the reference dependencies from the frontend so that the driver
/// can compute a dependency graph for the whole module, and use it to decide
/// which files need to be recompiled when doing incremental compilation.
class ReferenceDependenciesEmitter {
SourceFile *const SF;
const DependencyTracker &depTracker;
llvm::raw_ostream &out;
ReferenceDependenciesEmitter(SourceFile *const SF,
const DependencyTracker &depTracker,
llvm::raw_ostream &out)
: SF(SF), depTracker(depTracker), out(out) {}
public:
/// Emits the provided and depended-upon dependencies to a file
///
/// \param diags Where problems opening the file are emitted
/// \param SF The SourceFile containing the code with the dependences
/// \param depTracker The entities depended-upon
/// \param outputPath Where the dependencies are written
///
/// \return true on error
static bool emit(DiagnosticEngine &diags, SourceFile *SF,
const DependencyTracker &depTracker, StringRef outputPath);
/// Emit the dependencies.
static void emit(SourceFile *SF, const DependencyTracker &depTracker,
llvm::raw_ostream &out);
private:
/// Emits all the dependency information.
void emit() const;
void emitProvides() const;
void emitDepends() const;
void emitInterfaceHash() const;
};
/// Emits the declarations provided by a source file.
class ProvidesEmitter {
const SourceFile *const SF;
llvm::raw_ostream &out;
ProvidesEmitter(const SourceFile *const SF, llvm::raw_ostream &out)
: SF(SF), out(out) {}
public:
static void emit(const SourceFile *SF, llvm::raw_ostream &out);
private:
/// Aggregates declarations which are collected first and emitted later.
struct CollectedDeclarations {
/// Records every nominal declaration, and whether or not the declaration
/// changes the externally-observable shape of the type.
llvm::MapVector<const NominalTypeDecl *, bool> extendedNominals;
/// Records operator declarations so they can be included as top-level
/// declarations.
llvm::SmallVector<const FuncDecl *, 8> memberOperatorDecls;
/// Records extension declarations which are not introducing a conformance
/// to a public protocol and add a public member.
llvm::SmallVector<const ExtensionDecl *, 8> extensionsWithJustMembers;
/// Recursively computes the transitive closure over members
/// adding memberOperatorDecls and extendedNominals to the receiver.
void findNominalsAndOperators(const DeclRange members);
};
/// Emit all provided declartions.
void emit() const;
CollectedDeclarations emitTopLevelNames() const;
void emitNominalTypes(const llvm::MapVector<const NominalTypeDecl *, bool>
&extendedNominals) const;
void emitMembers(const CollectedDeclarations &cpd) const;
void emitDynamicLookupMembers() const;
void emitTopLevelDecl(const Decl *D, CollectedDeclarations &cpd) const;
void emitExtensionDecl(const ExtensionDecl *D,
CollectedDeclarations &cpd) const;
void emitNominalTypeDecl(const NominalTypeDecl *NTD,
CollectedDeclarations &cpd) const;
void emitValueDecl(const ValueDecl *VD) const;
static bool extendedTypeIsPrivate(TypeLoc inheritedType);
static bool declIsPrivate(const Decl *member);
};
/// Emit the depended-upon declartions.
class DependsEmitter {
/// The file that dependes upon the declarations.
const SourceFile *const SF;
/// The dependencies collected by the compiler.
const DependencyTracker &depTracker;
llvm::raw_ostream &out;
DependsEmitter(const SourceFile *SF, const DependencyTracker &depTracker,
llvm::raw_ostream &out)
: SF(SF), depTracker(depTracker), out(out) {}
public:
/// A NominalTypeDecl, its DeclBaseName, and whether it is externally-visible.
using MemberTableEntryTy = std::pair<ReferencedNameTracker::MemberPair, bool>;
/// Emit the dependencies
///
/// \param SF SourceFile containing the dependent code
/// \param depTracker Contains the dependencies found during compilation
/// \param out Where the dependencies are emitted
static void emit(const SourceFile *SF, const DependencyTracker &depTracker,
llvm::raw_ostream &out);
private:
/// Emit all the dependencies.
void emit() const;
void emitTopLevelNames(const ReferencedNameTracker *const tracker) const;
void emitMembers(const ArrayRef<MemberTableEntryTy> sortedMembers) const;
void emitNominalTypes(const ArrayRef<MemberTableEntryTy> sortedMembers) const;
void emitDynamicLookup(const ReferencedNameTracker *const tracker) const;
void emitExternal(const DependencyTracker &depTracker) const;
static SmallVector<std::pair<DeclBaseName, bool>, 16>
sortedByName(const llvm::DenseMap<DeclBaseName, bool> map);
};
} // namespace
static std::string mangleTypeAsContext(const NominalTypeDecl *type) {
Mangle::ASTMangler Mangler;
return Mangler.mangleTypeAsContextUSR(type);
}
std::vector<std::string>
swift::reversePathSortedFilenames(const ArrayRef<std::string> elts) {
std::vector<std::string> tmp(elts.begin(), elts.end());
std::sort(tmp.begin(), tmp.end(), [](const std::string &a,
const std::string &b) -> bool {
return std::lexicographical_compare(a.rbegin(), a.rend(),
b.rbegin(), b.rend());
});
return tmp;
}
static std::string escape(DeclBaseName name) {
return llvm::yaml::escape(name.userFacingName());
}
bool ReferenceDependenciesEmitter::emit(DiagnosticEngine &diags,
SourceFile *const SF,
const DependencyTracker &depTracker,
StringRef outputPath) {
// Before writing to the dependencies file path, preserve any previous file
// that may have been there. No error handling -- this is just a nicety, it
// doesn't matter if it fails.
llvm::sys::fs::rename(outputPath, outputPath + "~");
return withOutputFile(diags, outputPath, [&](llvm::raw_pwrite_stream &out) {
ReferenceDependenciesEmitter::emit(SF, depTracker, out);
return false;
});
}
void ReferenceDependenciesEmitter::emit(SourceFile *SF,
const DependencyTracker &depTracker,
llvm::raw_ostream &out) {
ReferenceDependenciesEmitter(SF, depTracker, out).emit();
}
void ReferenceDependenciesEmitter::emit() const {
assert(SF && "Cannot emit reference dependencies without a SourceFile");
out << "### Swift dependencies file v0 ###\n";
emitProvides();
emitDepends();
emitInterfaceHash();
}
bool swift::emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF,
const DependencyTracker &depTracker,
StringRef outputPath) {
return ReferenceDependenciesEmitter::emit(diags, SF, depTracker, outputPath);
}
void ProvidesEmitter::emit() const {
CollectedDeclarations cpd = emitTopLevelNames();
emitNominalTypes(cpd.extendedNominals);
emitMembers(cpd);
emitDynamicLookupMembers();
}
void ProvidesEmitter::emit(const SourceFile *SF, llvm::raw_ostream &out) {
ProvidesEmitter(SF, out).emit();
}
void ReferenceDependenciesEmitter::emitProvides() const {
ProvidesEmitter::emit(SF, out);
}
void ReferenceDependenciesEmitter::emitDepends() const {
DependsEmitter::emit(SF, depTracker, out);
}
void ReferenceDependenciesEmitter::emitInterfaceHash() const {
llvm::SmallString<32> interfaceHash;
SF->getInterfaceHash(interfaceHash);
out << reference_dependency_keys::interfaceHash << ": \"" << interfaceHash
<< "\"\n";
}
ProvidesEmitter::CollectedDeclarations
ProvidesEmitter::emitTopLevelNames() const {
out << providesTopLevel << ":\n";
CollectedDeclarations cpd;
for (const Decl *D : SF->Decls)
emitTopLevelDecl(D, cpd);
for (auto *operatorFunction : cpd.memberOperatorDecls)
out << "- \"" << escape(operatorFunction->getName()) << "\"\n";
return cpd;
}
void ProvidesEmitter::emitTopLevelDecl(const Decl *const D,
CollectedDeclarations &cpd) const {
switch (D->getKind()) {
case DeclKind::Module:
break;
case DeclKind::Import:
// FIXME: Handle re-exported decls.
break;
case DeclKind::Extension:
emitExtensionDecl(cast<ExtensionDecl>(D), cpd);
break;
case DeclKind::InfixOperator:
case DeclKind::PrefixOperator:
case DeclKind::PostfixOperator:
out << "- \"" << escape(cast<OperatorDecl>(D)->getName()) << "\"\n";
break;
case DeclKind::PrecedenceGroup:
out << "- \"" << escape(cast<PrecedenceGroupDecl>(D)->getName()) << "\"\n";
break;
case DeclKind::Enum:
case DeclKind::Struct:
case DeclKind::Class:
case DeclKind::Protocol:
emitNominalTypeDecl(cast<NominalTypeDecl>(D), cpd);
break;
case DeclKind::TypeAlias:
case DeclKind::Var:
case DeclKind::Func:
case DeclKind::Accessor:
emitValueDecl(cast<ValueDecl>(D));
break;
case DeclKind::PatternBinding:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
// No action necessary.
break;
case DeclKind::EnumCase:
case DeclKind::GenericTypeParam:
case DeclKind::AssociatedType:
case DeclKind::Param:
case DeclKind::Subscript:
case DeclKind::Constructor:
case DeclKind::Destructor:
case DeclKind::EnumElement:
case DeclKind::MissingMember:
// These can occur in malformed ASTs.
break;
}
}
void ProvidesEmitter::emitExtensionDecl(const ExtensionDecl *const ED,
CollectedDeclarations &cpd) const {
auto *NTD = ED->getExtendedNominal();
if (!NTD)
return;
if (NTD->getFormalAccess() <= AccessLevel::FilePrivate) {
return;
}
// Check if the extension is just adding members, or if it is
// introducing a conformance to a public protocol.
bool justMembers =
std::all_of(ED->getInherited().begin(), ED->getInherited().end(),
extendedTypeIsPrivate);
if (justMembers) {
if (std::all_of(ED->getMembers().begin(), ED->getMembers().end(),
declIsPrivate)) {
return;
}
cpd.extensionsWithJustMembers.push_back(ED);
}
cpd.extendedNominals[NTD] |= !justMembers;
cpd.findNominalsAndOperators(ED->getMembers());
}
void ProvidesEmitter::emitNominalTypeDecl(const NominalTypeDecl *const NTD,
CollectedDeclarations &cpd) const {
if (!NTD->hasName())
return;
if (NTD->getFormalAccess() <= AccessLevel::FilePrivate) {
return;
}
out << "- \"" << escape(NTD->getName()) << "\"\n";
cpd.extendedNominals[NTD] |= true;
cpd.findNominalsAndOperators(NTD->getMembers());
}
void ProvidesEmitter::CollectedDeclarations::findNominalsAndOperators(
const DeclRange members) {
for (const Decl *D : members) {
auto *VD = dyn_cast<ValueDecl>(D);
if (!VD)
continue;
if (VD->getFormalAccess() <= AccessLevel::FilePrivate) {
continue;
}
if (VD->getFullName().isOperator()) {
memberOperatorDecls.push_back(cast<FuncDecl>(VD));
continue;
}
auto nominal = dyn_cast<NominalTypeDecl>(D);
if (!nominal)
continue;
extendedNominals[nominal] |= true;
findNominalsAndOperators(nominal->getMembers());
}
}
void ProvidesEmitter::emitValueDecl(const ValueDecl *const VD) const {
if (!VD->hasName())
return;
if (VD->getFormalAccess() <= AccessLevel::FilePrivate) {
return;
}
out << "- \"" << escape(VD->getBaseName()) << "\"\n";
}
void ProvidesEmitter::emitNominalTypes(
const llvm::MapVector<const NominalTypeDecl *, bool> &extendedNominals)
const {
out << providesNominal << ":\n";
for (auto entry : extendedNominals) {
if (!entry.second)
continue;
out << "- \"";
out << mangleTypeAsContext(entry.first);
out << "\"\n";
}
}
void ProvidesEmitter::emitMembers(const CollectedDeclarations &cpd) const {
out << providesMember << ":\n";
for (auto entry : cpd.extendedNominals) {
out << "- [\"";
out << mangleTypeAsContext(entry.first);
out << "\", \"\"]\n";
}
// This is also part of providesMember.
for (auto *ED : cpd.extensionsWithJustMembers) {
auto mangledName = mangleTypeAsContext(ED->getExtendedNominal());
for (auto *member : ED->getMembers()) {
auto *VD = dyn_cast<ValueDecl>(member);
if (!VD || !VD->hasName() ||
VD->getFormalAccess() <= AccessLevel::FilePrivate) {
continue;
}
out << "- [\"" << mangledName << "\", \"" << escape(VD->getBaseName())
<< "\"]\n";
}
}
}
void ProvidesEmitter::emitDynamicLookupMembers() const {
if (SF->getASTContext().LangOpts.EnableObjCInterop) {
// FIXME: This requires a traversal of the whole file to compute.
// We should (a) see if there's a cheaper way to keep it up to date,
// and/or (b) see if we can fast-path cases where there's no ObjC
// involved.
out << providesDynamicLookup << ":\n";
class NameCollector : public VisibleDeclConsumer {
private:
SmallVector<DeclBaseName, 16> names;
public:
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override {
names.push_back(VD->getBaseName());
}
ArrayRef<DeclBaseName> getNames() {
llvm::array_pod_sort(
names.begin(), names.end(),
[](const DeclBaseName *lhs, const DeclBaseName *rhs) {
return lhs->compare(*rhs);
});
names.erase(std::unique(names.begin(), names.end()), names.end());
return names;
}
};
NameCollector collector;
SF->lookupClassMembers({}, collector);
for (DeclBaseName name : collector.getNames()) {
out << "- \"" << escape(name) << "\"\n";
}
}
}
bool ProvidesEmitter::extendedTypeIsPrivate(TypeLoc inheritedType) {
auto type = inheritedType.getType();
if (!type)
return true;
if (!type->isExistentialType()) {
// Be conservative. We don't know how to deal with other extended types.
return false;
}
auto layout = type->getExistentialLayout();
assert(!layout.explicitSuperclass && "Should not have a subclass existential "
"in the inheritance clause of an extension");
for (auto protoTy : layout.getProtocols()) {
if (!declIsPrivate(protoTy->getDecl()))
return false;
}
return true;
}
bool ProvidesEmitter::declIsPrivate(const Decl *member) {
auto *VD = dyn_cast<ValueDecl>(member);
if (!VD) {
switch (member->getKind()) {
case DeclKind::Import:
case DeclKind::PatternBinding:
case DeclKind::EnumCase:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
return true;
case DeclKind::Extension:
case DeclKind::InfixOperator:
case DeclKind::PrefixOperator:
case DeclKind::PostfixOperator:
return false;
default:
llvm_unreachable("everything else is a ValueDecl");
}
}
return VD->getFormalAccess() <= AccessLevel::FilePrivate;
}
void DependsEmitter::emit(const SourceFile *SF,
const DependencyTracker &depTracker,
llvm::raw_ostream &out) {
DependsEmitter(SF, depTracker, out).emit();
}
void DependsEmitter::emit() const {
const ReferencedNameTracker *const tracker = SF->getReferencedNameTracker();
assert(tracker && "Cannot emit reference dependencies without a tracker");
emitTopLevelNames(tracker);
auto &memberLookupTable = tracker->getUsedMembers();
std::vector<MemberTableEntryTy> sortedMembers{
memberLookupTable.begin(), memberLookupTable.end()
};
llvm::array_pod_sort(sortedMembers.begin(), sortedMembers.end(),
[](const MemberTableEntryTy *lhs,
const MemberTableEntryTy *rhs) -> int {
if (auto cmp = lhs->first.first->getName().compare(rhs->first.first->getName()))
return cmp;
if (auto cmp = lhs->first.second.compare(rhs->first.second))
return cmp;
// We can have two entries with the same member name if one of them
// was the special 'init' name and the other is the plain 'init' token.
if (lhs->second != rhs->second)
return lhs->second ? -1 : 1;
// Break type name ties by mangled name.
auto lhsMangledName = mangleTypeAsContext(lhs->first.first);
auto rhsMangledName = mangleTypeAsContext(rhs->first.first);
return lhsMangledName.compare(rhsMangledName);
});
emitMembers(sortedMembers);
emitNominalTypes(sortedMembers);
emitDynamicLookup(tracker);
emitExternal(depTracker);
}
void DependsEmitter::emitTopLevelNames(
const ReferencedNameTracker *const tracker) const {
out << dependsTopLevel << ":\n";
for (auto &entry : sortedByName(tracker->getTopLevelNames())) {
assert(!entry.first.empty());
out << "- ";
if (!entry.second)
out << "!private ";
out << "\"" << escape(entry.first) << "\"\n";
}
}
void DependsEmitter::emitMembers(
ArrayRef<MemberTableEntryTy> sortedMembers) const {
out << dependsMember << ":\n";
for (auto &entry : sortedMembers) {
assert(entry.first.first != nullptr);
if (entry.first.first->getFormalAccess() <= AccessLevel::FilePrivate)
continue;
out << "- ";
if (!entry.second)
out << "!private ";
out << "[\"";
out << mangleTypeAsContext(entry.first.first);
out << "\", \"";
if (!entry.first.second.empty())
out << escape(entry.first.second);
out << "\"]\n";
}
}
void DependsEmitter::emitNominalTypes(
ArrayRef<MemberTableEntryTy> sortedMembers) const {
out << dependsNominal << ":\n";
for (auto i = sortedMembers.begin(), e = sortedMembers.end(); i != e; ++i) {
bool isCascading = i->second;
while (i+1 != e && i[0].first.first == i[1].first.first) {
++i;
isCascading |= i->second;
}
if (i->first.first->getFormalAccess() <= AccessLevel::FilePrivate)
continue;
out << "- ";
if (!isCascading)
out << "!private ";
out << "\"";
out << mangleTypeAsContext(i->first.first);
out << "\"\n";
}
}
void DependsEmitter::emitDynamicLookup(
const ReferencedNameTracker *const tracker) const {
out << dependsDynamicLookup << ":\n";
for (auto &entry : sortedByName(tracker->getDynamicLookupNames())) {
assert(!entry.first.empty());
out << "- ";
if (!entry.second)
out << "!private ";
out << "\"" << escape(entry.first) << "\"\n";
}
}
void DependsEmitter::emitExternal(const DependencyTracker &depTracker) const {
out << dependsExternal << ":\n";
for (auto &entry : reversePathSortedFilenames(depTracker.getDependencies())) {
out << "- \"" << llvm::yaml::escape(entry) << "\"\n";
}
}
SmallVector<std::pair<DeclBaseName, bool>, 16>
DependsEmitter::sortedByName(const llvm::DenseMap<DeclBaseName, bool> map) {
SmallVector<std::pair<DeclBaseName, bool>, 16> pairs{map.begin(), map.end()};
llvm::array_pod_sort(pairs.begin(), pairs.end(),
[](const std::pair<DeclBaseName, bool> *first,
const std::pair<DeclBaseName, bool> *second) -> int {
return first->first.compare(second->first);
});
return pairs;
}