Skip to content

Commit 5eb7c7a

Browse files
committed
[cxx-interop] Add ability to specify protocol conformance on C++ side.
1 parent d069fff commit 5eb7c7a

File tree

8 files changed

+119
-35
lines changed

8 files changed

+119
-35
lines changed

include/swift/AST/Attr.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,7 @@ class DeclAttribute : public AttributeBase {
167167
kind : 1
168168
);
169169

170-
SWIFT_INLINE_BITFIELD(SynthesizedProtocolAttr, DeclAttribute,
171-
NumKnownProtocolKindBits+1,
172-
kind : NumKnownProtocolKindBits,
170+
SWIFT_INLINE_BITFIELD(SynthesizedProtocolAttr, DeclAttribute, 1,
173171
isUnchecked : 1
174172
);
175173

@@ -1364,22 +1362,22 @@ class ObjCBridgedAttr : public DeclAttribute {
13641362
/// synthesized conformances.
13651363
class SynthesizedProtocolAttr : public DeclAttribute {
13661364
LazyConformanceLoader *Loader;
1365+
ProtocolDecl *protocol;
13671366

13681367
public:
1369-
SynthesizedProtocolAttr(KnownProtocolKind protocolKind,
1368+
SynthesizedProtocolAttr(ProtocolDecl *protocol,
13701369
LazyConformanceLoader *Loader,
13711370
bool isUnchecked)
13721371
: DeclAttribute(DAK_SynthesizedProtocol, SourceLoc(), SourceRange(),
1373-
/*Implicit=*/true), Loader(Loader)
1372+
/*Implicit=*/true), Loader(Loader), protocol(protocol)
13741373
{
1375-
Bits.SynthesizedProtocolAttr.kind = unsigned(protocolKind);
13761374
Bits.SynthesizedProtocolAttr.isUnchecked = unsigned(isUnchecked);
13771375
}
13781376

13791377
/// Retrieve the known protocol kind naming the protocol to be
13801378
/// synthesized.
1381-
KnownProtocolKind getProtocolKind() const {
1382-
return KnownProtocolKind(Bits.SynthesizedProtocolAttr.kind);
1379+
ProtocolDecl *getProtocol() const {
1380+
return protocol;
13831381
}
13841382

13851383
bool isUnchecked() const {

lib/AST/ASTPrinter.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7069,13 +7069,14 @@ swift::getInheritedForPrinting(
70697069
llvm::SetVector<ProtocolDecl *> protocols;
70707070
llvm::TinyPtrVector<ProtocolDecl *> uncheckedProtocols;
70717071
for (auto attr : decl->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
7072-
if (auto *proto = ctx.getProtocol(attr->getProtocolKind())) {
7072+
if (auto *proto = attr->getProtocol()) {
70737073
// The SerialExecutor conformance is only synthesized on the root
70747074
// actor class, so we can just test resilience immediately.
70757075
if (proto->isSpecificProtocol(KnownProtocolKind::SerialExecutor) &&
70767076
cast<ClassDecl>(decl)->isResilient())
70777077
continue;
7078-
if (attr->getProtocolKind() == KnownProtocolKind::RawRepresentable &&
7078+
if (proto->getKnownProtocolKind() &&
7079+
*proto->getKnownProtocolKind() == KnownProtocolKind::RawRepresentable &&
70797080
isa<EnumDecl>(decl) &&
70807081
cast<EnumDecl>(decl)->hasRawType())
70817082
continue;

lib/AST/ConformanceLookupTable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
920920
// Find a SynthesizedProtocolAttr corresponding to the protocol.
921921
for (auto attr : conformingNominal->getAttrs()
922922
.getAttributes<SynthesizedProtocolAttr>()) {
923-
auto otherProto = ctx.getProtocol(attr->getProtocolKind());
923+
auto otherProto = attr->getProtocol();
924924
if (otherProto == impliedProto) {
925925
// Set the conformance loader to the loader stashed inside
926926
// the attribute.

lib/AST/ProtocolConformance.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,49 +1052,48 @@ void NominalTypeDecl::prepareConformanceTable() const {
10521052

10531053
SmallPtrSet<ProtocolDecl *, 2> protocols;
10541054

1055-
auto addSynthesized = [&](KnownProtocolKind kind) {
1056-
if (auto *proto = getASTContext().getProtocol(kind)) {
1057-
if (protocols.count(proto) == 0) {
1058-
ConformanceTable->addSynthesizedConformance(
1059-
mutableThis, proto, mutableThis);
1060-
protocols.insert(proto);
1061-
}
1055+
auto addSynthesized = [&](ProtocolDecl *proto) {
1056+
llvm::dbgs() << "S\n";
1057+
if (protocols.count(proto) == 0) {
1058+
ConformanceTable->addSynthesizedConformance(
1059+
mutableThis, proto, mutableThis);
1060+
protocols.insert(proto);
10621061
}
10631062
};
10641063

10651064
// Add protocols for any synthesized protocol attributes.
10661065
for (auto attr : getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
1067-
addSynthesized(attr->getProtocolKind());
1066+
addSynthesized(attr->getProtocol());
10681067
}
10691068

10701069
// Add any implicit conformances.
10711070
if (auto theEnum = dyn_cast<EnumDecl>(mutableThis)) {
10721071
if (theEnum->hasCases() && theEnum->hasOnlyCasesWithoutAssociatedValues()) {
10731072
// Simple enumerations conform to Equatable.
1074-
addSynthesized(KnownProtocolKind::Equatable);
1073+
addSynthesized(ctx.getProtocol(KnownProtocolKind::Equatable));
10751074

10761075
// Simple enumerations conform to Hashable.
1077-
addSynthesized(KnownProtocolKind::Hashable);
1076+
addSynthesized(ctx.getProtocol(KnownProtocolKind::Hashable));
10781077
}
10791078

10801079
// Enumerations with a raw type conform to RawRepresentable.
10811080
if (theEnum->hasRawType() && !theEnum->getRawType()->hasError()) {
1082-
addSynthesized(KnownProtocolKind::RawRepresentable);
1081+
addSynthesized(ctx.getProtocol(KnownProtocolKind::RawRepresentable));
10831082
}
10841083
}
10851084

10861085
// Actor classes conform to the actor protocol.
10871086
if (auto classDecl = dyn_cast<ClassDecl>(mutableThis)) {
10881087
if (classDecl->isDistributedActor()) {
1089-
addSynthesized(KnownProtocolKind::DistributedActor);
1088+
addSynthesized(ctx.getProtocol(KnownProtocolKind::DistributedActor));
10901089
} else if (classDecl->isActor()) {
1091-
addSynthesized(KnownProtocolKind::Actor);
1090+
addSynthesized(ctx.getProtocol(KnownProtocolKind::Actor));
10921091
}
10931092
}
10941093

10951094
// Global actors conform to the GlobalActor protocol.
10961095
if (mutableThis->getAttrs().hasAttribute<GlobalActorAttr>()) {
1097-
addSynthesized(KnownProtocolKind::GlobalActor);
1096+
addSynthesized(ctx.getProtocol(KnownProtocolKind::GlobalActor));
10981097
}
10991098
}
11001099

lib/ClangImporter/ImportDecl.cpp

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ void ClangImporter::Implementation::addSynthesizedProtocolAttrs(
430430

431431
for (auto kind : synthesizedProtocolAttrs) {
432432
nominal->getAttrs().add(
433-
new (ctx) SynthesizedProtocolAttr(kind, this, isUnchecked));
433+
new (ctx) SynthesizedProtocolAttr(ctx.getProtocol(kind), this, isUnchecked));
434434
}
435435
}
436436

@@ -2611,23 +2611,56 @@ namespace {
26112611
}
26122612

26132613
auto result = VisitRecordDecl(decl);
2614+
if (!result)
2615+
return nullptr;
26142616

2615-
if (auto classDecl = dyn_cast_or_null<ClassDecl>(result))
2617+
if (auto classDecl = dyn_cast<ClassDecl>(result))
26162618
validateForeignReferenceType(decl, classDecl);
26172619

26182620
// If this module is declared as a C++ module, try to synthesize
26192621
// conformances to Swift protocols from the Cxx module.
26202622
auto clangModule = decl->getOwningModule();
26212623
if (clangModule && requiresCPlusPlus(clangModule)) {
2622-
if (auto structDecl = dyn_cast_or_null<NominalTypeDecl>(result)) {
2623-
conformToCxxIteratorIfNeeded(Impl, structDecl, decl);
2624-
conformToCxxSequenceIfNeeded(Impl, structDecl, decl);
2625-
}
2624+
auto nominalDecl = cast<NominalTypeDecl>(result);
2625+
conformToCxxIteratorIfNeeded(Impl, nominalDecl, decl);
2626+
conformToCxxSequenceIfNeeded(Impl, nominalDecl, decl);
26262627
}
26272628

2629+
addExplicitProtocolConformances(cast<NominalTypeDecl>(result));
2630+
26282631
return result;
26292632
}
26302633

2634+
void addExplicitProtocolConformances(NominalTypeDecl *decl) {
2635+
auto clangDecl = decl->getClangDecl();
2636+
2637+
SmallVector<ValueDecl *, 2> results;
2638+
auto conformsToAttr =
2639+
llvm::find_if(clangDecl->getAttrs(), [](auto *attr) {
2640+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
2641+
return swiftAttr->getAttribute().startswith("conforms_to:");
2642+
return false;
2643+
});
2644+
if (conformsToAttr == clangDecl->getAttrs().end())
2645+
return;
2646+
2647+
auto name = cast<clang::SwiftAttrAttr>(*conformsToAttr)
2648+
->getAttribute()
2649+
.drop_front(StringRef("conforms_to:").size())
2650+
.str();
2651+
2652+
for (auto &module : Impl.SwiftContext.getLoadedModules()) {
2653+
module.second->lookupValue(Impl.SwiftContext.getIdentifier(name),
2654+
NLKind::UnqualifiedLookup, results);
2655+
}
2656+
for (auto proto : results) {
2657+
if (auto protocol = dyn_cast<ProtocolDecl>(proto)) {
2658+
decl->getAttrs().add(
2659+
new (Impl.SwiftContext) SynthesizedProtocolAttr(protocol, &Impl, false));
2660+
}
2661+
}
2662+
}
2663+
26312664
bool isSpecializationDepthGreaterThan(
26322665
const clang::ClassTemplateSpecializationDecl *decl, unsigned maxDepth) {
26332666
for (auto arg : decl->getTemplateArgs().asArray()) {
@@ -5126,10 +5159,11 @@ static bool conformsToProtocolInOriginalModule(NominalTypeDecl *nominal,
51265159
if (inheritanceListContainsProtocol(nominal, proto))
51275160
return true;
51285161

5129-
for (auto attr : nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>())
5130-
if (auto *otherProto = ctx.getProtocol(attr->getProtocolKind()))
5131-
if (otherProto == proto || otherProto->inheritsFrom(proto))
5132-
return true;
5162+
for (auto attr : nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
5163+
auto *otherProto = attr->getProtocol();
5164+
if (otherProto == proto || otherProto->inheritsFrom(proto))
5165+
return true;
5166+
}
51335167

51345168
// Only consider extensions from the original module...or from an overlay
51355169
// or the Swift half of a mixed-source framework.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_DESTRUCTORS_H
2+
#define TEST_INTEROP_CXX_CLASS_INPUTS_DESTRUCTORS_H
3+
4+
struct
5+
__attribute__((swift_attr("conforms_to:Testable")))
6+
HasTest {
7+
void test() const;
8+
};
9+
10+
struct
11+
__attribute__((swift_attr("conforms_to:Playable")))
12+
__attribute__((swift_attr("import_reference")))
13+
__attribute__((swift_attr("retain:immortal")))
14+
__attribute__((swift_attr("release:immortal")))
15+
HasPlay {
16+
void play() const;
17+
};
18+
19+
20+
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_DESTRUCTORS_H

test/Interop/Cxx/class/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,8 @@ module ForwardDeclaredInNamespace {
126126
header "forward-declared-in-namespace.h"
127127
requires cplusplus
128128
}
129+
130+
module ConformsTo {
131+
header "conforms-to.h"
132+
requires cplusplus
133+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-experimental-cxx-interop
2+
3+
import ConformsTo
4+
5+
protocol Testable {
6+
func test()
7+
}
8+
9+
protocol Playable {
10+
func play()
11+
}
12+
13+
func callee(_ _: Testable) {
14+
15+
}
16+
17+
func caller(_ x: HasTest) {
18+
callee(x)
19+
}
20+
21+
func callee(_ _: Playable) {
22+
23+
}
24+
25+
func caller(_ x: Playable) {
26+
callee(x)
27+
}

0 commit comments

Comments
 (0)