Skip to content
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
21 changes: 18 additions & 3 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1456,10 +1456,25 @@ DirectLookupRequest::evaluate(Evaluator &evaluator,
auto allFound = evaluateOrDefault(
ctx.evaluator, CXXNamespaceMemberLookup({cast<EnumDecl>(decl), name}),
{});
for (auto found : allFound)
Table.addMember(found);

populateLookupTableEntryFromExtensions(ctx, Table, baseName, decl);

// Bypass the regular member lookup table if we find something in
// the original C++ namespace. We don't want to store the C++ decl in the
// lookup table as the decl can be referenced from multiple namespace
// declarations due to inline namespaces. We still merge in the other
// entries found in the lookup table, to support finding members in
// namespace extensions.
if (!allFound.empty()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If you're going to duplicate the code anyway, might as well unconditionally take this path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The code isn't duplicated, the regular path does something additional - it marks the name as markLazilyComplete in the table.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OK, so, if we know that there are no lazy members (i.e., all members are in the lookup table for a given name), then we don't have to emit a namespace lookup request? Makes sense.

auto known = Table.find(name);
if (known != Table.end()) {
auto swiftLookupResult = maybeFilterOutAttrImplements(
known->second, name, includeAttrImplements);
for (auto foundSwiftDecl : swiftLookupResult) {
allFound.push_back(foundSwiftDecl);
}
}
return allFound;
}
} else if (isa_and_nonnull<clang::RecordDecl>(decl->getClangDecl())) {
auto allFound = evaluateOrDefault(
ctx.evaluator,
Expand Down
49 changes: 30 additions & 19 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4040,6 +4040,20 @@ lookupInClassTemplateSpecialization(
return found;
}

static bool isDirectLookupMemberContext(const clang::Decl *memberContext,
const clang::Decl *parent) {
if (memberContext->getCanonicalDecl() == parent->getCanonicalDecl())
return true;
if (auto namespaceDecl = dyn_cast<clang::NamespaceDecl>(memberContext)) {
if (namespaceDecl->isInline()) {
if (auto memberCtxParent =
dyn_cast<clang::Decl>(namespaceDecl->getParent()))
return isDirectLookupMemberContext(memberCtxParent, parent);
}
}
return false;
}

SmallVector<SwiftLookupTable::SingleEntry, 4>
ClangDirectLookupRequest::evaluate(Evaluator &evaluator,
ClangDirectLookupDescriptor desc) const {
Expand All @@ -4053,28 +4067,25 @@ ClangDirectLookupRequest::evaluate(Evaluator &evaluator,
getClangOwningModule(clangDecl, clangDecl->getASTContext());
auto *lookupTable = ctx.getClangModuleLoader()->findLookupTable(clangModule);

auto *swiftDeclContext = desc.decl->getInnermostDeclContext();
auto *declContextTypeDecl = swiftDeclContext->getSelfNominalTypeDecl();
auto effectiveClangContext =
ctx.getClangModuleLoader()->getEffectiveClangContext(declContextTypeDecl);

auto foundDecls = lookupTable->lookup(
SerializedSwiftName(desc.name.getBaseName()), effectiveClangContext);
SerializedSwiftName(desc.name.getBaseName()), EffectiveClangContext());
// Make sure that `clangDecl` is the parent of all the members we found.
SmallVector<SwiftLookupTable::SingleEntry, 4> filteredDecls;
llvm::copy_if(
foundDecls, std::back_inserter(filteredDecls),
[clangDecl](SwiftLookupTable::SingleEntry decl) {
auto first = decl.get<clang::NamedDecl *>()->getDeclContext();
auto second = cast<clang::DeclContext>(clangDecl);
if (auto firstDecl = dyn_cast<clang::Decl>(first)) {
if (auto secondDecl = dyn_cast<clang::Decl>(second))
return firstDecl->getCanonicalDecl() == secondDecl->getCanonicalDecl();
else
return false;
}
return first == second;
});
llvm::copy_if(foundDecls, std::back_inserter(filteredDecls),
[clangDecl](SwiftLookupTable::SingleEntry decl) {
auto foundClangDecl = decl.dyn_cast<clang::NamedDecl *>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Did you run into problems with this? Macros can't be members so this should be fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, this broke the stdlib build.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm, that's super interesting. Do you know how it broke the build or what decl it was? I might go investigate after this lands..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it was p_starttime

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

which I believe is a macro

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's super interesting... so it's a macro defined to a member of a struct. I guess this is the correct way to handle this...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wish this could just be a ranges::filter(isa-named-decl) | ranges::filter(...)

if (!foundClangDecl)
return false;
auto first = foundClangDecl->getDeclContext();
auto second = cast<clang::DeclContext>(clangDecl);
if (auto firstDecl = dyn_cast<clang::Decl>(first)) {
if (auto secondDecl = dyn_cast<clang::Decl>(second))
return isDirectLookupMemberContext(firstDecl, secondDecl);
else
return false;
}
return first == second;
});
return filteredDecls;
}

Expand Down
125 changes: 125 additions & 0 deletions test/Interop/Cxx/namespace/class-inline-namespace-irgen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: %target-swift-emit-ir -I %t/Inputs -enable-cxx-interop %t/test.swift | %FileCheck %t/test.swift


//--- Inputs/module.modulemap
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I love this new way to write tests :)

module namespaces {
header "test.h"
requires cplusplus
}
//--- Inputs/test.h
namespace Parent {
inline namespace InlineChild {

void functionInInlineChild();

template<class CharT>
struct TemplateInInlineChild {
};

typedef TemplateInInlineChild<char> TypedefInInlineChild;

struct InInlineChild {
};

namespace NamespaceInInlineChild {

struct InNamespaceInInlineChild {
};

} // namespace NamespaceInInlineChild

inline namespace SecondInlineChild {

struct InSecondInlineChild {
};

} // namespace SecondInlineChild
} // namespace InlineChild
} // namespace Parent

//--- test.swift

import namespaces;

extension Parent.TypedefInInlineChild {
var string: String {
return ""
}
}
// CHECK: define hidden swiftcc {{.*}} @"$sSo6ParentO11InlineChildO027__CxxTemplateInstN6Parent11b7Child21e2InbC4IcEEV4testE6stringSSvg"()

extension Parent.InInlineChild {
func doSomething() {
}
}
// CHECK: define hidden swiftcc void @"$sSo6ParentO11InlineChildO02InbC0V4testE11doSomethingyyF"()

extension Parent.InSecondInlineChild {
var x: Int {
return 2
}
}
// CHECK: define hidden swiftcc {{.*}} @"$sSo6ParentO11InlineChildO06SecondbC0O02IndbC0V4testE1xSivg"()

extension Parent.InlineChild.InSecondInlineChild {
var y: Int {
return 3
}
}
// define hidden swiftcc {{.*}} @"$sSo6ParentO11InlineChildO06SecondbC0O02IndbC0V4testE1ySivg"()

// CHECK: define hidden swiftcc {{.*}} @"$s4test3useySSSo6ParentO11InlineChildO027__CxxTemplateInstN6Parent11d7Child21g2IndE4IcEEVF"()
// CHECK: call swiftcc {{.*}} @"$sSo6ParentO11InlineChildO027__CxxTemplateInstN6Parent11b7Child21e2InbC4IcEEV4testE6stringSSvg"
func use(_ x: Parent.TypedefInInlineChild) -> String {
let s = x.string
return s
}

// CHECK: define hidden swiftcc {{.*}} @"$s4test4use2ySSSo6ParentO11InlineChildO027__CxxTemplateInstN6Parent11d7Child21g2IndE4IcEEVF"()
// CHECK: call swiftcc {{.*}} @"$sSo6ParentO11InlineChildO027__CxxTemplateInstN6Parent11b7Child21e2InbC4IcEEV4testE6stringSSvg"
func use2(_ x: Parent.InlineChild.TypedefInInlineChild) -> String {
let s = x.string
return s
}

// define swiftcc void @"$s4testAAyySo6ParentO11InlineChildO02IncD0VF"()
// CHECK: alloca %TSo6ParentO11InlineChildO027__CxxTemplateInstN6Parent11b7Child21e2InbC4IcEEV
// CHECK: call {{.*}} @{{_ZN6Parent11InlineChild21TemplateInInlineChildIcEC|"\?\?0\?\$TemplateInInlineChild@D@InlineChild@Parent@@QEAA@XZ"}}
// CHECK: call swiftcc void @"$sSo6ParentO11InlineChildO02InbC0V4testE11doSomethingyyF"(
// CHECK: call swiftcc {{.*}} @"$sSo6ParentO11InlineChildO06SecondbC0O02IndbC0V4testE1xSivg"(
// CHECK: call swiftcc {{.*}} @"$sSo6ParentO11InlineChildO06SecondbC0O02IndbC0V4testE1ySivg"(
// CHECK: call void @{{_ZN6Parent11InlineChild21functionInInlineChildEv|"\?functionInInlineChild@InlineChild@Parent@@YAXXZ"}}()
public func test(_ y: Parent.InlineChild.InInlineChild) {
let s = Parent.TypedefInInlineChild()
let s2 = use(s) + use2(s)
y.doSomething()
var i: Parent.InlineChild.SecondInlineChild.InSecondInlineChild?
let i2 = i?.x
let i3 = i?.y
Parent.InlineChild.functionInInlineChild()
}

extension Parent.InlineChild {
// CHECK: define hidden swiftcc void @"$sSo6ParentO11InlineChildO4testE011swiftFuncInB9NamespaceyyFZ"()
static func swiftFuncInInlineNamespace() {
}
}

// CHECK: define{{.*}} swiftcc void @"$s4test5test2yyF"()
// CHECK: call swiftcc void @"$sSo6ParentO11InlineChildO4testE011swiftFuncInB9NamespaceyyFZ"()
public func test2() {
Parent.InlineChild.swiftFuncInInlineNamespace()
}

// CHECK: define hidden swiftcc void @"$sSo6ParentO11InlineChildO011NamespaceInbC0O0edebC0V4testE15doSomethingElseyyF"()
extension Parent.NamespaceInInlineChild.InNamespaceInInlineChild {
func doSomethingElse() {}
}

// CHECK: define{{.*}} swiftcc void @"$s4test5test3yySo6ParentO11InlineChildO011NamespaceIndE0O0gfgdE0VF"()
// CHECK: call swiftcc void @"$sSo6ParentO11InlineChildO011NamespaceInbC0O0edebC0V4testE15doSomethingElseyyF"()
public func test3(_ x: Parent.InlineChild.NamespaceInInlineChild.InNamespaceInInlineChild) {
x.doSomethingElse()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: %target-swift-emit-ir -I %t/Inputs -enable-cxx-interop %t/test.swift | %FileCheck %t/test.swift


//--- Inputs/module.modulemap
module namespaces {
header "test.h"
requires cplusplus
}
//--- Inputs/test.h
namespace ExtendedInSwift {
void test(int, int);
} // namespace ExtendedInSwift

//--- test.swift

import namespaces;

extension ExtendedInSwift {
static func test() {
}
}

// CHECK: call void @{{.*}}(i32 0, i32 0)
// CHECK: call swiftcc void @"$sSo15ExtendedInSwiftO4testEACyyFZ"()
public func doTest() {
ExtendedInSwift.test(0, 0)
ExtendedInSwift.test()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t/Inputs %t/test.swift -enable-cxx-interop

//--- Inputs/module.modulemap
module namespaces {
header "test.h"
requires cplusplus
}
//--- Inputs/test.h
namespace Parent {
inline namespace InlineChild {

struct StructInInlineChildAndParent {};

struct StructInInlineChildAndSiblingInlineChild {};

inline namespace SecondInlineChild {
} // namespace SecondInlineChild
} // namespace InlineChild

struct StructInInlineChildAndParent {};

inline namespace SiblingInlineChild {

struct StructInInlineChildAndSiblingInlineChild {};

} // namespace SiblingInlineChild

} // namespace Parent

//--- test.swift

import namespaces;

extension Parent.StructInInlineChildAndParent { // expected-error {{ambiguous type name 'StructInInlineChildAndParent' in 'Parent'}}
}

extension Parent.StructInInlineChildAndSiblingInlineChild { // expected-error {{ambiguous type name 'StructInInlineChildAndSiblingInlineChild' in 'Parent'}}
}

extension Parent.InlineChild.StructInInlineChildAndParent { // ok
var string: String {
return ""
}
}

extension Parent.InlineChild.StructInInlineChildAndSiblingInlineChild { // ok
var string: String {
return ""
}
}

extension Parent.InlineChild.SecondInlineChild.StructInInlineChildAndParent { // expected-error {{'StructInInlineChildAndParent' is not a member type of enum '__ObjC.Parent.InlineChild.SecondInlineChild'}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t/Inputs %t/test.swift -enable-cxx-interop

//--- Inputs/module.modulemap
module namespaces {
header "test.h"
requires cplusplus
}
//--- Inputs/test.h
namespace Parent {
inline namespace InlineChild {

void functionInInlineChild();

} // namespace InlineChild
} // namespace Parent

//--- test.swift

import namespaces;

// Swift's typechecker currently doesn't allow calling a function from inline namespace when it's referenced through the parent namespace.
func test() {
Parent.functionInInlineChild() // expected-error {{type of expression is ambiguous without more context}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t/Inputs %t/test.swift -enable-cxx-interop

//--- Inputs/module.modulemap
module namespaces {
header "test.h"
requires cplusplus
}
//--- Inputs/test.h
inline namespace TopLevelInline {

struct InTopLevelInline {
};

} // namespace TopLevelInline

//--- test.swift

import namespaces;

extension InTopLevelInline { // expected-error {{cannot find type 'InTopLevelInline' in scope}}
}

extension TopLevelInline.InTopLevelInline { // ok
var string: String {
return ""
}
}