Skip to content

Commit ea9ce7e

Browse files
authored
Merge pull request #35085 from zoecarver/cxx/central-cxx-module
[cxx-interop] Re-implement namespaces using enums + extensions.
2 parents e6af501 + bd96959 commit ea9ce7e

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
@@ -2450,19 +2450,116 @@ namespace {
24502450
auto importedName = importFullName(decl, correctSwiftName);
24512451
if (!importedName) return nullptr;
24522452

2453-
auto dc =
2453+
auto extensionDC =
24542454
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
2455-
if (!dc)
2455+
if (!extensionDC)
24562456
return nullptr;
24572457

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

@@ -8543,6 +8640,7 @@ DeclContext *ClangImporter::Implementation::importDeclContextImpl(
85438640
auto decl = dyn_cast<clang::NamedDecl>(dc);
85448641
if (!decl)
85458642
return nullptr;
8643+
85468644
// Category decls with same name can be merged and using canonical decl always
85478645
// leads to the first category of the given name. We'd like to keep these
85488646
// 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)