diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index caf2625273829..4266ab81124b4 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -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 diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 82ac5e2003b3e..bed9ffb0212fe 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -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)) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index f3d724387f1d8..7016edd92b78b 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -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; @@ -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)) { @@ -3442,6 +3443,7 @@ class ExprAvailabilityWalker : public ASTWalker { ASTContext &Context; MemberAccessContext AccessContext = MemberAccessContext::Default; SmallVector ExprStack; + SmallVector PreconcurrencyCalleeStack; const ExportContext &Where; public: @@ -3469,6 +3471,19 @@ class ExprAvailabilityWalker : public ASTWalker { ExprStack.push_back(E); + if (auto *apply = dyn_cast(E)) { + bool preconcurrency = false; + auto *fn = apply->getFn(); + if (auto *selfApply = dyn_cast(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(E)) { diagnoseDeclRefAvailability(DR->getDeclRef(), DR->getSourceRange(), getEnclosingApplyExpr(), std::nullopt); @@ -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); } } @@ -3601,6 +3622,10 @@ class ExprAvailabilityWalker : public ASTWalker { assert(ExprStack.back() == E); ExprStack.pop_back(); + if (auto *apply = dyn_cast(E)) { + PreconcurrencyCalleeStack.pop_back(); + } + return Action::Continue(E); } @@ -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; } } @@ -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; } @@ -4403,9 +4433,10 @@ 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 @@ -4413,7 +4444,8 @@ 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()) @@ -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; } @@ -4444,7 +4477,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc, if (auto builtinConformance = dyn_cast(rootConf)){ if (builtinConformance->isMissing()) { diagnoseMissingConformance(loc, builtinConformance->getType(), - builtinConformance->getProtocol(), DC); + builtinConformance->getProtocol(), DC, + preconcurrency); } } @@ -4470,7 +4504,8 @@ swift::diagnoseConformanceAvailability(SourceLoc loc, } if (diagnoseExplicitUnavailability(loc, rootConf, ext, where, - warnIfConformanceUnavailablePreSwift6)) { + warnIfConformanceUnavailablePreSwift6, + preconcurrency)) { maybeEmitAssociatedTypeNote(); return true; } @@ -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; @@ -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; } diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index a8badf06c1246..f2c1122df4c9d 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -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, @@ -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. @@ -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. diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 04ede385f37e4..e6c13433586b1 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -788,7 +788,10 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) { }); } -bool SendableCheckContext::isExplicitSendableConformance() const { +bool SendableCheckContext::warnInMinimalChecking() const { + if (preconcurrencyContext) + return false; + if (!conformanceCheck) return false; @@ -806,7 +809,7 @@ bool SendableCheckContext::isExplicitSendableConformance() const { DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const { // If we're not supposed to diagnose existing data races from this context, // ignore the diagnostic entirely. - if (!isExplicitSendableConformance() && + if (!warnInMinimalChecking() && !shouldDiagnoseExistingDataRaces(fromDC)) return DiagnosticBehavior::Ignore; @@ -825,9 +828,7 @@ SendableCheckContext::implicitSendableDiagnosticBehavior() const { LLVM_FALLTHROUGH; case StrictConcurrency::Minimal: - // Explicit Sendable conformances always diagnose, even when strict - // strict checking is disabled. - if (isExplicitSendableConformance()) + if (warnInMinimalChecking()) return DiagnosticBehavior::Warning; return DiagnosticBehavior::Ignore; @@ -868,6 +869,13 @@ bool swift::hasExplicitSendableConformance(NominalTypeDecl *nominal, /// nominal type. DiagnosticBehavior SendableCheckContext::diagnosticBehavior( NominalTypeDecl *nominal) const { + // If we're in a preconcurrency context, don't override the default behavior + // based on explicit conformances. For example, a @preconcurrency @Sendable + // closure should not warn about an explicitly unavailable Sendable + // conformance in minimal checking. + if (preconcurrencyContext) + return defaultDiagnosticBehavior(); + if (hasExplicitSendableConformance(nominal)) return DiagnosticBehavior::Warning; @@ -1225,9 +1233,10 @@ bool swift::diagnoseNonSendableTypesInReference( } void swift::diagnoseMissingSendableConformance( - SourceLoc loc, Type type, const DeclContext *fromDC) { + SourceLoc loc, Type type, const DeclContext *fromDC, bool preconcurrency) { + SendableCheckContext sendableContext(fromDC, preconcurrency); diagnoseNonSendableTypes( - type, fromDC, /*inDerivedConformance*/Type(), + type, sendableContext, /*inDerivedConformance*/Type(), loc, diag::non_sendable_type); } @@ -2780,11 +2789,16 @@ namespace { continue; auto *closure = localFunc.getAbstractClosureExpr(); + auto *explicitClosure = dyn_cast_or_null(closure); + + bool preconcurrency = false; + if (explicitClosure) { + preconcurrency = explicitClosure->isIsolatedByPreconcurrency(); + } // Diagnose a `self` capture inside an escaping `sending` // `@Sendable` closure in a deinit, which almost certainly // means `self` would escape deinit at runtime. - auto *explicitClosure = dyn_cast_or_null(closure); auto *dc = getDeclContext(); if (explicitClosure && isa(dc) && !explicitClosure->getType()->isNoEscape() && @@ -2794,7 +2808,8 @@ namespace { if (var && var->isSelfParameter()) { ctx.Diags.diagnose(explicitClosure->getLoc(), diag::self_capture_deinit_task) - .warnUntilSwiftVersion(6); + .limitBehaviorWithPreconcurrency(DiagnosticBehavior::Warning, + preconcurrency); } } @@ -2822,6 +2837,9 @@ namespace { if (type->hasError()) continue; + SendableCheckContext sendableContext(getDeclContext(), + preconcurrency); + if (closure && closure->isImplicit()) { auto *patternBindingDecl = getTopPatternBindingDecl(); if (patternBindingDecl && patternBindingDecl->isAsyncLet()) { @@ -2843,21 +2861,21 @@ namespace { } else { // Fallback to a generic implicit capture missing sendable // conformance diagnostic. - diagnoseNonSendableTypes(type, getDeclContext(), + diagnoseNonSendableTypes(type, sendableContext, /*inDerivedConformance*/Type(), capture.getLoc(), diag::implicit_non_sendable_capture, decl->getName()); } } else if (fnType->isSendable()) { - diagnoseNonSendableTypes(type, getDeclContext(), + diagnoseNonSendableTypes(type, sendableContext, /*inDerivedConformance*/Type(), capture.getLoc(), diag::non_sendable_capture, decl->getName(), /*closure=*/closure != nullptr); } else { - diagnoseNonSendableTypes(type, getDeclContext(), + diagnoseNonSendableTypes(type, sendableContext, /*inDerivedConformance*/Type(), capture.getLoc(), diag::non_sendable_isolated_capture, @@ -4090,7 +4108,12 @@ namespace { if (!mayExecuteConcurrentlyWith(dc, findCapturedDeclContext(value))) return false; - SendableCheckContext sendableBehavior(dc); + bool preconcurrency = false; + if (auto *closure = dyn_cast(dc)) { + preconcurrency = closure->isIsolatedByPreconcurrency(); + } + + SendableCheckContext sendableBehavior(dc, preconcurrency); auto limit = sendableBehavior.defaultDiagnosticBehavior(); // Check whether this is a local variable, in which case we can @@ -4120,7 +4143,7 @@ namespace { ctx.Diags .diagnose(loc, diag::concurrent_access_of_inout_param, param->getName()) - .limitBehaviorUntilSwiftVersion(limit, 6); + .limitBehaviorWithPreconcurrency(limit, preconcurrency); return true; } } @@ -4135,7 +4158,7 @@ namespace { loc, diag::concurrent_access_of_local_capture, parent.dyn_cast(), var) - .limitBehaviorUntilSwiftVersion(limit, 6); + .limitBehaviorWithPreconcurrency(limit, preconcurrency); return true; } @@ -4145,7 +4168,7 @@ namespace { func->diagnose(diag::local_function_executed_concurrently, func) .fixItInsert(func->getAttributeInsertionLoc(false), "@Sendable ") - .limitBehaviorUntilSwiftVersion(limit, 6); + .limitBehaviorWithPreconcurrency(limit, preconcurrency); // Add the @Sendable attribute implicitly, so we don't diagnose // again. @@ -4155,7 +4178,8 @@ namespace { } // Concurrent access to some other local. - ctx.Diags.diagnose(loc, diag::concurrent_access_local, value); + ctx.Diags.diagnose(loc, diag::concurrent_access_local, value) + .limitBehaviorWithPreconcurrency(limit, preconcurrency); value->diagnose( diag::kind_declared_here, value->getDescriptiveKind()); return true; @@ -5980,7 +6004,8 @@ bool swift::contextRequiresStrictConcurrencyChecking( const DeclContext *dc, llvm::function_ref getType, llvm::function_ref isolatedByPreconcurrency) { - switch (dc->getASTContext().LangOpts.StrictConcurrencyLevel) { + auto concurrencyLevel = dc->getASTContext().LangOpts.StrictConcurrencyLevel; + switch (concurrencyLevel) { case StrictConcurrency::Complete: return true; @@ -6000,7 +6025,20 @@ bool swift::contextRequiresStrictConcurrencyChecking( // Don't take any more cues if this only got its type information by // being provided to a `@preconcurrency` operation. + // + // FIXME: contextRequiresStrictConcurrencyChecking is called from + // within the constraint system, but closures are only set to be isolated + // by preconcurrency in solution application because it's dependent on + // overload resolution. The constraint system either needs to check its + // own state on the current path, or not make type inference decisions based + // on concurrency checking level. if (isolatedByPreconcurrency(explicitClosure)) { + // If we're in minimal checking, preconcurrency always suppresses + // diagnostics. Targeted checking will still produce diagnostics if + // the outer context has adopted explicit concurrency features. + if (concurrencyLevel == StrictConcurrency::Minimal) + return false; + dc = dc->getParent(); continue; } diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 6449444dadf0d..ba0b6a4d5d192 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -320,7 +320,7 @@ bool diagnoseNonSendableTypesInReference( /// Produce a diagnostic for a missing conformance to Sendable. void diagnoseMissingSendableConformance( - SourceLoc loc, Type type, const DeclContext *fromDC); + SourceLoc loc, Type type, const DeclContext *fromDC, bool preconcurrency); /// If the given nominal type is public and does not explicitly /// state whether it conforms to Sendable, provide a diagnostic. @@ -374,12 +374,23 @@ static inline bool isImplicitSendableCheck(SendableCheck check) { /// Describes the context in which a \c Sendable check occurs. struct SendableCheckContext { const DeclContext * const fromDC; + bool preconcurrencyContext; const std::optional conformanceCheck; SendableCheckContext( const DeclContext *fromDC, std::optional conformanceCheck = std::nullopt) - : fromDC(fromDC), conformanceCheck(conformanceCheck) {} + : fromDC(fromDC), + preconcurrencyContext(false), + conformanceCheck(conformanceCheck) {} + + SendableCheckContext( + const DeclContext *fromDC, + bool preconcurrencyContext, + std::optional conformanceCheck = std::nullopt) + : fromDC(fromDC), + preconcurrencyContext(preconcurrencyContext), + conformanceCheck(conformanceCheck) {} /// Determine the default diagnostic behavior for a missing/unavailable /// Sendable conformance in this context. @@ -396,8 +407,8 @@ struct SendableCheckContext { Decl *decl, bool ignoreExplicitConformance = false) const; - /// Whether we are in an explicit conformance to Sendable. - bool isExplicitSendableConformance() const; + /// Whether to warn about a Sendable violation even in minimal checking. + bool warnInMinimalChecking() const; }; /// Diagnose any non-Sendable types that occur within the given type, using @@ -443,11 +454,14 @@ bool diagnoseNonSendableTypes( return diagnoseNonSendableTypes( type, fromContext, derivedConformance, typeLoc, [&](Type specificType, DiagnosticBehavior behavior) { + // FIXME: Reconcile preconcurrency declaration vs preconcurrency + // import behavior. auto preconcurrency = fromContext.preconcurrencyBehavior(specificType->getAnyNominal()); ctx.Diags.diagnose(diagnoseLoc, diag, type, diagArgs...) - .limitBehaviorUntilSwiftVersion(behavior, 6) + .limitBehaviorWithPreconcurrency(behavior, + fromContext.preconcurrencyContext) .limitBehaviorIf(preconcurrency); return (behavior == DiagnosticBehavior::Ignore || diff --git a/test/Concurrency/Runtime/async_stream.swift b/test/Concurrency/Runtime/async_stream.swift index a912dc533b7f6..3f181ccb76bc0 100644 --- a/test/Concurrency/Runtime/async_stream.swift +++ b/test/Concurrency/Runtime/async_stream.swift @@ -21,12 +21,12 @@ class NotSendable {} @MainActor func testWarnings() { var x = 0 _ = AsyncStream { - x += 1 // expected-warning {{mutation of captured var 'x' in concurrently-executing code; this is an error in the Swift 6 language mode}} + x += 1 // expected-warning {{mutation of captured var 'x' in concurrently-executing code}} return 0 } _ = AsyncThrowingStream { - x += 1 // expected-warning {{mutation of captured var 'x' in concurrently-executing code; this is an error in the Swift 6 language mode}} + x += 1 // expected-warning {{mutation of captured var 'x' in concurrently-executing code}} return } } diff --git a/test/Concurrency/preconcurrency_typealias.swift b/test/Concurrency/preconcurrency_typealias.swift index 4142eee0551bd..8eb9795df7ba9 100644 --- a/test/Concurrency/preconcurrency_typealias.swift +++ b/test/Concurrency/preconcurrency_typealias.swift @@ -1,7 +1,6 @@ -// RUN: %target-swift-frontend -emit-sil -o /dev/null -verify %s -// RUN: %target-swift-frontend -emit-sil -o /dev/null -verify %s -strict-concurrency=targeted -// RUN: %target-swift-frontend -emit-sil -o /dev/null -verify %s -verify-additional-prefix complete-tns- -strict-concurrency=complete -// RUN: %target-swift-frontend -emit-sil -o /dev/null -verify %s -verify-additional-prefix complete-tns- -strict-concurrency=complete -enable-upcoming-feature RegionBasedIsolation +// RUN: %target-swift-frontend -emit-sil -o /dev/null -verify %s -strict-concurrency=minimal +// RUN: %target-swift-frontend -emit-sil -o /dev/null -verify %s -strict-concurrency=targeted -verify-additional-prefix targeted-complete- +// RUN: %target-swift-frontend -emit-sil -o /dev/null -verify %s -strict-concurrency=complete -verify-additional-prefix targeted-complete- -verify-additional-prefix complete-tns- // REQUIRES: concurrency // REQUIRES: asserts @@ -31,7 +30,7 @@ func test() { var mutableVariable = 0 preconcurrencyFunc { mutableVariable += 1 // no sendable warning unless we have complete - // expected-complete-tns-warning @-1 {{mutation of captured var 'mutableVariable' in concurrently-executing code; this is an error in the Swift 6 language mode}} + // expected-complete-tns-warning @-1 {{mutation of captured var 'mutableVariable' in concurrently-executing code}} } mutableVariable += 1 } @@ -49,7 +48,7 @@ func testAsync() async { var mutableVariable = 0 preconcurrencyFunc { - mutableVariable += 1 // expected-warning{{mutation of captured var 'mutableVariable' in concurrently-executing code; this is an error in the Swift 6 language mode}} + mutableVariable += 1 // expected-targeted-complete-warning{{mutation of captured var 'mutableVariable' in concurrently-executing code}} } mutableVariable += 1 } diff --git a/test/Concurrency/predates_concurrency_swift6.swift b/test/Concurrency/predates_concurrency_swift6.swift index da3c5af934eba..7e20c81f9ba97 100644 --- a/test/Concurrency/predates_concurrency_swift6.swift +++ b/test/Concurrency/predates_concurrency_swift6.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-availability-checking -swift-version 6 %s -emit-sil -o /dev/null -verify +// RUN: %target-swift-frontend -target %target-swift-abi-5.1-triple -swift-version 6 %s -emit-sil -o /dev/null -verify // REQUIRES: concurrency @@ -162,3 +162,112 @@ do { } } } + + + +@preconcurrency +func withSendableClosure(_: @Sendable () -> Void) {} + +func conversionDowngrade() { + let ns: () -> Void = {} + withSendableClosure(ns) + // expected-warning@-1 {{converting non-sendable function value to '@Sendable () -> Void' may introduce data races}} +} + +@preconcurrency +func requireSendable(_: T) {} + +@preconcurrency +struct RequireSendable {} + +class NotSendable {} // expected-note 8 {{class 'NotSendable' does not conform to the 'Sendable' protocol}} + +class UnavailableSendable {} + +@available(*, unavailable) +extension UnavailableSendable: @unchecked Sendable {} +// expected-note@-1 8 {{conformance of 'UnavailableSendable' to 'Sendable' has been explicitly marked unavailable here}} + +typealias T = RequireSendable +// expected-warning@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + +typealias T2 = RequireSendable +// expected-warning@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} + +class C { + @preconcurrency + func requireSendable(_: T) {} + + @preconcurrency + static func requireSendableStatic(_: T) {} +} + +func testRequirementDowngrade(ns: NotSendable, us: UnavailableSendable, c: C) { + requireSendable(ns) + // expected-warning@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + + c.requireSendable(ns) + // expected-warning@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + + C.requireSendableStatic(ns) + // expected-warning@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + + requireSendable(us) + // expected-warning@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} + + c.requireSendable(us) + // expected-warning@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} + + C.requireSendableStatic(us) + // expected-warning@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} +} + + +protocol P2 {} + +extension NotSendable: P2 {} + +extension UnavailableSendable: P2 {} + +@preconcurrency +func requireSendableExistential(_: any P2 & Sendable) {} + +func requireSendableExistentialAlways(_: any P2 & Sendable) {} + +extension C { + @preconcurrency + func requireSendableExistential(_: any P2 & Sendable) {} + + @preconcurrency + static func requireSendableExistentialStatic(_: any P2 & Sendable) {} +} + +func testErasureDowngrade(ns: NotSendable, us: UnavailableSendable, c: C) { + requireSendableExistential(ns) + // expected-warning@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + + c.requireSendableExistential(ns) + // expected-warning@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + + C.requireSendableExistentialStatic(ns) + // expected-warning@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + + requireSendableExistential(us) + // expected-warning@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} + + c.requireSendableExistential(us) + // expected-warning@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} + + C.requireSendableExistentialStatic(us) + // expected-warning@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} + + withSendableClosure { + let ns = NotSendable() + requireSendableExistentialAlways(ns) + // expected-error@-1 {{type 'NotSendable' does not conform to the 'Sendable' protocol}} + + let us = UnavailableSendable() + requireSendableExistentialAlways(us) + // expected-error@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}} + } +} diff --git a/test/Concurrency/sendable_checking_captures_swift5.swift b/test/Concurrency/sendable_checking_captures_swift5.swift index c40d7e6c33d26..6006bb1ab6940 100644 --- a/test/Concurrency/sendable_checking_captures_swift5.swift +++ b/test/Concurrency/sendable_checking_captures_swift5.swift @@ -51,3 +51,21 @@ do { } } } + +class NotSendable {} // expected-complete-note {{class 'NotSendable' does not conform to the 'Sendable' protocol}} + +@available(*, unavailable) +extension NotSendable : @unchecked Sendable {} + +@preconcurrency func withSendableClosure(_: @Sendable () -> Void) {} + +func testPreconcurrencyDowngrade(ns: NotSendable) { + var x = 0 + withSendableClosure { + _ = ns + // expected-complete-warning@-1 {{capture of 'ns' with non-sendable type 'NotSendable' in a `@Sendable` closure}} + + x += 1 + // expected-complete-warning@-1 {{mutation of captured var 'x' in concurrently-executing code}} + } +} diff --git a/test/Concurrency/sendable_checking_captures_swift6.swift b/test/Concurrency/sendable_checking_captures_swift6.swift index 63d54a694f483..e94de6a1f14cd 100644 --- a/test/Concurrency/sendable_checking_captures_swift6.swift +++ b/test/Concurrency/sendable_checking_captures_swift6.swift @@ -45,8 +45,8 @@ do { sendable_preconcurrency { test.update() - // expected-error@-1 {{capture of 'test' with non-sendable type 'Test' in a `@Sendable` closure}} - // expected-error@-2 {{mutable capture of 'inout' parameter 'test' is not allowed in concurrently-executing code}} + // expected-warning@-1 {{capture of 'test' with non-sendable type 'Test' in a `@Sendable` closure}} + // expected-warning@-2 {{mutable capture of 'inout' parameter 'test' is not allowed in concurrently-executing code}} } } } diff --git a/test/Concurrency/sendable_objc_attr_in_type_context_swift6.swift b/test/Concurrency/sendable_objc_attr_in_type_context_swift6.swift index f5beac018385e..a54992dbb2754 100644 --- a/test/Concurrency/sendable_objc_attr_in_type_context_swift6.swift +++ b/test/Concurrency/sendable_objc_attr_in_type_context_swift6.swift @@ -104,13 +104,13 @@ func test_sendable_attr_in_type_context(test: Test) { // TOOD(diagnostics): Duplicate diagnostics TestWithSendableID().add(MyValue()) - // expected-error@-1 3 {{type 'MyValue' does not conform to the 'Sendable' protocol}} + // expected-warning@-1 3 {{type 'MyValue' does not conform to the 'Sendable' protocol}} TestWithSendableSuperclass().add(SendableMyValue()) // Ok // TOOD(diagnostics): Duplicate diagnostics TestWithSendableSuperclass().add(MyValue()) - // expected-error@-1 3 {{type 'MyValue' does not conform to the 'Sendable' protocol}} + // expected-warning@-1 3 {{type 'MyValue' does not conform to the 'Sendable' protocol}} } class TestConformanceWithStripping : InnerSendableTypes {