Skip to content

Commit bd96959

Browse files
committed
[cxx-interop] Re-implement namespaces using enums + extensions.
C++ namespaces are module-independent, but enums are owned by their module's in Swift. So, to prevent declaring two enums with the same name, this patch implements a new approach to namespaces: enums with extensions. Here's an example: ``` // Module A namespace N { void test1(); } // Module B namespace N { void test2(); } // __ObjC module enum N { } // Swift module A extension N { func test1() } // Swift module B extension N { func test1() } ``` Thanks to @gribozavr for the great idea.
1 parent 22acb2f commit bd96959

28 files changed

+794
-13
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,19 +2445,116 @@ namespace {
24452445
auto importedName = importFullName(decl, correctSwiftName);
24462446
if (!importedName) return nullptr;
24472447

2448-
auto dc =
2448+
auto extensionDC =
24492449
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
2450-
if (!dc)
2450+
if (!extensionDC)
24512451
return nullptr;
24522452

24532453
SourceLoc loc = Impl.importSourceLoc(decl->getBeginLoc());
2454-
2455-
// FIXME: If Swift gets namespaces, import as a namespace.
2456-
auto enumDecl = Impl.createDeclWithClangNode<EnumDecl>(
2454+
DeclContext *dc = nullptr;
2455+
// If this is a top-level namespace, don't put it in the module we're
2456+
// importing, put it in the "__ObjC" module that is implicitly imported.
2457+
// This way, if we have multiple modules that all open the same namespace,
2458+
// we won't import multiple enums with the same name in swift.
2459+
if (extensionDC->getContextKind() == DeclContextKind::FileUnit)
2460+
dc = Impl.ImportedHeaderUnit;
2461+
else {
2462+
// This is a nested namespace, we need to find its extension decl
2463+
// context and then use that to find the parent enum. It's important
2464+
// that we add this to the parent enum (in the "__ObjC" module) and not
2465+
// to the extension.
2466+
auto parentNS = cast<clang::NamespaceDecl>(decl->getParent());
2467+
auto parent = Impl.importDecl(parentNS, getVersion());
2468+
// Sometimes when the parent namespace is imported, this namespace
2469+
// also gets imported. If that's the case, then the parent namespace
2470+
// will be an enum (because it was able to be fully imported) in which
2471+
// case we need to bail here.
2472+
auto cachedResult =
2473+
Impl.ImportedDecls.find({decl->getCanonicalDecl(), getVersion()});
2474+
if (cachedResult != Impl.ImportedDecls.end())
2475+
return cachedResult->second;
2476+
dc = cast<ExtensionDecl>(parent)
2477+
->getExtendedType()
2478+
->getEnumOrBoundGenericEnum();
2479+
}
2480+
auto *enumDecl = Impl.createDeclWithClangNode<EnumDecl>(
24572481
decl, AccessLevel::Public, loc,
24582482
importedName.getDeclName().getBaseIdentifier(),
24592483
Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc);
2460-
enumDecl->setMemberLoader(&Impl, 0);
2484+
if (isa<clang::NamespaceDecl>(decl->getParent()))
2485+
cast<EnumDecl>(dc)->addMember(enumDecl);
2486+
2487+
// We are creating an extension, so put it at the top level. This is done
2488+
// after creating the enum, though, because we may need the correctly
2489+
// nested decl context above when creating the enum.
2490+
while (extensionDC->getParent() &&
2491+
extensionDC->getContextKind() != DeclContextKind::FileUnit)
2492+
extensionDC = extensionDC->getParent();
2493+
2494+
auto *extension = ExtensionDecl::create(Impl.SwiftContext, loc, nullptr,
2495+
{}, extensionDC, nullptr, decl);
2496+
Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{extension},
2497+
enumDecl->getDeclaredType());
2498+
Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{extension},
2499+
std::move(enumDecl));
2500+
// Keep track of what members we've already added so we don't add the same
2501+
// member twice. Note: we can't use "ImportedDecls" for this because we
2502+
// might import a decl that we don't add (for example, if it was a
2503+
// parameter to another decl).
2504+
SmallPtrSet<Decl *, 16> addedMembers;
2505+
for (auto redecl : decl->redecls()) {
2506+
// This will be reset as the EnumDecl after we return from
2507+
// VisitNamespaceDecl.
2508+
Impl.ImportedDecls[{redecl->getCanonicalDecl(), getVersion()}] =
2509+
extension;
2510+
2511+
// Insert these backwards into "namespaceDecls" so we can pop them off
2512+
// the end without loosing order.
2513+
SmallVector<clang::Decl *, 16> namespaceDecls;
2514+
auto addDeclsReversed = [&](auto decls) {
2515+
auto begin = decls.begin();
2516+
auto end = decls.end();
2517+
int currentSize = namespaceDecls.size();
2518+
int declCount = currentSize + std::distance(begin, end);
2519+
namespaceDecls.resize(declCount);
2520+
for (int index = declCount - 1; index >= currentSize; --index)
2521+
namespaceDecls[index] = *(begin++);
2522+
};
2523+
addDeclsReversed(redecl->decls());
2524+
while (!namespaceDecls.empty()) {
2525+
auto nd = dyn_cast<clang::NamedDecl>(namespaceDecls.pop_back_val());
2526+
// Make sure we only import the defenition of a record.
2527+
if (auto tagDecl = dyn_cast_or_null<clang::TagDecl>(nd))
2528+
// Some decls, for example ClassTemplateSpecializationDecls, won't
2529+
// have a definition here. That's OK.
2530+
nd = tagDecl->getDefinition() ? tagDecl->getDefinition() : tagDecl;
2531+
if (!nd)
2532+
continue;
2533+
2534+
// Special case class templates: import all their specilizations here.
2535+
if (auto classTemplate = dyn_cast<clang::ClassTemplateDecl>(nd)) {
2536+
addDeclsReversed(classTemplate->specializations());
2537+
continue;
2538+
}
2539+
2540+
auto member = Impl.importDecl(nd, getVersion());
2541+
if (!member || addedMembers.count(member) ||
2542+
isa<clang::NamespaceDecl>(nd))
2543+
continue;
2544+
// This happens (for example) when a struct is declared inside another
2545+
// struct inside a namespace but defined out of line.
2546+
assert(member->getDeclContext()->getAsDecl());
2547+
if (dyn_cast<ExtensionDecl>(member->getDeclContext()->getAsDecl()) !=
2548+
extension)
2549+
continue;
2550+
extension->addMember(member);
2551+
addedMembers.insert(member);
2552+
}
2553+
}
2554+
2555+
if (!extension->getMembers().empty())
2556+
enumDecl->addExtension(extension);
2557+
24612558
return enumDecl;
24622559
}
24632560

