Skip to content

Commit 91e730a

Browse files
authored
Merge pull request swiftlang#79425 from CrazyFanFan/feature/cxx_multiple_swift_conforms_to_protocol
[c++ interop] Swift should allow multiple SWIFT_CONFORMS_TO_PROTOCOL attributes on a C++ class
2 parents 8834f6c + c05d1fc commit 91e730a

File tree

5 files changed

+183
-18
lines changed

5 files changed

+183
-18
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ ERROR(both_returns_retained_returns_unretained, none,
294294
"SWIFT_RETURNS_UNRETAINED",
295295
(const clang::NamedDecl *))
296296

297+
ERROR(redundant_conformance_protocol,none,
298+
"redundant conformance of %0 to protocol '%1'", (Type, StringRef))
299+
297300
ERROR(returns_retained_or_returns_unretained_for_non_cxx_frt_values, none,
298301
"%0 cannot be annotated with either SWIFT_RETURNS_RETAINED or "
299302
"SWIFT_RETURNS_UNRETAINED because it is not returning "

lib/ClangImporter/ImportDecl.cpp

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969

7070
#include "llvm/ADT/STLExtras.h"
7171
#include "llvm/ADT/SmallBitVector.h"
72+
#include "llvm/ADT/SmallSet.h"
7273
#include "llvm/ADT/SmallString.h"
7374
#include "llvm/ADT/Statistic.h"
7475
#include "llvm/ADT/StringExtras.h"
@@ -3105,6 +3106,8 @@ namespace {
31053106
return result;
31063107
}
31073108

3109+
using ProtocolSet = llvm::SmallSet<ProtocolDecl *, 4>;
3110+
31083111
void
31093112
addExplicitProtocolConformances(NominalTypeDecl *decl,
31103113
const clang::CXXRecordDecl *clangDecl) {
@@ -3119,57 +3122,69 @@ namespace {
31193122
if (!clangDecl->hasAttrs())
31203123
return;
31213124

3122-
SmallVector<ValueDecl *, 1> results;
3123-
auto conformsToAttr =
3124-
llvm::find_if(clangDecl->getAttrs(), [](auto *attr) {
3125-
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
3126-
return swiftAttr->getAttribute().starts_with("conforms_to:");
3127-
return false;
3128-
});
3129-
if (conformsToAttr == clangDecl->getAttrs().end())
3130-
return;
3125+
ProtocolSet alreadyAdded;
3126+
llvm::for_each(clangDecl->getAttrs(), [&](auto *attr) {
3127+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3128+
if (swiftAttr->getAttribute().starts_with("conforms_to:"))
3129+
addExplicitProtocolConformance(decl, swiftAttr, alreadyAdded);
3130+
}
3131+
});
3132+
}
31313133

3132-
auto conformsToValue = cast<clang::SwiftAttrAttr>(*conformsToAttr)
3133-
->getAttribute()
3134+
void addExplicitProtocolConformance(NominalTypeDecl *decl,
3135+
clang::SwiftAttrAttr *conformsToAttr,
3136+
ProtocolSet &alreadyAdded) {
3137+
auto conformsToValue = conformsToAttr->getAttribute()
31343138
.drop_front(StringRef("conforms_to:").size())
31353139
.str();
31363140
auto names = StringRef(conformsToValue).split('.');
31373141
auto moduleName = names.first;
31383142
auto protocolName = names.second;
31393143
if (protocolName.empty()) {
3140-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3144+
HeaderLoc attrLoc(conformsToAttr->getLocation());
31413145
Impl.diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
31423146
return;
31433147
}
31443148

31453149
auto *mod = Impl.SwiftContext.getModuleByIdentifier(
31463150
Impl.SwiftContext.getIdentifier(moduleName));
31473151
if (!mod) {
3148-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3152+
HeaderLoc attrLoc(conformsToAttr->getLocation());
31493153
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to_module,
31503154
conformsToValue, moduleName);
31513155
return;
31523156
}
3157+
3158+
SmallVector<ValueDecl *, 1> results;
31533159
mod->lookupValue(Impl.SwiftContext.getIdentifier(protocolName),
31543160
NLKind::UnqualifiedLookup, results);
31553161
if (results.empty()) {
3156-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3162+
HeaderLoc attrLoc(conformsToAttr->getLocation());
31573163
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
31583164
moduleName);
31593165
return;
3160-
} else if (results.size() != 1) {
3161-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3166+
}
3167+
3168+
if (results.size() != 1) {
3169+
HeaderLoc attrLoc(conformsToAttr->getLocation());
31623170
Impl.diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
31633171
moduleName);
31643172
return;
31653173
}
31663174

