Skip to content

Commit 56d207a

Browse files
authored
Merge pull request #72277 from xedin/improvements-to-protocol-witness-matching
[Sema/ClangImporter] Improvements to witness matching
2 parents 7df8812 + 5890de3 commit 56d207a

12 files changed

+114
-81
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -820,18 +820,6 @@ class LocatorPathElt::Witness final : public StoredPointerElement<ValueDecl> {
820820
}
821821
};
822822

823-
class LocatorPathElt::ProtocolRequirement final : public StoredPointerElement<ValueDecl> {
824-
public:
825-
ProtocolRequirement(ValueDecl *decl)
826-
: StoredPointerElement(PathElementKind::ProtocolRequirement, decl) {}
827-
828-
ValueDecl *getDecl() const { return getStoredPointer(); }
829-
830-
static bool classof(const LocatorPathElt *elt) {
831-
return elt->getKind() == PathElementKind::ProtocolRequirement;
832-
}
833-
};
834-
835823
class LocatorPathElt::GenericParameter final : public StoredPointerElement<GenericTypeParamType> {
836824
public:
837825
GenericParameter(GenericTypeParamType *type)

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ SIMPLE_LOCATOR_PATH_ELT(ParentType)
161161

162162
/// The requirement that we're matching during protocol conformance
163163
/// checking.
164-
CUSTOM_LOCATOR_PATH_ELT(ProtocolRequirement)
164+
SIMPLE_LOCATOR_PATH_ELT(ProtocolRequirement)
165165

166166
/// Type parameter requirements.
167167
ABSTRACT_LOCATOR_PATH_ELT(AnyRequirement)

lib/AST/Type.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,9 @@ Type TypeBase::stripConcurrency(bool recurse, bool dropGlobalActor) {
994994
existentialType->getConstraintType().getPointer())
995995
return Type(this);
996996

997+
if (newConstraintType->getClassOrBoundGenericClass())
998+
return newConstraintType;
999+
9971000
return ExistentialType::get(newConstraintType);
9981001
}
9991002

lib/ClangImporter/ImportType.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,13 @@ class GetSendableType :
19161916
auto composition =
19171917
ProtocolCompositionType::get(ctx, members, inverses, explicitAnyObject);
19181918

1919-
return { composition, true };
1919+
// If we started from a protocol or a composition we should already
1920+
// be in an existential context. Otherwise we'd have to wrap a new
1921+
// composition into an existential.
1922+
if (isa<ProtocolType>(ty) || isa<ProtocolCompositionType>(ty))
1923+
return {composition, true};
1924+
1925+
return {ExistentialType::get(composition), true};
19201926
}
19211927

19221928
/// Visitor action: Recurse into the children of this type and try to add

lib/Sema/CSSimplify.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5586,6 +5586,35 @@ bool ConstraintSystem::repairFailures(
55865586
return false;
55875587
}
55885588