@@ -8583,6 +8680,7 @@ DeclContext *ClangImporter::Implementation::importDeclContextImpl(
85838680
auto decl = dyn_cast<clang::NamedDecl>(dc);
85848681
if (!decl)
85858682
return nullptr;
8683+
85868684
// Category decls with same name can be merged and using canonical decl always
85878685
// leads to the first category of the given name. We'd like to keep these
85888686
// categories separated.

test/ClangImporter/cxx_interop_ir.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ func indirectUsage() {
99
useT(makeT())
1010
}
1111

12-
// CHECK-LABEL: define hidden swiftcc %swift.type* @"$s6cxx_ir14reflectionInfo3argypXpSo2nsV1TV_tF"
13-
// CHECK: %0 = call swiftcc %swift.metadata_response @"$sSo2nsV1TVMa"({{i64|i32}} 0)
12+
// CHECK-LABEL: define hidden swiftcc %swift.type* @"$s6cxx_ir14reflectionInfo3argypXpSo2nsV10CXXInteropE1TV_tF"
13+
// CHECK: %0 = call swiftcc %swift.metadata_response @"$sSo2nsV10CXXInteropE1TVMa"({{i64|i32}} 0)
1414
func reflectionInfo(arg: namespacedT) -> Any.Type {
1515
return type(of: arg)
1616
}
1717

18-
// CHECK: define hidden swiftcc void @"$s6cxx_ir24namespaceManglesIntoName3argySo2nsV1TV_tF"
18+
// CHECK: define hidden swiftcc void @"$s6cxx_ir24namespaceManglesIntoName3argySo2nsV10CXXInteropE1TV_tF"
1919
func namespaceManglesIntoName(arg: namespacedT) {
2020
}
2121

22-
// CHECK: define hidden swiftcc void @"$s6cxx_ir42namespaceManglesIntoNameForUsingShadowDecl3argySo2nsV14NamespacedTypeV_tF"
22+
// CHECK: define hidden swiftcc void @"$s6cxx_ir42namespaceManglesIntoNameForUsingShadowDecl3argySo2nsV10CXXInteropE14NamespacedTypeV_tF"
2323
func namespaceManglesIntoNameForUsingShadowDecl(arg: NamespacedType) {
2424
}
2525

test/Interop/Cxx/class/linked-records-module-interface.swift

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

3-
// CHECK: enum Space {
3+
// CHECK: extension Space {
44
// CHECK: struct C {
55
// CHECK: struct D {
66
// CHECK: var B: Space.A.B
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_SECOND_HEADER_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_SECOND_HEADER_H
3+
4+
#include "classes.h"
5+
6+
struct ClassesNS1::ClassesNS2::DefinedInDefs {
7+
const char *basicMember() {
8+
return "ClassesNS1::ClassesNS2::DefinedInDefs::basicMember";
9+
}
10+
};
11+
12+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_SECOND_HEADER_H
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_H
3+
4+
namespace ClassesNS1 {
5+
struct BasicStruct {
6+
const char *basicMember() { return "ClassesNS1::BasicStruct::basicMember"; }
7+
};
8+
struct ForwardDeclaredStruct;
9+
} // namespace ClassesNS1
10+
11+
struct ClassesNS1::ForwardDeclaredStruct {
12+
const char *basicMember() {
13+
return "ClassesNS1::ForwardDeclaredStruct::basicMember";
14+
}
15+
};
16+
17+
namespace ClassesNS1 {
18+
namespace ClassesNS2 {
19+
struct BasicStruct {
20+
const char *basicMember() {
21+
return "ClassesNS1::ClassesNS2::BasicStruct::basicMember";
22+
}
23+
};
24+
struct ForwardDeclaredStruct;
25+
struct DefinedInDefs;
26+
} // namespace ClassesNS2
27+
} // namespace ClassesNS1
28+
29+
namespace ClassesNS1 {
30+
struct ClassesNS2::ForwardDeclaredStruct {
31+
const char *basicMember() {
32+
return "ClassesNS1::ClassesNS2::ForwardDeclaredStruct::basicMember";
33+
}
34+
};
35+
} // namespace ClassesNS1
36+
37+
namespace ClassesNS3 {
38+
struct BasicStruct {
39+
const char *basicMember() { return "ClassesNS3::BasicStruct::basicMember"; }
40+
};
41+
} // namespace ClassesNS3
42+
43+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_H
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_FREE_FUNCTIONS_SECOND_HEADER_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_FREE_FUNCTIONS_SECOND_HEADER_H
3+
4+
#include "free-functions.h"
5+
6+
inline const char *FunctionsNS1::definedInDefs() {
7+
return "FunctionsNS1::definedInDefs";
8+
}
9+
10+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_FREE_FUNCTIONS_SECOND_HEADER_H
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_FREE_FUNCTION_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_FREE_FUNCTION_H
3+
4+
namespace FunctionsNS1 {
5+
inline const char *basicFunctionTopLevel() {
6+
return "FunctionsNS1::basicFunctionTopLevel";
7+
}
8+
inline const char *forwardDeclared();
9+
inline const char *definedOutOfLine();
10+
} // namespace FunctionsNS1
11+
12+
namespace FunctionsNS1 {
13+
inline const char *forwardDeclared() { return "FunctionsNS1::forwardDeclared"; }
14+
} // namespace FunctionsNS1
15+
16+
inline const char *FunctionsNS1::definedOutOfLine() {
17+
return "FunctionsNS1::definedOutOfLine";
18+
}
19+
20+
namespace FunctionsNS1 {
21+
namespace FunctionsNS2 {
22+
inline const char *basicFunctionSecondLevel() {
23+
return "FunctionsNS1::FunctionsNS2::basicFunctionSecondLevel";
24+
}
25+
} // namespace FunctionsNS2
26+
} // namespace FunctionsNS1
27+
28+
namespace FunctionsNS1 {
29+
namespace FunctionsNS2 {
30+
namespace FunctionsNS3 {
31+
inline const char *basicFunctionLowestLevel() {
32+
return "FunctionsNS1::FunctionsNS2::FunctionsNS3::basicFunctionLowestLevel";
33+
}
34+
} // namespace FunctionsNS3
35+
} // namespace FunctionsNS2
36+
} // namespace FunctionsNS1
37+
38+
namespace FunctionsNS1 {
39+
inline const char *definedInDefs();
40+
}
41+
42+
namespace FunctionsNS1 {
43+
inline const char *sameNameInChild() { return "FunctionsNS1::sameNameInChild"; }
44+
inline const char *sameNameInSibling() {
45+
return "FunctionsNS1::sameNameInSibling";
46+
}
47+
namespace FunctionsNS2 {
48+
inline const char *sameNameInChild() {
49+
return "FunctionsNS1::FunctionsNS2::sameNameInChild";
50+
}
51+
} // namespace FunctionsNS2
52+
} // namespace FunctionsNS1
53+
54+
namespace FunctionsNS4 {
55+
inline const char *sameNameInSibling() {
56+
return "FunctionsNS4::sameNameInSibling";
57+
}
58+
} // namespace FunctionsNS4
59+
60+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_FREE_FUNCTION_H
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Classes {
2+
header "classes.h"
3+
}
4+
5+
module ClassesSecondHeader {
6+
// TODO: we shouldn't have to include both of these, and the decls defined in
7+
// these headers should be added to the correct module: SR-14214.
8+
header "classes.h"
9+
header "classes-second-header.h"
10+
}
11+
12+
module FreeFunctions {
13+
header "free-functions.h"
14+
}
15+
16+
module FreeFunctionsSecondHeader {
17+
header "free-functions.h"
18+
header "free-functions-second-header.h"
19+
}
20+
21+
module Templates {
22+
header "templates.h"
23+
}
24+
25+
module TemplatesSecondHeader {
26+
header "templates.h"
27+
header "templates-second-header.h"
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_TEMPLATES_SECOND_HEADER_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_TEMPLATES_SECOND_HEADER_H
3+
4+
template <class T>
5+
const char *TemplatesNS1::basicFunctionTemplateDefinedInDefs(T) {
6+
return "TemplatesNS1::basicFunctionTemplateDefinedInDefs";
7+
}
8+
9+
template <class> struct TemplatesNS1::BasicClassTemplateDefinedInDefs {
10+
const char *basicMember() {
11+
return "TemplatesNS1::BasicClassTemplateDefinedInDefs::basicMember";
12+
}
13+
};
14+
15+
using BasicClassTemplateDefinedInDefsChar =
16+
TemplatesNS1::BasicClassTemplateDefinedInDefs<char>;
17+
18+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_TEMPLATES_SECOND_HEADER_H

0 commit comments

Comments
 (0)