Skip to content

Commit 4adccbd

Browse files
authored
Merge pull request #83516 from egorzhdan/egorzhdan/extern-within-namespace
[cxx-interop] Import decls in extern blocks within namespaces
2 parents 14e5ba6 + 844787f commit 4adccbd

File tree

8 files changed

+176
-63
lines changed

8 files changed

+176
-63
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 76 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,72 +2738,87 @@ static void addNamespaceMembers(Decl *decl,
27382738
if (declOwner && declOwner != redeclOwner->getTopLevelModule())
27392739
continue;
27402740
}
2741-
for (auto member : redecl->decls()) {
2742-
if (auto classTemplate = dyn_cast<clang::ClassTemplateDecl>(member)) {
2743-
// Add all specializations to a worklist so we don't accidentally mutate
2744-
// the list of decls we're iterating over.
2745-
llvm::SmallPtrSet<const clang::ClassTemplateSpecializationDecl *, 16> specWorklist;
2746-
for (auto spec : classTemplate->specializations())
2747-
specWorklist.insert(spec);
2748-
for (auto spec : specWorklist) {
2749-
if (auto import =
2750-
ctx.getClangModuleLoader()->importDeclDirectly(spec))
2751-
if (addedMembers.insert(import).second)
2752-
members.push_back(import);
2753-
}
2754-
}
27552741

2756-
auto lookupAndAddMembers = [&](DeclName name) {
2757-
auto allResults = evaluateOrDefault(
2758-
ctx.evaluator, ClangDirectLookupRequest({decl, redecl, name}), {});
2759-
2760-
for (auto found : allResults) {
2761-
auto clangMember = cast<clang::NamedDecl *>(found);
2762-
if (auto importedDecl =
2763-
ctx.getClangModuleLoader()->importDeclDirectly(clangMember)) {
2764-
if (addedMembers.insert(importedDecl).second) {
2765-
members.push_back(importedDecl);
2766-
2767-
// Handle macro-expanded declarations.
2768-
importedDecl->visitAuxiliaryDecls([&](Decl *decl) {
2769-
auto valueDecl = dyn_cast<ValueDecl>(decl);
2770-
if (!valueDecl)
2771-
return;
2772-
2773-
// Bail out if the auxiliary decl was not produced by a macro.
2774-
auto module = decl->getDeclContext()->getParentModule();
2775-
auto *sf = module->getSourceFileContainingLocation(decl->getLoc());
2776-
if (!sf || sf->Kind != SourceFileKind::MacroExpansion)
2777-
return;
2778-
2779-
members.push_back(valueDecl);
2780-
});
2742+
std::function<void(clang::DeclContext *)> addDeclsFromContext =
2743+
[&](clang::DeclContext *declContext) {
2744+
for (auto member : declContext->decls()) {
2745+
if (auto classTemplate =
2746+
dyn_cast<clang::ClassTemplateDecl>(member)) {
2747+
// Add all specializations to a worklist so we don't accidentally
2748+
// mutate the list of decls we're iterating over.
2749+
llvm::SmallPtrSet<const clang::ClassTemplateSpecializationDecl *,
2750+
16>
2751+
specWorklist;
2752+
for (auto spec : classTemplate->specializations())
2753+
specWorklist.insert(spec);
2754+
for (auto spec : specWorklist) {
2755+
if (auto import =
2756+
ctx.getClangModuleLoader()->importDeclDirectly(spec))
2757+
if (addedMembers.insert(import).second)
2758+
members.push_back(import);
2759+
}
27812760
}
2782-
}
2783-
}
2784-
};
27852761

2786-
auto namedDecl = dyn_cast<clang::NamedDecl>(member);
2787-
if (!namedDecl)
2788-
continue;
2789-
auto name = ctx.getClangModuleLoader()->importName(namedDecl);
2790-
if (!name)
2791-
continue;
2792-
lookupAndAddMembers(name);
2793-
2794-
// Unscoped enums could have their enumerators present
2795-
// in the parent namespace.
2796-
if (auto *ed = dyn_cast<clang::EnumDecl>(member)) {
2797-
if (!ed->isScoped()) {
2798-
for (const auto *ecd : ed->enumerators()) {
2799-
auto name = ctx.getClangModuleLoader()->importName(ecd);
2800-
if (!name)
2762+
auto lookupAndAddMembers = [&](clang::NamedDecl *namedDecl) {
2763+
auto name = ctx.getClangModuleLoader()->importName(namedDecl);
2764+
if (!name)
2765+
return;
2766+
2767+
auto allResults = evaluateOrDefault(
2768+
ctx.evaluator, ClangDirectLookupRequest({decl, redecl, name}),
2769+
{});
2770+
2771+
for (auto found : allResults) {
2772+
auto clangMember = cast<clang::NamedDecl *>(found);
2773+
if (auto importedDecl =
2774+
ctx.getClangModuleLoader()->importDeclDirectly(
2775+
clangMember)) {
2776+
if (addedMembers.insert(importedDecl).second) {
2777+
members.push_back(importedDecl);
2778+
2779+
// Handle macro-expanded declarations.
2780+
importedDecl->visitAuxiliaryDecls([&](Decl *decl) {
2781+
auto valueDecl = dyn_cast<ValueDecl>(decl);
2782+
if (!valueDecl)
2783+
return;
2784+
2785+
// Bail out if the auxiliary decl was not produced by a
2786+
// macro.
2787+
auto module = decl->getDeclContext()->getParentModule();
2788+
auto *sf = module->getSourceFileContainingLocation(
2789+
decl->getLoc());
2790+
if (!sf || sf->Kind != SourceFileKind::MacroExpansion)
2791+
return;
2792+
2793+
members.push_back(valueDecl);
2794+
});
2795+
}
2796+
}
2797+
}
2798+
};
2799+
2800+
// Look through `extern` blocks.
2801+
if (auto linkageSpecDecl = dyn_cast<clang::LinkageSpecDecl>(member))
2802+
addDeclsFromContext(linkageSpecDecl);
2803+
2804+
auto namedDecl = dyn_cast<clang::NamedDecl>(member);
2805+
if (!namedDecl)
28012806
continue;
2802-
lookupAndAddMembers(name);
2807+
lookupAndAddMembers(namedDecl);
2808+
2809+
// Unscoped enums could have their enumerators present
2810+
// in the parent namespace.
2811+
if (auto *ed = dyn_cast<clang::EnumDecl>(member)) {
2812+
if (!ed->isScoped()) {
2813+
for (auto *ecd : ed->enumerators()) {
2814+
lookupAndAddMembers(ecd);
2815+
}
2816+
}
2817+
}
28032818
}
2804-
}
2805-
}
2806-
}
2819+
};
2820+
2821+
addDeclsFromContext(redecl);
28072822
}
28082823
}
28092824

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5064,6 +5064,11 @@ static bool isDirectLookupMemberContext(const clang::Decl *foundClangDecl,
50645064
return firstDecl->getCanonicalDecl() == parent->getCanonicalDecl();
50655065
}
50665066
}
5067+
// Look through `extern` blocks.
5068+
if (auto linkageSpecDecl = dyn_cast<clang::LinkageSpecDecl>(memberContext)) {
5069+
if (auto parentDecl = dyn_cast<clang::Decl>(linkageSpecDecl->getParent()))
5070+
return isDirectLookupMemberContext(foundClangDecl, parentDecl, parent);
5071+
}
50675072
return false;
50685073
}
50695074

