Skip to content

Commit 03ff464

Browse files
authored
Merge pull request #37695 from egorzhdan/cxx-namespaces
C++ Interop: import namespaces redecls as separate extensions
2 parents b6af50d + cfc9483 commit 03ff464

13 files changed

+201
-72
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2870,6 +2870,11 @@ void ClangImporter::lookupTypeDecl(
28702870
continue;
28712871
}
28722872
auto *imported = Impl.importDecl(clangDecl, Impl.CurrentVersion);
2873+
2874+
// Namespaces are imported as extensions for enums.
2875+
if (auto ext = dyn_cast_or_null<ExtensionDecl>(imported)) {
2876+
imported = ext->getExtendedNominal();
2877+
}
28732878
if (auto *importedType = dyn_cast_or_null<TypeDecl>(imported)) {
28742879
foundViaClang = true;
28752880
receiver(importedType);
@@ -3836,11 +3841,22 @@ void ClangImporter::Implementation::lookupValue(
38363841
// If the entry is not visible, skip it.
38373842
if (!isVisibleClangEntry(entry)) continue;
38383843

3839-
ValueDecl *decl;
3844+
ValueDecl *decl = nullptr;
38403845
// If it's a Clang declaration, try to import it.
38413846
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
3842-
decl = cast_or_null<ValueDecl>(
3843-
importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion));
3847+
bool isNamespace = isa<clang::NamespaceDecl>(clangDecl);
3848+
Decl *realDecl =
3849+
importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion,
3850+
/*useCanonicalDecl*/ !isNamespace);
3851+
3852+
if (isNamespace) {
3853+
if (auto extension = cast_or_null<ExtensionDecl>(realDecl))
3854+
realDecl = extension->getExtendedNominal();
3855+
}
3856+
3857+
if (!realDecl)
3858+
continue;
3859+
decl = cast<ValueDecl>(realDecl);
38443860
if (!decl) continue;
38453861
} else if (!name.isSpecial()) {
38463862
// Try to import a macro.

lib/ClangImporter/ImportDecl.cpp

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,73 +2469,98 @@ namespace {
24692469
}
24702470

24712471
Decl *VisitNamespaceDecl(const clang::NamespaceDecl *decl) {
2472-
// If we have a name for this declaration, use it.
2473-
Optional<ImportedName> correctSwiftName;
2474-
auto importedName = importFullName(decl, correctSwiftName);
2475-
if (!importedName) return nullptr;
2476-
2477-
auto extensionDC =
2478-
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
2479-
if (!extensionDC)
2480-
return nullptr;
2481-
2482-
SourceLoc loc = Impl.importSourceLoc(decl->getBeginLoc());
24832472
DeclContext *dc = nullptr;
24842473
// If this is a top-level namespace, don't put it in the module we're
24852474
// importing, put it in the "__ObjC" module that is implicitly imported.
24862475
// This way, if we have multiple modules that all open the same namespace,
24872476
// we won't import multiple enums with the same name in swift.
2488-
if (extensionDC->getContextKind() == DeclContextKind::FileUnit)
2477+
if (!decl->getParent()->isNamespace())
24892478
dc = Impl.ImportedHeaderUnit;
24902479
else {
24912480
// This is a nested namespace, we need to find its extension decl
24922481
// context and then use that to find the parent enum. It's important
24932482
// that we add this to the parent enum (in the "__ObjC" module) and not
24942483
// to the extension.
24952484
auto parentNS = cast<clang::NamespaceDecl>(decl->getParent());
2496-
auto parent = Impl.importDecl(parentNS, getVersion());
2485+
auto parent =
2486+
Impl.importDecl(parentNS, getVersion(), /*UseCanonicalDecl*/ false);
24972487
// Sometimes when the parent namespace is imported, this namespace
24982488
// also gets imported. If that's the case, then the parent namespace
24992489
// will be an enum (because it was able to be fully imported) in which
25002490
// case we need to bail here.
2501-
auto cachedResult =
2502-
Impl.ImportedDecls.find({decl->getCanonicalDecl(), getVersion()});
2491+
auto cachedResult = Impl.ImportedDecls.find({decl, getVersion()});
25032492
if (cachedResult != Impl.ImportedDecls.end())
25042493
return cachedResult->second;
25052494
dc = cast<ExtensionDecl>(parent)
25062495
->getExtendedType()
25072496
->getEnumOrBoundGenericEnum();
25082497
}
2509-
auto *enumDecl = Impl.createDeclWithClangNode<EnumDecl>(
2510-
decl, AccessLevel::Public, loc,
2511-
importedName.getDeclName().getBaseIdentifier(),
2512-
Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc);
2513-
if (isa<clang::NamespaceDecl>(decl->getParent()))
2514-
cast<EnumDecl>(dc)->addMember(enumDecl);
2515-
2516-
// We are creating an extension, so put it at the top level. This is done
2517-
// after creating the enum, though, because we may need the correctly
2518-
// nested decl context above when creating the enum.
2519-
while (extensionDC->getParent() &&
2520-
extensionDC->getContextKind() != DeclContextKind::FileUnit)
2521-
extensionDC = extensionDC->getParent();
2522-
2523-
auto *extension = ExtensionDecl::create(Impl.SwiftContext, loc, nullptr,
2524-
{}, extensionDC, nullptr, decl);
2525-
Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{extension},
2526-
enumDecl->getDeclaredType());
2527-
Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{extension},
2528-
std::move(enumDecl));
2529-
// Keep track of what members we've already added so we don't add the same
2530-
// member twice. Note: we can't use "ImportedDecls" for this because we
2531-
// might import a decl that we don't add (for example, if it was a
2532-
// parameter to another decl).
2533-
SmallPtrSet<Decl *, 16> addedMembers;
2498+
2499+
EnumDecl *enumDecl = nullptr;
2500+
// Try to find an already created enum for this namespace.
25342501
for (auto redecl : decl->redecls()) {
2535-
// This will be reset as the EnumDecl after we return from
2536-
// VisitNamespaceDecl.
2537-
Impl.ImportedDecls[{redecl->getCanonicalDecl(), getVersion()}] =
2538-
extension;
2502+
auto extension = Impl.ImportedDecls.find({redecl, getVersion()});
2503+
if (extension != Impl.ImportedDecls.end()) {
2504+
enumDecl = cast<EnumDecl>(
2505+
cast<ExtensionDecl>(extension->second)->getExtendedNominal());
2506+
break;
2507+
}
2508+
}
2509+
// If we're seeing this namespace for the first time, we need to create a
2510+
// new enum in the "__ObjC" module.
2511+
if (!enumDecl) {
2512+
// If we don't have a name for this declaration, bail.
2513+
Optional<ImportedName> correctSwiftName;
2514+
auto importedName = importFullName(decl, correctSwiftName);
2515+
if (!importedName)
2516+
return nullptr;
2517+
2518+
enumDecl = Impl.createDeclWithClangNode<EnumDecl>(
2519+
decl, AccessLevel::Public,
2520+
Impl.importSourceLoc(decl->getBeginLoc()),
2521+
importedName.getDeclName().getBaseIdentifier(),
2522+
Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc);
2523+
if (isa<clang::NamespaceDecl>(decl->getParent()))
2524+
cast<EnumDecl>(dc)->addMember(enumDecl);
2525+
}
2526+
2527+
for (auto redecl : decl->redecls()) {
2528+
if (Impl.ImportedDecls.find({redecl, getVersion()}) !=
2529+
Impl.ImportedDecls.end())
2530+
continue;
2531+
2532+
Optional<ImportedName> correctSwiftName;
2533+
auto importedName = importFullName(redecl, correctSwiftName);
2534+
if (!importedName)
2535+
continue;
2536+
2537+
auto extensionDC = Impl.importDeclContextOf(
2538+
redecl, importedName.getEffectiveContext());
2539+
if (!extensionDC)
2540+
continue;
2541+
2542+
// We are creating an extension, so put it at the top level. This is
2543+
// done after creating the enum, though, because we may need the
2544+
// correctly nested decl context above when creating the enum.
2545+
while (extensionDC->getParent() &&
2546+
extensionDC->getContextKind() != DeclContextKind::FileUnit)
2547+
extensionDC = extensionDC->getParent();
2548+
2549+
auto *extension = ExtensionDecl::create(
2550+
Impl.SwiftContext, Impl.importSourceLoc(decl->getBeginLoc()),
2551+
nullptr, {}, extensionDC, nullptr, redecl);
2552+
enumDecl->addExtension(extension);
2553+
Impl.ImportedDecls[{redecl, getVersion()}] = extension;
2554+
2555+
Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{extension},
2556+
enumDecl->getDeclaredType());
2557+
Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{extension},
2558+
std::move(enumDecl));
2559+
// Keep track of what members we've already added so we don't add the
2560+
// same member twice. Note: we can't use "ImportedDecls" for this
2561+
// because we might import a decl that we don't add (for example, if it
2562+
// was a parameter to another decl).
2563+
SmallPtrSet<Decl *, 16> addedMembers;
25392564

