Skip to content
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