Skip to content

Revert "C++ Interop: import namespaces redecls as separate extensions" #38651

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

Closed
wants to merge 1 commit into from
Closed
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
22 changes: 3 additions & 19 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2870,11 +2870,6 @@ void ClangImporter::lookupTypeDecl(
continue;
}
auto *imported = Impl.importDecl(clangDecl, Impl.CurrentVersion);

// Namespaces are imported as extensions for enums.
if (auto ext = dyn_cast_or_null<ExtensionDecl>(imported)) {
imported = ext->getExtendedNominal();
}
if (auto *importedType = dyn_cast_or_null<TypeDecl>(imported)) {
foundViaClang = true;
receiver(importedType);
Expand Down Expand Up @@ -3841,22 +3836,11 @@ void ClangImporter::Implementation::lookupValue(
// If the entry is not visible, skip it.
if (!isVisibleClangEntry(entry)) continue;

ValueDecl *decl = nullptr;
ValueDecl *decl;
// If it's a Clang declaration, try to import it.
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
bool isNamespace = isa<clang::NamespaceDecl>(clangDecl);
Decl *realDecl =
importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion,
/*useCanonicalDecl*/ !isNamespace);

if (isNamespace) {
if (auto extension = cast_or_null<ExtensionDecl>(realDecl))
realDecl = extension->getExtendedNominal();
}

if (!realDecl)
continue;
decl = cast<ValueDecl>(realDecl);
decl = cast_or_null<ValueDecl>(
importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion));
if (!decl) continue;
} else if (!name.isSpecial()) {
// Try to import a macro.
Expand Down
124 changes: 50 additions & 74 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2469,98 +2469,73 @@ namespace {
}

Decl *VisitNamespaceDecl(const clang::NamespaceDecl *decl) {
// If we have a name for this declaration, use it.
Optional<ImportedName> correctSwiftName;
auto importedName = importFullName(decl, correctSwiftName);
if (!importedName) return nullptr;

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

SourceLoc loc = Impl.importSourceLoc(decl->getBeginLoc());
DeclContext *dc = nullptr;
// If this is a top-level namespace, don't put it in the module we're
// importing, put it in the "__ObjC" module that is implicitly imported.
// This way, if we have multiple modules that all open the same namespace,
// we won't import multiple enums with the same name in swift.
if (!decl->getParent()->isNamespace())
if (extensionDC->getContextKind() == DeclContextKind::FileUnit)
dc = Impl.ImportedHeaderUnit;
else {
// This is a nested namespace, we need to find its extension decl
// context and then use that to find the parent enum. It's important
// that we add this to the parent enum (in the "__ObjC" module) and not
// to the extension.
auto parentNS = cast<clang::NamespaceDecl>(decl->getParent());
auto parent =
Impl.importDecl(parentNS, getVersion(), /*UseCanonicalDecl*/ false);
auto parent = Impl.importDecl(parentNS, getVersion());
// Sometimes when the parent namespace is imported, this namespace
// also gets imported. If that's the case, then the parent namespace
// will be an enum (because it was able to be fully imported) in which
// case we need to bail here.
auto cachedResult = Impl.ImportedDecls.find({decl, getVersion()});
auto cachedResult =
Impl.ImportedDecls.find({decl->getCanonicalDecl(), getVersion()});
if (cachedResult != Impl.ImportedDecls.end())
return cachedResult->second;
dc = cast<ExtensionDecl>(parent)
->getExtendedType()
->getEnumOrBoundGenericEnum();
}

EnumDecl *enumDecl = nullptr;
// Try to find an already created enum for this namespace.
auto *enumDecl = Impl.createDeclWithClangNode<EnumDecl>(
decl, AccessLevel::Public, loc,
importedName.getDeclName().getBaseIdentifier(),
Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc);
if (isa<clang::NamespaceDecl>(decl->getParent()))
cast<EnumDecl>(dc)->addMember(enumDecl);

// We are creating an extension, so put it at the top level. This is done
// after creating the enum, though, because we may need the correctly
// nested decl context above when creating the enum.
while (extensionDC->getParent() &&
extensionDC->getContextKind() != DeclContextKind::FileUnit)
extensionDC = extensionDC->getParent();

auto *extension = ExtensionDecl::create(Impl.SwiftContext, loc, nullptr,
{}, extensionDC, nullptr, decl);
Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{extension},
enumDecl->getDeclaredType());
Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{extension},
std::move(enumDecl));
// Keep track of what members we've already added so we don't add the same
// member twice. Note: we can't use "ImportedDecls" for this because we
// might import a decl that we don't add (for example, if it was a
// parameter to another decl).
SmallPtrSet<Decl *, 16> addedMembers;
for (auto redecl : decl->redecls()) {
auto extension = Impl.ImportedDecls.find({redecl, getVersion()});
if (extension != Impl.ImportedDecls.end()) {
enumDecl = cast<EnumDecl>(
cast<ExtensionDecl>(extension->second)->getExtendedNominal());
break;
}
}
// If we're seeing this namespace for the first time, we need to create a
// new enum in the "__ObjC" module.
if (!enumDecl) {
// If we don't have a name for this declaration, bail.
Optional<ImportedName> correctSwiftName;
auto importedName = importFullName(decl, correctSwiftName);
if (!importedName)
return nullptr;

enumDecl = Impl.createDeclWithClangNode<EnumDecl>(
decl, AccessLevel::Public,
Impl.importSourceLoc(decl->getBeginLoc()),
importedName.getDeclName().getBaseIdentifier(),
Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc);
if (isa<clang::NamespaceDecl>(decl->getParent()))
cast<EnumDecl>(dc)->addMember(enumDecl);
}

