Skip to content

[ClangImporter] Handle submodule imports in bridging headers #14487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
108 changes: 55 additions & 53 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ namespace {
void handleImport(const clang::Module *imported) {
if (!imported)
return;
Impl.DeferredHeaderImports.push_back(imported);
Impl.ImportedHeaderExports.push_back(
const_cast<clang::Module *>(imported));
}

void InclusionDirective(clang::SourceLocation HashLoc,
Expand Down Expand Up @@ -1596,16 +1597,15 @@ ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule(
void ClangImporter::Implementation::handleDeferredImports()
{
clang::ASTReader &R = *Instance->getModuleManager();
llvm::SmallSet<clang::serialization::SubmoduleID, 32> seenSubmodules;
for (clang::serialization::SubmoduleID ID : PCHImportedSubmodules) {
DeferredHeaderImports.push_back(R.getSubmodule(ID));
if (!seenSubmodules.insert(ID).second)
continue;
ImportedHeaderExports.push_back(R.getSubmodule(ID));
}
PCHImportedSubmodules.clear();
for (const clang::Module *M : DeferredHeaderImports) {
ModuleDecl *nativeImported =
finishLoadingClangModule(M, /*preferAdapter=*/true);
ImportedHeaderExports.push_back({ /*filter=*/{}, nativeImported });
}
DeferredHeaderImports.clear();
for (const clang::Module *M : ImportedHeaderExports)
(void)finishLoadingClangModule(M, /*preferAdapter=*/true);
}

ModuleDecl *ClangImporter::getImportedHeaderModule() const {
Expand Down Expand Up @@ -3015,45 +3015,44 @@ void ClangModuleUnit::getImportedModules(
if (filter != ModuleDecl::ImportFilter::Public)
imports.push_back({ModuleDecl::AccessPathTy(), owner.getStdlibModule()});

SmallVector<clang::Module *, 8> imported;
if (!clangModule) {
// This is the special "imported headers" module.
if (filter != ModuleDecl::ImportFilter::Private) {
imports.append(owner.ImportedHeaderExports.begin(),
owner.ImportedHeaderExports.end());
imported.append(owner.ImportedHeaderExports.begin(),
owner.ImportedHeaderExports.end());
}
return;
}

auto topLevelAdapter = getAdapterModule();
} else {
clangModule->getExportedModules(imported);
if (filter != ModuleDecl::ImportFilter::Public) {
if (filter == ModuleDecl::ImportFilter::All) {
llvm::SmallPtrSet<clang::Module *, 8> knownModules;
imported.append(clangModule->Imports.begin(), clangModule->Imports.end());
imported.erase(std::remove_if(imported.begin(), imported.end(),
[&](clang::Module *mod) -> bool {
return !knownModules.insert(mod).second;
}),
imported.end());
} else {
llvm::SmallPtrSet<clang::Module *, 8> knownModules(imported.begin(),
imported.end());
SmallVector<clang::Module *, 8> privateImports;
std::copy_if(clangModule->Imports.begin(), clangModule->Imports.end(),
std::back_inserter(privateImports), [&](clang::Module *mod) {
return knownModules.count(mod) == 0;
});
imported.swap(privateImports);
}

SmallVector<clang::Module *, 8> imported;
clangModule->getExportedModules(imported);
if (filter != ModuleDecl::ImportFilter::Public) {
if (filter == ModuleDecl::ImportFilter::All) {
llvm::SmallPtrSet<clang::Module *, 8> knownModules;
imported.append(clangModule->Imports.begin(), clangModule->Imports.end());
imported.erase(std::remove_if(imported.begin(), imported.end(),
[&](clang::Module *mod) -> bool {
return !knownModules.insert(mod).second;
}),
imported.end());
} else {
llvm::SmallPtrSet<clang::Module *, 8> knownModules(imported.begin(),
imported.end());
SmallVector<clang::Module *, 8> privateImports;
std::copy_if(clangModule->Imports.begin(), clangModule->Imports.end(),
std::back_inserter(privateImports), [&](clang::Module *mod) {
return knownModules.count(mod) == 0;
});
imported.swap(privateImports);
// FIXME: The parent module isn't exactly a private import, but it is
// needed for link dependencies.
if (clangModule->Parent)
imported.push_back(clangModule->Parent);
}

// FIXME: The parent module isn't exactly a private import, but it is
// needed for link dependencies.
if (clangModule->Parent)
imported.push_back(clangModule->Parent);
}

auto topLevelAdapter = getAdapterModule();
for (auto importMod : imported) {
auto wrapper = owner.getWrapperForModule(importMod);

Expand All @@ -3062,11 +3061,12 @@ void ClangModuleUnit::getImportedModules(
// HACK: Deal with imports of submodules by importing the top-level module
// as well.
auto importTopLevel = importMod->getTopLevelModule();
if (importTopLevel != importMod &&
importTopLevel != clangModule->getTopLevelModule()) {
auto topLevelWrapper = owner.getWrapperForModule(importTopLevel);
imports.push_back({ ModuleDecl::AccessPathTy(),
topLevelWrapper->getParentModule() });
if (importTopLevel != importMod) {
if (!clangModule || importTopLevel != clangModule->getTopLevelModule()){
auto topLevelWrapper = owner.getWrapperForModule(importTopLevel);
imports.push_back({ ModuleDecl::AccessPathTy(),
topLevelWrapper->getParentModule() });
}
}
actualMod = wrapper->getParentModule();
} else if (actualMod == topLevelAdapter) {
Expand All @@ -3081,13 +3081,6 @@ void ClangModuleUnit::getImportedModules(
void ClangModuleUnit::getImportedModulesForLookup(
SmallVectorImpl<ModuleDecl::ImportedModule> &imports) const {

if (!clangModule) {
// This is the special "imported headers" module.
imports.append(owner.ImportedHeaderExports.begin(),
owner.ImportedHeaderExports.end());
return;
}

// Reuse our cached list of imports if we have one.
if (!importedModulesForLookup.empty()) {
imports.append(importedModulesForLookup.begin(),
Expand All @@ -3096,11 +3089,20 @@ void ClangModuleUnit::getImportedModulesForLookup(
}

size_t firstImport = imports.size();
auto topLevel = clangModule->getTopLevelModule();
auto topLevelAdapter = getAdapterModule();

SmallVector<clang::Module *, 8> imported;
clangModule->getExportedModules(imported);
const clang::Module *topLevel;
ModuleDecl *topLevelAdapter = getAdapterModule();
if (!clangModule) {
// This is the special "imported headers" module.
imported.append(owner.ImportedHeaderExports.begin(),
owner.ImportedHeaderExports.end());
topLevel = nullptr;
} else {
clangModule->getExportedModules(imported);
topLevel = clangModule->getTopLevelModule();
}

if (imported.empty())
return;

Expand Down
3 changes: 1 addition & 2 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation

bool IsReadingBridgingPCH;
llvm::SmallVector<clang::serialization::SubmoduleID, 2> PCHImportedSubmodules;
llvm::SmallVector<const clang::Module*, 2> DeferredHeaderImports;

const Version CurrentVersion;

Expand Down Expand Up @@ -529,7 +528,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
ClangModuleUnit *ImportedHeaderUnit = nullptr;

/// The modules re-exported by imported headers.
llvm::SmallVector<ModuleDecl::ImportedModule, 8> ImportedHeaderExports;
llvm::SmallVector<clang::Module *, 8> ImportedHeaderExports;

/// The modules that requested imported headers.
///
Expand Down
1 change: 1 addition & 0 deletions test/ClangImporter/Inputs/submodules-bridging-header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <ctypes/bits.h>
14 changes: 7 additions & 7 deletions test/ClangImporter/pch-bridging-header.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@

// First test the explicit frontend-based bridging PCH generation and use works
// RUN: %target-swift-frontend -emit-pch -o %t/sdk-bridging-header.pch %S/Inputs/sdk-bridging-header.h
// RUN: %target-swift-frontend -parse -verify %s -import-objc-header %t/sdk-bridging-header.pch
// RUN: %target-swift-frontend -typecheck -verify %s -import-objc-header %t/sdk-bridging-header.pch

// Now test the driver-automated version is inert when disabled
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse -disable-bridging-pch -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -typecheck -disable-bridging-pch -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h
// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1

// Test the driver-automated version works by default
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -typecheck -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h
// RUN: ls %t/tmp/*.pch >/dev/null 2>&1
// RUN: llvm-objdump -raw-clang-ast %t/tmp/*.pch | llvm-bcanalyzer -dump | %FileCheck %s
// CHECK: ORIGINAL_FILE{{.*}}Inputs/sdk-bridging-header.h

// Test the driver-automated version deletes its PCH file when done
// RUN: rm %t/tmp/*.pch
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse %s -import-objc-header %S/Inputs/sdk-bridging-header.h
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -typecheck %s -import-objc-header %S/Inputs/sdk-bridging-header.h
// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1

// Test -emit-pch invocation but with a persistent PCH
// RUN: %target-swift-frontend -emit-pch -pch-output-dir %t/pch %S/Inputs/sdk-bridging-header.h
// RUN: %target-swift-frontend -parse -verify %s -import-objc-header %S/Inputs/sdk-bridging-header.h -pch-output-dir %t/pch -pch-disable-validation
// RUN: %target-swift-frontend -typecheck -verify %s -import-objc-header %S/Inputs/sdk-bridging-header.h -pch-output-dir %t/pch -pch-disable-validation
// RUN: ls %t/pch/*.pch >/dev/null 2>&1

// Test implicit use of persistent PCH
// RUN: %target-swift-frontend -parse -verify %s -import-objc-header %S/Inputs/sdk-bridging-header.h -pch-output-dir %t/pch2
// RUN: %target-swift-frontend -typecheck -verify %s -import-objc-header %S/Inputs/sdk-bridging-header.h -pch-output-dir %t/pch2
// RUN: ls %t/pch2/*.pch >/dev/null 2>&1

// RUN: touch %t/header.with.dot.h
Expand All @@ -37,7 +37,7 @@
// RUN: ls %t/pch_with_dot/*swift*clang*.pch | count 2

// Test the driver-automated version using persistent PCH
// RUN: %target-swiftc_driver -parse -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h -pch-output-dir %t/pch3
// RUN: %target-swiftc_driver -typecheck -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h -pch-output-dir %t/pch3
// RUN: ls %t/pch3/*.pch >/dev/null 2>&1
// RUN: llvm-objdump -raw-clang-ast %t/pch3/*.pch | llvm-bcanalyzer -dump | %FileCheck %s -check-prefix=PERSISTENT
// PERSISTENT: ORIGINAL_FILE{{.*}}Inputs/sdk-bridging-header.h
Expand Down
11 changes: 11 additions & 0 deletions test/ClangImporter/submodules-bridging-header.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules/ -import-objc-header %S/Inputs/submodules-bridging-header.h %s

// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-pch -o %t/submodules-bridging-header.pch %S/Inputs/submodules-bridging-header.h
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules/ -import-objc-header %t/submodules-bridging-header.pch %s

// From ctypes.bits submodule
public var x : DWORD = MY_INT
public var y : CInt = x
public var z : DWORD = "" // expected-error {{cannot convert value}}