25402565
// Insert these backwards into "namespaceDecls" so we can pop them off
25412566
// the end without loosing order.
@@ -2566,7 +2591,8 @@ namespace {
25662591
continue;
25672592
}
25682593

2569-
auto member = Impl.importDecl(nd, getVersion());
2594+
bool useCanonicalDecl = !isa<clang::NamespaceDecl>(nd);
2595+
auto member = Impl.importDecl(nd, getVersion(), useCanonicalDecl);
25702596
if (!member || addedMembers.count(member) ||
25712597
isa<clang::NamespaceDecl>(nd))
25722598
continue;
@@ -2581,10 +2607,7 @@ namespace {
25812607
}
25822608
}
25832609

2584-
if (!extension->getMembers().empty())
2585-
enumDecl->addExtension(extension);
2586-
2587-
return enumDecl;
2610+
return Impl.ImportedDecls[{decl, getVersion()}];
25882611
}
25892612

25902613
Decl *VisitUsingDirectiveDecl(const clang::UsingDirectiveDecl *decl) {
@@ -9235,7 +9258,8 @@ DeclContext *ClangImporter::Implementation::importDeclContextImpl(
92359258
// Category decls with same name can be merged and using canonical decl always
92369259
// leads to the first category of the given name. We'd like to keep these
92379260
// categories separated.
9238-
auto useCanonical = !isa<clang::ObjCCategoryDecl>(decl);
9261+
auto useCanonical =
9262+
!isa<clang::ObjCCategoryDecl>(decl) && !isa<clang::NamespaceDecl>(decl);
92399263
auto swiftDecl = importDeclForDeclContext(ImportingDecl, decl->getName(),
92409264
decl, CurrentVersion, useCanonical);
92419265
if (!swiftDecl)

lib/ClangImporter/ImporterImpl.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -881,10 +881,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
881881
///
882882
/// \returns The imported declaration, or null if this declaration could
883883
/// not be represented in Swift.
884-
Decl *importDeclReal(const clang::NamedDecl *ClangDecl, Version version) {
884+
Decl *importDeclReal(const clang::NamedDecl *ClangDecl, Version version,
885+
bool useCanonicalDecl = true) {
885886
return importDeclAndCacheImpl(ClangDecl, version,
886887
/*SuperfluousTypedefsAreTransparent=*/false,
887-
/*UseCanonicalDecl*/true);
888+
/*UseCanonicalDecl*/ useCanonicalDecl);
888889
}
889890

890891
/// Import a cloned version of the given declaration, which is part of

lib/ClangImporter/SwiftLookupTable.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,10 @@ class EffectiveClangContext {
197197
DC = omDecl->getCanonicalDecl();
198198
} else if (auto fDecl = dyn_cast<clang::FunctionDecl>(dc)) {
199199
DC = fDecl->getCanonicalDecl();
200-
} else if (auto nsDecl = dyn_cast<clang::NamespaceDecl>(dc)) {
201-
DC = nsDecl->getCanonicalDecl();
202200
} else {
203201
assert(isa<clang::TranslationUnitDecl>(dc) ||
204202
isa<clang::LinkageSpecDecl>(dc) ||
203+
isa<clang::NamespaceDecl>(dc) ||
205204
isa<clang::ObjCContainerDecl>(dc) &&
206205
"No other kinds of effective Clang contexts");
207206
DC = dc;

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ module FreeFunctionsSecondHeader {
2222
requires cplusplus
2323
}
2424

25+
module Submodules {
26+
module SubmoduleA {
27+
header "submodule-a.h"
28+
requires cplusplus
29+
}
30+
module SubmoduleB {
31+
header "submodule-b.h"
32+
requires cplusplus
33+
}
34+
}
35+
2536
module Templates {
2637
header "templates.h"
2738
requires cplusplus
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_A_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_A_H
3+
4+
namespace NS1 {
5+
6+
namespace NS2 {
7+
8+
struct BasicDeepA {};
9+
10+
} // namespace NS2
11+
12+
struct BasicA {};
13+
14+
} // namespace NS1
15+
16+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_A_H
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_B_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_B_H
3+
4+
namespace NS1 {
5+
6+
namespace NS2 {
7+
8+
struct BasicDeepB {};
9+
10+
} // namespace NS2
11+
12+
struct BasicB {};
13+
14+
} // namespace NS1
15+
16+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_B_H

test/Interop/Cxx/namespace/classes-module-interface.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-swift-ide-test -print-module -module-to-print=Classes -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
22

33
// CHECK-NOT: extension
4-
// CHECK: extension ClassesNS1.ClassesNS2 {
4+
// CHECK: extension ClassesNS1 {
55
// CHECK: struct BasicStruct {
66
// CHECK: init()
77
// CHECK: mutating func basicMember() -> UnsafePointer<CChar>!
@@ -13,7 +13,7 @@
1313
// CHECK: }
1414
// CHECK-NOT: extension
1515

16-
// CHECK: extension ClassesNS1 {
16+
// CHECK: extension ClassesNS1.ClassesNS2 {
1717
// CHECK: struct BasicStruct {
1818
// CHECK: init()
1919
// CHECK: mutating func basicMember() -> UnsafePointer<CChar>!

test/Interop/Cxx/namespace/free-functions-module-interface.swift

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
11
// RUN: %target-swift-ide-test -print-module -module-to-print=FreeFunctions -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
22

33
// CHECK-NOT: extension
4+
// CHECK: extension FunctionsNS1 {
5+
// CHECK: static func basicFunctionTopLevel() -> UnsafePointer<CChar>!
6+
// CHECK: static func definedOutOfLine() -> UnsafePointer<CChar>!
7+
// CHECK: static func forwardDeclared() -> UnsafePointer<CChar>!
8+
// CHECK: }
9+
// CHECK-NOT: extension
10+
11+
// CHECK: extension FunctionsNS1.FunctionsNS2 {
12+
// CHECK: static func basicFunctionSecondLevel() -> UnsafePointer<CChar>!
13+
// CHECK: }
14+
// CHECK-NOT: extension
15+
416
// CHECK: extension FunctionsNS1.FunctionsNS2.FunctionsNS3 {
517
// CHECK: static func basicFunctionLowestLevel() -> UnsafePointer<CChar>!
618
// CHECK: }
719
// CHECK-NOT: extension
820

21+
// CHECK: extension FunctionsNS1 {
22+
// CHECK: static func definedInDefs() -> UnsafePointer<CChar>!
23+
// CHECK: }
24+
// CHECK-NOT: extension
25+
926
// CHECK: extension FunctionsNS1 {
1027
// CHECK: static func sameNameInChild() -> UnsafePointer<CChar>!
1128
// CHECK: static func sameNameInSibling() -> UnsafePointer<CChar>!
12-
// CHECK: static func definedInDefs() -> UnsafePointer<CChar>!
13-
// CHECK: static func forwardDeclared() -> UnsafePointer<CChar>!
14-
// CHECK: static func basicFunctionTopLevel() -> UnsafePointer<CChar>!
15-
// CHECK: static func definedOutOfLine() -> UnsafePointer<CChar>!
1629
// CHECK: }
1730
// CHECK-NOT: extension
1831

1932
// CHECK: extension FunctionsNS1.FunctionsNS2 {
2033
// CHECK: static func sameNameInChild() -> UnsafePointer<CChar>!
21-
// CHECK: static func basicFunctionSecondLevel() -> UnsafePointer<CChar>!
2234
// CHECK: }
2335
// CHECK-NOT: extension
2436

test/Interop/Cxx/namespace/free-functions-second-header-module-interface.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22

33
// TODO: This file doesn't really test anything because functions need not be defined.
44
// CHECK: extension FunctionsNS1 {
5-
// CHECK-NOT: extension
65
// CHECK: static func definedInDefs() -> UnsafePointer<CChar>!
76
// CHECK: }
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=Submodules.SubmoduleA -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s -check-prefix=CHECK-A
2+
// RUN: %target-swift-ide-test -print-module -module-to-print=Submodules.SubmoduleB -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s -check-prefix=CHECK-B
3+
4+
// CHECK-A-NOT: extension
5+
// CHECK-A: extension NS1 {
6+
// CHECK-A: struct BasicA {
7+
// CHECK-A: }
8+
// CHECK-A: }
9+
// CHECK-A-NOT: extension
10+
11+
// CHECK-A: extension NS1.NS2 {
12+
// CHECK-A: struct BasicDeepA {
13+
// CHECK-A: }
14+
// CHECK-A: }
15+
// CHECK-A-NOT: extension
16+
17+
// CHECK-B-NOT: extension
18+
// CHECK-B: extension NS1 {
19+
// CHECK-B: struct BasicB {
20+
// CHECK-B: }
21+
// CHECK-B: }
22+
// CHECK-B-NOT: extension
23+
24+
// CHECK-B: extension NS1.NS2 {
25+
// CHECK-B: struct BasicDeepB {
26+
// CHECK-B: }
27+
// CHECK-B: }
28+
// CHECK-B-NOT: extension

0 commit comments

Comments
 (0)