Skip to content

Commit 60ed12e

Browse files
authored
Merge pull request #77476 from hborla/6.0-preconcurrency-downgrade
[6.0][Concurrency] Fix preconcurrency downgrade behavior for `Sendable` closures and generic requirements.
2 parents 48c1b42 + 1585f60 commit 60ed12e

12 files changed

+306
-57
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,30 @@ namespace swift {
656656
InFlightDiagnostic &limitBehaviorUntilSwiftVersion(
657657
DiagnosticBehavior limit, unsigned majorVersion);
658658

659+
/// Limits the diagnostic behavior to \c limit accordingly if
660+
/// preconcurrency applies. Otherwise, the behavior limit only applies
661+
/// prior to the given language mode.
662+
///
663+
/// `@preconcurrency` applied to a nominal declaration or an import
664+
/// statement will limit concurrency diagnostic behavior based on the
665+
/// strict concurrency checking level. Under minimal checking,
666+
/// preconcurrency will suppress the diagnostics. With strict concurrency
667+
/// checking, including when building with the Swift 6 language mode,
668+
/// preconcurrency errors are downgraded to warnings. This allows libraries
669+
/// to stage in concurrency annotations even after their clients have
670+
/// migrated to Swift 6 using `@preconcurrency` alongside the newly added
671+
/// `@Sendable` or `@MainActor` annotations.
672+
InFlightDiagnostic
673+
&limitBehaviorWithPreconcurrency(DiagnosticBehavior limit,
674+
bool preconcurrency,
675+
unsigned languageMode = 6) {
676+
if (preconcurrency) {
677+
return limitBehavior(limit);
678+
}
679+
680+
return limitBehaviorUntilSwiftVersion(limit, languageMode);
681+
}
682+
659683
/// Limit the diagnostic behavior to warning until the specified version.
660684
///
661685
/// 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
@@ -269,10 +269,16 @@ getConcurrencyFixBehavior(ConstraintSystem &cs, ConstraintKind constraintKind,
269269

270270
// For a @preconcurrency callee outside of a strict concurrency
271271
// context, ignore.
272-
if (cs.hasPreconcurrencyCallee(locator) &&
273-
!contextRequiresStrictConcurrencyChecking(
274-
cs.DC, GetClosureType{cs}, ClosureIsolatedByPreconcurrency{cs}))
272+
if (cs.hasPreconcurrencyCallee(locator)) {
273+
// Preconcurrency failures are always downgraded to warnings, even in
274+
// Swift 6 mode.
275+
if (contextRequiresStrictConcurrencyChecking(
276+
cs.DC, GetClosureType{cs}, ClosureIsolatedByPreconcurrency{cs})) {
277+
return FixBehavior::DowngradeToWarning;
278+
}
279+
275280
return FixBehavior::Suppress;
281+
}
276282

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

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,7 +2848,8 @@ bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
28482848
const RootProtocolConformance *rootConf,
28492849
const ExtensionDecl *ext,
28502850
const ExportContext &where,
2851-
bool warnIfConformanceUnavailablePreSwift6) {
2851+
bool warnIfConformanceUnavailablePreSwift6,
2852+
bool preconcurrency) {
28522853
auto *attr = AvailableAttr::isUnavailable(ext);
28532854
if (!attr)
28542855
return false;
@@ -2909,7 +2910,7 @@ bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
29092910
diags.diagnose(loc, diag::conformance_availability_unavailable,
29102911
type, proto,
29112912
platform.empty(), platform, EncodedMessage.Message)
2912-
.limitBehaviorUntilSwiftVersion(behavior, 6)
2913+
.limitBehaviorWithPreconcurrency(behavior, preconcurrency)
29132914
.warnUntilSwiftVersionIf(warnIfConformanceUnavailablePreSwift6, 6);
29142915

29152916
switch (attr->getVersionAvailability(ctx)) {
@@ -3442,6 +3443,7 @@ class ExprAvailabilityWalker : public ASTWalker {
34423443
ASTContext &Context;
34433444
MemberAccessContext AccessContext = MemberAccessContext::Default;
34443445
SmallVector<const Expr *, 16> ExprStack;
3446+
SmallVector<bool, 4> PreconcurrencyCalleeStack;
34453447
const ExportContext &Where;
34463448

34473449
public:
@@ -3469,6 +3471,19 @@ class ExprAvailabilityWalker : public ASTWalker {
34693471

34703472
ExprStack.push_back(E);
34713473

3474+
if (auto *apply = dyn_cast<ApplyExpr>(E)) {
3475+
bool preconcurrency = false;
3476+
auto *fn = apply->getFn();
3477+
if (auto *selfApply = dyn_cast<SelfApplyExpr>(fn)) {
3478+
fn = selfApply->getFn();
3479+
}
3480+
auto declRef = fn->getReferencedDecl();
3481+
if (auto *decl = declRef.getDecl()) {
3482+
preconcurrency = decl->preconcurrency();
3483+
}
3484+
PreconcurrencyCalleeStack.push_back(preconcurrency);
3485+
}
3486+
34723487
if (auto DR = dyn_cast<DeclRefExpr>(E)) {
34733488
diagnoseDeclRefAvailability(DR->getDeclRef(), DR->getSourceRange(),
34743489
getEnclosingApplyExpr(), std::nullopt);
@@ -3573,9 +3588,15 @@ class ExprAvailabilityWalker : public ASTWalker {
35733588
EE->getLoc(),
35743589
Where.getDeclContext());
35753590

3591+
bool preconcurrency = false;
3592+
if (!PreconcurrencyCalleeStack.empty()) {
3593+
preconcurrency = PreconcurrencyCalleeStack.back();
3594+
}
3595+
35763596
for (ProtocolConformanceRef C : EE->getConformances()) {
35773597
diagnoseConformanceAvailability(E->getLoc(), C, Where, Type(), Type(),
3578-
/*useConformanceAvailabilityErrorsOpt=*/true);
3598+
/*useConformanceAvailabilityErrorsOpt=*/true,
3599+
/*preconcurrency=*/preconcurrency);
35793600
}
35803601
}
35813602

@@ -3601,6 +3622,10 @@ class ExprAvailabilityWalker : public ASTWalker {
36013622
assert(ExprStack.back() == E);
36023623
ExprStack.pop_back();
36033624

3625+
if (auto *apply = dyn_cast<ApplyExpr>(E)) {
3626+
PreconcurrencyCalleeStack.pop_back();
3627+
}
3628+
36043629
return Action::Continue(E);
36053630
}
36063631

@@ -3875,8 +3900,12 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
38753900
return true;
38763901

38773902
if (R.isValid()) {
3878-
if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(),
3879-
Where)) {
3903+
if (diagnoseSubstitutionMapAvailability(
3904+
R.Start, declRef.getSubstitutions(), Where,
3905+
Type(), Type(),
3906+
/*warnIfConformanceUnavailablePreSwift6*/false,
3907+
/*suppressParameterizationCheckForOptional*/false,
3908+
/*preconcurrency*/D->preconcurrency())) {
38803909
return true;
38813910
}
38823911
}
@@ -4348,7 +4377,8 @@ class ProblematicTypeFinder : public TypeDeclFinder {
43484377
/*depTy=*/Type(),
43494378
/*replacementTy=*/Type(),
43504379
/*warnIfConformanceUnavailablePreSwift6=*/false,
4351-
/*suppressParameterizationCheckForOptional=*/ty->isOptional());
4380+
/*suppressParameterizationCheckForOptional=*/ty->isOptional(),
4381+
/*preconcurrency*/ty->getAnyNominal()->preconcurrency());
43524382
return Action::Continue;
43534383
}
43544384

@@ -4403,17 +4433,19 @@ void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc,
44034433
}
44044434

