mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge remote-tracking branch 'origin/master' into master-next
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user