lib/ClangImporter/ImportDecl.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,11 +1155,14 @@ namespace {
11551155
return nullptr;
11561156
// If this is a top-level namespace, don't put it in the module we're
11571157
// importing, put it in the "__ObjC" module that is implicitly imported.
1158-
if (!decl->getParent()->isNamespace())
1158+
auto clangDC = decl->getDeclContext();
1159+
while (isa<clang::LinkageSpecDecl>(clangDC))
1160+
clangDC = clangDC->getParent();
1161+
if (!clangDC->isNamespace())
11591162
dc = Impl.ImportedHeaderUnit;
11601163
else {
11611164
// This is a nested namespace, so just lookup it's parent normally.
1162-
auto parentNS = cast<clang::NamespaceDecl>(decl->getParent());
1165+
auto parentNS = cast<clang::NamespaceDecl>(clangDC);
11631166
auto parent =
11641167
Impl.importDecl(parentNS, getVersion(), /*UseCanonicalDecl*/ false);
11651168
// The parent namespace might not be imported if it's `swift_private`.

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,6 +2034,20 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
20342034
namedMember = def;
20352035
addEntryToLookupTable(table, namedMember, nameImporter);
20362036
}
2037+
if (auto linkageSpecDecl =
2038+
dyn_cast<clang::LinkageSpecDecl>(canonicalMember)) {
2039+
std::function<void(clang::DeclContext *)> addDeclsFromContext =
2040+
[&](clang::DeclContext *declContext) {
2041+
for (auto nestedDecl : declContext->decls()) {
2042+
if (auto namedMember = dyn_cast<clang::NamedDecl>(nestedDecl))
2043+
addEntryToLookupTable(table, namedMember, nameImporter);
2044+
else if (auto nestedLinkageSpecDecl =
2045+
dyn_cast<clang::LinkageSpecDecl>(nestedDecl))
2046+
addDeclsFromContext(nestedLinkageSpecDecl);
2047+
}
2048+
};
2049+
addDeclsFromContext(linkageSpecDecl);
2050+
}
20372051
}
20382052
}
20392053
if (auto usingDecl = dyn_cast<clang::UsingDecl>(named)) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace Outer {
2+
namespace Inner {
3+
extern "C" {
4+
int foobar() { return 123; }
5+
struct NestedType {
6+
char c;
7+
};
8+
}
9+
} // namespace Inner
10+
11+
inline namespace InnerInline {
12+
extern "C" {
13+
int baz() { return 321; }
14+
}
15+
} // namespace InnerInline
16+
} // namespace Outer
17+
18+
namespace ExternWithinExtern {
19+
extern "C" {
20+
extern "C++" {
21+
namespace Inner {
22+
extern "C" {
23+
int deep() { return 42; }
24+
}
25+
} // namespace Inner
26+
}
27+
}
28+
} // namespace ExternWithinExtern

