Skip to content

Commit dc01137

Browse files
authored
Merge pull request #77459 from hborla/preconcurrency-downgrade
[Concurrency] Fix preconcurrency downgrade behavior for `Sendable` closures and generic requirements.
2 parents 3c22407 + 7331c5e commit dc01137

12 files changed

+261
-57
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,30 @@ namespace swift {
686686
InFlightDiagnostic &limitBehaviorUntilSwiftVersion(
687687
DiagnosticBehavior limit, unsigned majorVersion);
688688

689+
/// Limits the diagnostic behavior to \c limit accordingly if
690+
/// preconcurrency applies. Otherwise, the behavior limit only applies
691+
/// prior to the given language mode.
692+
///
693+
/// `@preconcurrency` applied to a nominal declaration or an import
694+
/// statement will limit concurrency diagnostic behavior based on the
695+
/// strict concurrency checking level. Under minimal checking,
696+
/// preconcurrency will suppress the diagnostics. With strict concurrency
697+
/// checking, including when building with the Swift 6 language mode,
698+
/// preconcurrency errors are downgraded to warnings. This allows libraries
699+
/// to stage in concurrency annotations even after their clients have
700+
/// migrated to Swift 6 using `@preconcurrency` alongside the newly added
701+
/// `@Sendable` or `@MainActor` annotations.
702+
InFlightDiagnostic
703+
&limitBehaviorWithPreconcurrency(DiagnosticBehavior limit,
704+
bool preconcurrency,
705+
unsigned languageMode = 6) {
706+
if (preconcurrency) {
707+
return limitBehavior(limit);
708+
}
709+
710+
return limitBehaviorUntilSwiftVersion(limit, languageMode);
711+
}
712+
689713
/// Limit the diagnostic behavior to warning until the specified version.
690714
///
691715
/// This helps stage in fixes for stricter diagnostics as warnings

lib/Sema/CSFix.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,10 +294,16 @@ getConcurrencyFixBehavior(ConstraintSystem &cs, ConstraintKind constraintKind,
294294

295295
// For a @preconcurrency callee outside of a strict concurrency
296296
// context, ignore.
297-
if (cs.hasPreconcurrencyCallee(locator) &&
298-
!contextRequiresStrictConcurrencyChecking(
299-
cs.DC, GetClosureType{cs}, ClosureIsolatedByPreconcurrency{cs}))
297+
if (cs.hasPreconcurrencyCallee(locator)) {
298+
// Preconcurrency failures are always downgraded to warnings, even in
299+
// Swift 6 mode.
300+
if (contextRequiresStrictConcurrencyChecking(
301+
cs.DC, GetClosureType{cs}, ClosureIsolatedByPreconcurrency{cs})) {
302+
return FixBehavior::DowngradeToWarning;
303+
}
304+
300305
return FixBehavior::Suppress;
306+
}
301307

302308
// Otherwise, warn until Swift 6.
303309
if (!cs.getASTContext().LangOpts.isSwiftVersionAtLeast(6))

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ concreteSyntaxDeclForAvailableAttribute(const Decl *AbstractSyntaxDecl);
5353
static bool diagnoseExplicitUnavailability(
5454
SourceLoc loc, const RootProtocolConformance *rootConf,
5555
const ExtensionDecl *ext, const ExportContext &where,
56-
bool warnIfConformanceUnavailablePreSwift6 = false);
56+
bool warnIfConformanceUnavailablePreSwift6 = false,
57+
bool preconcurrency = false);
5758

5859
/// Emit a diagnostic for references to declarations that have been
5960
/// marked as unavailable, either through "unavailable" or "obsoleted:".
@@ -66,7 +67,8 @@ static bool diagnoseSubstitutionMapAvailability(
6667
SourceLoc loc, SubstitutionMap subs, const ExportContext &where,
6768
Type depTy = Type(), Type replacementTy = Type(),
6869
bool warnIfConformanceUnavailablePreSwift6 = false,
69-
bool suppressParameterizationCheckForOptional = false);
70+
bool suppressParameterizationCheckForOptional = false,
71+
bool preconcurrency = false);
7072

