@@ -779,7 +779,10 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
779779 });
780780}
781781
782- bool SendableCheckContext::isExplicitSendableConformance () const {
782+ bool SendableCheckContext::warnInMinimalChecking () const {
783+ if (preconcurrencyContext)
784+ return false ;
785+
783786 if (!conformanceCheck)
784787 return false ;
785788
@@ -797,7 +800,7 @@ bool SendableCheckContext::isExplicitSendableConformance() const {
797800DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior () const {
798801 // If we're not supposed to diagnose existing data races from this context,
799802 // ignore the diagnostic entirely.
800- if (!isExplicitSendableConformance () &&
803+ if (!warnInMinimalChecking () &&
801804 !shouldDiagnoseExistingDataRaces (fromDC))
802805 return DiagnosticBehavior::Ignore;
803806
@@ -816,9 +819,7 @@ SendableCheckContext::implicitSendableDiagnosticBehavior() const {
816819 LLVM_FALLTHROUGH;
817820
818821 case StrictConcurrency::Minimal:
819- // Explicit Sendable conformances always diagnose, even when strict
820- // strict checking is disabled.
821- if (isExplicitSendableConformance ())
822+ if (warnInMinimalChecking ())
822823 return DiagnosticBehavior::Warning;
823824
824825 return DiagnosticBehavior::Ignore;
@@ -832,6 +833,13 @@ SendableCheckContext::implicitSendableDiagnosticBehavior() const {
832833// / nominal type.
833834DiagnosticBehavior SendableCheckContext::diagnosticBehavior (
834835 NominalTypeDecl *nominal) const {
836+ // If we're in a preconcurrency context, don't override the default behavior
837+ // based on explicit conformances. For example, a @preconcurrency @Sendable
838+ // closure should not warn about an explicitly unavailable Sendable
839+ // conformance in minimal checking.
840+ if (preconcurrencyContext)
841+ return defaultDiagnosticBehavior ();
842+
835843 if (hasExplicitSendableConformance (nominal))
836844 return DiagnosticBehavior::Warning;
837845
@@ -2759,11 +2767,16 @@ namespace {
27592767 continue ;
27602768
27612769 auto *closure = localFunc.getAbstractClosureExpr ();
2770+ auto *explicitClosure = dyn_cast_or_null<ClosureExpr>(closure);
2771+
2772+ bool preconcurrency = false ;
2773+ if (explicitClosure) {
2774+ preconcurrency = explicitClosure->isIsolatedByPreconcurrency ();
2775+ }
27622776
27632777 // Diagnose a `self` capture inside an escaping `sending`
27642778 // `@Sendable` closure in a deinit, which almost certainly
27652779 // means `self` would escape deinit at runtime.
2766- auto *explicitClosure = dyn_cast_or_null<ClosureExpr>(closure);
27672780 auto *dc = getDeclContext ();
27682781 if (explicitClosure && isa<DestructorDecl>(dc) &&
27692782 !explicitClosure->getType ()->isNoEscape () &&
@@ -2773,7 +2786,8 @@ namespace {
27732786 if (var && var->isSelfParameter ()) {
27742787 ctx.Diags .diagnose (explicitClosure->getLoc (),
27752788 diag::self_capture_deinit_task)
2776- .warnUntilSwiftVersion (6 );
2789+ .limitBehaviorWithPreconcurrency (DiagnosticBehavior::Warning,
2790+ preconcurrency);
27772791 }
27782792 }
27792793
@@ -2801,6 +2815,9 @@ namespace {
28012815 if (type->hasError ())
28022816 continue ;
28032817
2818+ SendableCheckContext sendableContext (getDeclContext (),
2819+ preconcurrency);
2820+
28042821 if (closure && closure->isImplicit ()) {
28052822 auto *patternBindingDecl = getTopPatternBindingDecl ();
28062823 if (patternBindingDecl && patternBindingDecl->isAsyncLet ()) {
@@ -2811,20 +2828,20 @@ namespace {
28112828
28122829 // Fallback to a generic implicit capture missing sendable
28132830 // conformance diagnostic.
2814- diagnoseNonSendableTypes (type, getDeclContext () ,
2831+ diagnoseNonSendableTypes (type, sendableContext ,
28152832 /* inDerivedConformance*/ Type (),
28162833 capture.getLoc (),
28172834 diag::implicit_non_sendable_capture,
28182835 decl->getName ());
28192836 } else if (fnType->isSendable ()) {
2820- diagnoseNonSendableTypes (type, getDeclContext () ,
2837+ diagnoseNonSendableTypes (type, sendableContext ,
28212838 /* inDerivedConformance*/ Type (),
28222839 capture.getLoc (),
28232840 diag::non_sendable_capture,
28242841 decl->getName (),
28252842 /* closure=*/ closure != nullptr );
28262843 } else {
2827- diagnoseNonSendableTypes (type, getDeclContext () ,
2844+ diagnoseNonSendableTypes (type, sendableContext ,
28282845 /* inDerivedConformance*/ Type (),
28292846 capture.getLoc (),
28302847 diag::non_sendable_isolated_capture,
@@ -4066,7 +4083,12 @@ namespace {
40664083 if (!mayExecuteConcurrentlyWith (dc, findCapturedDeclContext (value)))
40674084 return false ;
40684085
4069- SendableCheckContext sendableBehavior (dc);
4086+ bool preconcurrency = false ;
4087+ if (auto *closure = dyn_cast<ClosureExpr>(dc)) {
4088+ preconcurrency = closure->isIsolatedByPreconcurrency ();
4089+ }
4090+
4091+ SendableCheckContext sendableBehavior (dc, preconcurrency);
40704092 auto limit = sendableBehavior.defaultDiagnosticBehavior ();
40714093
40724094 // Check whether this is a local variable, in which case we can
@@ -4096,7 +4118,7 @@ namespace {
40964118 ctx.Diags
40974119 .diagnose (loc, diag::concurrent_access_of_inout_param,
40984120 param->getName ())
4099- .limitBehaviorUntilSwiftVersion (limit, 6 );
4121+ .limitBehaviorWithPreconcurrency (limit, preconcurrency );
41004122 return true ;
41014123 }
41024124 }
@@ -4111,7 +4133,7 @@ namespace {
41114133 loc, diag::concurrent_access_of_local_capture,
41124134 parent.dyn_cast <LoadExpr *>(),
41134135 var)
4114- .limitBehaviorUntilSwiftVersion (limit, 6 );
4136+ .limitBehaviorWithPreconcurrency (limit, preconcurrency );
41154137 return true ;
41164138 }
41174139
@@ -4121,7 +4143,7 @@ namespace {
41214143
41224144 func->diagnose (diag::local_function_executed_concurrently, func)
41234145 .fixItInsert (func->getAttributeInsertionLoc (false ), " @Sendable " )
4124- .limitBehaviorUntilSwiftVersion (limit, 6 );
4146+ .limitBehaviorWithPreconcurrency (limit, preconcurrency );
41254147
41264148 // Add the @Sendable attribute implicitly, so we don't diagnose
41274149 // again.
@@ -4131,7 +4153,8 @@ namespace {
41314153 }
41324154
41334155 // Concurrent access to some other local.
4134- ctx.Diags .diagnose (loc, diag::concurrent_access_local, value);
4156+ ctx.Diags .diagnose (loc, diag::concurrent_access_local, value)
4157+ .limitBehaviorWithPreconcurrency (limit, preconcurrency);
41354158 value->diagnose (
41364159 diag::kind_declared_here, value->getDescriptiveKind ());
41374160 return true ;
@@ -6194,7 +6217,8 @@ bool swift::contextRequiresStrictConcurrencyChecking(
61946217 const DeclContext *dc,
61956218 llvm::function_ref<Type(const AbstractClosureExpr *)> getType,
61966219 llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency) {
6197- switch (dc->getASTContext ().LangOpts .StrictConcurrencyLevel ) {
6220+ auto concurrencyLevel = dc->getASTContext ().LangOpts .StrictConcurrencyLevel ;
6221+ switch (concurrencyLevel) {
61986222 case StrictConcurrency::Complete:
61996223 return true ;
62006224
@@ -6214,7 +6238,20 @@ bool swift::contextRequiresStrictConcurrencyChecking(
62146238
62156239 // Don't take any more cues if this only got its type information by
62166240 // being provided to a `@preconcurrency` operation.
6241+ //
6242+ // FIXME: contextRequiresStrictConcurrencyChecking is called from
6243+ // within the constraint system, but closures are only set to be isolated
6244+ // by preconcurrency in solution application because it's dependent on
6245+ // overload resolution. The constraint system either needs to check its
6246+ // own state on the current path, or not make type inference decisions based
6247+ // on concurrency checking level.
62176248 if (isolatedByPreconcurrency (explicitClosure)) {
6249+ // If we're in minimal checking, preconcurrency always suppresses
6250+ // diagnostics. Targeted checking will still produce diagnostics if
6251+ // the outer context has adopted explicit concurrency features.
6252+ if (concurrencyLevel == StrictConcurrency::Minimal)
6253+ return false ;
6254+
62186255 dc = dc->getParent ();
62196256 continue ;
62206257 }
0 commit comments