mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
To avoid ambiguity, ImportResolution and a few other things used the term “decl path” instead of “access path”. Switch back to the correct terminology now that the compiler is becoming more consistent about it.
1190 lines
44 KiB
C++
1190 lines
44 KiB
C++
//===--- ImportResolution.cpp - Import Resolution -------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file performs import resolution.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "swift-import-resolution"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/DiagnosticsSema.h"
|
|
#include "swift/AST/ModuleLoader.h"
|
|
#include "swift/AST/ModuleNameLookup.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/SourceFile.h"
|
|
#include "swift/AST/SubstitutionMap.h"
|
|
#include "swift/AST/TypeCheckRequests.h"
|
|
#include "swift/Basic/Statistic.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include "swift/Parse/Parser.h"
|
|
#include "swift/Subsystems.h"
|
|
#include "clang/Basic/Module.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/TinyPtrVector.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Host.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
#include <algorithm>
|
|
#include <system_error>
|
|
using namespace swift;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: ImportResolver and supporting types
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
using ImportedModule = ModuleDecl::ImportedModule;
|
|
using ImportedModuleDesc = SourceFile::ImportedModuleDesc;
|
|
using ImportOptions = SourceFile::ImportOptions;
|
|
using ImportFlags = SourceFile::ImportFlags;
|
|
|
|
namespace {
|
|
/// Represents an import which the ImportResolver knows exists, but which has
|
|
/// not yet had its options checked, module loaded, or cross-imports found.
|
|
///
|
|
/// An UnboundImport may represent a physical ImportDecl written in the
|
|
/// source, or it may represent a cross-import overlay that has been found and
|
|
/// needs to be loaded.
|
|
struct UnboundImport {
|
|
/// The source location to use when diagnosing errors for this import.
|
|
SourceLoc importLoc;
|
|
|
|
/// The options for this import, such as "exported" or
|
|
/// "implementation-only". Use this field, not \c attrs, to determine the
|
|
/// behavior expected for this import.
|
|
ImportOptions options;
|
|
|
|
/// If \c options includes \c PrivateImport, the filename we should import
|
|
/// private declarations from.
|
|
StringRef privateImportFileName;
|
|
|
|
/// The module names being imported. There will usually be just one for the
|
|
/// top-level module, but a submodule import will have more.
|
|
ImportPath::Module modulePath;
|
|
|
|
/// If this is a scoped import, the names of the declaration being imported;
|
|
/// otherwise empty. (Currently the compiler doesn't support nested scoped
|
|
/// imports, so there should always be zero or one elements, but
|
|
/// \c ImportPath::Access is the common currency type for this.)
|
|
ImportPath::Access accessPath;
|
|
|
|
// Names of explicitly imported SPI groups via @_spi.
|
|
ArrayRef<Identifier> spiGroups;
|
|
|
|
/// If this UnboundImport directly represents an ImportDecl, contains the
|
|
/// ImportDecl it represents. This should only be used for diagnostics and
|
|
/// for updating the AST; if you want to read information about the import,
|
|
/// get it from the other fields in \c UnboundImport rather than from the
|
|
/// \c ImportDecl.
|
|
///
|
|
/// If this UnboundImport represents a cross-import, contains the declaring
|
|
/// module's \c ModuleDecl.
|
|
PointerUnion<ImportDecl *, ModuleDecl *> importOrUnderlyingModuleDecl;
|
|
|
|
NullablePtr<ImportDecl> getImportDecl() const {
|
|
return importOrUnderlyingModuleDecl.is<ImportDecl *>() ?
|
|
importOrUnderlyingModuleDecl.get<ImportDecl *>() : nullptr;
|
|
}
|
|
|
|
NullablePtr<ModuleDecl> getUnderlyingModule() const {
|
|
return importOrUnderlyingModuleDecl.is<ModuleDecl *>() ?
|
|
importOrUnderlyingModuleDecl.get<ModuleDecl *>() : nullptr;
|
|
}
|
|
|
|
/// Create an UnboundImport for a user-written import declaration.
|
|
explicit UnboundImport(ImportDecl *ID);
|
|
|
|
/// Create an UnboundImport for a cross-import overlay.
|
|
explicit UnboundImport(ASTContext &ctx,
|
|
const UnboundImport &base, Identifier overlayName,
|
|
const ImportedModuleDesc &declaringImport,
|
|
const ImportedModuleDesc &bystandingImport);
|
|
|
|
/// Diagnoses if the import would simply load the module \p SF already
|
|
/// belongs to, with no actual effect.
|
|
///
|
|
/// Some apparent self-imports do actually load a different module; this
|
|
/// method allows them.
|
|
bool checkNotTautological(const SourceFile &SF);
|
|
|
|
/// Make sure the module actually loaded, and diagnose if it didn't.
|
|
bool checkModuleLoaded(ModuleDecl *M, SourceFile &SF);
|
|
|
|
/// Find the top-level module for this module; that is, if \p M is the
|
|
/// module \c Foo.Bar.Baz, this finds \c Foo.
|
|
///
|
|
/// Specifically, this method returns:
|
|
///
|
|
/// \li \p M if \p M is a top-level module.
|
|
/// \li \c nullptr if \p M is a submodule of \c SF's parent module. (This
|
|
/// corner case can occur in mixed-source frameworks, where Swift code
|
|
/// can import a Clang submodule of itself.)
|
|
/// \li The top-level parent (i.e. ancestor with no parent) module above
|
|
/// \p M otherwise.
|
|
NullablePtr<ModuleDecl> getTopLevelModule(ModuleDecl *M, SourceFile &SF);
|
|
|
|
/// Diagnose any errors concerning the \c @_exported, \c @_implementationOnly,
|
|
/// \c \@testable, or \c @_private attributes, including a
|
|
/// non-implementation-only import of a fragile library from a resilient one.
|
|
void validateOptions(NullablePtr<ModuleDecl> topLevelModule, SourceFile &SF);
|
|
|
|
/// Create an \c ImportedModuleDesc from the information in this
|
|
/// UnboundImport.
|
|
ImportedModuleDesc makeDesc(ModuleDecl *module) const {
|
|
return ImportedModuleDesc({ accessPath, module }, options,
|
|
privateImportFileName, spiGroups);
|
|
}
|
|
|
|
private:
|
|
void validatePrivate(ModuleDecl *topLevelModule);
|
|
void validateImplementationOnly(ASTContext &ctx);
|
|
void validateTestable(ModuleDecl *topLevelModule);
|
|
void validateResilience(NullablePtr<ModuleDecl> topLevelModule,
|
|
SourceFile &SF);
|
|
|
|
/// Diagnoses an inability to import \p modulePath in this situation and, if
|
|
/// \p attrs is provided and has an \p attrKind, invalidates the attribute and
|
|
/// offers a fix-it to remove it.
|
|
void diagnoseInvalidAttr(DeclAttrKind attrKind, DiagnosticEngine &diags,
|
|
Diag<Identifier> diagID);
|
|
};
|
|
|
|
class ImportResolver final : public DeclVisitor<ImportResolver> {
|
|
friend DeclVisitor<ImportResolver>;
|
|
|
|
SourceFile &SF;
|
|
ASTContext &ctx;
|
|
|
|
/// Imports which still need their options checked, modules loaded, and
|
|
/// cross-imports found.
|
|
SmallVector<UnboundImport, 4> unboundImports;
|
|
|
|
/// The list of fully bound imports.
|
|
SmallVector<ImportedModuleDesc, 16> boundImports;
|
|
|
|
/// All imported modules which should be considered when cross-importing.
|
|
/// This is basically the transitive import graph, but with only top-level
|
|
/// modules and without reexports from Objective-C modules.
|
|
///
|
|
/// We use a \c SmallSetVector here because this doubles as the worklist for
|
|
/// cross-importing, so we want to keep it in order; this is feasible
|
|
/// because this set is usually fairly small.
|
|
SmallSetVector<ImportedModuleDesc, 64> crossImportableModules;
|
|
|
|
/// The subset of \c crossImportableModules which may declare cross-imports.
|
|
///
|
|
/// This is a performance optimization. Since most modules do not register
|
|
/// any cross-imports, we can usually compare against this list, which is
|
|
/// much, much smaller than \c crossImportableModules.
|
|
SmallVector<ImportedModuleDesc, 16> crossImportDeclaringModules;
|
|
|
|
/// The index of the next module in \c visibleModules that should be
|
|
/// cross-imported.
|
|
size_t nextModuleToCrossImport = 0;
|
|
|
|
public:
|
|
ImportResolver(SourceFile &SF) : SF(SF), ctx(SF.getASTContext()) {
|
|
addImplicitImports();
|
|
}
|
|
|
|
void addImplicitImports() {
|
|
// TODO: Support cross-module imports.
|
|
for (auto &import : SF.getParentModule()->getImplicitImports()) {
|
|
assert(!(SF.Kind == SourceFileKind::SIL &&
|
|
import.Module->isStdlibModule()));
|
|
ImportedModule importedMod{ImportPath::Access(), import.Module};
|
|
boundImports.emplace_back(importedMod, import.Options);
|
|
}
|
|
}
|
|
|
|
/// Retrieve the finalized imports.
|
|
ArrayRef<ImportedModuleDesc> getFinishedImports() const {
|
|
return boundImports;
|
|
}
|
|
|
|
private:
|
|
// We only need to visit import decls.
|
|
void visitImportDecl(ImportDecl *ID);
|
|
|
|
// Ignore other decls.
|
|
void visitDecl(Decl *D) {}
|
|
|
|
template<typename ...ArgTypes>
|
|
InFlightDiagnostic diagnose(ArgTypes &&...Args) {
|
|
return ctx.Diags.diagnose(std::forward<ArgTypes>(Args)...);
|
|
}
|
|
|
|
/// Check a single unbound import, bind it, add it to \c boundImports,
|
|
/// and add its cross-import overlays to \c unboundImports.
|
|
void bindImport(UnboundImport &&I);
|
|
|
|
/// Adds \p I and \p M to \c boundImports and \c visibleModules.
|
|
void addImport(const UnboundImport &I, ModuleDecl *M);
|
|
|
|
/// Adds \p desc and everything it re-exports to \c visibleModules using
|
|
/// the settings from \c desc.
|
|
void addCrossImportableModules(ImportedModuleDesc desc);
|
|
|
|
/// * If \p I is a cross-import overlay, registers \p M as overlaying
|
|
/// \p I.underlyingModule in \c SF.
|
|
/// * Discovers any cross-imports between \p I and previously bound imports,
|
|
/// then adds them to \c unboundImports using source locations from \p I.
|
|
void crossImport(ModuleDecl *M, UnboundImport &I);
|
|
|
|
/// Discovers any cross-imports between \p newImport and
|
|
/// \p oldImports and adds them to \c unboundImports, using source
|
|
/// locations from \p I.
|
|
void findCrossImportsInLists(UnboundImport &I,
|
|
ArrayRef<ImportedModuleDesc> declaring,
|
|
ArrayRef<ImportedModuleDesc> bystanding,
|
|
bool shouldDiagnoseRedundantCrossImports);
|
|
|
|
/// Discovers any cross-imports between \p declaringImport and
|
|
/// \p bystandingImport and adds them to \c unboundImports, using source
|
|
/// locations from \p I.
|
|
void findCrossImports(UnboundImport &I,
|
|
const ImportedModuleDesc &declaringImport,
|
|
const ImportedModuleDesc &bystandingImport,
|
|
bool shouldDiagnoseRedundantCrossImports);
|
|
|
|
/// Load a module referenced by an import statement.
|
|
///
|
|
/// Returns null if no module can be loaded.
|
|
ModuleDecl *getModule(ImportPath::Module ModuleID);
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: performImportResolution
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// performImportResolution - This walks the AST to resolve imports.
|
|
///
|
|
/// Before we can type-check a source file, we need to make declarations
|
|
/// imported from other modules available. This is done by processing top-level
|
|
/// \c ImportDecl nodes, along with related validation.
|
|
///
|
|
/// Import resolution operates on a parsed but otherwise unvalidated AST.
|
|
void swift::performImportResolution(SourceFile &SF) {
|
|
// If we've already performed import resolution, bail.
|
|
if (SF.ASTStage == SourceFile::ImportsResolved)
|
|
return;
|
|
|
|
FrontendStatsTracer tracer(SF.getASTContext().Stats,
|
|
"Import resolution");
|
|
|
|
// If we're silencing parsing warnings, then also silence import warnings.
|
|
// This is necessary for secondary files as they can be parsed and have their
|
|
// imports resolved multiple times.
|
|
auto &diags = SF.getASTContext().Diags;
|
|
auto didSuppressWarnings = diags.getSuppressWarnings();
|
|
auto shouldSuppress = SF.getParsingOptions().contains(
|
|
SourceFile::ParsingFlags::SuppressWarnings);
|
|
diags.setSuppressWarnings(didSuppressWarnings || shouldSuppress);
|
|
SWIFT_DEFER { diags.setSuppressWarnings(didSuppressWarnings); };
|
|
|
|
ImportResolver resolver(SF);
|
|
|
|
// Resolve each import declaration.
|
|
for (auto D : SF.getTopLevelDecls())
|
|
resolver.visit(D);
|
|
for (auto D : SF.getHoistedDecls())
|
|
resolver.visit(D);
|
|
|
|
SF.setImports(resolver.getFinishedImports());
|
|
|
|
SF.ASTStage = SourceFile::ImportsResolved;
|
|
verify(SF);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: Import handling generally
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void ImportResolver::visitImportDecl(ImportDecl *ID) {
|
|
assert(unboundImports.empty());
|
|
|
|
unboundImports.emplace_back(ID);
|
|
while(!unboundImports.empty())
|
|
bindImport(unboundImports.pop_back_val());
|
|
}
|
|
|
|
void ImportResolver::bindImport(UnboundImport &&I) {
|
|
auto ID = I.getImportDecl();
|
|
|
|
if (!I.checkNotTautological(SF)) {
|
|
// No need to process this import further.
|
|
if (ID)
|
|
ID.get()->setModule(SF.getParentModule());
|
|
return;
|
|
}
|
|
|
|
ModuleDecl *M = getModule(I.modulePath);
|
|
if (!I.checkModuleLoaded(M, SF)) {
|
|
// Can't process further. checkModuleLoaded() will have diagnosed this.
|
|
if (ID)
|
|
ID.get()->setModule(nullptr);
|
|
return;
|
|
}
|
|
|
|
auto topLevelModule = I.getTopLevelModule(M, SF);
|
|
|
|
I.validateOptions(topLevelModule, SF);
|
|
|
|
if (topLevelModule && topLevelModule != M) {
|
|
// If we have distinct submodule and top-level module, add both.
|
|
addImport(I, M);
|
|
addImport(I, topLevelModule.get());
|
|
}
|
|
else {
|
|
// Add only the import itself.
|
|
addImport(I, M);
|
|
}
|
|
|
|
crossImport(M, I);
|
|
|
|
if (ID)
|
|
ID.get()->setModule(M);
|
|
}
|
|
|
|
void ImportResolver::addImport(const UnboundImport &I, ModuleDecl *M) {
|
|
auto importDesc = I.makeDesc(M);
|
|
addCrossImportableModules(importDesc);
|
|
boundImports.push_back(importDesc);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: Import module loading
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ModuleDecl *
|
|
ImportResolver::getModule(ImportPath::Module modulePath) {
|
|
assert(!modulePath.empty());
|
|
auto moduleID = modulePath[0];
|
|
|
|
// The Builtin module cannot be explicitly imported unless we're a .sil file.
|
|
if (SF.Kind == SourceFileKind::SIL &&
|
|
moduleID.Item == ctx.TheBuiltinModule->getName())
|
|
return ctx.TheBuiltinModule;
|
|
|
|
// If the imported module name is the same as the current module,
|
|
// skip the Swift module loader and use the Clang module loader instead.
|
|
// This allows a Swift module to extend a Clang module of the same name.
|
|
//
|
|
// FIXME: We'd like to only use this in SIL mode, but unfortunately we use it
|
|
// for clang overlays as well.
|
|
if (moduleID.Item == SF.getParentModule()->getName() &&
|
|
modulePath.size() == 1) {
|
|
if (auto importer = ctx.getClangModuleLoader())
|
|
return importer->loadModule(moduleID.Loc, modulePath);
|
|
return nullptr;
|
|
}
|
|
|
|
return ctx.getModule(modulePath);
|
|
}
|
|
|
|
NullablePtr<ModuleDecl>
|
|
UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) {
|
|
if (modulePath.size() == 1)
|
|
return M;
|
|
|
|
// If we imported a submodule, import the top-level module as well.
|
|
Identifier topLevelName = modulePath.front().Item;
|
|
ModuleDecl *topLevelModule = SF.getASTContext().getLoadedModule(topLevelName);
|
|
|
|
if (!topLevelModule) {
|
|
// Clang can sometimes import top-level modules as if they were
|
|
// submodules.
|
|
assert(!M->getFiles().empty() &&
|
|
isa<ClangModuleUnit>(M->getFiles().front()));
|
|
return M;
|
|
}
|
|
|
|
if (topLevelModule == SF.getParentModule())
|
|
// This can happen when compiling a mixed-source framework (or overlay)
|
|
// that imports a submodule of its C part.
|
|
return nullptr;
|
|
|
|
return topLevelModule;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: Implicit imports
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ArrayRef<ImplicitImport>
|
|
ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator,
|
|
ModuleDecl *module) const {
|
|
SmallVector<ImplicitImport, 4> imports;
|
|
|
|
auto &ctx = module->getASTContext();
|
|
auto &importInfo = module->getImplicitImportInfo();
|
|
|
|
// Add an implicit stdlib if needed.
|
|
switch (importInfo.StdlibKind) {
|
|
case ImplicitStdlibKind::None:
|
|
break;
|
|
case ImplicitStdlibKind::Builtin:
|
|
imports.emplace_back(ctx.TheBuiltinModule);
|
|
break;
|
|
case ImplicitStdlibKind::Stdlib: {
|
|
auto *stdlib = ctx.getStdlibModule(/*loadIfAbsent*/ true);
|
|
assert(stdlib && "Missing stdlib?");
|
|
imports.emplace_back(stdlib);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add any modules we were asked to implicitly import.
|
|
for (auto moduleName : importInfo.ModuleNames) {
|
|
auto *importModule = ctx.getModule(
|
|
ImportPath::Module::Builder(moduleName).get());
|
|
if (!importModule) {
|
|
ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import, moduleName.str());
|
|
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);
|
|
}
|
|
continue;
|
|
}
|
|
imports.emplace_back(importModule);
|
|
}
|
|
|
|
// Add any pre-loaded modules.
|
|
for (auto &module : importInfo.AdditionalModules) {
|
|
imports.emplace_back(module.first, module.second ? ImportFlags::Exported
|
|
: ImportOptions());
|
|
}
|
|
|
|
auto *clangImporter =
|
|
static_cast<ClangImporter *>(ctx.getClangModuleLoader());
|
|
|
|
// Implicitly import the bridging header module if needed.
|
|
auto bridgingHeaderPath = importInfo.BridgingHeaderPath;
|
|
if (!bridgingHeaderPath.empty() &&
|
|
!clangImporter->importBridgingHeader(bridgingHeaderPath, module)) {
|
|
auto *headerModule = clangImporter->getImportedHeaderModule();
|
|
assert(headerModule && "Didn't load bridging header?");
|
|
imports.emplace_back(headerModule, ImportFlags::Exported);
|
|
}
|
|
|
|
// Implicitly import the underlying Clang half of this module if needed.
|
|
if (importInfo.ShouldImportUnderlyingModule) {
|
|
auto *underlyingMod = clangImporter->loadModule(
|
|
SourceLoc(), ImportPath::Module::Builder(module->getName()).get());
|
|
if (underlyingMod) {
|
|
imports.emplace_back(underlyingMod, ImportFlags::Exported);
|
|
} else {
|
|
ctx.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found,
|
|
module->getName());
|
|
}
|
|
}
|
|
|
|
return ctx.AllocateCopy(imports);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: Import validation (except for scoped imports)
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Create an UnboundImport for a user-written import declaration.
|
|
UnboundImport::UnboundImport(ImportDecl *ID)
|
|
: importLoc(ID->getLoc()), options(), privateImportFileName(),
|
|
modulePath(ID->getModulePath()), accessPath(ID->getAccessPath()),
|
|
importOrUnderlyingModuleDecl(ID)
|
|
{
|
|
if (ID->isExported())
|
|
options |= ImportFlags::Exported;
|
|
|
|
if (ID->getAttrs().hasAttribute<TestableAttr>())
|
|
options |= ImportFlags::Testable;
|
|
|
|
if (ID->getAttrs().hasAttribute<ImplementationOnlyAttr>())
|
|
options |= ImportFlags::ImplementationOnly;
|
|
|
|
if (auto *privateImportAttr =
|
|
ID->getAttrs().getAttribute<PrivateImportAttr>()) {
|
|
options |= ImportFlags::PrivateImport;
|
|
privateImportFileName = privateImportAttr->getSourceFile();
|
|
}
|
|
|
|
SmallVector<Identifier, 4> spiGroups;
|
|
for (auto attr : ID->getAttrs().getAttributes<SPIAccessControlAttr>()) {
|
|
options |= SourceFile::ImportFlags::SPIAccessControl;
|
|
auto attrSPIs = attr->getSPIGroups();
|
|
spiGroups.append(attrSPIs.begin(), attrSPIs.end());
|
|
}
|
|
this->spiGroups = ID->getASTContext().AllocateCopy(spiGroups);
|
|
}
|
|
|
|
bool UnboundImport::checkNotTautological(const SourceFile &SF) {
|
|
// Exit early if this is not a self-import.
|
|
if (modulePath.front().Item != SF.getParentModule()->getName() ||
|
|
// Overlays use an @_exported self-import to load their clang module.
|
|
options.contains(ImportFlags::Exported) ||
|
|
// Imports of your own submodules are allowed in cross-language libraries.
|
|
modulePath.size() != 1 ||
|
|
// SIL files self-import to get decls from the rest of the module.
|
|
SF.Kind == SourceFileKind::SIL)
|
|
return true;
|
|
|
|
ASTContext &ctx = SF.getASTContext();
|
|
|
|
StringRef filename = llvm::sys::path::filename(SF.getFilename());
|
|
if (filename.empty())
|
|
ctx.Diags.diagnose(importLoc, diag::sema_import_current_module,
|
|
modulePath.front().Item);
|
|
else
|
|
ctx.Diags.diagnose(importLoc, diag::sema_import_current_module_with_file,
|
|
filename, modulePath.front().Item);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool UnboundImport::checkModuleLoaded(ModuleDecl *M, SourceFile &SF) {
|
|
if (M)
|
|
return true;
|
|
|
|
ASTContext &ctx = SF.getASTContext();
|
|
|
|
SmallString<64> modulePathStr;
|
|
llvm::interleave(modulePath, [&](ImportPath::Element elem) {
|
|
modulePathStr += elem.Item.str();
|
|
},
|
|
[&] { modulePathStr += "."; });
|
|
|
|
auto diagKind = diag::sema_no_import;
|
|
if (ctx.LangOpts.DebuggerSupport)
|
|
diagKind = diag::sema_no_import_repl;
|
|
ctx.Diags.diagnose(importLoc, diagKind, modulePathStr);
|
|
|
|
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);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UnboundImport::validateOptions(NullablePtr<ModuleDecl> topLevelModule,
|
|
SourceFile &SF) {
|
|
validateImplementationOnly(SF.getASTContext());
|
|
|
|
if (auto *top = topLevelModule.getPtrOrNull()) {
|
|
// FIXME: Having these two calls in this if condition seems dubious.
|
|
//
|
|
// Here's the deal: Per getTopLevelModule(), we will only skip this block
|
|
// if you are in a mixed-source module and trying to import a submodule from
|
|
// your clang half. But that means you're trying to @testable import or
|
|
// @_private import part of yourself--and, moreover, a clang part of
|
|
// yourself--which doesn't make any sense to do. Shouldn't we diagnose that?
|
|
//
|
|
// I'm leaving this alone for now because I'm trying to refactor without
|
|
// changing behavior, but it smells funny.
|
|
validateTestable(top);
|
|
validatePrivate(top);
|
|
}
|
|
|
|
validateResilience(topLevelModule, SF);
|
|
}
|
|
|
|
void UnboundImport::validatePrivate(ModuleDecl *topLevelModule) {
|
|
assert(topLevelModule);
|
|
ASTContext &ctx = topLevelModule->getASTContext();
|
|
|
|
if (!options.contains(ImportFlags::PrivateImport))
|
|
return;
|
|
|
|
if (topLevelModule->arePrivateImportsEnabled())
|
|
return;
|
|
|
|
diagnoseInvalidAttr(DAK_PrivateImport, ctx.Diags,
|
|
diag::module_not_compiled_for_private_import);
|
|
privateImportFileName = StringRef();
|
|
}
|
|
|
|
void UnboundImport::validateImplementationOnly(ASTContext &ctx) {
|
|
if (!options.contains(ImportFlags::ImplementationOnly) ||
|
|
!options.contains(ImportFlags::Exported))
|
|
return;
|
|
|
|
// Remove one flag to maintain the invariant.
|
|
options -= ImportFlags::ImplementationOnly;
|
|
|
|
diagnoseInvalidAttr(DAK_ImplementationOnly, ctx.Diags,
|
|
diag::import_implementation_cannot_be_exported);
|
|
}
|
|
|
|
void UnboundImport::validateTestable(ModuleDecl *topLevelModule) {
|
|
assert(topLevelModule);
|
|
ASTContext &ctx = topLevelModule->getASTContext();
|
|
|
|
if (!options.contains(ImportFlags::Testable) ||
|
|
topLevelModule->isTestingEnabled() ||
|
|
topLevelModule->isNonSwiftModule() ||
|
|
!ctx.LangOpts.EnableTestableAttrRequiresTestableModule)
|
|
return;
|
|
|
|
diagnoseInvalidAttr(DAK_Testable, ctx.Diags, diag::module_not_testable);
|
|
}
|
|
|
|
void UnboundImport::validateResilience(NullablePtr<ModuleDecl> topLevelModule,
|
|
SourceFile &SF) {
|
|
if (options.contains(ImportFlags::ImplementationOnly))
|
|
return;
|
|
|
|
// Per getTopLevelModule(), we'll only get nullptr here for non-Swift modules,
|
|
// so these two really mean the same thing.
|
|
if (!topLevelModule || topLevelModule.get()->isNonSwiftModule())
|
|
return;
|
|
|
|
if (!SF.getParentModule()->isResilient() ||
|
|
topLevelModule.get()->isResilient())
|
|
return;
|
|
|
|
ASTContext &ctx = SF.getASTContext();
|
|
ctx.Diags.diagnose(modulePath.front().Loc,
|
|
diag::module_not_compiled_with_library_evolution,
|
|
topLevelModule.get()->getName(),
|
|
SF.getParentModule()->getName());
|
|
// FIXME: Once @_implementationOnly is a real feature, we should have a fix-it
|
|
// to add it.
|
|
}
|
|
|
|
void UnboundImport::diagnoseInvalidAttr(DeclAttrKind attrKind,
|
|
DiagnosticEngine &diags,
|
|
Diag<Identifier> diagID) {
|
|
auto diag = diags.diagnose(modulePath.front().Loc, diagID,
|
|
modulePath.front().Item);
|
|
|
|
auto *ID = getImportDecl().getPtrOrNull();
|
|
if (!ID) return;
|
|
auto *attr = ID->getAttrs().getAttribute(attrKind);
|
|
if (!attr) return;
|
|
|
|
diag.fixItRemove(attr->getRangeWithAt());
|
|
attr->setInvalid();
|
|
}
|
|
|
|
evaluator::SideEffect
|
|
CheckInconsistentImplementationOnlyImportsRequest::evaluate(
|
|
Evaluator &evaluator, ModuleDecl *mod) const {
|
|
bool hasAnyImplementationOnlyImports =
|
|
llvm::any_of(mod->getFiles(), [](const FileUnit *F) -> bool {
|
|
auto *SF = dyn_cast<SourceFile>(F);
|
|
return SF && SF->hasImplementationOnlyImports();
|
|
});
|
|
if (!hasAnyImplementationOnlyImports)
|
|
return {};
|
|
|
|
auto diagnose = [mod](const ImportDecl *normalImport,
|
|
const ImportDecl *implementationOnlyImport) {
|
|
auto &diags = mod->getDiags();
|
|
{
|
|
InFlightDiagnostic warning =
|
|
diags.diagnose(normalImport, diag::warn_implementation_only_conflict,
|
|
normalImport->getModule()->getName());
|
|
if (normalImport->getAttrs().isEmpty()) {
|
|
// Only try to add a fix-it if there's no other annotations on the
|
|
// import to avoid creating things like
|
|
// `@_implementationOnly @_exported import Foo`. The developer can
|
|
// resolve those manually.
|
|
warning.fixItInsert(normalImport->getStartLoc(),
|
|
"@_implementationOnly ");
|
|
}
|
|
}
|
|
diags.diagnose(implementationOnlyImport,
|
|
diag::implementation_only_conflict_here);
|
|
};
|
|
|
|
llvm::DenseMap<ModuleDecl *, std::vector<const ImportDecl *>> normalImports;
|
|
llvm::DenseMap<ModuleDecl *, const ImportDecl *> implementationOnlyImports;
|
|
|
|
for (const FileUnit *file : mod->getFiles()) {
|
|
auto *SF = dyn_cast<SourceFile>(file);
|
|
if (!SF)
|
|
continue;
|
|
|
|
for (auto *topLevelDecl : SF->getTopLevelDecls()) {
|
|
auto *nextImport = dyn_cast<ImportDecl>(topLevelDecl);
|
|
if (!nextImport)
|
|
continue;
|
|
|
|
ModuleDecl *module = nextImport->getModule();
|
|
if (!module)
|
|
continue;
|
|
|
|
if (nextImport->getAttrs().hasAttribute<ImplementationOnlyAttr>()) {
|
|
// We saw an implementation-only import.
|
|
bool isNew =
|
|
implementationOnlyImports.insert({module, nextImport}).second;
|
|
if (!isNew)
|
|
continue;
|
|
|
|
auto seenNormalImportPosition = normalImports.find(module);
|
|
if (seenNormalImportPosition != normalImports.end()) {
|
|
for (auto *seenNormalImport : seenNormalImportPosition->getSecond())
|
|
diagnose(seenNormalImport, nextImport);
|
|
|
|
// We're done with these; keep the map small if possible.
|
|
normalImports.erase(seenNormalImportPosition);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// We saw a non-implementation-only import. Is that in conflict with what
|
|
// we've seen?
|
|
if (auto *seenImplementationOnlyImport =
|
|
implementationOnlyImports.lookup(module)) {
|
|
diagnose(nextImport, seenImplementationOnlyImport);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, record it for later.
|
|
normalImports[module].push_back(nextImport);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: Scoped imports
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Returns true if a decl with the given \p actual kind can legally be
|
|
/// imported via the given \p expected kind.
|
|
static bool isCompatibleImportKind(ImportKind expected, ImportKind actual) {
|
|
if (expected == actual)
|
|
return true;
|
|
if (expected != ImportKind::Type)
|
|
return false;
|
|
|
|
switch (actual) {
|
|
case ImportKind::Module:
|
|
llvm_unreachable("module imports do not bring in decls");
|
|
case ImportKind::Type:
|
|
llvm_unreachable("individual decls cannot have abstract import kind");
|
|
case ImportKind::Struct:
|
|
case ImportKind::Class:
|
|
case ImportKind::Enum:
|
|
return true;
|
|
case ImportKind::Protocol:
|
|
case ImportKind::Var:
|
|
case ImportKind::Func:
|
|
return false;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled ImportKind in switch.");
|
|
}
|
|
|
|
static bool isNominalImportKind(ImportKind kind) {
|
|
switch (kind) {
|
|
case ImportKind::Module:
|
|
llvm_unreachable("module imports do not bring in decls");
|
|
case ImportKind::Struct:
|
|
case ImportKind::Class:
|
|
case ImportKind::Enum:
|
|
case ImportKind::Protocol:
|
|
return true;
|
|
case ImportKind::Type:
|
|
case ImportKind::Var:
|
|
case ImportKind::Func:
|
|
return false;
|
|
}
|
|
llvm_unreachable("unhandled kind");
|
|
}
|
|
|
|
static const char *getImportKindString(ImportKind kind) {
|
|
switch (kind) {
|
|
case ImportKind::Module:
|
|
llvm_unreachable("module imports do not bring in decls");
|
|
case ImportKind::Type:
|
|
return "typealias";
|
|
case ImportKind::Struct:
|
|
return "struct";
|
|
case ImportKind::Class:
|
|
return "class";
|
|
case ImportKind::Enum:
|
|
return "enum";
|
|
case ImportKind::Protocol:
|
|
return "protocol";
|
|
case ImportKind::Var:
|
|
return "var";
|
|
case ImportKind::Func:
|
|
return "func";
|
|
}
|
|
|
|
llvm_unreachable("Unhandled ImportKind in switch.");
|
|
}
|
|
|
|
ArrayRef<ValueDecl *>
|
|
ScopedImportLookupRequest::evaluate(Evaluator &evaluator,
|
|
ImportDecl *import) const {
|
|
using namespace namelookup;
|
|
|
|
auto importKind = import->getImportKind();
|
|
assert(importKind != ImportKind::Module);
|
|
|
|
// If we weren't able to load the module referenced by the import, we're done.
|
|
// The fact that we failed to load the module has already been diagnosed by
|
|
// import resolution.
|
|
auto *module = import->getModule();
|
|
if (!module)
|
|
return ArrayRef<ValueDecl *>();
|
|
|
|
/// Validate the scoped import.
|
|
///
|
|
/// We validate the scope by making sure that the named declaration exists
|
|
/// and is of the kind indicated by the keyword. This can't be done until
|
|
/// we've performed import resolution, since that can introduce additional
|
|
/// imports (such as cross-import overlays) which could provide the declaration.
|
|
auto &ctx = module->getASTContext();
|
|
auto accessPath = import->getAccessPath();
|
|
auto modulePath = import->getModulePath();
|
|
auto *topLevelModule = module->getTopLevelModule();
|
|
|
|
// Lookup the referenced decl in the top-level module. This is necessary as
|
|
// the Clang importer currently handles submodules by importing their decls
|
|
// into the top-level module.
|
|
// FIXME: Doesn't handle scoped testable imports correctly.
|
|
assert(accessPath.size() == 1 && "can't handle sub-decl imports");
|
|
SmallVector<ValueDecl *, 8> decls;
|
|
lookupInModule(topLevelModule, accessPath.front().Item, decls,
|
|
NLKind::QualifiedLookup, ResolutionKind::Overloadable,
|
|
import->getDeclContext()->getModuleScopeContext());
|
|
|
|
auto importLoc = import->getLoc();
|
|
if (decls.empty()) {
|
|
ctx.Diags.diagnose(importLoc, diag::decl_does_not_exist_in_module,
|
|
static_cast<unsigned>(importKind),
|
|
accessPath.front().Item, modulePath.front().Item)
|
|
.highlight(accessPath.getSourceRange());
|
|
return ArrayRef<ValueDecl *>();
|
|
}
|
|
|
|
Optional<ImportKind> actualKind = ImportDecl::findBestImportKind(decls);
|
|
if (!actualKind.hasValue()) {
|
|
// FIXME: print entire module name?
|
|
ctx.Diags.diagnose(importLoc, diag::ambiguous_decl_in_module,
|
|
accessPath.front().Item, module->getName());
|
|
for (auto next : decls)
|
|
ctx.Diags.diagnose(next, diag::found_candidate);
|
|
|
|
} else if (!isCompatibleImportKind(importKind, *actualKind)) {
|
|
Optional<InFlightDiagnostic> emittedDiag;
|
|
if (*actualKind == ImportKind::Type && isNominalImportKind(importKind)) {
|
|
assert(decls.size() == 1 &&
|
|
"if we start suggesting ImportKind::Type for, e.g., a mix of "
|
|
"structs and classes, we'll need a different message here");
|
|
assert(isa<TypeAliasDecl>(decls.front()) &&
|
|
"ImportKind::Type is only the best choice for a typealias");
|
|
auto *typealias = cast<TypeAliasDecl>(decls.front());
|
|
emittedDiag.emplace(ctx.Diags.diagnose(
|
|
importLoc, diag::imported_decl_is_wrong_kind_typealias,
|
|
typealias->getDescriptiveKind(),
|
|
TypeAliasType::get(typealias, Type(), SubstitutionMap(),
|
|
typealias->getUnderlyingType()),
|
|
getImportKindString(importKind)));
|
|
} else {
|
|
emittedDiag.emplace(ctx.Diags.diagnose(
|
|
importLoc, diag::imported_decl_is_wrong_kind,
|
|
accessPath.front().Item, getImportKindString(importKind),
|
|
static_cast<unsigned>(*actualKind)));
|
|
}
|
|
|
|
emittedDiag->fixItReplace(SourceRange(import->getKindLoc()),
|
|
getImportKindString(*actualKind));
|
|
emittedDiag->flush();
|
|
|
|
if (decls.size() == 1)
|
|
ctx.Diags.diagnose(decls.front(), diag::decl_declared_here,
|
|
decls.front()->getName());
|
|
}
|
|
return ctx.AllocateCopy(decls);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: Cross-import overlays
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static bool canCrossImport(const ImportedModuleDesc &import) {
|
|
if (import.importOptions.contains(ImportFlags::Testable))
|
|
return false;
|
|
if (import.importOptions.contains(ImportFlags::PrivateImport))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Create an UnboundImport for a cross-import overlay.
|
|
UnboundImport::UnboundImport(ASTContext &ctx, const UnboundImport &base,
|
|
Identifier overlayName,
|
|
const ImportedModuleDesc &declaringImport,
|
|
const ImportedModuleDesc &bystandingImport)
|
|
: importLoc(base.importLoc), options(), privateImportFileName(),
|
|
// Cross-imports are not backed by an ImportDecl, so we need to provide
|
|
// our own storage for their module paths.
|
|
modulePath(
|
|
ImportPath::Module::Builder(overlayName, base.modulePath[0].Loc)
|
|
.copyTo(ctx)),
|
|
// If the declaring import was scoped, inherit that scope in the
|
|
// overlay's import.
|
|
accessPath(declaringImport.module.accessPath),
|
|
importOrUnderlyingModuleDecl(declaringImport.module.importedModule)
|
|
{
|
|
// A cross-import is never private or testable, and never comes from a private
|
|
// or testable import.
|
|
assert(canCrossImport(declaringImport));
|
|
assert(canCrossImport(bystandingImport));
|
|
|
|
auto &declaringOptions = declaringImport.importOptions;
|
|
auto &bystandingOptions = bystandingImport.importOptions;
|
|
|
|
// If both are exported, the cross-import is exported.
|
|
if (declaringOptions.contains(ImportFlags::Exported) &&
|
|
bystandingOptions.contains(ImportFlags::Exported))
|
|
options |= ImportFlags::Exported;
|
|
|
|
// If either are implementation-only, the cross-import is
|
|
// implementation-only.
|
|
if (declaringOptions.contains(ImportFlags::ImplementationOnly) ||
|
|
bystandingOptions.contains(ImportFlags::ImplementationOnly))
|
|
options |= ImportFlags::ImplementationOnly;
|
|
}
|
|
|
|
void ImportResolver::crossImport(ModuleDecl *M, UnboundImport &I) {
|
|
// FIXME: There is a fundamental problem with this find-as-we-go approach:
|
|
// The '@_exported import'-ed modules in this module's other files should be
|
|
// taken into account, but they haven't been bound yet, and binding them would
|
|
// require cross-importing. Chicken, meet egg.
|
|
//
|
|
// The way to fix this is probably to restructure import resolution so we
|
|
// first bind all exported imports in all files, then bind all other imports
|
|
// in each file. This may become simpler if we bind all ImportDecls before we
|
|
// start computing cross-imports, but I haven't figured that part out yet.
|
|
//
|
|
// Fixing this is tracked within Apple by rdar://problem/59527118. I haven't
|
|
// filed an SR because I plan to address it myself, but if this comment is
|
|
// still here in April 2020 without an SR number, please file a Swift bug and
|
|
// harass @brentdax to fill in the details.
|
|
|
|
if (!SF.shouldCrossImport())
|
|
return;
|
|
|
|
if (I.getUnderlyingModule()) {
|
|
auto underlying = I.getUnderlyingModule().get();
|
|
|
|
// If this is a clang module, and it has a clang overlay, we want the
|
|
// separately-imported overlay to sit on top of the clang overlay.
|
|
if (underlying->isNonSwiftModule())
|
|
underlying = underlying->getTopLevelModule(true);
|
|
|
|
// FIXME: Should we warn if M doesn't reexport underlyingModule?
|
|
SF.addSeparatelyImportedOverlay(M, underlying);
|
|
}
|
|
|
|
auto newImports = crossImportableModules.getArrayRef()
|
|
.drop_front(nextModuleToCrossImport);
|
|
|
|
if (newImports.empty())
|
|
// Nothing to do except crash when we read past the end of
|
|
// crossImportableModules in that assert at the bottom.
|
|
return;
|
|
|
|
for (auto &newImport : newImports) {
|
|
if (!canCrossImport(newImport))
|
|
continue;
|
|
|
|
// First we check if any of the imports of modules that have declared
|
|
// cross-imports have declared one with this module.
|
|
findCrossImportsInLists(I, crossImportDeclaringModules, {newImport},
|
|
/*shouldDiagnoseRedundantCrossImports=*/false);
|
|
|
|
// If this module doesn't declare any cross-imports, we're done with this
|
|
// import.
|
|
if (!newImport.module.importedModule->mightDeclareCrossImportOverlays())
|
|
continue;
|
|
|
|
// Fine, we need to do the slow-but-rare thing: check if this import
|
|
// declares a cross-import with any previous one.
|
|
auto oldImports =
|
|
// Slice from the start of crossImportableModules up to newImport.
|
|
llvm::makeArrayRef(crossImportableModules.getArrayRef().data(),
|
|
&newImport);
|
|
findCrossImportsInLists(I, {newImport}, oldImports,
|
|
/*shouldDiagnoseRedundantCrossImports=*/true);
|
|
|
|
// Add this to the list of imports everyone needs to check against.
|
|
crossImportDeclaringModules.push_back(newImport);
|
|
}
|
|
|
|
// Catch potential memory smashers
|
|
assert(newImports.data() ==
|
|
&crossImportableModules[nextModuleToCrossImport] &&
|
|
"findCrossImports() should never mutate visibleModules");
|
|
|
|
nextModuleToCrossImport = crossImportableModules.size();
|
|
}
|
|
|
|
void ImportResolver::findCrossImportsInLists(
|
|
UnboundImport &I, ArrayRef<ImportedModuleDesc> declaring,
|
|
ArrayRef<ImportedModuleDesc> bystanding,
|
|
bool shouldDiagnoseRedundantCrossImports) {
|
|
for (auto &declaringImport : declaring) {
|
|
if (!canCrossImport(declaringImport))
|
|
continue;
|
|
|
|
for (auto &bystandingImport : bystanding) {
|
|
if (!canCrossImport(bystandingImport))
|
|
continue;
|
|
|
|
findCrossImports(I, declaringImport, bystandingImport,
|
|
shouldDiagnoseRedundantCrossImports);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImportResolver::findCrossImports(
|
|
UnboundImport &I, const ImportedModuleDesc &declaringImport,
|
|
const ImportedModuleDesc &bystandingImport,
|
|
bool shouldDiagnoseRedundantCrossImports) {
|
|
assert(&declaringImport != &bystandingImport);
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Discovering cross-imports for '"
|
|
<< declaringImport.module.importedModule->getName()
|
|
<< "' -> '"
|
|
<< bystandingImport.module.importedModule->getName()
|
|
<< "'\n");
|
|
|
|
if (ctx.Stats)
|
|
++ctx.Stats->getFrontendCounters().NumCrossImportsChecked;
|
|
|
|
// Find modules we need to import.
|
|
SmallVector<Identifier, 4> names;
|
|
declaringImport.module.importedModule->findDeclaredCrossImportOverlays(
|
|
bystandingImport.module.importedModule->getName(), names, I.importLoc);
|
|
|
|
// If we're diagnosing cases where we cross-import in both directions, get the
|
|
// inverse list. Otherwise, leave the list empty.
|
|
SmallVector<Identifier, 4> oppositeNames;
|
|
if (shouldDiagnoseRedundantCrossImports)
|
|
bystandingImport.module.importedModule->findDeclaredCrossImportOverlays(
|
|
declaringImport.module.importedModule->getName(), oppositeNames,
|
|
I.importLoc);
|
|
|
|
if (ctx.Stats && !names.empty())
|
|
++ctx.Stats->getFrontendCounters().NumCrossImportsFound;
|
|
|
|
// Add import statements.
|
|
for (auto &name : names) {
|
|
// If we are actually compiling part of this overlay, don't try to load the
|
|
// overlay.
|
|
if (name == SF.getParentModule()->getName())
|
|
continue;
|
|
|
|
unboundImports.emplace_back(
|
|
declaringImport.module.importedModule->getASTContext(), I, name,
|
|
declaringImport, bystandingImport);
|
|
|
|
if (llvm::is_contained(oppositeNames, name))
|
|
ctx.Diags.diagnose(I.importLoc, diag::cross_imported_by_both_modules,
|
|
declaringImport.module.importedModule->getName(),
|
|
bystandingImport.module.importedModule->getName(),
|
|
name);
|
|
|
|
if (ctx.LangOpts.EnableCrossImportRemarks)
|
|
ctx.Diags.diagnose(I.importLoc, diag::cross_import_added,
|
|
declaringImport.module.importedModule->getName(),
|
|
bystandingImport.module.importedModule->getName(),
|
|
name);
|
|
|
|
LLVM_DEBUG({
|
|
auto &crossImportOptions = unboundImports.back().options;
|
|
llvm::dbgs() << " ";
|
|
if (crossImportOptions.contains(ImportFlags::Exported))
|
|
llvm::dbgs() << "@_exported ";
|
|
if (crossImportOptions.contains(ImportFlags::ImplementationOnly))
|
|
llvm::dbgs() << "@_implementationOnly ";
|
|
llvm::dbgs() << "import " << name << "\n";
|
|
});
|
|
}
|
|
}
|
|
|
|
static bool isSubmodule(ModuleDecl* M) {
|
|
auto clangMod = M->findUnderlyingClangModule();
|
|
return clangMod && clangMod->Parent;
|
|
}
|
|
|
|
void ImportResolver::addCrossImportableModules(ImportedModuleDesc importDesc) {
|
|
// FIXME: namelookup::getAllImports() doesn't quite do what we need (mainly
|
|
// w.r.t. scoped imports), but it seems like we could extend it to do so, and
|
|
// then eliminate most of this.
|
|
|
|
SmallVector<ImportedModule, 16> importsWorklist = { importDesc.module };
|
|
|
|
while (!importsWorklist.empty()) {
|
|
auto nextImport = importsWorklist.pop_back_val();
|
|
|
|
// If they are both scoped, and they are *differently* scoped, this import
|
|
// cannot possibly expose anything new. Skip it.
|
|
if (!importDesc.module.accessPath.empty() &&
|
|
!nextImport.accessPath.empty() &&
|
|
!importDesc.module.accessPath.isSameAs(nextImport.accessPath))
|
|
continue;
|
|
|
|
// If we are importing a submodule, treat it as though we imported its
|
|
// top-level module (or rather, the top-level module's clang overlay if it
|
|
// has one).
|
|
if (isSubmodule(nextImport.importedModule)) {
|
|
nextImport.importedModule =
|
|
nextImport.importedModule->getTopLevelModule(/*overlay=*/true);
|
|
|
|
// If the rewritten import is now for our own parent module, this was an
|
|
// import of our own clang submodule in a mixed-language module. We don't
|
|
// want to process our own cross-imports.
|
|
if (nextImport.importedModule == SF.getParentModule())
|
|
continue;
|
|
}
|
|
|
|
// Drop this module into the ImportDesc so we treat it as imported with the
|
|
// same options and scope as `I`.
|
|
importDesc.module.importedModule = nextImport.importedModule;
|
|
|
|
// Add it to the list of cross-importable modules. If it's already there,
|
|
// we've already done the rest of the work of this loop iteration and can
|
|
// skip it.
|
|
if (!crossImportableModules.insert(importDesc))
|
|
continue;
|
|
|
|
// We don't consider the re-exports of ObjC modules because ObjC re-exports
|
|
// everything, so there isn't enough signal there to work from.
|
|
if (nextImport.importedModule->isNonSwiftModule())
|
|
continue;
|
|
|
|
// Add the module's re-exports to worklist.
|
|
nextImport.importedModule->getImportedModules(
|
|
importsWorklist, ModuleDecl::ImportFilterKind::Public);
|
|
}
|
|
}
|
|
|
|
LLVM_ATTRIBUTE_USED static void dumpCrossImportOverlays(ModuleDecl* M) {
|
|
llvm::dbgs() << "'" << M->getName() << "' declares cross-imports with bystanders:\n";
|
|
|
|
SmallVector<Identifier, 4> secondaries;
|
|
M->getDeclaredCrossImportBystanders(secondaries);
|
|
|
|
for (auto secondary : secondaries)
|
|
llvm::dbgs() << " " << secondary << "\n";
|
|
}
|