Skip to content

[cxx-interop] serialize x-refs for class template specializations #72643

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
Apr 1, 2024
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
6 changes: 6 additions & 0 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4837,13 +4837,19 @@ TinyPtrVector<ValueDecl *> CXXNamespaceMemberLookup::evaluate(
auto &ctx = namespaceDecl->getASTContext();

TinyPtrVector<ValueDecl *> result;
llvm::SmallPtrSet<clang::NamedDecl *, 8> importedDecls;
for (auto redecl : clangNamespaceDecl->redecls()) {
auto allResults = evaluateOrDefault(
ctx.evaluator, ClangDirectLookupRequest({namespaceDecl, redecl, name}),
{});

for (auto found : allResults) {
auto clangMember = found.get<clang::NamedDecl *>();
auto it = importedDecls.insert(clangMember);
// Skip over members already found during lookup in
// prior redeclarations.
if (!it.second)
continue;
if (auto import =
ctx.getClangModuleLoader()->importDeclDirectly(clangMember))
result.push_back(cast<ValueDecl>(import));
Expand Down
3 changes: 2 additions & 1 deletion lib/Serialization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ target_link_libraries(swiftSerialization PRIVATE
swiftClangImporter
swiftOption
swiftAST
swiftSIL)
swiftSIL
clangIndex)

set_swift_llvm_is_available(swiftSerialization)
36 changes: 30 additions & 6 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "clang/AST/Attr.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/AttributeCommonInfo.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -2425,12 +2426,35 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
}

if (!privateDiscriminator.empty()) {
ModuleDecl *searchModule = M;
if (!searchModule)
searchModule = nominal->getModuleContext();
searchModule->lookupMember(values, nominal, memberName,
privateDiscriminator);

if (importedFromClang) {
// This is a clang imported class template, that's
// serialized using original template name, and
// its USR that denotes the specific specialization.
auto members = nominal->lookupDirect(memberName);
for (const auto &m : members) {
if (!m->hasClangNode())
continue;
if (auto *ctd =
dyn_cast<clang::ClassTemplateDecl>(m->getClangDecl())) {
for (const auto *spec : ctd->specializations()) {
llvm::SmallString<128> buffer;
clang::index::generateUSRForDecl(spec, buffer);
if (privateDiscriminator.str() == buffer) {
if (auto import = getContext()
.getClangModuleLoader()
->importDeclDirectly(spec))
values.push_back(cast<ValueDecl>(import));
}
}
}
}
} else {
ModuleDecl *searchModule = M;
if (!searchModule)
searchModule = nominal->getModuleContext();
searchModule->lookupMember(values, nominal, memberName,
privateDiscriminator);
}
} else {
auto members = nominal->lookupDirect(memberName);
values.append(members.begin(), members.end());
Expand Down
48 changes: 41 additions & 7 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "swift/Strings.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Serialization/ASTReader.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallString.h"
Expand Down Expand Up @@ -2060,6 +2061,22 @@ getStableClangDeclPathComponentKind(
llvm_unreachable("bad kind");
}

static Identifier getClangTemplateSpecializationXRefDiscriminator(
ASTContext &ctx, Identifier &name,
const clang::ClassTemplateSpecializationDecl *ctsd) {
auto it = name.str().find("<");
if (it == StringRef::npos)
return Identifier();
// Serialize a C++ class template specialization name as original
// class template name, and use its USR as the discriminator, that
// will let Swift find the correct specialization when this cross
// reference is deserialized.
name = ctx.getIdentifier(name.str().substr(0, it));
llvm::SmallString<128> buffer;
clang::index::generateUSRForDecl(ctsd, buffer);
return ctx.getIdentifier(buffer.str());
}

void Serializer::writeCrossReference(const DeclContext *DC, uint32_t pathLen) {
using namespace decls_block;

Expand Down Expand Up @@ -2116,11 +2133,19 @@ void Serializer::writeCrossReference(const DeclContext *DC, uint32_t pathLen) {

bool isProtocolExt = DC->getParent()->getExtendedProtocolDecl();

Identifier name = generic->getName();
if (generic->hasClangNode()) {
if (auto *ctsd = dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
generic->getClangDecl())) {
assert(discriminator.empty());
discriminator = getClangTemplateSpecializationXRefDiscriminator(
getASTContext(), name, ctsd);
}
}
XRefTypePathPieceLayout::emitRecord(Out, ScratchRecord, abbrCode,
addDeclBaseNameRef(generic->getName()),
addDeclBaseNameRef(name),
addDeclBaseNameRef(discriminator),
isProtocolExt,
generic->hasClangNode());
isProtocolExt, generic->hasClangNode());
break;
}

Expand Down Expand Up @@ -2290,10 +2315,19 @@ void Serializer::writeCrossReference(const Decl *D) {
discriminator = containingFile->getDiscriminatorForPrivateDecl(type);
}

XRefTypePathPieceLayout::emitRecord(Out, ScratchRecord, abbrCode,
addDeclBaseNameRef(type->getName()),
addDeclBaseNameRef(discriminator),
isProtocolExt, D->hasClangNode());
Identifier name = type->getName();
if (type->hasClangNode()) {
if (auto *ctsd = dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
type->getClangDecl())) {
assert(discriminator.empty());
discriminator = getClangTemplateSpecializationXRefDiscriminator(
getASTContext(), name, ctsd);
}
}

XRefTypePathPieceLayout::emitRecord(
Out, ScratchRecord, abbrCode, addDeclBaseNameRef(name),
addDeclBaseNameRef(discriminator), isProtocolExt, D->hasClangNode());
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swiftxx-frontend -emit-module %t/Inputs/test.swift -module-name TestA -I %t/Inputs -o %t/test-part.swiftmodule
// RUN: %target-swiftxx-frontend -merge-modules -emit-module %t/test-part.swiftmodule -module-name TestA -I %t/Inputs -o %t/TestA.swiftmodule -sil-verify-none
// RUN: %target-swift-ide-test -print-module -module-to-print=TestA -I %t/ -source-filename=test -enable-experimental-cxx-interop | %FileCheck %s

//--- Inputs/module.modulemap
module CxxHeader {
header "header.h"
requires cplusplus
}

//--- Inputs/header.h

#include <stddef.h>

namespace std2 {

template<class T>
class vec {
public:
using Element = T;
using RawIterator = const T * _Nonnull;
vec() {}
vec(const vec<T> &other) : items(other.items) { }
~vec() {}

T * _Nonnull begin() {
return items;
}
T * _Nonnull end() {
return items + 10;
}
RawIterator begin() const {
return items;
}
RawIterator end() const {
return items + 10;
}
size_t size() const {
return 10;
}

private:
T items[10];
};

} // namespace std2

namespace ns2 {

class App {
public:

inline std2::vec<App> getApps() const {
return {};
}
int x = 0;
};

} // namespace ns2

using vec2Apps = std2::vec<ns2::App>;

//--- Inputs/test.swift

import Cxx
import CxxHeader

extension vec2Apps : CxxSequence {
}

public func testFunction() -> [Int] {
let applications = ns2.App().getApps()
return applications.map { Int($0.x) }
}

// CHECK: func testFunction() -> [Int]