5589+
if (auto *VD = getAsDecl<ValueDecl>(anchor)) {
5590+
// Matching a witness to a ObjC protocol requirement.
5591+
if (VD->isObjC() && VD->isProtocolRequirement() &&
5592+
path[0].is<LocatorPathElt::Witness>() &&
5593+
// Note that the condition below is very important,
5594+
// we need to wait until the very last moment to strip
5595+
// the concurrency annotations from the inner most type.
5596+
conversionsOrFixes.empty()) {
5597+
// Allow requirements to introduce `swift_attr` annotations
5598+
// (note that `swift_attr` in type contexts weren't supported
5599+
// before) and for witnesses to adopt them gradually by matching
5600+
// with a warning in non-strict concurrency mode.
5601+
if (!(Context.isSwiftVersionAtLeast(6) ||
5602+
Context.LangOpts.StrictConcurrencyLevel ==
5603+
StrictConcurrency::Complete)) {
5604+
auto strippedLHS = lhs->stripConcurrency(/*resursive=*/true,
5605+
/*dropGlobalActor=*/true);
5606+
auto strippedRHS = rhs->stripConcurrency(/*resursive=*/true,
5607+
/*dropGlobalActor=*/true);
5608+
auto result = matchTypes(strippedLHS, strippedRHS, matchKind,
5609+
flags | TMF_ApplyingFix, locator);
5610+
if (!result.isFailure()) {
5611+
increaseScore(SK_MissingSynthesizableConformance, locator);
5612+
return true;
5613+
}
5614+
}
5615+
}
5616+
}
5617+
55895618
auto elt = path.back();
55905619
switch (elt.getKind()) {
55915620
case ConstraintLocator::LValueConversion: {
@@ -8560,7 +8589,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
85608589
auto synthesizeConformance = [&]() {
85618590
ProtocolConformanceRef synthesized(protocol);
85628591
auto witnessLoc = getConstraintLocator(
8563-
/*anchor=*/{}, LocatorPathElt::Witness(witness));
8592+
locator.getAnchor(), LocatorPathElt::Witness(witness));
85648593
SynthesizedConformances.insert({witnessLoc, synthesized});
85658594
return recordConformance(synthesized);
85668595
};

lib/Sema/ConstraintLocator.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,7 @@ void LocatorPathElt::dump(raw_ostream &out) const {
304304
break;
305305
}
306306
case ConstraintLocator::ProtocolRequirement: {
307-
auto reqElt = elt.castTo<LocatorPathElt::ProtocolRequirement>();
308-
out << "protocol requirement ";
309-
reqElt.getDecl()->dumpRef(out);
307+
out << "protocol requirement";
310308
break;
311309
}
312310
case ConstraintLocator::Witness: {

lib/Sema/ConstraintSystem.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7985,6 +7985,16 @@ void constraints::dumpAnchor(ASTNode anchor, SourceManager *SM,
79857985
out << '@';
79867986
pattern->getLoc().print(out, *SM);
79877987
}
7988+
} else if (auto *decl = anchor.dyn_cast<Decl *>()) {
7989+
if (auto *VD = dyn_cast<ValueDecl>(decl)) {
7990+
VD->dumpRef(out);
7991+
} else {
7992+
out << "<<" << Decl::getKindName(decl->getKind()) << ">>";
7993+
if (SM) {
7994+
out << "@";
7995+
decl->getLoc().print(out, *SM);
7996+
}
7997+
}
79887998
}
79897999
// TODO(diagnostics): Implement the rest of the cases.
79908000
}

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 31 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,6 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
10261026

10271027
// Initialized by the setup operation.
10281028
std::optional<ConstraintSystem> cs;
1029-
ConstraintLocator *locator = nullptr;
10301029
ConstraintLocator *reqLocator = nullptr;
10311030
ConstraintLocator *witnessLocator = nullptr;
10321031
Type witnessType, openWitnessType;
@@ -1115,8 +1114,8 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11151114
selfTy = syntheticEnv->mapTypeIntoContext(selfTy);
11161115