7173
/// Diagnose uses of unavailable declarations in types.
7274
static bool
@@ -2985,7 +2987,8 @@ getExplicitUnavailabilityDiagnosticInfo(const Decl *decl,
29852987
bool diagnoseExplicitUnavailability(
29862988
SourceLoc loc, const RootProtocolConformance *rootConf,
29872989
const ExtensionDecl *ext, const ExportContext &where,
2988-
bool warnIfConformanceUnavailablePreSwift6) {
2990+
bool warnIfConformanceUnavailablePreSwift6,
2991+
bool preconcurrency) {
29892992
// Invertible protocols are never unavailable.
29902993
if (rootConf->getProtocol()->getInvertibleProtocolKind())
29912994
return false;
@@ -3011,7 +3014,7 @@ bool diagnoseExplicitUnavailability(
30113014
diags
30123015
.diagnose(loc, diag::conformance_availability_unavailable, type, proto,
30133016
platform.empty(), platform, EncodedMessage.Message)
3014-
.limitBehaviorUntilSwiftVersion(behavior, 6)
3017+
.limitBehaviorWithPreconcurrency(behavior, preconcurrency)
30153018
.warnUntilSwiftVersionIf(warnIfConformanceUnavailablePreSwift6, 6);
30163019

30173020
switch (diagnosticInfo->getStatus()) {
@@ -3523,6 +3526,7 @@ class ExprAvailabilityWalker : public ASTWalker {
35233526
ASTContext &Context;
35243527
MemberAccessContext AccessContext = MemberAccessContext::Default;
35253528
SmallVector<const Expr *, 16> ExprStack;
3529+
SmallVector<bool, 4> PreconcurrencyCalleeStack;
35263530
const ExportContext &Where;
35273531

35283532
public:
@@ -3546,6 +3550,15 @@ class ExprAvailabilityWalker : public ASTWalker {
35463550

35473551
ExprStack.push_back(E);
35483552

3553+
if (auto *apply = dyn_cast<ApplyExpr>(E)) {
3554+
bool preconcurrency = false;
3555+
auto declRef = apply->getFn()->getReferencedDecl();
3556+
if (auto *decl = declRef.getDecl()) {
3557+
preconcurrency = decl->preconcurrency();
3558+
}
3559+
PreconcurrencyCalleeStack.push_back(preconcurrency);
3560+
}
3561+
35493562
if (auto DR = dyn_cast<DeclRefExpr>(E)) {
35503563
diagnoseDeclRefAvailability(DR->getDeclRef(), DR->getSourceRange(),
35513564
getEnclosingApplyExpr(), std::nullopt);
@@ -3668,9 +3681,15 @@ class ExprAvailabilityWalker : public ASTWalker {
36683681
EE->getLoc(),
36693682
Where.getDeclContext());
36703683

3684+
bool preconcurrency = false;
3685+
if (!PreconcurrencyCalleeStack.empty()) {
3686+
preconcurrency = PreconcurrencyCalleeStack.back();
3687+
}
3688+
36713689
for (ProtocolConformanceRef C : EE->getConformances()) {
36723690
diagnoseConformanceAvailability(E->getLoc(), C, Where, Type(), Type(),
3673-
/*useConformanceAvailabilityErrorsOpt=*/true);
3691+
/*useConformanceAvailabilityErrorsOpt=*/true,
3692+
/*preconcurrency=*/preconcurrency);
36743693
}
36753694
}
36763695

@@ -3696,6 +3715,10 @@ class ExprAvailabilityWalker : public ASTWalker {
36963715
assert(ExprStack.back() == E);
36973716
ExprStack.pop_back();
36983717

3718+
if (auto *apply = dyn_cast<ApplyExpr>(E)) {
3719+
PreconcurrencyCalleeStack.pop_back();
3720+
}
3721+
36993722
return Action::Continue(E);
37003723
}
37013724

@@ -3990,8 +4013,12 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
39904013
}
39914014

39924015
if (R.isValid()) {
3993-
if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(),
3994-
Where)) {
4016+
if (diagnoseSubstitutionMapAvailability(
4017+
R.Start, declRef.getSubstitutions(), Where,
4018+
Type(), Type(),
4019+
/*warnIfConformanceUnavailablePreSwift6*/false,
4020+
/*suppressParameterizationCheckForOptional*/false,
4021+
/*preconcurrency*/D->preconcurrency())) {
39954022
return true;
39964023
}
39974024
}
@@ -4489,7 +4516,8 @@ class ProblematicTypeFinder : public TypeDeclFinder {
44894516
/*depTy=*/Type(),
44904517
/*replacementTy=*/Type(),
44914518
/*warnIfConformanceUnavailablePreSwift6=*/false,
4492-
/*suppressParameterizationCheckForOptional=*/ty->isOptional());
4519+
/*suppressParameterizationCheckForOptional=*/ty->isOptional(),
4520+
/*preconcurrency*/ty->getAnyNominal()->preconcurrency());
44934521
return Action::Continue;
44944522
}
44954523

@@ -4539,17 +4567,19 @@ void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc,
45394567
}
45404568

