Skip to content

Commit 0c7b1ce

Browse files
committed
[cxx-interop] serialize x-refs for class template specializations
Fixes #70253
1 parent 8484c61 commit 0c7b1ce

File tree

5 files changed

+157
-14
lines changed

5 files changed

+157
-14
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4837,13 +4837,19 @@ TinyPtrVector<ValueDecl *> CXXNamespaceMemberLookup::evaluate(
48374837
auto &ctx = namespaceDecl->getASTContext();
48384838

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

48454846
for (auto found : allResults) {
48464847
auto clangMember = found.get<clang::NamedDecl *>();
4848+
auto it = importedDecls.insert(clangMember);
4849+
// Skip over members already found during lookup in
4850+
// prior redeclarations.
4851+
if (!it.second)
4852+
continue;
48474853
if (auto import =
48484854
ctx.getClangModuleLoader()->importDeclDirectly(clangMember))
48494855
result.push_back(cast<ValueDecl>(import));

lib/Serialization/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ target_link_libraries(swiftSerialization PRIVATE
1818
swiftClangImporter
1919
swiftOption
2020
swiftAST
21-
swiftSIL)
21+
swiftSIL
22+
clangIndex)
2223

2324
set_swift_llvm_is_available(swiftSerialization)

lib/Serialization/Deserialization.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "clang/AST/Attr.h"
4444
#include "clang/Basic/SourceManager.h"
4545
#include "clang/Basic/AttributeCommonInfo.h"
46+
#include "clang/Index/USRGeneration.h"
4647
#include "llvm/ADT/Statistic.h"
4748
#include "llvm/Support/Compiler.h"
4849
#include "llvm/Support/Debug.h"
@@ -2425,12 +2426,35 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
24252426
}
24262427

24272428
if (!privateDiscriminator.empty()) {
2428-
ModuleDecl *searchModule = M;
2429-
if (!searchModule)
2430-
searchModule = nominal->getModuleContext();
2431-
searchModule->lookupMember(values, nominal, memberName,
2432-
privateDiscriminator);
2433-
2429+
if (importedFromClang) {
2430+
// This is a clang imported class template, that's
2431+
// serialized using original template name, and
2432+
// its USR that denotes the specific specialization.
2433+
auto members = nominal->lookupDirect(memberName);
2434+
for (const auto &m : members) {
2435+
if (!m->hasClangNode())
2436+
continue;
2437+
if (auto *ctd =
2438+
dyn_cast<clang::ClassTemplateDecl>(m->getClangDecl())) {
2439+
for (const auto *spec : ctd->specializations()) {
2440+
llvm::SmallString<128> buffer;
2441+
clang::index::generateUSRForDecl(spec, buffer);
2442+
if (privateDiscriminator.str() == buffer) {
2443+
if (auto import = getContext()
2444+
.getClangModuleLoader()
2445+
->importDeclDirectly(spec))
2446+
values.push_back(cast<ValueDecl>(import));
2447+
}
2448+
}
2449+
}
2450+
}
2451+
} else {
2452+
ModuleDecl *searchModule = M;
2453+
if (!searchModule)
2454+
searchModule = nominal->getModuleContext();
2455+
searchModule->lookupMember(values, nominal, memberName,
2456+
privateDiscriminator);
2457+
}
24342458
} else {
24352459
auto members = nominal->lookupDirect(memberName);
24362460
values.append(members.begin(), members.end());

lib/Serialization/Serialization.cpp

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include "swift/Strings.h"
5656
#include "clang/AST/DeclTemplate.h"
5757
#include "clang/Frontend/CompilerInstance.h"
58+
#include "clang/Index/USRGeneration.h"
5859
#include "clang/Serialization/ASTReader.h"
5960
#include "llvm/ADT/SmallSet.h"
6061
#include "llvm/ADT/SmallString.h"
@@ -2060,6 +2061,22 @@ getStableClangDeclPathComponentKind(
20602061
llvm_unreachable("bad kind");
20612062
}
20622063

2064+
static Identifier getClangTemplateSpecializationXRefDiscriminator(
2065+
ASTContext &ctx, Identifier &name,
2066+
const clang::ClassTemplateSpecializationDecl *ctsd) {
2067+
auto it = name.str().find("<");
2068+
if (it == StringRef::npos)
2069+
return Identifier();
2070+
// Serialize a C++ class template specialization name as original
2071+
// class template name, and use its USR as the discriminator, that
2072+
// will let Swift find the correct specialization when this cross
2073+
// reference is deserialized.
2074+
name = ctx.getIdentifier(name.str().substr(0, it));
2075+
llvm::SmallString<128> buffer;
2076+
clang::index::generateUSRForDecl(ctsd, buffer);
2077+
return ctx.getIdentifier(buffer.str());
2078+
}
2079+
20632080
void Serializer::writeCrossReference(const DeclContext *DC, uint32_t pathLen) {
20642081
using namespace decls_block;
20652082

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

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

2136+
Identifier name = generic->getName();
2137+
if (generic->hasClangNode()) {
2138+
if (auto *ctsd = dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
2139+
generic->getClangDecl())) {
2140+
assert(discriminator.empty());
2141+
discriminator = getClangTemplateSpecializationXRefDiscriminator(
2142+
getASTContext(), name, ctsd);
2143+
}
2144+
}
21192145
XRefTypePathPieceLayout::emitRecord(Out, ScratchRecord, abbrCode,
2120-
addDeclBaseNameRef(generic->getName()),
2146+
addDeclBaseNameRef(name),
21212147
addDeclBaseNameRef(discriminator),
2122-
isProtocolExt,
2123-
generic->hasClangNode());
2148+
isProtocolExt, generic->hasClangNode());
21242149
break;
21252150
}
21262151

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

