[c++ interop] Swift should allow multiple SWIFT_CONFORMS_TO_PROTOCOL attributes on a C++ class.

This commit is contained in:
Crazy凡
2025-02-11 16:26:39 +08:00
parent 374fc90b42
commit c05d1fc135
5 changed files with 183 additions and 18 deletions

View File

@@ -294,6 +294,9 @@ ERROR(both_returns_retained_returns_unretained, none,
"SWIFT_RETURNS_UNRETAINED",
(const clang::NamedDecl *))
ERROR(redundant_conformance_protocol,none,
"redundant conformance of %0 to protocol '%1'", (Type, StringRef))
ERROR(returns_retained_or_returns_unretained_for_non_cxx_frt_values, none,
"%0 cannot be annotated with either SWIFT_RETURNS_RETAINED or "
"SWIFT_RETURNS_UNRETAINED because it is not returning "

View File

@@ -69,6 +69,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
@@ -3105,6 +3106,8 @@ namespace {
return result;
}
using ProtocolSet = llvm::SmallSet<ProtocolDecl *, 4>;
void
addExplicitProtocolConformances(NominalTypeDecl *decl,
const clang::CXXRecordDecl *clangDecl) {
@@ -3119,25 +3122,26 @@ namespace {
if (!clangDecl->hasAttrs())
return;
SmallVector<ValueDecl *, 1> results;
auto conformsToAttr =
llvm::find_if(clangDecl->getAttrs(), [](auto *attr) {
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
return swiftAttr->getAttribute().starts_with("conforms_to:");
return false;
});
if (conformsToAttr == clangDecl->getAttrs().end())
return;
ProtocolSet alreadyAdded;
llvm::for_each(clangDecl->getAttrs(), [&](auto *attr) {
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
if (swiftAttr->getAttribute().starts_with("conforms_to:"))
addExplicitProtocolConformance(decl, swiftAttr, alreadyAdded);
}
});
}
auto conformsToValue = cast<clang::SwiftAttrAttr>(*conformsToAttr)
->getAttribute()
void addExplicitProtocolConformance(NominalTypeDecl *decl,
clang::SwiftAttrAttr *conformsToAttr,
ProtocolSet &alreadyAdded) {
auto conformsToValue = conformsToAttr->getAttribute()
.drop_front(StringRef("conforms_to:").size())
.str();
auto names = StringRef(conformsToValue).split('.');
auto moduleName = names.first;
auto protocolName = names.second;
if (protocolName.empty()) {
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
HeaderLoc attrLoc(conformsToAttr->getLocation());
Impl.diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
return;
}
@@ -3145,20 +3149,24 @@ namespace {
auto *mod = Impl.SwiftContext.getModuleByIdentifier(
Impl.SwiftContext.getIdentifier(moduleName));
if (!mod) {
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
HeaderLoc attrLoc(conformsToAttr->getLocation());
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to_module,
conformsToValue, moduleName);
return;
}
SmallVector<ValueDecl *, 1> results;
mod->lookupValue(Impl.SwiftContext.getIdentifier(protocolName),
NLKind::UnqualifiedLookup, results);
if (results.empty()) {
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
HeaderLoc attrLoc(conformsToAttr->getLocation());
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
moduleName);
return;
} else if (results.size() != 1) {
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
}
if (results.size() != 1) {
HeaderLoc attrLoc(conformsToAttr->getLocation());
Impl.diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
moduleName);
return;
@@ -3166,10 +3174,17 @@ namespace {
auto result = results.front();
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
auto [_, inserted] = alreadyAdded.insert(protocol);
if (!inserted) {
HeaderLoc attrLoc(conformsToAttr->getLocation());
Impl.diagnose(attrLoc, diag::redundant_conformance_protocol,
decl->getDeclaredInterfaceType(), conformsToValue);
}
decl->getAttrs().add(
new (Impl.SwiftContext) SynthesizedProtocolAttr(protocol, &Impl, false));
} else {
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
HeaderLoc attrLoc((conformsToAttr)->getLocation());
Impl.diagnose(attrLoc, diag::conforms_to_not_protocol, result,
conformsToValue);
}

View File

@@ -16,6 +16,13 @@ struct
void play() const;
};
struct __attribute__((swift_attr("conforms_to:SwiftTest.Testable")))
__attribute__((swift_attr(
"conforms_to:SwiftTest.Playable"))) MultipleConformanceHasTestAndPlay {
void test() const;
void play() const;
};
struct
__attribute__((swift_attr("conforms_to:ImportedModule.ProtocolFromImportedModule")))
HasImportedConf {
@@ -24,6 +31,8 @@ struct
struct DerivedFromHasTest : HasTest {};
struct DerivedFromDerivedFromHasTest : HasTest {};
struct DerivedFromMultipleConformanceHasTestAndPlay
: MultipleConformanceHasTestAndPlay {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.Testable")))
DerivedFromDerivedFromHasTestWithDuplicateArg : HasTest {};

View File

@@ -16,6 +16,65 @@ struct __attribute__((swift_attr("conforms_to:SwiftTest.X"))) CX {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.A"))) CA {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.B"))) CB {};
struct __attribute__((swift_attr("conforms_to:X")))
__attribute__((swift_attr("conforms_to:X"))) CXX {};
struct __attribute__((swift_attr("conforms_to:X")))
__attribute__((swift_attr("conforms_to:Mod.X"))) CXModX {};
struct __attribute__((swift_attr("conforms_to:X")))
__attribute__((swift_attr("conforms_to:SwiftTest.X"))) CXTextX {};
struct __attribute__((swift_attr("conforms_to:X")))
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CXA {};
struct __attribute__((swift_attr("conforms_to:X")))
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CXB {};
struct __attribute__((swift_attr("conforms_to:X")))
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CXC {};
struct __attribute__((swift_attr("conforms_to:Mod.X")))
__attribute__((swift_attr("conforms_to:Mod.X"))) CModXModX {};
struct __attribute__((swift_attr("conforms_to:Mod.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.X"))) CModXTestX {};
struct __attribute__((swift_attr("conforms_to:Mod.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CModXA {};
struct __attribute__((swift_attr("conforms_to:Mod.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CModXB {};
struct __attribute__((swift_attr("conforms_to:Mod.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CModXC {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.X"))) CTestXTextX {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CTextXA {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CTextXB {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.X")))
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CTextXC {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.A")))
__attribute__((swift_attr("conforms_to:SwiftTest.A"))) CAA {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.A")))
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CAB {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.A")))
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CAC {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.B")))
__attribute__((swift_attr("conforms_to:SwiftTest.B"))) CBB {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.B")))
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CBC {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.C")))
__attribute__((swift_attr("conforms_to:SwiftTest.C"))) CCC {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.D"))) CD {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.D"))) CDD: CD {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.D"))) CD2 {};
struct CCDCD2 : CD, CD2 {};
struct __attribute__((swift_attr("conforms_to:SwiftTest.D")))
__attribute__((swift_attr("conforms_to:SwiftTest.E"))) CDE {};
//--- test.swift
import Test
@@ -25,10 +84,78 @@ struct B {}
protocol A {}
protocol A {}
protocol C {}
protocol D {}
protocol E: D {}
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
func test(_ inv: CInv, _ invMod: CModInv, _ x: CX, _ a: CA, _ b: CB) {}
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
// CHECK: error: expected module name and protocol name separated by '.' in protocol conformance; 'X' is invalid
func test(_ xx: CXX, _ xModx: CXModX, _ xTextX: CXTextX, _ cxa: CXA, _ cxb: CXB, _ cxc: CXC) {}
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
// CHECK: module 'Mod' in specified protocol conformance 'Mod.X' is not found; did you mean to import it first?
func test(_ modXModX: CModXModX, _ modXTestX: CModXTestX, _ modXA: CModXA, _ modXB: CModXB, _ modXC: CModXC) {}
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
// CHECK: error: protocol 'X' in specified protocol conformance is not found in module 'SwiftTest'
func test(_ testXTextX: CTestXTextX, _ textXA: CTextXA, _ textXB: CTextXB, _ textXC: CTextXC) {}
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
// CHECK: error: ambiguous reference to protocol 'A' in specified protocol conformance; module 'SwiftTest' contains multiple protocols named 'A'
func test(_ aa: CAA, _ ab: CAB, _ ac: CAC) {}
// CHECK: error: struct 'B' referenced in protocol conformance 'SwiftTest.B' is not a protocol
func test(_ bb: CBB, _ bc: CBC) {}
// CHECK: error: redundant conformance of 'CCC' to protocol 'SwiftTest.C'
func test(_ cc: CCC) {}
// CHECK-NOT: error: redundant conformance of 'CDD' to protocol 'SwiftTest.D'
// CHECK-NOT: error: redundant conformance of 'CCDCD2' to protocol 'SwiftTest.D'
// CHECK-NOT: error: redundant conformance of 'CDE' to protocol 'SwiftTest.D'
func test(_ dd: CDD, _ dd2: CCDCD2, de: CDE) {}

View File

@@ -35,13 +35,24 @@ func callee(_ _: Playable) {
func caller(_ x: Playable) {
callee(x)
}
func caller(_ x: MultipleConformanceHasTestAndPlay) {
callee(x as Testable)
callee(x as Playable)
}
func caller(_ x: DerivedFromHasPlay) { callee(x) }
func caller(_ x: DerivedFromDerivedFromHasPlay) { callee(x) }
func caller(_ x: DerivedFromMultipleConformanceHasTestAndPlay) {
callee(x as Testable)
callee(x as Playable)
}
func caller(_ x: HasTestAndPlay) {
callee(x as Testable)
callee(x as Playable)
}
func caller(_ x: DerivedFromHasTestAndPlay) {
callee(x as Testable)
callee(x as Playable)