Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions include/swift/AST/Import.h
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,20 @@ class ImportPath : public detail::ImportPathBase<ImportPath> {
Access getAccessPath(ImportKind importKind) const {
return getAccessPath(isScopedImportKind(importKind));
}

private:
struct UnsafePrivateConstructorTag {};

// Doesn't require a module name like the public constructor.
// Only used for getEmptyKey() and getTombstoneKey().
ImportPath(Raw raw, UnsafePrivateConstructorTag tag) : ImportPathBase(raw) {}
public:
static ImportPath getEmptyKey() {
return swift::ImportPath(llvm::DenseMapInfo<Raw>::getEmptyKey(), UnsafePrivateConstructorTag{});
}
static ImportPath getTombstoneKey() {
return swift::ImportPath(llvm::DenseMapInfo<Raw>::getTombstoneKey(), UnsafePrivateConstructorTag{});
}
};

// MARK: - Abstractions of imports
Expand Down Expand Up @@ -810,6 +824,27 @@ struct DenseMapInfo<swift::AttributedImport<ModuleInfo>> {
a.accessLevelRange == b.accessLevelRange;
}
};

template <>
class DenseMapInfo<swift::ImportPath> {
using ImportPath = swift::ImportPath;
public:
static ImportPath getEmptyKey() {
return swift::ImportPath::getEmptyKey();
}
static ImportPath getTombstoneKey() {
return swift::ImportPath::getTombstoneKey();
}

static unsigned getHashValue(const ImportPath &val) {
return llvm::DenseMapInfo<ImportPath::Raw>::getHashValue(val.getRaw());
}

static bool isEqual(const ImportPath &lhs,
const ImportPath &rhs) {
return lhs == rhs;
}
};
}