44054435
static void diagnoseMissingConformance(
4406-
SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC) {
4436+
SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC,
4437+
bool preconcurrency) {
44074438
assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable));
4408-
diagnoseMissingSendableConformance(loc, type, fromDC);
4439+
diagnoseMissingSendableConformance(loc, type, fromDC, preconcurrency);
44094440
}
44104441

44114442
bool
44124443
swift::diagnoseConformanceAvailability(SourceLoc loc,
44134444
ProtocolConformanceRef conformance,
44144445
const ExportContext &where,
44154446
Type depTy, Type replacementTy,
4416-
bool warnIfConformanceUnavailablePreSwift6) {
4447+
bool warnIfConformanceUnavailablePreSwift6,
4448+
bool preconcurrency) {
44174449
assert(!where.isImplicit());
44184450

44194451
if (conformance.isInvalid() || conformance.isAbstract())
@@ -4425,7 +4457,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
44254457
for (auto patternConf : pack->getPatternConformances()) {
44264458
diagnosed |= diagnoseConformanceAvailability(
44274459
loc, patternConf, where, depTy, replacementTy,
4428-
warnIfConformanceUnavailablePreSwift6);
4460+
warnIfConformanceUnavailablePreSwift6,
4461+
preconcurrency);
44294462
}
44304463
return diagnosed;
44314464
}
@@ -4444,7 +4477,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
44444477
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
44454478
if (builtinConformance->isMissing()) {
44464479
diagnoseMissingConformance(loc, builtinConformance->getType(),
4447-
builtinConformance->getProtocol(), DC);
4480+
builtinConformance->getProtocol(), DC,
4481+
preconcurrency);
44484482
}
44494483
}
44504484