test/Interop/Cxx/namespace/Inputs/module.modulemap

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ module ClassesSecondHeader {
1212
requires cplusplus
1313
}
1414

15+
module ExternWithinNamespace {
16+
header "extern-within-namespace.h"
17+
export *
18+
requires cplusplus
19+
}
20+
1521
module FreeFunctions {
1622
header "free-functions.h"
1723
requires cplusplus
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=ExternWithinNamespace -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift | %FileCheck %s
2+
3+
// CHECK: enum Outer {
4+
// CHECK-NEXT: enum Inner {
5+
// CHECK-NEXT: static func foobar()
6+
// CHECK-NEXT: struct NestedType {
7+
// CHECK: }
8+
// CHECK-NEXT: }
9+
// CHECK-NEXT: enum InnerInline {
10+
// CHECK-NEXT: static func baz() -> Int32
11+
// CHECK-NEXT: }
12+
// CHECK-NEXT: }
13+
// CHECK-NEXT: enum ExternWithinExtern {
14+
// CHECK-NEXT: enum Inner {
15+
// CHECK-NEXT: static func deep() -> Int32
16+
// CHECK-NEXT: }
17+
// CHECK-NEXT: }
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=upcoming-swift)
2+
3+
// REQUIRES: executable_test
4+
5+
import StdlibUnittest
6+
import ExternWithinNamespace
7+
8+
var ExternTestSuite = TestSuite("Extern block within namespaces")
9+
10+
ExternTestSuite.test("Function within extern block within namespace") {
11+
let r = Outer.Inner.foobar()
12+
expectEqual(r, 123)
13+
}
14+
15+
ExternTestSuite.test("Function within extern block within inline namespace") {
16+
let r = Outer.InnerInline.baz()
17+
expectEqual(r, 321)
18+
}
19+
20+
ExternTestSuite.test("Function within extern block within extern block") {
21+
let r = ExternWithinExtern.Inner.deep()
22+
expectEqual(r, 42)
23+
}
24+
25+
runAllTests()

0 commit comments

Comments
 (0)