#endif
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,9 @@ namespace swift {
/// Enables dumping macro expansions.
bool DumpMacroExpansions = false;

/// Enables dumping imports for each SourceFile.
bool DumpSourceFileImports = false;

/// The model of concurrency to be used.
ConcurrencyModel ActiveConcurrencyModel = ConcurrencyModel::Standard;

Expand Down
10 changes: 9 additions & 1 deletion include/swift/Basic/Located.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ struct DenseMapInfo<swift::Located<T>> {
}

static unsigned getHashValue(const swift::Located<T> &LocatedVal) {
return combineHashValue(DenseMapInfo<T>::getHashValue(LocatedVal.Item),
return detail::combineHashValue(DenseMapInfo<T>::getHashValue(LocatedVal.Item),
DenseMapInfo<swift::SourceLoc>::getHashValue(LocatedVal.Loc));
}

Expand All @@ -82,4 +82,12 @@ struct DenseMapInfo<swift::Located<T>> {
};
} // namespace llvm

namespace swift {
template<typename T>
llvm::hash_code hash_value(const Located<T> &LocatedVal) {
return llvm::DenseMapInfo<Located<T>>::getHashValue(LocatedVal);
}
} // namespace swift


#endif // SWIFT_BASIC_LOCATED_H
6 changes: 6 additions & 0 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,12 @@ bool isCxxStdModule(const clang::Module *module);
/// (std_vector, std_iosfwd, etc).
bool isCxxStdModule(StringRef moduleName, bool IsSystem);

/// Build the path corresponding to the Swift wrapper for a clang module.
/// Returns ImportPath::Builder to leave control over if and where to clone the
/// path to the caller.
ImportPath::Builder getSwiftModulePath(ASTContext &SwiftContext,
const clang::Module *M);

/// Returns the pointee type if the given type is a C++ `const`
/// reference type, `None` otherwise.
std::optional<clang::QualType>
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ def dump_macro_expansions : Flag<["-"], "dump-macro-expansions">,
def emit_macro_expansion_files : Separate<["-"], "emit-macro-expansion-files">,
HelpText<"Specify when to emit macro expansion file: 'none', 'debug', or 'diagnostics'">;

def dump_source_file_imports : Flag<["-"], "dump-source-file-imports">,
HelpText<"Dumps the list of imports for each source file">;

def analyze_request_evaluator : Flag<["-"], "analyze-request-evaluator">,
Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>,
HelpText<"Print out request evaluator cache statistics at the end of the compilation job">;
Expand Down
4 changes: 1 addition & 3 deletions include/swift/Subsystems.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ namespace swift {

/// Resolve imports for a source file generated to adapt a given
/// Clang module.
void performImportResolutionForClangMacroBuffer(
SourceFile &SF, ModuleDecl *clangModule
);
void performImportResolutionForClangMacroBuffer(SourceFile &SF);

/// Once type-checking is complete, this instruments code with calls to an
/// intrinsic that record the expected values of local variables so they can
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2664,6 +2664,11 @@ void
SourceFile::setImports(ArrayRef<AttributedImport<ImportedModule>> imports) {
assert(!Imports && "Already computed imports");
Imports = getASTContext().AllocateCopy(imports);
if (getASTContext().LangOpts.DumpSourceFileImports) {
llvm::errs() << "imports for " << getFilename() << ":\n";
for (auto Import : imports)
llvm::errs() << "\t" << Import.module.importedModule->getName() << "\n";
}
}

std::optional<AttributedImport<ImportedModule>>
Expand Down
98 changes: 85 additions & 13 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2826,6 +2826,74 @@ ClangModuleUnit *ClangImporter::Implementation::getWrapperForModule(
if (auto mainModule = SwiftContext.MainModule) {
implicitImportInfo = mainModule->getImplicitImportInfo();
}

if (!underlying->isSubModule()) {
// Make sure that synthesized Swift code in the clang module wrapper
// (e.g. _SwiftifyImport macro expansions) can access the same symbols
// as if it were actually in the clang module, by copying the imports.
// Because this top-level module wrapper contains all the imported decls
// of the clang submodules, we need to add the imports of all the
// transitive submodules, since we don't know at this point of the
// compilation which submodules will contain relevant macros.
// We also need to add (transitive) explicit submodules as imports,
// to make sure that they are marked as imported *somewhere* (clang modules
// including them don't count) - otherwise their decls won't be found after
// non-visible clang decls are filtered out.
llvm::SmallVector<const clang::Module *, 32> SubmoduleWorklist;
llvm::DenseSet<ImportPath> Imported;
SubmoduleWorklist.push_back(underlying);
ImportPath::Builder underlyingSwiftModulePath =
getSwiftModulePath(underlying);
Imported.insert(underlyingSwiftModulePath.get());
for (auto UI : implicitImportInfo.AdditionalUnloadedImports)
Imported.insert(UI.module.getImportPath());
assert(implicitImportInfo.AdditionalImports.empty());

auto addImplicitImport = [&implicitImportInfo, &Imported,
this](const clang::Module *M,
bool guaranteedUnique) {
ImportPath::Builder builder = getSwiftModulePath(M);
if (!guaranteedUnique && Imported.count(builder.get()))
return;

// Don't perform this clone for modules already added to the list
ImportPath importedModulePath = builder.copyTo(SwiftContext);

#ifndef NDEBUG
const bool performSanityCheck = true;
#else
const bool performSanityCheck = false;
#endif
if (!guaranteedUnique || performSanityCheck) {
bool WasInserted = Imported.insert(importedModulePath).second;
assert(WasInserted);
}

UnloadedImportedModule importedModule(importedModulePath,
ImportKind::Module);
implicitImportInfo.AdditionalUnloadedImports.push_back(
std::move(importedModule));
};

while (!SubmoduleWorklist.empty()) {
const clang::Module *CurrModule = SubmoduleWorklist.pop_back_val();
if (CurrModule->IsExplicit) {
// We don't add imports under the same TLM, and submodules form
// a tree, so these don't require deduplication.
addImplicitImport(CurrModule, /*guaranteedUnique=*/true);
}
for (auto *I : CurrModule->Imports) {
// `underlying` is the current TLM. Only explicit submodules need to
// be imported under the same TLM, which is handled above.
if (I->getTopLevelModule() == underlying)
continue;
addImplicitImport(I, /*guaranteedUnique=*/false);
}
for (auto *Submodule : CurrModule->submodules())
SubmoduleWorklist.push_back(Submodule);
}
}

ClangModuleUnit *file = nullptr;
auto wrapper = ModuleDecl::create(name, SwiftContext, implicitImportInfo,
[&](ModuleDecl *wrapper, auto addFile) {
Expand Down Expand Up @@ -3718,19 +3786,7 @@ ImportDecl *swift::createImportDecl(ASTContext &Ctx,
auto *ImportedMod = ClangN.getClangModule();
assert(ImportedMod);

ImportPath::Builder importPath;
auto *TmpMod = ImportedMod;
while (TmpMod) {
// If this is a C++ stdlib module, print its name as `CxxStdlib` instead of
// `std`. `CxxStdlib` is the only accepted spelling of the C++ stdlib module
// name in Swift.
Identifier moduleName = !TmpMod->isSubModule() && TmpMod->Name == "std"
? Ctx.Id_CxxStdlib
: Ctx.getIdentifier(TmpMod->Name);
importPath.push_back(moduleName);
TmpMod = TmpMod->Parent;
}
std::reverse(importPath.begin(), importPath.end());
ImportPath::Builder importPath = getSwiftModulePath(Ctx, ImportedMod);

bool IsExported = false;
for (auto *ExportedMod : Exported) {
Expand Down Expand Up @@ -8665,6 +8721,22 @@ bool importer::isCxxStdModule(StringRef moduleName, bool IsSystem) {
return false;
}

ImportPath::Builder importer::getSwiftModulePath(ASTContext &SwiftContext, const clang::Module *M) {
if (isCxxStdModule(M))
return ImportPath::Builder(SwiftContext.Id_CxxStdlib);
ImportPath::Builder builder;
while (M) {
builder.push_back(SwiftContext.getIdentifier(M->Name));
M = M->Parent;
}
std::reverse(builder.begin(), builder.end());
return builder;
}

ImportPath::Builder ClangImporter::Implementation::getSwiftModulePath(const clang::Module *M) {
return ::getSwiftModulePath(SwiftContext, M);
}

std::optional<clang::QualType>
importer::getCxxReferencePointeeTypeOrNone(const clang::Type *type) {
if (type->isReferenceType())
Expand Down
2 changes: 2 additions & 0 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
/// \returns The named module, or null if the module has not been imported.
ModuleDecl *getNamedModule(StringRef name);

ImportPath::Builder getSwiftModulePath(const clang::Module *M);

/// Returns the "Foundation" module, if it can be loaded.
///
/// After this has been called, the Foundation module will or won't be loaded
Expand Down
3 changes: 3 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1734,6 +1734,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.DumpMacroExpansions = Args.hasArg(
OPT_dump_macro_expansions);

Opts.DumpSourceFileImports = Args.hasArg(
OPT_dump_source_file_imports);

if (const Arg *A = Args.getLastArg(OPT_debug_requirement_machine))
Opts.DebugRequirementMachine = A->getValue();

Expand Down
4 changes: 4 additions & 0 deletions lib/IDE/ModuleInterfacePrinting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ static bool shouldPrintImport(ImportDecl *ImportD, ModuleDecl *OrigMod,
return true;
if (ImportedClangMod == OrigClangMod)
return false;
// We don't want to print modules that just happen to be part of our TLM,
// but submodules from other TLMs should still be included
if (!ImportedClangMod->isSubModuleOf(OrigClangMod->getTopLevelModule()))
return true;
return ImportedClangMod->isSubModuleOf(OrigClangMod);
}

Expand Down
38 changes: 18 additions & 20 deletions lib/Sema/ImportResolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ class ImportResolver final : public DeclVisitor<ImportResolver> {
/// The list of fully bound imports.
SmallVector<AttributedImport<ImportedModule>, 16> boundImports;

/// Set of imported top-level clang modules. We normally don't expect
/// duplicated imports, but importing multiple submodules of the same clang
/// TLM would cause the same TLM to be imported once per submodule.
SmallPtrSet<const ModuleDecl*, 16> seenClangTLMs;

/// 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.
Expand Down Expand Up @@ -319,21 +324,12 @@ void swift::performImportResolution(SourceFile &SF) {
verify(SF);
}

void swift::performImportResolutionForClangMacroBuffer(
SourceFile &SF, ModuleDecl *clangModule
) {
// If we've already performed import resolution, bail.
if (SF.ASTStage == SourceFile::ImportsResolved)
return;
void swift::performImportResolutionForClangMacroBuffer(SourceFile &SF) {
assert(SF.ASTStage == SourceFile::Unprocessed);

// `getWrapperForModule` has already declared all the implicit clang module
// imports we need
ImportResolver resolver(SF);
resolver.addImplicitImport(clangModule);

// FIXME: This is a hack that we shouldn't need, but be sure that we can
// see the Swift standard library.
if (auto stdlib = SF.getASTContext().getStdlibModule())
resolver.addImplicitImport(stdlib);

SF.setImports(resolver.getFinishedImports());
SF.setImportedUnderlyingModule(resolver.getUnderlyingClangModule());

Expand Down Expand Up @@ -387,14 +383,16 @@ void ImportResolver::bindImport(UnboundImport &&I) {

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.
if (!M->isNonSwiftModule() || topLevelModule != M ||
seenClangTLMs.insert(M).second) {
addImport(I, M);
if (topLevelModule && topLevelModule != M &&
seenClangTLMs.insert(topLevelModule.get()).second) {
// If we have distinct submodule and top-level module, add both.
// Importing the submodule ensures that it gets loaded, but the decls
// are imported to the TLM, so import that for visibility.
addImport(I, topLevelModule.get());
}
}

crossImport(M, I);
Expand Down
6 changes: 2 additions & 4 deletions lib/Sema/TypeCheckMacros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1060,10 +1060,8 @@ createMacroSourceFile(std::unique_ptr<llvm::MemoryBuffer> buffer,
/*parsingOpts=*/{}, /*isPrimary=*/false);
if (auto parentSourceFile = dc->getParentSourceFile())
macroSourceFile->setImports(parentSourceFile->getImports());
else if (auto clangModuleUnit =
dyn_cast<ClangModuleUnit>(dc->getModuleScopeContext())) {
auto clangModule = clangModuleUnit->getParentModule();
performImportResolutionForClangMacroBuffer(*macroSourceFile, clangModule);
else if (isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
performImportResolutionForClangMacroBuffer(*macroSourceFile);
}
return macroSourceFile;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

typedef int a_t;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

typedef int b_t;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
typedef int c_t;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
typedef int d_t;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
typedef int e_t;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module ModuleA {
header "module-a.h"
export *
}
module ModuleB {
header "module-b.h"
}
module ModuleOuter {
module ModuleC {
header "module-c.h"
export *
}
explicit module ModuleD {
header "module-d.h"
export *
}
}
module ModuleDeep {
module ModuleDeepNested {
module ModuleDeepNestedNested {
header "module-e.h"
}
}
}
Loading