for (auto redecl : decl->redecls()) {
if (Impl.ImportedDecls.find({redecl, getVersion()}) !=
Impl.ImportedDecls.end())
continue;

Optional<ImportedName> correctSwiftName;
auto importedName = importFullName(redecl, correctSwiftName);
if (!importedName)
continue;

auto extensionDC = Impl.importDeclContextOf(
redecl, importedName.getEffectiveContext());
if (!extensionDC)
continue;

// We are creating an extension, so put it at the top level. This is
// done after creating the enum, though, because we may need the
// correctly nested decl context above when creating the enum.
while (extensionDC->getParent() &&
extensionDC->getContextKind() != DeclContextKind::FileUnit)
extensionDC = extensionDC->getParent();

auto *extension = ExtensionDecl::create(
Impl.SwiftContext, Impl.importSourceLoc(decl->getBeginLoc()),
nullptr, {}, extensionDC, nullptr, redecl);
enumDecl->addExtension(extension);
Impl.ImportedDecls[{redecl, getVersion()}] = extension;

Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{extension},
enumDecl->getDeclaredType());
Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{extension},
std::move(enumDecl));
// Keep track of what members we've already added so we don't add the
// same member twice. Note: we can't use "ImportedDecls" for this
// because we might import a decl that we don't add (for example, if it
// was a parameter to another decl).
SmallPtrSet<Decl *, 16> addedMembers;
// This will be reset as the EnumDecl after we return from
// VisitNamespaceDecl.
Impl.ImportedDecls[{redecl->getCanonicalDecl(), getVersion()}] =
extension;

// Insert these backwards into "namespaceDecls" so we can pop them off
// the end without loosing order.
Expand Down Expand Up @@ -2591,8 +2566,7 @@ namespace {
continue;
}

bool useCanonicalDecl = !isa<clang::NamespaceDecl>(nd);
auto member = Impl.importDecl(nd, getVersion(), useCanonicalDecl);
auto member = Impl.importDecl(nd, getVersion());
if (!member || addedMembers.count(member) ||
isa<clang::NamespaceDecl>(nd))
continue;
Expand All @@ -2607,7 +2581,10 @@ namespace {
}
}

return Impl.ImportedDecls[{decl, getVersion()}];
if (!extension->getMembers().empty())
enumDecl->addExtension(extension);

return enumDecl;
}