11171116
// Open up the type of the requirement.
1118-
reqLocator = cs->getConstraintLocator(
1119-
static_cast<Expr *>(nullptr), LocatorPathElt::ProtocolRequirement(req));
1117+
reqLocator =
1118+
cs->getConstraintLocator(req, ConstraintLocator::ProtocolRequirement);
11201119
OpenedTypeMap reqReplacements;
11211120
reqType = cs->getTypeOfMemberReference(selfTy, req, dc,
11221121
/*isDynamicResult=*/false,
@@ -1151,10 +1150,8 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11511150

11521151
// Open up the witness type.
11531152
witnessType = witness->getInterfaceType();
1154-
// FIXME: witness as a base locator?
1155-
locator = cs->getConstraintLocator({});
1156-
witnessLocator = cs->getConstraintLocator({},
1157-
LocatorPathElt::Witness(witness));
1153+
witnessLocator =
1154+
cs->getConstraintLocator(req, LocatorPathElt::Witness(witness));
11581155
if (witness->getDeclContext()->isTypeContext()) {
11591156
openWitnessType = cs->getTypeOfMemberReference(
11601157
selfTy, witness, dc, /*isDynamicResult=*/false,
@@ -1174,44 +1171,8 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11741171
// Match a type in the requirement to a type in the witness.
11751172
auto matchTypes = [&](Type reqType,
11761173
Type witnessType) -> std::optional<RequirementMatch> {
1177-
// `swift_attr` attributes in the type context were ignored before,
1178-
// which means that we need to maintain status quo to avoid breaking
1179-
// witness matching by stripping everything concurrency related from
1180-
// inner types.
1181-
auto shouldStripConcurrency = [&]() {
1182-
if (!req->isObjC())
1183-
return false;
1184-
1185-
auto &ctx = dc->getASTContext();
1186-
return !(ctx.isSwiftVersionAtLeast(6) ||
1187-
ctx.LangOpts.StrictConcurrencyLevel ==
1188-
StrictConcurrency::Complete);
1189-
};
1190-
1191-
if (shouldStripConcurrency()) {
1192-
if (reqType->is<FunctionType>()) {
1193-
auto *fnTy = reqType->castTo<FunctionType>();
1194-
SmallVector<AnyFunctionType::Param, 4> params;
1195-
llvm::transform(fnTy->getParams(), std::back_inserter(params),
1196-
[&](const AnyFunctionType::Param &param) {
1197-
return param.withType(
1198-
param.getPlainType()->stripConcurrency(
1199-
/*recursive=*/true,
1200-
/*dropGlobalActor=*/true));
1201-
});
1202-
1203-
auto resultTy =
1204-
fnTy->getResult()->stripConcurrency(/*recursive=*/true,
1205-
/*dropGlobalActor=*/true);
1206-
1207-
reqType = FunctionType::get(params, resultTy, fnTy->getExtInfo());
1208-
} else {
1209-
reqType = reqType->stripConcurrency(/*recursive=*/true,
1210-
/*dropGlobalActor=*/true);
1211-
}
1212-
}
1213-
1214-
cs->addConstraint(ConstraintKind::Bind, reqType, witnessType, locator);
1174+
cs->addConstraint(ConstraintKind::Bind, reqType, witnessType,
1175+
witnessLocator);
12151176
// FIXME: Check whether this has already failed.
12161177
return std::nullopt;
12171178
};
@@ -1235,14 +1196,31 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
12351196
return *result;
12361197
}
12371198
}
1238-
bool requiresNonSendable = false;
1239-
if (!solution || solution->Fixes.size()) {
1240-
/// If the *only* problems are that `@Sendable` attributes are missing,
1241-
/// allow the match in some circumstances.
1242-
requiresNonSendable = solution
1243-
&& llvm::all_of(solution->Fixes, [](constraints::ConstraintFix *fix) {
1244-
return fix->getKind() == constraints::FixKind::AddSendableAttribute;
1245-
});
1199+
1200+
bool requiresNonSendable = [&]() {
1201+
if (!solution)
1202+
return false;
1203+
1204+
// If the *only* problems are that `@Sendable` attributes are missing,
1205+
// allow the match in some circumstances.
1206+
if (!solution->Fixes.empty()) {
1207+
return llvm::all_of(solution->Fixes,
1208+
[](constraints::ConstraintFix *fix) {
1209+
return fix->getKind() ==
1210+
constraints::FixKind::AddSendableAttribute;
1211+
});
1212+
}
1213+
1214+
// If there are no other issues, let's check whether this are
1215+
// missing Sendable conformances when matching ObjC requirements.
1216+
// This is not an error until Swift 6 because `swift_attr` wasn't
1217+
// allowed in type contexts initially.
1218+
return req->isObjC() &&
1219+
solution->getFixedScore()
1220+
.Data[SK_MissingSynthesizableConformance] > 0;
1221+
}();
1222+
1223+
if (!solution || !solution->Fixes.empty()) {
12461224
if (!requiresNonSendable)
12471225
return RequirementMatch(witness, MatchKind::TypeConflict,
12481226
witnessType);

test/Concurrency/sendable_objc_attr_in_type_context_swift5.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^blo
5858
@end
5959

6060
@protocol InnerSendableTypes
61+
-(void) testComposition:(SWIFT_SENDABLE MyValue *)composition;
6162
-(void) test:(NSDictionary<NSString *, SWIFT_SENDABLE id> *)options;
6263
-(void) testWithCallback:(NSString *)name handler:(MAIN_ACTOR void (^)(NSDictionary<NSString *, SWIFT_SENDABLE id> *, NSError * _Nullable))handler;
6364
@end
@@ -109,21 +110,26 @@ func test_sendable_attr_in_type_context(test: Test) {
109110
}
110111

111112
class TestConformanceWithStripping : InnerSendableTypes {
112-
func test(_ options: [String: Any]) { // Ok
113+
func testComposition(_: MyValue) {
114+
// expected-warning@-1 {{sendability of function types in instance method 'testComposition' does not match requirement in protocol 'InnerSendableTypes'; this is an error in the Swift 6 language mode}}
115+
}
116+
117+
func test(_ options: [String: Any]) {
118+
// expected-warning@-1 {{sendability of function types in instance method 'test' does not match requirement in protocol 'InnerSendableTypes'; this is an error in the Swift 6 language mode}}
113119
}
114120

115121
func test(withCallback name: String, handler: @escaping @MainActor ([String : Any], (any Error)?) -> Void) { // Ok
122+
// expected-warning@-1 {{sendability of function types in instance method 'test(withCallback:handler:)' does not match requirement in protocol 'InnerSendableTypes'; this is an error in the Swift 6 language mode}}
116123
}
117124
}
118125

119126
class TestConformanceWithoutStripping : InnerSendableTypes {
120-
// expected-error@-1 {{type 'TestConformanceWithoutStripping' does not conform to protocol 'InnerSendableTypes'}}
127+
func testComposition(_: any MyValue & Sendable) { // Ok
128+
}
121129

122-
func test(_ options: [String: any Sendable]) {
123-
// expected-note@-1 {{candidate has non-matching type '([String : any Sendable]) -> ()'}}
130+
func test(_ options: [String: any Sendable]) { // Ok
124131
}
125132

126-
func test(withCallback name: String, handler: @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) {
127-
// expected-note@-1 {{candidate has non-matching type '(String, @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) -> ()'}}
133+
func test(withCallback name: String, handler: @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) { // Ok
128134
}
129135
}

test/Concurrency/sendable_objc_attr_in_type_context_swift5_strict.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^blo
5959
@end
6060

6161
@protocol InnerSendableTypes
62+
-(void) testComposition:(SWIFT_SENDABLE MyValue *)composition;
6263
-(void) test:(NSDictionary<NSString *, SWIFT_SENDABLE id> *)options;
6364
-(void) testWithCallback:(NSString *)name handler:(MAIN_ACTOR void (^)(NSDictionary<NSString *, SWIFT_SENDABLE id> *, NSError * _Nullable))handler;
6465
@end
@@ -116,6 +117,10 @@ func test_sendable_attr_in_type_context(test: Test) {
116117
class TestConformanceWithStripping : InnerSendableTypes {
117118
// expected-error@-1 {{type 'TestConformanceWithStripping' does not conform to protocol 'InnerSendableTypes'}}
118119

120+
func testComposition(_: MyValue) {
121+
// expected-note@-1 {{candidate has non-matching type '(MyValue) -> ()'}}
122+
}
123+
119124
func test(_ options: [String: Any]) {
120125
// expected-note@-1 {{candidate has non-matching type '([String : Any]) -> ()'}}
121126
}
@@ -126,6 +131,9 @@ class TestConformanceWithStripping : InnerSendableTypes {
126131
}
127132

128133
class TestConformanceWithoutStripping : InnerSendableTypes {
134+
func testComposition(_: MyValue & Sendable) { // Ok
135+
}
136+
129137
func test(_ options: [String: any Sendable]) { // Ok
130138
}
131139

test/Concurrency/sendable_objc_attr_in_type_context_swift6.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^blo
5858
@end
5959

6060
@protocol InnerSendableTypes
61+
-(void) testComposition:(SWIFT_SENDABLE MyValue *)composition;
6162
-(void) test:(NSDictionary<NSString *, SWIFT_SENDABLE id> *)options;
6263
-(void) testWithCallback:(NSString *)name handler:(MAIN_ACTOR void (^)(NSDictionary<NSString *, SWIFT_SENDABLE id> *, NSError * _Nullable))handler;
6364
@end
@@ -115,6 +116,10 @@ func test_sendable_attr_in_type_context(test: Test) {
115116
class TestConformanceWithStripping : InnerSendableTypes {
116117
// expected-error@-1 {{type 'TestConformanceWithStripping' does not conform to protocol 'InnerSendableTypes'}}
117118

119+
func testComposition(_: MyValue) {
120+
// expected-note@-1 {{candidate has non-matching type '(MyValue) -> ()'}}
121+
}
122+
118123
func test(_ options: [String: Any]) {
119124
// expected-note@-1 {{candidate has non-matching type '([String : Any]) -> ()'}}
120125
}
@@ -125,6 +130,9 @@ class TestConformanceWithStripping : InnerSendableTypes {
125130
}
126131

127132
class TestConformanceWithoutStripping : InnerSendableTypes {
133+
func testComposition(_: any MyValue & Sendable) { // Ok
134+
}
135+
128136
func test(_ options: [String: any Sendable]) { // Ok
129137
}
130138

test/IDE/print_clang_objc_async.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,12 @@ import _Concurrency
129129
// CHECK-LABEL: class NXSender :
130130
// CHECK: func sendAny(_ obj: any Sendable) -> any Sendable
131131
// CHECK: func sendOptionalAny(_ obj: (any Sendable)?) -> (any Sendable)?
132-
// FIXME(https://github.com/apple/swift/issues/65730): Compositions not wrappped in existentials
133-
// CHECK: func sendSendable(_ sendable: SendableClass & Sendable) -> SendableClass & Sendable
134-
// CHECK: func sendSendableSubclasses(_ sendableSubclass: NonSendableClass & Sendable) -> NonSendableClass & Sendable
132+
// CHECK: func sendSendable(_ sendable: any SendableClass & Sendable) -> any SendableClass & Sendable
133+
// CHECK: func sendSendableSubclasses(_ sendableSubclass: any NonSendableClass & Sendable) -> any NonSendableClass & Sendable
135134
// CHECK: func sendProto(_ obj: any LabellyProtocol & Sendable) -> any LabellyProtocol & Sendable
136135
// CHECK: func sendProtos(_ obj: any LabellyProtocol & ObjCClub & Sendable) -> any LabellyProtocol & ObjCClub & Sendable
137136
// CHECK: func sendAnyArray(_ array: [any Sendable]) -> [any Sendable]
138-
// CHECK: func sendGeneric(_ generic: GenericObject<SendableClass> & Sendable) -> GenericObject<SendableClass> & Sendable
137+
// CHECK: func sendGeneric(_ generic: any GenericObject<SendableClass> & Sendable) -> any GenericObject<SendableClass> & Sendable
139138
// CHECK: func sendPtr(_ val: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer
140139
// CHECK: func sendStringArray(_ obj: [String]) -> [String]
141140
// CHECK: func sendAnyTypedef(_ obj: any Sendable) -> any Sendable

0 commit comments

Comments
 (0)