Skip to content

Commit 21f23dc

Browse files
CrazyFanFanDougGregor
authored andcommitted
[c++ interop] Swift should allow multiple SWIFT_CONFORMS_TO_PROTOCOL attributes on a C++ class.
(cherry picked from commit c05d1fc)
1 parent 0bfa526 commit 21f23dc

File tree

5 files changed

+236
-1
lines changed

5 files changed

+236
-1
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ ERROR(both_returns_retained_returns_unretained, none,
290290
"SWIFT_RETURNS_UNRETAINED",
291291
(const clang::NamedDecl *))
292292

293+
ERROR(redundant_conformance_protocol,none,
294+
"redundant conformance of %0 to protocol '%1'", (Type, StringRef))
295+
293296
ERROR(returns_retained_or_returns_unretained_for_non_cxx_frt_values, none,
294297
"%0 cannot be annotated with either SWIFT_RETURNS_RETAINED or "
295298
"SWIFT_RETURNS_UNRETAINED because it is not returning "

lib/ClangImporter/ImportDecl.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474

7575
#include "llvm/ADT/STLExtras.h"
7676
#include "llvm/ADT/SmallBitVector.h"
77+
#include "llvm/ADT/SmallSet.h"
7778
#include "llvm/ADT/SmallString.h"
7879
#include "llvm/ADT/Statistic.h"
7980
#include "llvm/ADT/StringExtras.h"
@@ -3138,6 +3139,90 @@ namespace {
31383139
return result;
31393140
}
31403141

3142+
using ProtocolSet = llvm::SmallSet<ProtocolDecl *, 4>;
3143+
3144+
void
3145+
addExplicitProtocolConformances(NominalTypeDecl *decl,
3146+
const clang::CXXRecordDecl *clangDecl) {
3147+
// Propagate conforms_to attribute from public base classes.
3148+
for (auto base : clangDecl->bases()) {
3149+
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
3150+
continue;
3151+
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
3152+
addExplicitProtocolConformances(decl, baseClangDecl);
3153+
}
3154+
3155+
if (!clangDecl->hasAttrs())
3156+
return;
3157+
3158+
ProtocolSet alreadyAdded;
3159+
llvm::for_each(clangDecl->getAttrs(), [&](auto *attr) {
3160+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3161+
if (swiftAttr->getAttribute().starts_with("conforms_to:"))
3162+
addExplicitProtocolConformance(decl, swiftAttr, alreadyAdded);
3163+
}
3164+
});
3165+
}
3166+
3167+
void addExplicitProtocolConformance(NominalTypeDecl *decl,
3168+
clang::SwiftAttrAttr *conformsToAttr,
3169+
ProtocolSet &alreadyAdded) {
3170+
auto conformsToValue = conformsToAttr->getAttribute()
3171+
.drop_front(StringRef("conforms_to:").size())
3172+
.str();
3173+
auto names = StringRef(conformsToValue).split('.');
3174+
auto moduleName = names.first;
3175+
auto protocolName = names.second;
3176+
if (protocolName.empty()) {
3177+
HeaderLoc attrLoc(conformsToAttr->getLocation());
3178+
Impl.diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
3179+
return;
3180+
}
3181+
3182+
auto *mod = Impl.SwiftContext.getModuleByIdentifier(
3183+
Impl.SwiftContext.getIdentifier(moduleName));
3184+
if (!mod) {
3185+
HeaderLoc attrLoc(conformsToAttr->getLocation());
3186+
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to_module,
3187+
conformsToValue, moduleName);
3188+
return;
3189+
}
3190+
3191+
SmallVector<ValueDecl *, 1> results;
3192+
mod->lookupValue(Impl.SwiftContext.getIdentifier(protocolName),
3193+
NLKind::UnqualifiedLookup, results);
3194+
if (results.empty()) {
3195+
HeaderLoc attrLoc(conformsToAttr->getLocation());
3196+
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
3197+
moduleName);
3198+
return;
3199+
}
3200+
3201+
if (results.size() != 1) {
3202+
HeaderLoc attrLoc(conformsToAttr->getLocation());
3203+
Impl.diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
3204+
moduleName);
3205+
return;
3206+
}
3207+
3208+
auto result = results.front();
3209+
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
3210+
auto [_, inserted] = alreadyAdded.insert(protocol);
3211+
if (!inserted) {
3212+
HeaderLoc attrLoc(conformsToAttr->getLocation());
3213+
Impl.diagnose(attrLoc, diag::redundant_conformance_protocol,
3214+
decl->getDeclaredInterfaceType(), conformsToValue);
3215+
}
3216+
3217+
decl->getAttrs().add(
3218+
new (Impl.SwiftContext) SynthesizedProtocolAttr(protocol, &Impl, false));
3219+
} else {
3220+
HeaderLoc attrLoc((conformsToAttr)->getLocation());
3221+
Impl.diagnose(attrLoc, diag::conforms_to_not_protocol, result,
3222+
conformsToValue);
3223+
}
3224+
}
3225+
31413226
bool isSpecializationDepthGreaterThan(
31423227
const clang::ClassTemplateSpecializationDecl *decl, unsigned maxDepth) {
31433228
for (auto arg : decl->getTemplateArgs().asArray()) {

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)