2293-
XRefTypePathPieceLayout::emitRecord(Out, ScratchRecord, abbrCode,
2294-
addDeclBaseNameRef(type->getName()),
2295-
addDeclBaseNameRef(discriminator),
2296-
isProtocolExt, D->hasClangNode());
2318+
Identifier name = type->getName();
2319+
if (type->hasClangNode()) {
2320+
if (auto *ctsd = dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
2321+
type->getClangDecl())) {
2322+
assert(discriminator.empty());
2323+
discriminator = getClangTemplateSpecializationXRefDiscriminator(
2324+
getASTContext(), name, ctsd);
2325+
}
2326+
}
2327+
2328+
XRefTypePathPieceLayout::emitRecord(
2329+
Out, ScratchRecord, abbrCode, addDeclBaseNameRef(name),
2330+
addDeclBaseNameRef(discriminator), isProtocolExt, D->hasClangNode());
22972331
return;
22982332
}
22992333

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
// RUN: %target-swiftxx-frontend -emit-module %t/Inputs/test.swift -module-name TestA -I %t/Inputs -o %t/test-part.swiftmodule
4+
// 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
5+
// RUN: %target-swift-ide-test -print-module -module-to-print=TestA -I %t/ -source-filename=test -enable-experimental-cxx-interop | %FileCheck %s
6+
7+
//--- Inputs/module.modulemap
8+
module CxxHeader {
9+
header "header.h"
10+
requires cplusplus
11+
}
12+
13+
//--- Inputs/header.h
14+
15+
#include <stddef.h>
16+
17+
namespace std2 {
18+
19+
template<class T>
20+
class vec {
21+
public:
22+
using Element = T;
23+
using RawIterator = const T * _Nonnull;
24+
vec() {}
25+
vec(const vec<T> &other) : items(other.items) { }
26+
~vec() {}
27+
28+
T * _Nonnull begin() {
29+
return items;
30+
}
31+
T * _Nonnull end() {
32+
return items + 10;
33+
}
34+
RawIterator begin() const {
35+
return items;
36+
}
37+
RawIterator end() const {
38+
return items + 10;
39+
}
40+
size_t size() const {
41+
return 10;
42+
}
43+
44+
private:
45+
T items[10];
46+
};
47+
48+
} // namespace std2
49+
50+
namespace ns2 {
51+
52+
class App {
53+
public:
54+
55+
inline std2::vec<App> getApps() const {
56+
return {};
57+
}
58+
int x = 0;
59+
};
60+
61+
} // namespace ns2
62+
63+
using vec2Apps = std2::vec<ns2::App>;
64+
65+
//--- Inputs/test.swift
66+
67+
import Cxx
68+
import CxxHeader
69+
70+
extension vec2Apps : CxxSequence {
71+
}
72+
73+
public func testFunction() -> [Int] {
74+
let applications = ns2.App().getApps()
75+
return applications.map { Int($0.x) }
76+
}
77+
78+
// CHECK: func testFunction() -> [Int]

0 commit comments

Comments
 (0)