Decl *VisitUsingDirectiveDecl(const clang::UsingDirectiveDecl *decl) {
Expand Down Expand Up @@ -9258,8 +9235,7 @@ DeclContext *ClangImporter::Implementation::importDeclContextImpl(
// Category decls with same name can be merged and using canonical decl always
// leads to the first category of the given name. We'd like to keep these
// categories separated.
auto useCanonical =
!isa<clang::ObjCCategoryDecl>(decl) && !isa<clang::NamespaceDecl>(decl);
auto useCanonical = !isa<clang::ObjCCategoryDecl>(decl);
auto swiftDecl = importDeclForDeclContext(ImportingDecl, decl->getName(),
decl, CurrentVersion, useCanonical);
if (!swiftDecl)
Expand Down
5 changes: 2 additions & 3 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -881,11 +881,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
///
/// \returns The imported declaration, or null if this declaration could
/// not be represented in Swift.
Decl *importDeclReal(const clang::NamedDecl *ClangDecl, Version version,
bool useCanonicalDecl = true) {
Decl *importDeclReal(const clang::NamedDecl *ClangDecl, Version version) {
return importDeclAndCacheImpl(ClangDecl, version,
/*SuperfluousTypedefsAreTransparent=*/false,
/*UseCanonicalDecl*/ useCanonicalDecl);
/*UseCanonicalDecl*/true);
}

/// Import a cloned version of the given declaration, which is part of
Expand Down
3 changes: 2 additions & 1 deletion lib/ClangImporter/SwiftLookupTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,11 @@ class EffectiveClangContext {
DC = omDecl->getCanonicalDecl();
} else if (auto fDecl = dyn_cast<clang::FunctionDecl>(dc)) {
DC = fDecl->getCanonicalDecl();
} else if (auto nsDecl = dyn_cast<clang::NamespaceDecl>(dc)) {
DC = nsDecl->getCanonicalDecl();
} else {
assert(isa<clang::TranslationUnitDecl>(dc) ||
isa<clang::LinkageSpecDecl>(dc) ||
isa<clang::NamespaceDecl>(dc) ||
isa<clang::ObjCContainerDecl>(dc) &&
"No other kinds of effective Clang contexts");
DC = dc;
Expand Down
11 changes: 0 additions & 11 deletions test/Interop/Cxx/namespace/Inputs/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,6 @@ module FreeFunctionsSecondHeader {
requires cplusplus
}

module Submodules {
module SubmoduleA {
header "submodule-a.h"
requires cplusplus
}
module SubmoduleB {
header "submodule-b.h"
requires cplusplus
}
}

module Templates {
header "templates.h"
requires cplusplus
Expand Down
16 changes: 0 additions & 16 deletions test/Interop/Cxx/namespace/Inputs/submodule-a.h

This file was deleted.

16 changes: 0 additions & 16 deletions test/Interop/Cxx/namespace/Inputs/submodule-b.h

This file was deleted.

4 changes: 2 additions & 2 deletions test/Interop/Cxx/namespace/classes-module-interface.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %target-swift-ide-test -print-module -module-to-print=Classes -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s

// CHECK-NOT: extension
// CHECK: extension ClassesNS1 {
// CHECK: extension ClassesNS1.ClassesNS2 {
// CHECK: struct BasicStruct {
// CHECK: init()
// CHECK: mutating func basicMember() -> UnsafePointer<CChar>!
Expand All @@ -13,7 +13,7 @@
// CHECK: }
// CHECK-NOT: extension

// CHECK: extension ClassesNS1.ClassesNS2 {
// CHECK: extension ClassesNS1 {
// CHECK: struct BasicStruct {
// CHECK: init()
// CHECK: mutating func basicMember() -> UnsafePointer<CChar>!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,24 @@
// RUN: %target-swift-ide-test -print-module -module-to-print=FreeFunctions -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s

// CHECK-NOT: extension
// CHECK: extension FunctionsNS1 {
// CHECK: static func basicFunctionTopLevel() -> UnsafePointer<CChar>!
// CHECK: static func definedOutOfLine() -> UnsafePointer<CChar>!
// CHECK: static func forwardDeclared() -> UnsafePointer<CChar>!
// CHECK: }
// CHECK-NOT: extension

// CHECK: extension FunctionsNS1.FunctionsNS2 {
// CHECK: static func basicFunctionSecondLevel() -> UnsafePointer<CChar>!
// CHECK: }
// CHECK-NOT: extension

// CHECK: extension FunctionsNS1.FunctionsNS2.FunctionsNS3 {
// CHECK: static func basicFunctionLowestLevel() -> UnsafePointer<CChar>!
// CHECK: }
// CHECK-NOT: extension

// CHECK: extension FunctionsNS1 {
// CHECK: static func definedInDefs() -> UnsafePointer<CChar>!
// CHECK: }
// CHECK-NOT: extension

// CHECK: extension FunctionsNS1 {
// CHECK: static func sameNameInChild() -> UnsafePointer<CChar>!
// CHECK: static func sameNameInSibling() -> UnsafePointer<CChar>!
// CHECK: static func definedInDefs() -> UnsafePointer<CChar>!
// CHECK: static func forwardDeclared() -> UnsafePointer<CChar>!
// CHECK: static func basicFunctionTopLevel() -> UnsafePointer<CChar>!
// CHECK: static func definedOutOfLine() -> UnsafePointer<CChar>!
// CHECK: }
// CHECK-NOT: extension

// CHECK: extension FunctionsNS1.FunctionsNS2 {
// CHECK: static func sameNameInChild() -> UnsafePointer<CChar>!
// CHECK: static func basicFunctionSecondLevel() -> UnsafePointer<CChar>!
// CHECK: }
// CHECK-NOT: extension

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

// TODO: This file doesn't really test anything because functions need not be defined.
// CHECK: extension FunctionsNS1 {
// CHECK-NOT: extension
// CHECK: static func definedInDefs() -> UnsafePointer<CChar>!
// CHECK: }
28 changes: 0 additions & 28 deletions test/Interop/Cxx/namespace/submodules-module-interface.swift

This file was deleted.

Loading