Merge remote-tracking branch 'origin/master' into master-next

This commit is contained in:
swift-ci
2018-11-08 10:49:55 -08:00
3 changed files with 307 additions and 7 deletions

View File

@@ -14,6 +14,7 @@
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/FileSystem.h"
#include "swift/AST/Module.h"
#include "swift/Frontend/Frontend.h"
@@ -431,6 +432,157 @@ static void printImports(raw_ostream &out, ModuleDecl *M) {
}
}
// FIXME: Copied from ASTPrinter.cpp...
static bool isPublicOrUsableFromInline(const ValueDecl *VD) {
AccessScope scope =
VD->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
return scope.isPublic();
}
static bool isPublicOrUsableFromInline(Type ty) {
// Note the double negative here: we're looking for any referenced decls that
// are *not* public-or-usableFromInline.
return !ty.findIf([](Type typePart) -> bool {
// FIXME: If we have an internal typealias for a non-internal type, we ought
// to be able to print it by desugaring.
if (auto *aliasTy = dyn_cast<NameAliasType>(typePart.getPointer()))
return !isPublicOrUsableFromInline(aliasTy->getDecl());
if (auto *nominal = typePart->getAnyNominal())
return !isPublicOrUsableFromInline(nominal);
return false;
});
}
namespace {
/// Collects protocols that are conformed to by a particular nominal. Since
/// ASTPrinter will only print the public ones, the non-public ones get left by
/// the wayside. This is a problem when a non-public protocol inherits from a
/// public protocol; the generated parseable interface still needs to make that
/// dependency public.
///
/// The solution implemented here is to generate synthetic extensions that
/// declare the extra conformances. This isn't perfect (it loses the sugared
/// spelling of the protocol type, as well as the locality in the file), but it
/// does work.
class InheritedProtocolCollector {
/// Protocols that will be included by the ASTPrinter without any extra work.
SmallVector<ProtocolDecl *, 8> IncludedProtocols;
/// Protocols that will not be printed by the ASTPrinter.
SmallVector<ProtocolDecl *, 8> ExtraProtocols;
/// For each type in \p directlyInherited, classify the protocols it refers to
/// as included for printing or not, and record them in the appropriate
/// vectors.
void recordProtocols(ArrayRef<TypeLoc> directlyInherited) {
for (TypeLoc inherited : directlyInherited) {
Type inheritedTy = inherited.getType();
if (!inheritedTy || !inheritedTy->isExistentialType())
continue;
bool canPrintNormally = isPublicOrUsableFromInline(inheritedTy);
SmallVectorImpl<ProtocolDecl *> &whichProtocols =
canPrintNormally ? IncludedProtocols : ExtraProtocols;
ExistentialLayout layout = inheritedTy->getExistentialLayout();
for (ProtocolType *protoTy : layout.getProtocols())
whichProtocols.push_back(protoTy->getDecl());
// FIXME: This ignores layout constraints, but currently we don't support
// any of those besides 'AnyObject'.
}
}
public:
using PerTypeMap = llvm::MapVector<const NominalTypeDecl *,
InheritedProtocolCollector>;
/// Given that we're about to print \p D, record its protocols in \p map.
///
/// \sa recordProtocols
static void collectProtocols(PerTypeMap &map, const Decl *D) {
ArrayRef<TypeLoc> directlyInherited;
const NominalTypeDecl *nominal;
const IterableDeclContext *memberContext;
if ((nominal = dyn_cast<NominalTypeDecl>(D))) {
directlyInherited = nominal->getInherited();
memberContext = nominal;
} else if (auto *extension = dyn_cast<ExtensionDecl>(D)) {
if (extension->isConstrainedExtension()) {
// Conditional conformances never apply to inherited protocols, nor
// can they provide unconditional conformances that might be used in
// other extensions.
return;
}
nominal = extension->getExtendedNominal();
directlyInherited = extension->getInherited();
memberContext = extension;
} else {
return;
}
map[nominal].recordProtocols(directlyInherited);
// Recurse to find any nested types.
for (const Decl *member : memberContext->getMembers())
collectProtocols(map, member);
}
/// If there were any public protocols that need to be printed (i.e. they
/// weren't conformed to explicitly or inherited by another printed protocol),
/// do so now by printing a dummy extension on \p nominal to \p out.
void
printSynthesizedExtensionIfNeeded(raw_ostream &out,
const PrintOptions &printOptions,
const NominalTypeDecl *nominal) const {
if (ExtraProtocols.empty())
return;
SmallPtrSet<ProtocolDecl *, 16> handledProtocols;
// First record all protocols that have already been handled.
for (ProtocolDecl *proto : IncludedProtocols) {
proto->walkInheritedProtocols(
[&handledProtocols](ProtocolDecl *inherited) -> TypeWalker::Action {
handledProtocols.insert(inherited);
return TypeWalker::Action::Continue;
});
}
// Then walk the remaining ones, and see what we need to print.
// Note: We could do this in one pass, but the logic is easier to
// understand if we build up the list and then print it, even if it takes
// a bit more memory.
SmallVector<ProtocolDecl *, 16> protocolsToPrint;
for (ProtocolDecl *proto : ExtraProtocols) {
proto->walkInheritedProtocols(
[&](ProtocolDecl *inherited) -> TypeWalker::Action {
if (!handledProtocols.insert(inherited).second)
return TypeWalker::Action::SkipChildren;
if (isPublicOrUsableFromInline(inherited)) {
protocolsToPrint.push_back(inherited);
return TypeWalker::Action::SkipChildren;
}
return TypeWalker::Action::Continue;
});
}
if (protocolsToPrint.empty())
return;
out << "extension ";
nominal->getDeclaredType().print(out, printOptions);
out << " : ";
swift::interleave(protocolsToPrint,
[&out, &printOptions](ProtocolDecl *proto) {
proto->getDeclaredType()->print(out, printOptions);
}, [&out] { out << ", "; });
out << " {}\n";
}
};
} // end anonymous namespace
bool swift::emitParseableInterface(raw_ostream &out,
ParseableInterfaceOptions const &Opts,
ModuleDecl *M) {
@@ -440,13 +592,26 @@ bool swift::emitParseableInterface(raw_ostream &out,
printImports(out, M);
const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile();
InheritedProtocolCollector::PerTypeMap inheritedProtocolMap;
SmallVector<Decl *, 16> topLevelDecls;
M->getTopLevelDecls(topLevelDecls);
for (const Decl *D : topLevelDecls) {
if (!D->shouldPrintInContext(printOptions))
continue;
D->print(out, printOptions);
out << "\n";
InheritedProtocolCollector::collectProtocols(inheritedProtocolMap, D);
}
// Print dummy extensions for any protocols that were indirectly conformed to.
for (const auto &nominalAndCollector : inheritedProtocolMap) {
const InheritedProtocolCollector &collector = nominalAndCollector.second;
collector.printSynthesizedExtensionIfNeeded(out, printOptions,
nominalAndCollector.first);
}
return false;
}