| //===--- ParseableInterfaceSupport.cpp - swiftinterface files ------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2019 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 "swift/AST/ASTContext.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/DiagnosticsFrontend.h" |
| #include "swift/AST/DiagnosticsSema.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/FileSystem.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/Basic/STLExtras.h" |
| #include "swift/Frontend/Frontend.h" |
| #include "swift/Frontend/ParseableInterfaceSupport.h" |
| #include "swift/Frontend/PrintingDiagnosticConsumer.h" |
| #include "swift/SILOptimizer/PassManager/Passes.h" |
| #include "swift/Serialization/ModuleFormat.h" |
| #include "swift/Serialization/SerializationOptions.h" |
| #include "swift/Serialization/Validation.h" |
| #include "clang/Basic/Module.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/Regex.h" |
| #include "llvm/Support/StringSaver.h" |
| |
| using namespace swift; |
| |
| version::Version swift::InterfaceFormatVersion({1, 0}); |
| |
| /// Diagnose any scoped imports in \p imports, i.e. those with a non-empty |
| /// access path. These are not yet supported by parseable interfaces, since the |
| /// information about the declaration kind is not preserved through the binary |
| /// serialization that happens as an intermediate step in non-whole-module |
| /// builds. |
| /// |
| /// These come from declarations like `import class FooKit.MainFooController`. |
| static void diagnoseScopedImports(DiagnosticEngine &diags, |
| ArrayRef<ModuleDecl::ImportedModule> imports){ |
| for (const ModuleDecl::ImportedModule &importPair : imports) { |
| if (importPair.first.empty()) |
| continue; |
| diags.diagnose(importPair.first.front().second, |
| diag::parseable_interface_scoped_import_unsupported); |
| } |
| } |
| |
| /// Prints to \p out a comment containing a format version number, tool version |
| /// string as well as any relevant command-line flags in \p Opts used to |
| /// construct \p M. |
| static void printToolVersionAndFlagsComment(raw_ostream &out, |
| ParseableInterfaceOptions const &Opts, |
| ModuleDecl *M) { |
| auto &Ctx = M->getASTContext(); |
| auto ToolsVersion = swift::version::getSwiftFullVersion( |
| Ctx.LangOpts.EffectiveLanguageVersion); |
| out << "// " SWIFT_INTERFACE_FORMAT_VERSION_KEY ": " |
| << InterfaceFormatVersion << "\n"; |
| out << "// " SWIFT_TOOLS_VERSION_KEY ": " |
| << ToolsVersion << "\n"; |
| out << "// " SWIFT_MODULE_FLAGS_KEY ": " |
| << Opts.ParseableInterfaceFlags << "\n"; |
| } |
| |
| llvm::Regex swift::getSwiftInterfaceFormatVersionRegex() { |
| return llvm::Regex("^// " SWIFT_INTERFACE_FORMAT_VERSION_KEY |
| ": ([0-9\\.]+)$", llvm::Regex::Newline); |
| } |
| |
| llvm::Regex swift::getSwiftInterfaceModuleFlagsRegex() { |
| return llvm::Regex("^// " SWIFT_MODULE_FLAGS_KEY ":(.*)$", |
| llvm::Regex::Newline); |
| } |
| |
| /// Prints the imported modules in \p M to \p out in the form of \c import |
| /// source declarations. |
| static void printImports(raw_ostream &out, ModuleDecl *M) { |
| // FIXME: This is very similar to what's in Serializer::writeInputBlock, but |
| // it's not obvious what higher-level optimization would be factored out here. |
| ModuleDecl::ImportFilter allImportFilter; |
| allImportFilter |= ModuleDecl::ImportFilterKind::Public; |
| allImportFilter |= ModuleDecl::ImportFilterKind::Private; |
| |
| SmallVector<ModuleDecl::ImportedModule, 8> allImports; |
| M->getImportedModules(allImports, allImportFilter); |
| ModuleDecl::removeDuplicateImports(allImports); |
| diagnoseScopedImports(M->getASTContext().Diags, allImports); |
| |
| // Collect the public imports as a subset so that we can mark them with |
| // '@_exported'. |
| SmallVector<ModuleDecl::ImportedModule, 8> publicImports; |
| M->getImportedModules(publicImports, ModuleDecl::ImportFilterKind::Public); |
| llvm::SmallSet<ModuleDecl::ImportedModule, 8, |
| ModuleDecl::OrderImportedModules> publicImportSet; |
| publicImportSet.insert(publicImports.begin(), publicImports.end()); |
| |
| for (auto import : allImports) { |
| if (import.second->isOnoneSupportModule() || |
| import.second->isBuiltinModule()) { |
| continue; |
| } |
| |
| if (publicImportSet.count(import)) |
| out << "@_exported "; |
| out << "import "; |
| import.second->getReverseFullModuleName().printForward(out); |
| |
| // Write the access path we should be honoring but aren't. |
| // (See diagnoseScopedImports above.) |
| if (!import.first.empty()) { |
| out << "/*"; |
| for (const auto &accessPathElem : import.first) |
| out << "." << accessPathElem.first; |
| out << "*/"; |
| } |
| |
| out << "\n"; |
| } |
| } |
| |
| // 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<TypeAliasType>(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 { |
| static const StringLiteral DummyProtocolName; |
| |
| /// 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; |
| /// Protocols that can be printed, but whose conformances are constrained with |
| /// something that \e can't be printed. |
| SmallVector<const ProtocolType *, 8> ConditionalConformanceProtocols; |
| |
| /// 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'. |
| } |
| } |
| |
| /// For each type in \p directlyInherited, record any protocols that we would |
| /// have printed in ConditionalConformanceProtocols. |
| void recordConditionalConformances(ArrayRef<TypeLoc> directlyInherited) { |
| for (TypeLoc inherited : directlyInherited) { |
| Type inheritedTy = inherited.getType(); |
| if (!inheritedTy || !inheritedTy->isExistentialType()) |
| continue; |
| |
| ExistentialLayout layout = inheritedTy->getExistentialLayout(); |
| for (ProtocolType *protoTy : layout.getProtocols()) |
| if (isPublicOrUsableFromInline(protoTy)) |
| ConditionalConformanceProtocols.push_back(protoTy); |
| // 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; |
| } |
| |
| if (!isPublicOrUsableFromInline(nominal)) |
| return; |
| |
| map[nominal].recordProtocols(directlyInherited); |
| |
| // Recurse to find any nested types. |
| for (const Decl *member : memberContext->getMembers()) |
| collectProtocols(map, member); |
| } |
| |
| /// If \p D is an extension providing conditional conformances, record those |
| /// in \p map. |
| /// |
| /// \sa recordConditionalConformances |
| static void collectSkippedConditionalConformances(PerTypeMap &map, |
| const Decl *D) { |
| auto *extension = dyn_cast<ExtensionDecl>(D); |
| if (!extension || !extension->isConstrainedExtension()) |
| return; |
| |
| const NominalTypeDecl *nominal = extension->getExtendedNominal(); |
| if (!isPublicOrUsableFromInline(nominal)) |
| return; |
| |
| map[nominal].recordConditionalConformances(extension->getInherited()); |
| // No recursion here because extensions are never nested. |
| } |
| |
| /// Returns true if the conformance of \p nominal to \p proto is declared in |
| /// module \p M. |
| static bool conformanceDeclaredInModule(ModuleDecl *M, |
| const NominalTypeDecl *nominal, |
| ProtocolDecl *proto) { |
| SmallVector<ProtocolConformance *, 4> conformances; |
| nominal->lookupConformance(M, proto, conformances); |
| return llvm::all_of(conformances, |
| [M](const ProtocolConformance *conformance) -> bool { |
| return M == conformance->getDeclContext()->getParentModule(); |
| }); |
| } |
| |
| /// 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, |
| ModuleDecl *M, |
| 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) && |
| conformanceDeclaredInModule(M, nominal, 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"; |
| } |
| |
| /// If there were any conditional conformances that couldn't be printed, |
| /// make a dummy extension that conforms to all of them, constrained by a |
| /// fake protocol. |
| bool printInaccessibleConformanceExtensionIfNeeded( |
| raw_ostream &out, const PrintOptions &printOptions, |
| const NominalTypeDecl *nominal) const { |
| if (ConditionalConformanceProtocols.empty()) |
| return false; |
| assert(nominal->isGenericContext()); |
| |
| out << "extension "; |
| nominal->getDeclaredType().print(out, printOptions); |
| out << " : "; |
| swift::interleave(ConditionalConformanceProtocols, |
| [&out, &printOptions](const ProtocolType *protoTy) { |
| protoTy->print(out, printOptions); |
| }, [&out] { out << ", "; }); |
| out << " where " |
| << nominal->getGenericSignature()->getGenericParams().front()->getName() |
| << " : " << DummyProtocolName << " {}\n"; |
| return true; |
| } |
| |
| /// Print a fake protocol declaration for use by |
| /// #printInaccessibleConformanceExtensionIfNeeded. |
| static void printDummyProtocolDeclaration(raw_ostream &out) { |
| out << "\n@usableFromInline\ninternal protocol " << DummyProtocolName |
| << " {}\n"; |
| } |
| }; |
| |
| const StringLiteral InheritedProtocolCollector::DummyProtocolName = |
| "_ConstraintThatIsNotPartOfTheAPIOfThisLibrary"; |
| } // end anonymous namespace |
| |
| bool swift::emitParseableInterface(raw_ostream &out, |
| ParseableInterfaceOptions const &Opts, |
| ModuleDecl *M) { |
| assert(M); |
| |
| printToolVersionAndFlagsComment(out, Opts, M); |
| 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) || |
| !printOptions.CurrentPrintabilityChecker->shouldPrint(D, printOptions)){ |
| InheritedProtocolCollector::collectSkippedConditionalConformances( |
| inheritedProtocolMap, D); |
| continue; |
| } |
| |
| D->print(out, printOptions); |
| out << "\n"; |
| |
| InheritedProtocolCollector::collectProtocols(inheritedProtocolMap, D); |
| } |
| |
| // Print dummy extensions for any protocols that were indirectly conformed to. |
| bool needDummyProtocolDeclaration = false; |
| for (const auto &nominalAndCollector : inheritedProtocolMap) { |
| const NominalTypeDecl *nominal = nominalAndCollector.first; |
| const InheritedProtocolCollector &collector = nominalAndCollector.second; |
| collector.printSynthesizedExtensionIfNeeded(out, printOptions, M, nominal); |
| needDummyProtocolDeclaration |= |
| collector.printInaccessibleConformanceExtensionIfNeeded(out, |
| printOptions, |
| nominal); |
| } |
| if (needDummyProtocolDeclaration) |
| InheritedProtocolCollector::printDummyProtocolDeclaration(out); |
| |
| return false; |
| } |