mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[cxx-interop] Import decls in extern blocks within namespaces
This teaches ClangImporter to import C++ decls that are declared within `extern "C" { ... }`/`extern "C++" { ... }` blocks which are nested in namespaces.
rdar://139067788
This commit is contained in:
@@ -2738,72 +2738,87 @@ static void addNamespaceMembers(Decl *decl,
|
||||
if (declOwner && declOwner != redeclOwner->getTopLevelModule())
|
||||
continue;
|
||||
}
|
||||
for (auto member : redecl->decls()) {
|
||||
if (auto classTemplate = dyn_cast<clang::ClassTemplateDecl>(member)) {
|
||||
// Add all specializations to a worklist so we don't accidentally mutate
|
||||
// the list of decls we're iterating over.
|
||||
llvm::SmallPtrSet<const clang::ClassTemplateSpecializationDecl *, 16> specWorklist;
|
||||
for (auto spec : classTemplate->specializations())
|
||||
specWorklist.insert(spec);
|
||||
for (auto spec : specWorklist) {
|
||||
if (auto import =
|
||||
ctx.getClangModuleLoader()->importDeclDirectly(spec))
|
||||
if (addedMembers.insert(import).second)
|
||||
members.push_back(import);
|
||||
}
|
||||
}
|
||||
|
||||
auto lookupAndAddMembers = [&](DeclName name) {
|
||||
auto allResults = evaluateOrDefault(
|
||||
ctx.evaluator, ClangDirectLookupRequest({decl, redecl, name}), {});
|
||||
std::function<void(clang::DeclContext *)> addDeclsFromContext =
|
||||
[&](clang::DeclContext *declContext) {
|
||||
for (auto member : declContext->decls()) {
|
||||
if (auto classTemplate =
|
||||
dyn_cast<clang::ClassTemplateDecl>(member)) {
|
||||
// Add all specializations to a worklist so we don't accidentally
|
||||
// mutate the list of decls we're iterating over.
|
||||
llvm::SmallPtrSet<const clang::ClassTemplateSpecializationDecl *,
|
||||
16>
|
||||
specWorklist;
|
||||
for (auto spec : classTemplate->specializations())
|
||||
specWorklist.insert(spec);
|
||||
for (auto spec : specWorklist) {
|
||||
if (auto import =
|
||||
ctx.getClangModuleLoader()->importDeclDirectly(spec))
|
||||
if (addedMembers.insert(import).second)
|
||||
members.push_back(import);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto found : allResults) {
|
||||
auto clangMember = cast<clang::NamedDecl *>(found);
|
||||
if (auto importedDecl =
|
||||
ctx.getClangModuleLoader()->importDeclDirectly(clangMember)) {
|
||||
if (addedMembers.insert(importedDecl).second) {
|
||||
members.push_back(importedDecl);
|
||||
auto lookupAndAddMembers = [&](clang::NamedDecl *namedDecl) {
|
||||
auto name = ctx.getClangModuleLoader()->importName(namedDecl);
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
// Handle macro-expanded declarations.
|
||||
importedDecl->visitAuxiliaryDecls([&](Decl *decl) {
|
||||
auto valueDecl = dyn_cast<ValueDecl>(decl);
|
||||
if (!valueDecl)
|
||||
return;
|
||||
auto allResults = evaluateOrDefault(
|
||||
ctx.evaluator, ClangDirectLookupRequest({decl, redecl, name}),
|
||||
{});
|
||||
|
||||
// Bail out if the auxiliary decl was not produced by a macro.
|
||||
auto module = decl->getDeclContext()->getParentModule();
|
||||
auto *sf = module->getSourceFileContainingLocation(decl->getLoc());
|
||||
if (!sf || sf->Kind != SourceFileKind::MacroExpansion)
|
||||
return;
|
||||
for (auto found : allResults) {
|
||||
auto clangMember = cast<clang::NamedDecl *>(found);
|
||||
if (auto importedDecl =
|
||||
ctx.getClangModuleLoader()->importDeclDirectly(
|
||||
clangMember)) {
|
||||
if (addedMembers.insert(importedDecl).second) {
|
||||
members.push_back(importedDecl);
|
||||
|
||||
members.push_back(valueDecl);
|
||||
});
|
||||
// Handle macro-expanded declarations.
|
||||
importedDecl->visitAuxiliaryDecls([&](Decl *decl) {
|
||||
auto valueDecl = dyn_cast<ValueDecl>(decl);
|
||||
if (!valueDecl)
|
||||
return;
|
||||
|
||||
// Bail out if the auxiliary decl was not produced by a
|
||||
// macro.
|
||||
auto module = decl->getDeclContext()->getParentModule();
|
||||
auto *sf = module->getSourceFileContainingLocation(
|
||||
decl->getLoc());
|
||||
if (!sf || sf->Kind != SourceFileKind::MacroExpansion)
|
||||
return;
|
||||
|
||||
members.push_back(valueDecl);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Look through `extern` blocks.
|
||||
if (auto linkageSpecDecl = dyn_cast<clang::LinkageSpecDecl>(member))
|
||||
addDeclsFromContext(linkageSpecDecl);
|
||||
|
||||
auto namedDecl = dyn_cast<clang::NamedDecl>(member);
|
||||
if (!namedDecl)
|
||||
continue;
|
||||
lookupAndAddMembers(namedDecl);
|
||||
|
||||
// Unscoped enums could have their enumerators present
|
||||
// in the parent namespace.
|
||||
if (auto *ed = dyn_cast<clang::EnumDecl>(member)) {
|
||||
if (!ed->isScoped()) {
|
||||
for (auto *ecd : ed->enumerators()) {
|
||||
lookupAndAddMembers(ecd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
auto namedDecl = dyn_cast<clang::NamedDecl>(member);
|
||||
if (!namedDecl)
|
||||
continue;
|
||||
auto name = ctx.getClangModuleLoader()->importName(namedDecl);
|
||||
if (!name)
|
||||
continue;
|
||||
lookupAndAddMembers(name);
|
||||
|
||||
// Unscoped enums could have their enumerators present
|
||||
// in the parent namespace.
|
||||
if (auto *ed = dyn_cast<clang::EnumDecl>(member)) {
|
||||
if (!ed->isScoped()) {
|
||||
for (const auto *ecd : ed->enumerators()) {
|
||||
auto name = ctx.getClangModuleLoader()->importName(ecd);
|
||||
if (!name)
|
||||
continue;
|
||||
lookupAndAddMembers(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addDeclsFromContext(redecl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5060,6 +5060,11 @@ static bool isDirectLookupMemberContext(const clang::Decl *foundClangDecl,
|
||||
return firstDecl->getCanonicalDecl() == parent->getCanonicalDecl();
|
||||
}
|
||||
}
|
||||
// Look through `extern` blocks.
|
||||
if (auto linkageSpecDecl = dyn_cast<clang::LinkageSpecDecl>(memberContext)) {
|
||||
if (auto parentDecl = dyn_cast<clang::Decl>(linkageSpecDecl->getParent()))
|
||||
return isDirectLookupMemberContext(foundClangDecl, parentDecl, parent);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1154,11 +1154,14 @@ namespace {
|
||||
return nullptr;
|
||||
// If this is a top-level namespace, don't put it in the module we're
|
||||
// importing, put it in the "__ObjC" module that is implicitly imported.
|
||||
if (!decl->getParent()->isNamespace())
|
||||
auto clangDC = decl->getDeclContext();
|
||||
while (isa<clang::LinkageSpecDecl>(clangDC))
|
||||
clangDC = clangDC->getParent();
|
||||
if (!clangDC->isNamespace())
|
||||
dc = Impl.ImportedHeaderUnit;
|
||||
else {
|
||||
// This is a nested namespace, so just lookup it's parent normally.
|
||||
auto parentNS = cast<clang::NamespaceDecl>(decl->getParent());
|
||||
auto parentNS = cast<clang::NamespaceDecl>(clangDC);
|
||||
auto parent =
|
||||
Impl.importDecl(parentNS, getVersion(), /*UseCanonicalDecl*/ false);
|
||||
// The parent namespace might not be imported if it's `swift_private`.
|
||||
|
||||
@@ -2034,6 +2034,20 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
|
||||
namedMember = def;
|
||||
addEntryToLookupTable(table, namedMember, nameImporter);
|
||||
}
|
||||
if (auto linkageSpecDecl =
|
||||
dyn_cast<clang::LinkageSpecDecl>(canonicalMember)) {
|
||||
std::function<void(clang::DeclContext *)> addDeclsFromContext =
|
||||
[&](clang::DeclContext *declContext) {
|
||||
for (auto nestedDecl : declContext->decls()) {
|
||||
if (auto namedMember = dyn_cast<clang::NamedDecl>(nestedDecl))
|
||||
addEntryToLookupTable(table, namedMember, nameImporter);
|
||||
else if (auto nestedLinkageSpecDecl =
|
||||
dyn_cast<clang::LinkageSpecDecl>(nestedDecl))
|
||||
addDeclsFromContext(nestedLinkageSpecDecl);
|
||||
}
|
||||
};
|
||||
addDeclsFromContext(linkageSpecDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto usingDecl = dyn_cast<clang::UsingDecl>(named)) {
|
||||
|
||||
28
test/Interop/Cxx/namespace/Inputs/extern-within-namespace.h
Normal file
28
test/Interop/Cxx/namespace/Inputs/extern-within-namespace.h
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Outer {
|
||||
namespace Inner {
|
||||
extern "C" {
|
||||
int foobar() { return 123; }
|
||||
struct NestedType {
|
||||
char c;
|
||||
};
|
||||
}
|
||||
} // namespace Inner
|
||||
|
||||
inline namespace InnerInline {
|
||||
extern "C" {
|
||||
int baz() { return 321; }
|
||||
}
|
||||
} // namespace InnerInline
|
||||
} // namespace Outer
|
||||
|
||||
namespace ExternWithinExtern {
|
||||
extern "C" {
|
||||
extern "C++" {
|
||||
namespace Inner {
|
||||
extern "C" {
|
||||
int deep() { return 42; }
|
||||
}
|
||||
} // namespace Inner
|
||||
}
|
||||
}
|
||||
} // namespace ExternWithinExtern
|
||||
@@ -12,6 +12,12 @@ module ClassesSecondHeader {
|
||||
requires cplusplus
|
||||
}
|
||||
|
||||
module ExternWithinNamespace {
|
||||
header "extern-within-namespace.h"
|
||||
export *
|
||||
requires cplusplus
|
||||
}
|
||||
|
||||
module FreeFunctions {
|
||||
header "free-functions.h"
|
||||
requires cplusplus
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=ExternWithinNamespace -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift | %FileCheck %s
|
||||
|
||||
// CHECK: enum Outer {
|
||||
// CHECK-NEXT: enum Inner {
|
||||
// CHECK-NEXT: static func foobar()
|
||||
// CHECK-NEXT: struct NestedType {
|
||||
// CHECK: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: enum InnerInline {
|
||||
// CHECK-NEXT: static func baz() -> Int32
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: enum ExternWithinExtern {
|
||||
// CHECK-NEXT: enum Inner {
|
||||
// CHECK-NEXT: static func deep() -> Int32
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
25
test/Interop/Cxx/namespace/extern-within-namespace.swift
Normal file
25
test/Interop/Cxx/namespace/extern-within-namespace.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=upcoming-swift)
|
||||
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import StdlibUnittest
|
||||
import ExternWithinNamespace
|
||||
|
||||
var ExternTestSuite = TestSuite("Extern block within namespaces")
|
||||
|
||||
ExternTestSuite.test("Function within extern block within namespace") {
|
||||
let r = Outer.Inner.foobar()
|
||||
expectEqual(r, 123)
|
||||
}
|
||||
|
||||
ExternTestSuite.test("Function within extern block within inline namespace") {
|
||||
let r = Outer.InnerInline.baz()
|
||||
expectEqual(r, 321)
|
||||
}
|
||||
|
||||
ExternTestSuite.test("Function within extern block within extern block") {
|
||||
let r = ExternWithinExtern.Inner.deep()
|
||||
expectEqual(r, 42)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
Reference in New Issue
Block a user