Skip to content

C++ Interop: import namespace aliases #37443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2590,8 +2590,42 @@ namespace {
}

Decl *VisitNamespaceAliasDecl(const clang::NamespaceAliasDecl *decl) {
// FIXME: Implement once Swift has namespaces.
return nullptr;
Optional<ImportedName> correctSwiftName;
auto importedName = importFullName(decl, correctSwiftName);
auto name = importedName.getDeclName().getBaseIdentifier();
if (name.empty())
return nullptr;

if (correctSwiftName)
return importCompatibilityTypeAlias(decl, importedName,
*correctSwiftName);

auto dc =
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
if (!dc)
return nullptr;

auto aliasedDecl =
Impl.importDecl(decl->getAliasedNamespace(), getActiveSwiftVersion());
if (!aliasedDecl)
return nullptr;

Type aliasedType;
if (auto aliasedTypeDecl = dyn_cast<TypeDecl>(aliasedDecl))
aliasedType = aliasedTypeDecl->getDeclaredInterfaceType();
else if (auto aliasedExtDecl = dyn_cast<ExtensionDecl>(aliasedDecl))
// This happens if the alias points to its parent namespace.
aliasedType = aliasedExtDecl->getExtendedType();
else
return nullptr;

auto result = Impl.createDeclWithClangNode<TypeAliasDecl>(
decl, AccessLevel::Public, Impl.importSourceLoc(decl->getBeginLoc()),
SourceLoc(), name, Impl.importSourceLoc(decl->getLocation()),
/*GenericParams=*/nullptr, dc);
result->setUnderlyingType(aliasedType);

return result;
}

Decl *VisitLabelDecl(const clang::LabelDecl *decl) {
Expand Down
31 changes: 31 additions & 0 deletions test/Interop/Cxx/namespace/Inputs/classes.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,35 @@ struct BasicStruct {
};
} // namespace ClassesNS3

namespace GlobalAliasToNS1 = ClassesNS1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest a test that references a namespace in another module, but I think we need to sort out SR-14214 first.


namespace ClassesNS4 {
namespace AliasToGlobalNS1 = ::ClassesNS1;
namespace AliasToGlobalNS2 = ::ClassesNS1::ClassesNS2;

namespace ClassesNS5 {
struct BasicStruct {};
} // namespace ClassesNS5

namespace AliasToInnerNS5 = ClassesNS5;
namespace AliasToNS2 = ClassesNS1::ClassesNS2;

namespace AliasChainToNS1 = GlobalAliasToNS1;
namespace AliasChainToNS2 = AliasChainToNS1::ClassesNS2;
} // namespace ClassesNS4

Comment on lines +43 to +59
Copy link
Contributor

@varungandhi-apple varungandhi-apple May 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are more complex test cases covered by other tests? For example, consider code with namespaces that are shadowed

namespace X { }
namespace Y { namespace X { }; namespace Z = X; /* IIUC, refers to ::Y::X */ }

or code with using namespace ABC. Do these get translated correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There wasn't a test for such case, but I've added it now. Also added a test for an alias pointing to its parent namespace, and a couple of other similar cases.

Using-directives & using-declarations currently aren't imported at all (these C++ constructs, along with inline namespaces, are among those that I don't know how to express ergonomically in Swift).

namespace ClassesNS5 {
struct BasicStruct {};
namespace AliasToAnotherNS5 = ClassesNS4::ClassesNS5;

namespace ClassesNS5 {
struct BasicStruct {};
namespace AliasToNS5NS5 = ClassesNS5;
} // namespace ClassesNS5

namespace AliasToGlobalNS5 = ::ClassesNS5;
namespace AliasToLocalNS5 = ClassesNS5;
namespace AliasToNS5 = ::ClassesNS5::ClassesNS5;
} // namespace ClassesNS5