31673175
auto result = results.front();
31683176
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
3177+
auto [_, inserted] = alreadyAdded.insert(protocol);
3178+
if (!inserted) {
3179+
HeaderLoc attrLoc(conformsToAttr->getLocation());
3180+
Impl.diagnose(attrLoc, diag::redundant_conformance_protocol,
3181+
decl->getDeclaredInterfaceType(), conformsToValue);
3182+
}
3183+
31693184
decl->getAttrs().add(
31703185
new (Impl.SwiftContext) SynthesizedProtocolAttr(protocol, &Impl, false));
31713186
} else {
3172-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3187+
HeaderLoc attrLoc((conformsToAttr)->getLocation());
31733188
Impl.diagnose(attrLoc, diag::conforms_to_not_protocol, result,
31743189
conformsToValue);
31753190
}

test/Interop/Cxx/class/Inputs/conforms-to.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ struct
1616
void play() const;
1717
};
1818

19+
struct __attribute__((swift_attr("conforms_to:SwiftTest.Testable")))
20+
__attribute__((swift_attr(
21+
"conforms_to:SwiftTest.Playable"))) MultipleConformanceHasTestAndPlay {
22+
void test() const;
23+
void play() const;
24+
};
25+
1926
struct
2027
__attribute__((swift_attr("conforms_to:ImportedModule.ProtocolFromImportedModule")))
2128
HasImportedConf {
@@ -24,6 +31,8 @@ struct
2431

2532
struct DerivedFromHasTest : HasTest {};
2633
struct DerivedFromDerivedFromHasTest : HasTest {};
34+
struct DerivedFromMultipleConformanceHasTestAndPlay
35+
: MultipleConformanceHasTestAndPlay {};
2736

2837
struct __attribute__((swift_attr("conforms_to:SwiftTest.Testable")))
2938
DerivedFromDerivedFromHasTestWithDuplicateArg : HasTest {};

test/Interop/Cxx/class/conforms-to-errors.swift

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,65 @@ struct __attribute__((swift_attr("conforms_to:SwiftTest.X"))) CX {};
1616
struct __attribute__((swift_attr("conforms_to:SwiftTest.A"))) CA {};
1717
struct __attribute__((swift_attr("conforms_to:SwiftTest.B"))) CB {};
1818

19+
struct __attribute__((swift_attr("conforms_to:X")))
20+
__attribute__((swift_attr("conforms_to:X"))) CXX {};
21+
struct __attribute__((swift_attr("conforms_to:X")))
22+
__attribute__((swift_attr("conforms_to:Mod.X"))) CXModX {};
23+
struct __attribute__((swift_attr("conforms_to:X")))
24+
__attribute__((swift_attr("conforms_to:SwiftTest.X"))) CXTextX {};
25+
struct __attribute__((swift_attr("conforms_to:X")))
26+
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CXA {};
27+
struct __attribute__((swift_attr("conforms_to:X")))
28+
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CXB {};
29+
struct __attribute__((swift_attr("conforms_to:X")))
30+
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CXC {};
31+
32+
33+
struct __attribute__((swift_attr("conforms_to:Mod.X")))
34+
__attribute__((swift_attr("conforms_to:Mod.X"))) CModXModX {};
35+
struct __attribute__((swift_attr("conforms_to:Mod.X")))
36+
__attribute__((swift_attr("conforms_to:SwiftTest.X"))) CModXTestX {};
37+
struct __attribute__((swift_attr("conforms_to:Mod.X")))
38+
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CModXA {};
39+
struct __attribute__((swift_attr("conforms_to:Mod.X")))
40+
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CModXB {};
41+
struct __attribute__((swift_attr("conforms_to:Mod.X")))
42+
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CModXC {};
43+
44+
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
45+
__attribute__((swift_attr("conforms_to:SwiftTest.X"))) CTestXTextX {};
46+
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
47+
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CTextXA {};
48+
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
49+
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CTextXB {};
50+
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
51+
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CTextXC {};
52+
53+
54+
struct __attribute__((swift_attr("conforms_to:SwiftTest.A")))
55+
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CAA {};
56+
struct __attribute__((swift_attr("conforms_to:SwiftTest.A")))
57+
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CAB {};
58+
struct __attribute__((swift_attr("conforms_to:SwiftTest.A")))
59+
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CAC {};
60+
61+
struct __attribute__((swift_attr("conforms_to:SwiftTest.B")))
62+
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CBB {};
63+
struct __attribute__((swift_attr("conforms_to:SwiftTest.B")))
64+
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CBC {};
65+
66+
struct __attribute__((swift_attr("conforms_to:SwiftTest.C")))
67+
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CCC {};
68+
69+
struct __attribute__((swift_attr("conforms_to:SwiftTest.D"))) CD {};
70+
struct __attribute__((swift_attr("conforms_to:SwiftTest.D"))) CDD: CD {};
71+
72+
struct __attribute__((swift_attr("conforms_to:SwiftTest.D"))) CD2 {};
73+
struct CCDCD2 : CD, CD2 {};
74+
75+
struct __attribute__((swift_attr("conforms_to:SwiftTest.D")))
76+
__attribute__((swift_attr("conforms_to:SwiftTest.E"))) CDE {};
77+
1978
//--- test.swift
2079

2180
import Test
@@ -25,10 +84,78 @@ struct B {}
2584
protocol A {}
2685
protocol A {}
2786

87+
protocol C {}
88+
protocol D {}
89+
protocol E: D {}
90+
2891
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
2992
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
3093
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
3194
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
3295
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
33-
3496
func test(_ inv: CInv, _ invMod: CModInv, _ x: CX, _ a: CA, _ b: CB) {}
97+
98+
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
99+
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
100+
101+
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
102+
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
103+
104+
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
105+
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
106+
107+
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
108+
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
109+
110+
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
111+
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
112+
113+
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
114+
func test(_ xx: CXX, _ xModx: CXModX, _ xTextX: CXTextX, _ cxa: CXA, _ cxb: CXB, _ cxc: CXC) {}
115+
116+
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
117+
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
118+
119+
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
120+
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
121+
122+
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
123+
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
124+
125+
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
126+
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
127+
128+
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
129+
func test(_ modXModX: CModXModX, _ modXTestX: CModXTestX, _ modXA: CModXA, _ modXB: CModXB, _ modXC: CModXC) {}
130+
131+
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
132+
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
133+
134+
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
135+
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
136+
137+
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
138+
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
139+
140+
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
141+
func test(_ testXTextX: CTestXTextX, _ textXA: CTextXA, _ textXB: CTextXB, _ textXC: CTextXC) {}
142+
143+
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
144+
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
145+
146+
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
147+
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
148+
149+
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
150+
func test(_ aa: CAA, _ ab: CAB, _ ac: CAC) {}
151+
152+
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
153+
func test(_ bb: CBB, _ bc: CBC) {}
154+
155+
// CHECK: error: redundant conformance of 'CCC' to protocol 'SwiftTest.C'
156+
func test(_ cc: CCC) {}
157+
158+
// CHECK-NOT: error: redundant conformance of 'CDD' to protocol 'SwiftTest.D'
159+
// CHECK-NOT: error: redundant conformance of 'CCDCD2' to protocol 'SwiftTest.D'
160+
// CHECK-NOT: error: redundant conformance of 'CDE' to protocol 'SwiftTest.D'
161+
func test(_ dd: CDD, _ dd2: CCDCD2, de: CDE) {}

test/Interop/Cxx/class/conforms-to.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,24 @@ func callee(_ _: Playable) {
3535
func caller(_ x: Playable) {
3636
callee(x)
3737
}
38+
39+
func caller(_ x: MultipleConformanceHasTestAndPlay) {
40+
callee(x as Testable)
41+
callee(x as Playable)
42+
}
43+
3844
func caller(_ x: DerivedFromHasPlay) { callee(x) }
3945
func caller(_ x: DerivedFromDerivedFromHasPlay) { callee(x) }
46+
func caller(_ x: DerivedFromMultipleConformanceHasTestAndPlay) {
47+
callee(x as Testable)
48+
callee(x as Playable)
49+
}
4050

4151
func caller(_ x: HasTestAndPlay) {
4252
callee(x as Testable)
4353
callee(x as Playable)
4454
}
55+
4556
func caller(_ x: DerivedFromHasTestAndPlay) {
4657
callee(x as Testable)
4758
callee(x as Playable)

0 commit comments

Comments
 (0)