45414569
static void diagnoseMissingConformance(
4542-
SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC) {
4570+
SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC,
4571+
bool preconcurrency) {
45434572
assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable));
4544-
diagnoseMissingSendableConformance(loc, type, fromDC);
4573+
diagnoseMissingSendableConformance(loc, type, fromDC, preconcurrency);
45454574
}
45464575

45474576
bool
45484577
swift::diagnoseConformanceAvailability(SourceLoc loc,
45494578
ProtocolConformanceRef conformance,
45504579
const ExportContext &where,
45514580
Type depTy, Type replacementTy,
4552-
bool warnIfConformanceUnavailablePreSwift6) {
4581+
bool warnIfConformanceUnavailablePreSwift6,
4582+
bool preconcurrency) {
45534583
assert(!where.isImplicit());
45544584

45554585
if (conformance.isInvalid() || conformance.isAbstract())
@@ -4561,7 +4591,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
45614591
for (auto patternConf : pack->getPatternConformances()) {
45624592
diagnosed |= diagnoseConformanceAvailability(
45634593
loc, patternConf, where, depTy, replacementTy,
4564-
warnIfConformanceUnavailablePreSwift6);
4594+
warnIfConformanceUnavailablePreSwift6,
4595+
preconcurrency);
45654596
}
45664597
return diagnosed;
45674598
}
@@ -4581,7 +4612,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
45814612
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
45824613
if (builtinConformance->isMissing()) {
45834614
diagnoseMissingConformance(loc, builtinConformance->getType(),
4584-
builtinConformance->getProtocol(), DC);
4615+
builtinConformance->getProtocol(), DC,
4616+
preconcurrency);
45854617
}
45864618
}
45874619

@@ -4611,7 +4643,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
46114643
// FIXME: diagnoseExplicitUnavailability() should take unmet requirement
46124644
if (diagnoseExplicitUnavailability(
46134645
loc, rootConf, ext, where,
4614-
warnIfConformanceUnavailablePreSwift6)) {
4646+
warnIfConformanceUnavailablePreSwift6,
4647+
preconcurrency)) {
46154648
maybeEmitAssociatedTypeNote();
46164649
return true;
46174650
}
@@ -4640,7 +4673,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
46404673
SubstitutionMap subConformanceSubs = concreteConf->getSubstitutionMap();
46414674
if (diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where,
46424675
depTy, replacementTy,
4643-
warnIfConformanceUnavailablePreSwift6))
4676+
warnIfConformanceUnavailablePreSwift6,
4677+
preconcurrency))
46444678
return true;
46454679

46464680
return false;
@@ -4649,12 +4683,14 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
46494683
bool diagnoseSubstitutionMapAvailability(
46504684
SourceLoc loc, SubstitutionMap subs, const ExportContext &where, Type depTy,
46514685
Type replacementTy, bool warnIfConformanceUnavailablePreSwift6,
4652-
bool suppressParameterizationCheckForOptional) {
4686+
bool suppressParameterizationCheckForOptional,
4687+
bool preconcurrency) {
46534688
bool hadAnyIssues = false;
46544689
for (ProtocolConformanceRef conformance : subs.getConformances()) {
46554690
if (diagnoseConformanceAvailability(loc, conformance, where,
46564691
depTy, replacementTy,
4657-
warnIfConformanceUnavailablePreSwift6))
4692+
warnIfConformanceUnavailablePreSwift6,
4693+
preconcurrency))
46584694
hadAnyIssues = true;
46594695
}
46604696

lib/Sema/TypeCheckAvailability.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ diagnoseConformanceAvailability(SourceLoc loc,
230230
const ExportContext &context,
231231
Type depTy=Type(),
232232
Type replacementTy=Type(),
233-
bool warnIfConformanceUnavailablePreSwift6 = false);
233+
bool warnIfConformanceUnavailablePreSwift6 = false,
234+
bool preconcurrency = false);
234235

235236
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
236237
/// was emitted.

0 commit comments

Comments
 (0)