Skip to content

Commit c2f88ab

Browse files
committed
[Constraint system] Always add recoverable concurrency fixes.
This avoids us having to go down the less-efficient "salvage" path when dealing with concurrency issues. It also fixes overloading behavior when dealing with `@preconcurrency` and `@Sendable` functions, such as in #59909.
1 parent 2e3aa67 commit c2f88ab

File tree

2 files changed

+57
-32
lines changed

2 files changed

+57
-32
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,42 +2717,47 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
27172717
}
27182718

27192719
/// The behavior limit to apply to a concurrency check.
2720-
auto getConcurrencyFixBehavior = [&](bool forSendable) {
2721-
// We can only handle the downgrade for conversions.
2722-
switch (kind) {
2723-
case ConstraintKind::Conversion:
2724-
case ConstraintKind::ArgumentConversion:
2725-
break;
2720+
auto getConcurrencyFixBehavior = [&](
2721+
bool forSendable
2722+
) -> Optional<FixBehavior> {
2723+
// We can only handle the downgrade for conversions.
2724+
switch (kind) {
2725+
case ConstraintKind::Conversion:
2726+
case ConstraintKind::ArgumentConversion:
2727+
break;
27262728

2727-
default:
2728-
return FixBehavior::Error;
2729-
}
2729+
default:
2730+
if (!shouldAttemptFixes())
2731+
return None;
2732+
2733+
return FixBehavior::Error;
2734+
}
27302735

2731-
// For a @preconcurrency callee outside of a strict concurrency context,
2732-
// ignore.
2733-
if (hasPreconcurrencyCallee(this, locator) &&
2734-
!contextRequiresStrictConcurrencyChecking(DC, GetClosureType{*this}))
2735-
return FixBehavior::Suppress;
2736+
// For a @preconcurrency callee outside of a strict concurrency
2737+
// context, ignore.
2738+
if (hasPreconcurrencyCallee(this, locator) &&
2739+
!contextRequiresStrictConcurrencyChecking(DC, GetClosureType{*this}))
2740+
return FixBehavior::Suppress;
27362741

2737-
// Otherwise, warn until Swift 6.
2738-
if (!getASTContext().LangOpts.isSwiftVersionAtLeast(6))
2739-
return FixBehavior::DowngradeToWarning;
2742+
// Otherwise, warn until Swift 6.
2743+
if (!getASTContext().LangOpts.isSwiftVersionAtLeast(6))
2744+
return FixBehavior::DowngradeToWarning;
27402745

2741-
return FixBehavior::Error;
2746+
return FixBehavior::Error;
27422747
};
27432748

27442749
// A @Sendable function can be a subtype of a non-@Sendable function.
27452750
if (func1->isSendable() != func2->isSendable()) {
27462751
// Cannot add '@Sendable'.
27472752
if (func2->isSendable() || kind < ConstraintKind::Subtype) {
2748-
if (!shouldAttemptFixes())
2749-
return getTypeMatchFailure(locator);
2750-
2751-
auto *fix = AddSendableAttribute::create(
2752-
*this, func1, func2, getConstraintLocator(locator),
2753-
getConcurrencyFixBehavior(true));
2754-
if (recordFix(fix))
2753+
if (auto fixBehavior = getConcurrencyFixBehavior(true)) {
2754+
auto *fix = AddSendableAttribute::create(
2755+
*this, func1, func2, getConstraintLocator(locator), *fixBehavior);
2756+
if (recordFix(fix))
2757+
return getTypeMatchFailure(locator);
2758+
} else {
27552759
return getTypeMatchFailure(locator);
2760+
}
27562761
}
27572762
}
27582763

@@ -2780,15 +2785,16 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
27802785
return getTypeMatchFailure(locator);
27812786
} else if (func1->getGlobalActor() && !func2->isAsync()) {
27822787
// Cannot remove a global actor from a synchronous function.
2783-
if (!shouldAttemptFixes())
2784-
return getTypeMatchFailure(locator);
2788+
if (auto fixBehavior = getConcurrencyFixBehavior(false)) {
2789+
auto *fix = MarkGlobalActorFunction::create(
2790+
*this, func1, func2, getConstraintLocator(locator),
2791+
*fixBehavior);
27852792

2786-
auto *fix = MarkGlobalActorFunction::create(
2787-
*this, func1, func2, getConstraintLocator(locator),
2788-
getConcurrencyFixBehavior(false));
2789-
2790-
if (recordFix(fix))
2793+
if (recordFix(fix))
2794+
return getTypeMatchFailure(locator);
2795+
} else {
27912796
return getTypeMatchFailure(locator);
2797+
}
27922798
} else if (kind < ConstraintKind::Subtype) {
27932799
return getTypeMatchFailure(locator);
27942800
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s
2+
// REQUIRES: concurrency
3+
4+
// https://github.com/apple/swift/issues/59909
5+
struct Future<T> { }
6+
7+
extension Future {
8+
@preconcurrency
9+
func flatMap<NewValue>(_ callback: @escaping @Sendable (T) -> Future<NewValue>) -> Future<NewValue> { // #1
10+
fatalError()
11+
}
12+
}
13+
14+
extension Future {
15+
@available(*, deprecated, message: "")
16+
func flatMap<NewValue>(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (T) -> Future<NewValue>) -> Future<NewValue> { // #2
17+
return self.flatMap(callback)
18+
}
19+
}

0 commit comments

Comments
 (0)