Skip to content

[6.0][Concurrency] Fix preconcurrency downgrade behavior for Sendable closures and generic requirements. #77476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 12, 2024
24 changes: 24 additions & 0 deletions include/swift/AST/DiagnosticEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,30 @@ namespace swift {
InFlightDiagnostic &limitBehaviorUntilSwiftVersion(
DiagnosticBehavior limit, unsigned majorVersion);

/// Limits the diagnostic behavior to \c limit accordingly if
/// preconcurrency applies. Otherwise, the behavior limit only applies
/// prior to the given language mode.
///
/// `@preconcurrency` applied to a nominal declaration or an import
/// statement will limit concurrency diagnostic behavior based on the
/// strict concurrency checking level. Under minimal checking,
/// preconcurrency will suppress the diagnostics. With strict concurrency
/// checking, including when building with the Swift 6 language mode,
/// preconcurrency errors are downgraded to warnings. This allows libraries
/// to stage in concurrency annotations even after their clients have
/// migrated to Swift 6 using `@preconcurrency` alongside the newly added
/// `@Sendable` or `@MainActor` annotations.
InFlightDiagnostic
&limitBehaviorWithPreconcurrency(DiagnosticBehavior limit,
bool preconcurrency,
unsigned languageMode = 6) {
if (preconcurrency) {
return limitBehavior(limit);
}

return limitBehaviorUntilSwiftVersion(limit, languageMode);
}

/// Limit the diagnostic behavior to warning until the specified version.
///
/// This helps stage in fixes for stricter diagnostics as warnings
Expand Down
12 changes: 9 additions & 3 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,16 @@ getConcurrencyFixBehavior(ConstraintSystem &cs, ConstraintKind constraintKind,

// For a @preconcurrency callee outside of a strict concurrency
// context, ignore.
if (cs.hasPreconcurrencyCallee(locator) &&
!contextRequiresStrictConcurrencyChecking(
cs.DC, GetClosureType{cs}, ClosureIsolatedByPreconcurrency{cs}))
if (cs.hasPreconcurrencyCallee(locator)) {
// Preconcurrency failures are always downgraded to warnings, even in
// Swift 6 mode.
if (contextRequiresStrictConcurrencyChecking(
cs.DC, GetClosureType{cs}, ClosureIsolatedByPreconcurrency{cs})) {
return FixBehavior::DowngradeToWarning;
}

return FixBehavior::Suppress;
}

// Otherwise, warn until Swift 6.
if (!cs.getASTContext().LangOpts.isSwiftVersionAtLeast(6))
Expand Down
68 changes: 53 additions & 15 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2848,7 +2848,8 @@ bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where,
bool warnIfConformanceUnavailablePreSwift6) {
bool warnIfConformanceUnavailablePreSwift6,
bool preconcurrency) {
auto *attr = AvailableAttr::isUnavailable(ext);
if (!attr)
return false;
Expand Down Expand Up @@ -2909,7 +2910,7 @@ bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
diags.diagnose(loc, diag::conformance_availability_unavailable,
type, proto,
platform.empty(), platform, EncodedMessage.Message)
.limitBehaviorUntilSwiftVersion(behavior, 6)
.limitBehaviorWithPreconcurrency(behavior, preconcurrency)
.warnUntilSwiftVersionIf(warnIfConformanceUnavailablePreSwift6, 6);

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

public:
Expand Down Expand Up @@ -3469,6 +3471,19 @@ class ExprAvailabilityWalker : public ASTWalker {

ExprStack.push_back(E);

if (auto *apply = dyn_cast<ApplyExpr>(E)) {
bool preconcurrency = false;
auto *fn = apply->getFn();
if (auto *selfApply = dyn_cast<SelfApplyExpr>(fn)) {
fn = selfApply->getFn();
}
auto declRef = fn->getReferencedDecl();
if (auto *decl = declRef.getDecl()) {
preconcurrency = decl->preconcurrency();
}
PreconcurrencyCalleeStack.push_back(preconcurrency);
}

if (auto DR = dyn_cast<DeclRefExpr>(E)) {
diagnoseDeclRefAvailability(DR->getDeclRef(), DR->getSourceRange(),
getEnclosingApplyExpr(), std::nullopt);
Expand Down Expand Up @@ -3573,9 +3588,15 @@ class ExprAvailabilityWalker : public ASTWalker {
EE->getLoc(),
Where.getDeclContext());

bool preconcurrency = false;
if (!PreconcurrencyCalleeStack.empty()) {
preconcurrency = PreconcurrencyCalleeStack.back();
}

for (ProtocolConformanceRef C : EE->getConformances()) {
diagnoseConformanceAvailability(E->getLoc(), C, Where, Type(), Type(),
/*useConformanceAvailabilityErrorsOpt=*/true);
/*useConformanceAvailabilityErrorsOpt=*/true,
/*preconcurrency=*/preconcurrency);
}
}

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

if (auto *apply = dyn_cast<ApplyExpr>(E)) {
PreconcurrencyCalleeStack.pop_back();
}

return Action::Continue(E);
}

Expand Down Expand Up @@ -3875,8 +3900,12 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
return true;

if (R.isValid()) {
if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(),
Where)) {
if (diagnoseSubstitutionMapAvailability(
R.Start, declRef.getSubstitutions(), Where,
Type(), Type(),
/*warnIfConformanceUnavailablePreSwift6*/false,
/*suppressParameterizationCheckForOptional*/false,
/*preconcurrency*/D->preconcurrency())) {
return true;
}
}
Expand Down Expand Up @@ -4348,7 +4377,8 @@ class ProblematicTypeFinder : public TypeDeclFinder {
/*depTy=*/Type(),
/*replacementTy=*/Type(),
/*warnIfConformanceUnavailablePreSwift6=*/false,
/*suppressParameterizationCheckForOptional=*/ty->isOptional());
/*suppressParameterizationCheckForOptional=*/ty->isOptional(),
/*preconcurrency*/ty->getAnyNominal()->preconcurrency());
return Action::Continue;
}

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