@@ -4470,7 +4504,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
44704504
}
44714505

44724506
if (diagnoseExplicitUnavailability(loc, rootConf, ext, where,
4473-
warnIfConformanceUnavailablePreSwift6)) {
4507+
warnIfConformanceUnavailablePreSwift6,
4508+
preconcurrency)) {
44744509
maybeEmitAssociatedTypeNote();
44754510
return true;
44764511
}
@@ -4498,7 +4533,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
44984533
SubstitutionMap subConformanceSubs = concreteConf->getSubstitutionMap();
44994534
if (diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where,
45004535
depTy, replacementTy,
4501-
warnIfConformanceUnavailablePreSwift6))
4536+
warnIfConformanceUnavailablePreSwift6,
4537+
preconcurrency))
45024538
return true;
45034539

45044540
return false;
@@ -4510,12 +4546,14 @@ swift::diagnoseSubstitutionMapAvailability(SourceLoc loc,
45104546
const ExportContext &where,
45114547
Type depTy, Type replacementTy,
45124548
bool warnIfConformanceUnavailablePreSwift6,
4513-
bool suppressParameterizationCheckForOptional) {
4549+
bool suppressParameterizationCheckForOptional,
4550+
bool preconcurrency) {
45144551
bool hadAnyIssues = false;
45154552
for (ProtocolConformanceRef conformance : subs.getConformances()) {
45164553
if (diagnoseConformanceAvailability(loc, conformance, where,
45174554
depTy, replacementTy,
4518-
warnIfConformanceUnavailablePreSwift6))
4555+
warnIfConformanceUnavailablePreSwift6,
4556+
preconcurrency))
45194557
hadAnyIssues = true;
45204558
}
45214559

lib/Sema/TypeCheckAvailability.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ diagnoseConformanceAvailability(SourceLoc loc,
228228
const ExportContext &context,
229229
Type depTy=Type(),
230230
Type replacementTy=Type(),
231-
bool warnIfConformanceUnavailablePreSwift6 = false);
231+
bool warnIfConformanceUnavailablePreSwift6 = false,
232+
bool preconcurrency = false);
232233

233234
bool diagnoseSubstitutionMapAvailability(
234235
SourceLoc loc,
@@ -237,7 +238,8 @@ bool diagnoseSubstitutionMapAvailability(
237238
Type depTy = Type(),
238239
Type replacementTy = Type(),
239240
bool warnIfConformanceUnavailablePreSwift6 = false,
240-
bool suppressParameterizationCheckForOptional = false);
241+
bool suppressParameterizationCheckForOptional = false,
242+
bool preconcurrency = false);
241243

242244
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
243245
/// was emitted.
@@ -274,7 +276,8 @@ bool diagnoseExplicitUnavailability(
274276
const RootProtocolConformance *rootConf,
275277
const ExtensionDecl *ext,
276278
const ExportContext &where,
277-
bool warnIfConformanceUnavailablePreSwift6 = false);
279+
bool warnIfConformanceUnavailablePreSwift6 = false,
280+
bool preconcurrency = false);
278281

279282
/// Diagnose uses of the runtime support of the given type, such as
280283
/// type metadata and dynamic casting.

0 commit comments

Comments
 (0)