#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_H
4 changes: 4 additions & 0 deletions test/Interop/Cxx/namespace/classes-irgen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Classes
// CHECK: call i8* @{{_ZN10ClassesNS111BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS1@@QEAAPEBDXZ"}}(%"struct.ClassesNS1::BasicStruct"*
// CHECK: call i8* @{{_ZN10ClassesNS110ClassesNS211BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS2@ClassesNS1@@QEAAPEBDXZ"}}(%"struct.ClassesNS1::ClassesNS2::BasicStruct"*
// CHECK: call i8* @{{_ZN10ClassesNS311BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS3@@QEAAPEBDXZ"}}(%"struct.ClassesNS3::BasicStruct"*
// CHECK: call i8* @{{_ZN10ClassesNS111BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS1@@QEAAPEBDXZ"}}(%"struct.ClassesNS1::BasicStruct"*
// CHECK: ret void
public func basicTests() {
var basicStructInst = ClassesNS1.BasicStruct()
Expand All @@ -16,6 +17,9 @@ public func basicTests() {

var siblingBasicStruct = ClassesNS3.BasicStruct()
siblingBasicStruct.basicMember()

var basicStructViaAlias = ClassesNS4.AliasToGlobalNS1.BasicStruct()
basicStructViaAlias.basicMember()
}

// CHECK-LABEL: define {{.*}}void @"$s4main15forwardDeclaredyyF"()
Expand Down
31 changes: 31 additions & 0 deletions test/Interop/Cxx/namespace/classes-module-interface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,34 @@
// CHECK: }
// CHECK: }
// CHECK-NOT: extension

// CHECK: typealias GlobalAliasToNS1 = ClassesNS1
// CHECK: extension ClassesNS4 {
// CHECK: typealias AliasToGlobalNS1 = ClassesNS1
// CHECK: typealias AliasToGlobalNS2 = ClassesNS1.ClassesNS2
// CHECK: typealias AliasToInnerNS5 = ClassesNS4.ClassesNS5
// CHECK: typealias AliasToNS2 = ClassesNS1.ClassesNS2
// CHECK: typealias AliasChainToNS1 = ClassesNS1
// CHECK: typealias AliasChainToNS2 = ClassesNS1.ClassesNS2
// CHECK: }
// CHECK: extension ClassesNS4.ClassesNS5 {
// CHECK: struct BasicStruct {
// CHECK: init()
// CHECK: }
// CHECK: }
// CHECK: extension ClassesNS5 {
// CHECK: struct BasicStruct {
// CHECK: init()
// CHECK: }
// CHECK: typealias AliasToAnotherNS5 = ClassesNS4.ClassesNS5
// CHECK: typealias AliasToGlobalNS5 = ClassesNS5
// CHECK: typealias AliasToLocalNS5 = ClassesNS5.ClassesNS5
// CHECK: typealias AliasToNS5 = ClassesNS5.ClassesNS5
// CHECK: }
// CHECK: extension ClassesNS5.ClassesNS5 {
// CHECK: struct BasicStruct {
// CHECK: init()
// CHECK: }
// CHECK: typealias AliasToNS5NS5 = ClassesNS5.ClassesNS5
// CHECK: }
// CHECK-NOT: extension
4 changes: 4 additions & 0 deletions test/Interop/Cxx/namespace/classes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ NamespacesTestSuite.test("Basic classes") {
var siblingBasicStruct = ClassesNS3.BasicStruct()
let siblingMemberCString = siblingBasicStruct.basicMember()
expectEqual(String(cString: siblingMemberCString!), "ClassesNS3::BasicStruct::basicMember")

var basicStructViaAlias = ClassesNS4.AliasToGlobalNS1.BasicStruct()
let basicMemberViaAliasCString = basicStructViaAlias.basicMember()
expectEqual(String(cString: basicMemberViaAliasCString!), "ClassesNS1::BasicStruct::basicMember")
}

NamespacesTestSuite.test("Forward declared classes") {
Expand Down