static void diagnoseMissingConformance(
SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC) {
SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC,
bool preconcurrency) {
assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable));
diagnoseMissingSendableConformance(loc, type, fromDC);
diagnoseMissingSendableConformance(loc, type, fromDC, preconcurrency);
}

bool
swift::diagnoseConformanceAvailability(SourceLoc loc,
ProtocolConformanceRef conformance,
const ExportContext &where,
Type depTy, Type replacementTy,
bool warnIfConformanceUnavailablePreSwift6) {
bool warnIfConformanceUnavailablePreSwift6,
bool preconcurrency) {
assert(!where.isImplicit());

if (conformance.isInvalid() || conformance.isAbstract())
Expand All @@ -4425,7 +4457,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
for (auto patternConf : pack->getPatternConformances()) {
diagnosed |= diagnoseConformanceAvailability(
loc, patternConf, where, depTy, replacementTy,
warnIfConformanceUnavailablePreSwift6);
warnIfConformanceUnavailablePreSwift6,
preconcurrency);
}
return diagnosed;
}
Expand All @@ -4444,7 +4477,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
if (builtinConformance->isMissing()) {
diagnoseMissingConformance(loc, builtinConformance->getType(),
builtinConformance->getProtocol(), DC);
builtinConformance->getProtocol(), DC,
preconcurrency);
}
}

Expand All @@ -4470,7 +4504,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
}

if (diagnoseExplicitUnavailability(loc, rootConf, ext, where,
warnIfConformanceUnavailablePreSwift6)) {
warnIfConformanceUnavailablePreSwift6,
preconcurrency)) {
maybeEmitAssociatedTypeNote();
return true;
}
Expand Down Expand Up @@ -4498,7 +4533,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
SubstitutionMap subConformanceSubs = concreteConf->getSubstitutionMap();
if (diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where,
depTy, replacementTy,
warnIfConformanceUnavailablePreSwift6))
warnIfConformanceUnavailablePreSwift6,
preconcurrency))
return true;

return false;
Expand All @@ -4510,12 +4546,14 @@ swift::diagnoseSubstitutionMapAvailability(SourceLoc loc,
const ExportContext &where,
Type depTy, Type replacementTy,
bool warnIfConformanceUnavailablePreSwift6,
bool suppressParameterizationCheckForOptional) {
bool suppressParameterizationCheckForOptional,
bool preconcurrency) {
bool hadAnyIssues = false;
for (ProtocolConformanceRef conformance : subs.getConformances()) {
if (diagnoseConformanceAvailability(loc, conformance, where,
depTy, replacementTy,
warnIfConformanceUnavailablePreSwift6))
warnIfConformanceUnavailablePreSwift6,
preconcurrency))
hadAnyIssues = true;
}

Expand Down
9 changes: 6 additions & 3 deletions lib/Sema/TypeCheckAvailability.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ diagnoseConformanceAvailability(SourceLoc loc,
const ExportContext &context,
Type depTy=Type(),
Type replacementTy=Type(),
bool warnIfConformanceUnavailablePreSwift6 = false);
bool warnIfConformanceUnavailablePreSwift6 = false,
bool preconcurrency = false);

bool diagnoseSubstitutionMapAvailability(
SourceLoc loc,
Expand All @@ -237,7 +238,8 @@ bool diagnoseSubstitutionMapAvailability(
Type depTy = Type(),
Type replacementTy = Type(),
bool warnIfConformanceUnavailablePreSwift6 = false,
bool suppressParameterizationCheckForOptional = false);
bool suppressParameterizationCheckForOptional = false,
bool preconcurrency = false);

/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
/// was emitted.
Expand Down Expand Up @@ -274,7 +276,8 @@ bool diagnoseExplicitUnavailability(
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where,
bool warnIfConformanceUnavailablePreSwift6 = false);
bool warnIfConformanceUnavailablePreSwift6 = false,
bool preconcurrency = false);

/// Diagnose uses of the runtime support of the given type, such as
/// type metadata and dynamic casting.
Expand Down
Loading