[Concurrency] Allow witnesses to adopt concurrency before requirements

Allow witnesses to introduce `any Sendable` types into their interface
before requirements (predicated on presence of `@preconcurrency` and
Swift 5 language mode) as a pathway for concurrency adoption.

Resolves: rdar://134503878
This commit is contained in:
Pavel Yaskevich
2024-11-06 15:24:33 -08:00
parent 229ca247a3
commit 73d658ae5a
2 changed files with 46 additions and 28 deletions

View File

@@ -5412,35 +5412,38 @@ bool ConstraintSystem::repairFailures(
if (auto *VD = getAsDecl<ValueDecl>(anchor)) {
// Matching a witness to an protocol requirement.
if (isa<ProtocolDecl>(VD->getDeclContext()) &&
VD->isProtocolRequirement() &&
VD->preconcurrency() &&
path[0].is<LocatorPathElt::Witness>() &&
// Note that the condition below is very important,
// we need to wait until the very last moment to strip
// the concurrency annotations from the innermost type.
conversionsOrFixes.empty()) {
// Allow requirements to introduce `swift_attr` and other
// concurrency related annotations (e.g. `& Sendable` or `@Sendable`)
// (note that `swift_attr` in type contexts weren't supported
// before) and for witnesses to adopt them gradually by matching
// with a warning in non-strict concurrency mode.
if (!(Context.isSwiftVersionAtLeast(6) ||
Context.LangOpts.StrictConcurrencyLevel ==
StrictConcurrency::Complete)) {
auto strippedLHS = lhs->stripConcurrency(/*recursive=*/true,
/*dropGlobalActor=*/true);
auto strippedRHS = rhs->stripConcurrency(/*recursive=*/true,
/*dropGlobalActor=*/true);
if (auto witnessElt = path[0].getAs<LocatorPathElt::Witness>()) {
if (isa<ProtocolDecl>(VD->getDeclContext()) &&
VD->isProtocolRequirement()) {
auto *witness = witnessElt->getDecl();
if ((VD->preconcurrency() || witness->preconcurrency()) &&
// Note that the condition below is very important,
// we need to wait until the very last moment to strip
// the concurrency annotations from the innermost type.
conversionsOrFixes.empty()) {
// Allow requirements/witnesses to introduce `swift_attr` and other
// concurrency related annotations (e.g. `& Sendable` or `@Sendable`)
// (note that `swift_attr` in type contexts weren't supported
// before) and for witnesses to adopt them gradually by matching
// with a warning in non-strict concurrency mode.
if (!(Context.isSwiftVersionAtLeast(6) ||
Context.LangOpts.StrictConcurrencyLevel ==
StrictConcurrency::Complete)) {
auto strippedLHS = lhs->stripConcurrency(/*recursive=*/true,
/*dropGlobalActor=*/true);
auto strippedRHS = rhs->stripConcurrency(/*recursive=*/true,
/*dropGlobalActor=*/true);
// If nothing got stripped there is no reason to re-match
// the types.
if (!strippedLHS->isEqual(lhs) || !strippedRHS->isEqual(rhs)) {
auto result = matchTypes(strippedLHS, strippedRHS, matchKind,
flags | TMF_ApplyingFix, locator);
if (!result.isFailure()) {
increaseScore(SK_MissingSynthesizableConformance, locator);
return true;
// If nothing got stripped there is no reason to re-match
// the types.
if (!strippedLHS->isEqual(lhs) || !strippedRHS->isEqual(rhs)) {
auto result = matchTypes(strippedLHS, strippedRHS, matchKind,
flags | TMF_ApplyingFix, locator);
if (!result.isFailure()) {
increaseScore(SK_MissingSynthesizableConformance, locator);
return true;
}
}
}
}
}

View File

@@ -53,3 +53,18 @@ struct S4 : Q { // expected-error {{type 'S4' does not conform to protocol 'Q'}}
func test(_: [() -> Any?]) {}
// expected-note@-1 {{candidate has non-matching type '([() -> Any?]) -> ()'}}
}
// Test that client is allowed to adopt concurrency first.
protocol PreConcurrency {
var prop: [String: Any] { get } // expected-swift6-note {{protocol requires property 'prop' with type '[String : Any]'}}
func req(_: [String: Any], _: ((Any)?) -> Void) // expected-swift6-note {{protocol requires function 'req' with type '([String : Any], (Any?) -> Void) -> ()'}}
}
class Adopter {
@preconcurrency var prop: [String: any Sendable] = [:] // expected-swift6-note {{candidate has non-matching type '[String : any Sendable]'}}
}
extension Adopter : PreConcurrency { // expected-swift6-error {{type 'Adopter' does not conform to protocol 'PreConcurrency'}} expected-swift6-note {{add stubs for conformance}}
@preconcurrency func req(_: [String: any Sendable], _: ((any Sendable)?) -> Void) {}
// expected-swift6-note@-1 {{candidate has non-matching type '([String : any Sendable], ((any Sendable)?) -> Void) -> ()'}}
}