Skip to content

Commit c06a1a8

Browse files
committed
[Sema] WitnessMatching: Warn about sendability mismatches associated with ObjC requirements
Since importer ignored `swift_attr` that appeared in a type context until recently we need to maintain status quo with concurrency stripping from ObjC requirements in modes that don't have full concurrency checking enabled.
1 parent 0f26da4 commit c06a1a8

File tree

3 files changed

+59
-52
lines changed

3 files changed

+59
-52
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5603,6 +5603,35 @@ bool ConstraintSystem::repairFailures(
56035603
return false;
56045604
}
56055605

5606+
if (auto *VD = getAsDecl<ValueDecl>(anchor)) {
5607+
// Matching a witness to a ObjC protocol requirement.
5608+
if (VD->isObjC() && VD->isProtocolRequirement() &&
5609+
path[0].is<LocatorPathElt::Witness>() &&
5610+
// Note that the condition below is very important,
5611+
// we need to wait until the very last moment to strip
5612+
// the concurrency annotations from the inner most type.
5613+
conversionsOrFixes.empty()) {
5614+
// `swift_attr` attributes in the type context were ignored before,
5615+
// which means that we need to maintain status quo to avoid breaking
5616+
// witness matching by stripping everything concurrency related from
5617+
// inner types in non-full checking mode.
5618+
if (!(Context.isSwiftVersionAtLeast(6) ||
5619+
Context.LangOpts.StrictConcurrencyLevel ==
5620+
StrictConcurrency::Complete)) {
5621+
auto strippedLHS = lhs->stripConcurrency(/*resursive=*/true,
5622+
/*dropGlobalActor=*/true);
5623+
auto strippedRHS = rhs->stripConcurrency(/*resursive=*/true,
5624+
/*dropGlobalActor=*/true);
5625+
auto result = matchTypes(strippedLHS, strippedRHS, matchKind,
5626+
flags | TMF_ApplyingFix, locator);
5627+
if (!result.isFailure()) {
5628+
increaseScore(SK_MissingSynthesizableConformance, locator);
5629+
return true;
5630+
}
5631+
}
5632+
}
5633+
}
5634+
56065635
auto elt = path.back();
56075636
switch (elt.getKind()) {
56085637
case ConstraintLocator::LValueConversion: {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,43 +1171,6 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11711171
// Match a type in the requirement to a type in the witness.
11721172
auto matchTypes = [&](Type reqType,
11731173
Type witnessType) -> std::optional<RequirementMatch> {
1174-
// `swift_attr` attributes in the type context were ignored before,
1175-
// which means that we need to maintain status quo to avoid breaking
1176-
// witness matching by stripping everything concurrency related from
1177-
// inner types.
1178-
auto shouldStripConcurrency = [&]() {
1179-
if (!req->isObjC())
1180-
return false;
1181-
1182-
auto &ctx = dc->getASTContext();
1183-
return !(ctx.isSwiftVersionAtLeast(6) ||
1184-
ctx.LangOpts.StrictConcurrencyLevel ==
1185-
StrictConcurrency::Complete);
1186-
};
1187-
1188-
if (shouldStripConcurrency()) {
1189-
if (reqType->is<FunctionType>()) {
1190-
auto *fnTy = reqType->castTo<FunctionType>();
1191-
SmallVector<AnyFunctionType::Param, 4> params;
1192-
llvm::transform(fnTy->getParams(), std::back_inserter(params),
1193-
[&](const AnyFunctionType::Param &param) {
1194-
return param.withType(
1195-
param.getPlainType()->stripConcurrency(
1196-
/*recursive=*/true,
1197-
/*dropGlobalActor=*/true));
1198-
});
1199-
1200-
auto resultTy =
1201-
fnTy->getResult()->stripConcurrency(/*recursive=*/true,
1202-
/*dropGlobalActor=*/true);
1203-
1204-
reqType = FunctionType::get(params, resultTy, fnTy->getExtInfo());
1205-
} else {
1206-
reqType = reqType->stripConcurrency(/*recursive=*/true,
1207-
/*dropGlobalActor=*/true);
1208-
}
1209-
}
1210-
12111174
cs->addConstraint(ConstraintKind::Bind, reqType, witnessType,
12121175
witnessLocator);
12131176
// FIXME: Check whether this has already failed.
@@ -1233,14 +1196,31 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
12331196
return *result;
12341197
}
12351198
}
1236-
bool requiresNonSendable = false;
1237-
if (!solution || solution->Fixes.size()) {
1238-
/// If the *only* problems are that `@Sendable` attributes are missing,
1239-
/// allow the match in some circumstances.
1240-
requiresNonSendable = solution
1241-
&& llvm::all_of(solution->Fixes, [](constraints::ConstraintFix *fix) {
1242-
return fix->getKind() == constraints::FixKind::AddSendableAttribute;
1243-
});
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()) {
12441224
if (!requiresNonSendable)
12451225
return RequirementMatch(witness, MatchKind::TypeConflict,
12461226
witnessType);

test/Concurrency/sendable_objc_attr_in_type_context_swift5.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,21 +109,19 @@ func test_sendable_attr_in_type_context(test: Test) {
109109
}
110110

111111
class TestConformanceWithStripping : InnerSendableTypes {
112-
func test(_ options: [String: Any]) { // Ok
112+
func test(_ options: [String: Any]) {
113+
// 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}}
113114
}
114115

115116
func test(withCallback name: String, handler: @escaping @MainActor ([String : Any], (any Error)?) -> Void) { // Ok
117+
// 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}}
116118
}
117119
}
118120

119121
class TestConformanceWithoutStripping : InnerSendableTypes {
120-
// expected-error@-1 {{type 'TestConformanceWithoutStripping' does not conform to protocol 'InnerSendableTypes'}}
121-
122-
func test(_ options: [String: any Sendable]) {
123-
// expected-note@-1 {{candidate has non-matching type '([String : any Sendable]) -> ()'}}
122+
func test(_ options: [String: any Sendable]) { // Ok
124123
}
125124

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) -> ()'}}
125+
func test(withCallback name: String, handler: @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) { // Ok
128126
}
129127
}

0 commit comments

Comments
 (0)