Skip to content

Commit cfc9483

Browse files
committed
C++ Interop: import namespaces redecls as separate extensions
Previously a namespace declaration was imported along with all of its redeclarations, and their members were added to a single Swift extension. This was problematic when a single namespace is declared in multiple modules – the extension belonged to only one of them. For an example of this, try printing a module interface for `std.string`/`std.iosfwd` – it will be empty, even though the declarations from those modules are actually imported into Swift correctly. This change makes sure that when we're importing different redeclarations of the same namespace, we're adding them as separate extensions to appropriate modules.
1 parent 60a195e commit cfc9483

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)