2929#include " TypeCheckObjC.h"
3030#include " TypeCheckUnsafe.h"
3131#include " swift/AST/ASTContext.h"
32+ #include " swift/AST/ASTContextGlobalCache.h"
3233#include " swift/AST/ASTMangler.h"
3334#include " swift/AST/ASTPrinter.h"
3435#include " swift/AST/AccessScope.h"
@@ -2352,6 +2353,10 @@ static bool hasRuntimeConformanceInfo(ProtocolDecl *proto) {
23522353 || proto->isSpecificProtocol (KnownProtocolKind::Escapable);
23532354}
23542355
2356+ static void diagnoseConformanceIsolationErrors (
2357+ const NormalProtocolConformance *conformance
2358+ );
2359+
23552360static void ensureRequirementsAreSatisfied (ASTContext &ctx,
23562361 NormalProtocolConformance *conformance);
23572362
@@ -2685,6 +2690,8 @@ checkIndividualConformance(NormalProtocolConformance *conformance) {
26852690 diagnoseUnsafeUse (unsafeUse);
26862691 }
26872692 }
2693+
2694+ diagnoseConformanceIsolationErrors (conformance);
26882695}
26892696
26902697// / Add the next associated type deduction to the string representation
@@ -3543,61 +3550,14 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement,
35433550 }
35443551 }
35453552
3546- // Complain that this witness cannot conform to the requirement due to
3547- // actor isolation.
3548- witness
3549- ->diagnose (diag::actor_isolated_witness,
3550- isDistributed && !isDistributedDecl (witness),
3551- refResult.isolation , witness, requirementIsolation,
3552- Conformance->getProtocol ())
3553- .limitBehaviorUntilSwiftVersion (behavior, 6 );
3554-
3555- // If we need 'distributed' on the witness, add it.
3556- if (missingOptions.contains (MissingFlags::WitnessDistributed)) {
3557- witness->diagnose (
3558- diag::note_add_distributed_to_decl, witness)
3559- .fixItInsert (witness->getAttributeInsertionLoc (true ),
3560- " distributed " );
3561- missingOptions -= MissingFlags::WitnessDistributed;
3562- }
3563-
3564- // If 'nonisolated' or 'preconcurrency' might help us, provide those as
3565- // options.
3566- if (!isDistributedDecl (requirement) && !isDistributedDecl (witness)) {
3567- // One way to address the issue is to make the witness function nonisolated.
3568- if ((isa<AbstractFunctionDecl>(witness) || isa<SubscriptDecl>(witness)) &&
3569- !hasExplicitGlobalActorAttr (witness)) {
3570- witness->diagnose (diag::note_add_nonisolated_to_decl, witness)
3571- .fixItInsert (witness->getAttributeInsertionLoc (true ), " nonisolated " );
3572- }
3573-
3574- // Another way to address the issue is to mark the conformance as
3575- // isolated to the global actor or "@preconcurrency".
3576- if (Conformance->getSourceKind () == ConformanceEntryKind::Explicit &&
3577- !Conformance->isIsolated () &&
3578- !Conformance->isPreconcurrency () &&
3579- !suggestedPreconcurrencyOrIsolated &&
3580- !requirementIsolation.isActorIsolated ()) {
3581- if (Context.LangOpts .hasFeature (Feature::IsolatedConformances) &&
3582- refResult.isolation .isGlobalActor ()) {
3583- std::string globalActorStr = " @" +
3584- refResult.isolation .getGlobalActor ().getString ();
3585- Context.Diags .diagnose (Conformance->getProtocolNameLoc (),
3586- diag::add_isolated_to_conformance,
3587- globalActorStr,
3588- Proto->getName (), refResult.isolation )
3589- .fixItInsert (Conformance->getProtocolNameLoc (),
3590- globalActorStr + " " );
3591- }
3592-
3593- Context.Diags .diagnose (Conformance->getProtocolNameLoc (),
3594- diag::add_preconcurrency_to_conformance,
3595- Proto->getName ())
3596- .fixItInsert (Conformance->getProtocolNameLoc (), " @preconcurrency " );
3597-
3598- suggestedPreconcurrencyOrIsolated = true ;
3599- }
3600- }
3553+ // Note the isolation issue with this witness, to be diagnosed later.
3554+ ASTContext &ctx = DC->getASTContext ();
3555+ ctx.getGlobalCache ().conformanceIsolationErrors [Conformance].push_back (
3556+ WitnessIsolationError{
3557+ requirement, witness, behavior,
3558+ missingOptions.contains (MissingFlags::WitnessDistributed),
3559+ requirementIsolation, refResult.isolation
3560+ });
36013561
36023562 return std::nullopt ;
36033563}
@@ -4829,6 +4789,140 @@ void ConformanceChecker::resolveSingleWitness(ValueDecl *requirement) {
48294789 }
48304790}
48314791
4792+ void WitnessIsolationError::diagnose (
4793+ const NormalProtocolConformance *conformance,
4794+ bool &suggestedPreconcurrencyOrIsolated
4795+ ) const {
4796+ bool isDistributed = referenceIsolation.isDistributedActor () &&
4797+ !witness->getAttrs ().hasAttribute <NonisolatedAttr>();
4798+
4799+ // Complain that this witness cannot conform to the requirement due to
4800+ // actor isolation.
4801+ witness
4802+ ->diagnose (diag::actor_isolated_witness,
4803+ isDistributed && !isDistributedDecl (witness),
4804+ referenceIsolation, witness, requirementIsolation,
4805+ conformance->getProtocol ())
4806+ .limitBehaviorUntilSwiftVersion (behavior, 6 );
4807+
4808+ // If we need 'distributed' on the witness, add it.
4809+ if (isMissingDistributed) {
4810+ witness->diagnose (
4811+ diag::note_add_distributed_to_decl, witness)
4812+ .fixItInsert (witness->getAttributeInsertionLoc (true ), " distributed " );
4813+ }
4814+
4815+ // If either of these are distributed, there's nothing more to say.
4816+ if (isDistributedDecl (requirement) || isDistributedDecl (witness))
4817+ return ;
4818+
4819+ // If 'nonisolated' or 'preconcurrency' might help us, provide those as
4820+ // options.
4821+ // One way to address the issue is to make the witness function nonisolated.
4822+ if ((isa<AbstractFunctionDecl>(witness) || isa<SubscriptDecl>(witness)) &&
4823+ !hasExplicitGlobalActorAttr (witness)) {
4824+ witness->diagnose (diag::note_add_nonisolated_to_decl, witness)
4825+ .fixItInsert (witness->getAttributeInsertionLoc (true ), " nonisolated " );
4826+ }
4827+
4828+ // Another way to address the issue is to mark the conformance as
4829+ // isolated to the global actor or "@preconcurrency".
4830+ if (conformance->getSourceKind () == ConformanceEntryKind::Explicit &&
4831+ !conformance->isIsolated () &&
4832+ !conformance->isPreconcurrency () &&
4833+ !suggestedPreconcurrencyOrIsolated &&
4834+ !requirementIsolation.isActorIsolated ()) {
4835+ auto proto = conformance->getProtocol ();
4836+ ASTContext &ctx = proto->getASTContext ();
4837+ if (ctx.LangOpts .hasFeature (Feature::IsolatedConformances) &&
4838+ referenceIsolation.isGlobalActor ()) {
4839+ std::string globalActorStr = " @" +
4840+ referenceIsolation.getGlobalActor ().getString ();
4841+ ctx.Diags .diagnose (conformance->getProtocolNameLoc (),
4842+ diag::add_isolated_to_conformance,
4843+ globalActorStr,
4844+ proto->getName (), referenceIsolation)
4845+ .fixItInsert (conformance->getProtocolNameLoc (),
4846+ globalActorStr + " " );
4847+ }
4848+
4849+ ctx.Diags .diagnose (conformance->getProtocolNameLoc (),
4850+ diag::add_preconcurrency_to_conformance,
4851+ proto->getName ())
4852+ .fixItInsert (conformance->getProtocolNameLoc (), " @preconcurrency " );
4853+
4854+ suggestedPreconcurrencyOrIsolated = true ;
4855+ }
4856+ }
4857+
4858+ void AssociatedConformanceIsolationError::diagnose (
4859+ const NormalProtocolConformance *conformance
4860+ ) const {
4861+ auto outerIsolation = conformance->getIsolation ();
4862+ auto innerIsolation = isolatedConformance->getIsolation ();
4863+
4864+ ASTContext &ctx = conformance->getDeclContext ()->getASTContext ();
4865+
4866+ // If the conformance we're checking isn't isolated at all, it
4867+ // needs to be isolated to the given global actor.
4868+ if (!outerIsolation.isGlobalActor ()) {
4869+ std::string globalActorStr = " @" +
4870+ innerIsolation.getGlobalActor ().getString ();
4871+ ctx.Diags .diagnose (
4872+ conformance->getLoc (),
4873+ diag::nonisolated_conformance_depends_on_isolated_conformance,
4874+ conformance->getType (),
4875+ conformance->getProtocol ()->getName (),
4876+ innerIsolation,
4877+ isolatedConformance->getType (),
4878+ isolatedConformance->getProtocol ()->getName (),
4879+ globalActorStr
4880+ ).fixItInsert (conformance->getProtocolNameLoc (),
4881+ globalActorStr + " " );
4882+
4883+ return ;
4884+ }
4885+
4886+ assert (outerIsolation != innerIsolation);
4887+ ctx.Diags .diagnose (
4888+ conformance->getLoc (),
4889+ diag::isolated_conformance_mismatch_with_associated_isolation,
4890+ outerIsolation,
4891+ conformance->getType (),
4892+ conformance->getProtocol ()->getName (),
4893+ innerIsolation,
4894+ isolatedConformance->getType (),
4895+ isolatedConformance->getProtocol ()->getName ()
4896+ );
4897+ }
4898+
4899+ static void diagnoseConformanceIsolationErrors (
4900+ const NormalProtocolConformance *conformance
4901+ ) {
4902+ // Check whether we have any conformance isolation errors.
4903+ ASTContext &ctx = conformance->getDeclContext ()->getASTContext ();
4904+ auto &globalCache = ctx.getGlobalCache ();
4905+ auto known = globalCache.conformanceIsolationErrors .find (conformance);
4906+ if (known == globalCache.conformanceIsolationErrors .end ())
4907+ return ;
4908+
4909+ // Take the isolation errors out of the global cache.
4910+ auto isolationErrors = std::move (known->second );
4911+ globalCache.conformanceIsolationErrors .erase (known);
4912+
4913+ bool suggestedPreconcurrencyOrIsolated = false ;
4914+ for (const auto &error : isolationErrors) {
4915+ if (std::holds_alternative<WitnessIsolationError>(error)) {
4916+ const auto &witnessError = std::get<WitnessIsolationError>(error);
4917+ witnessError.diagnose (conformance, suggestedPreconcurrencyOrIsolated);
4918+ } else {
4919+ const auto &assocConformanceError =
4920+ std::get<AssociatedConformanceIsolationError>(error);
4921+ assocConformanceError.diagnose (conformance);
4922+ }
4923+ }
4924+ }
4925+
48324926// / FIXME: It feels like this could be part of findExistentialSelfReferences().
48334927static std::optional<Requirement>
48344928hasInvariantSelfRequirement (const ProtocolDecl *proto,
@@ -5136,8 +5230,6 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
51365230 if (where.isImplicit ())
51375231 return ;
51385232
5139- bool diagnosedIsolatedConformanceIssue = false ;
5140-
51415233 conformance->forEachAssociatedConformance (
51425234 [&](Type depTy, ProtocolDecl *proto, unsigned index) {
51435235 auto assocConf = conformance->getAssociatedConformance (depTy, proto);
@@ -5161,54 +5253,23 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
51615253 where.withRefinedAvailability (availability), depTy, replacementTy);
51625254 }
51635255
5164- if (!diagnosedIsolatedConformanceIssue) {
5165- auto outerIsolation = conformance->getIsolation ();
5166- bool foundIssue = ProtocolConformanceRef (assocConf)
5167- .forEachIsolatedConformance (
5168- [&](ProtocolConformance *isolatedConformance) {
5169- auto innerIsolation = isolatedConformance->getIsolation ();
5170-
5171- // If the conformance we're checking isn't isolated at all, it
5172- // needs "isolated".
5173- if (!outerIsolation.isGlobalActor ()) {
5174- std::string globalActorStr = " @" +
5175- innerIsolation.getGlobalActor ().getString ();
5176- ctx.Diags .diagnose (
5177- conformance->getLoc (),
5178- diag::nonisolated_conformance_depends_on_isolated_conformance,
5179- typeInContext, conformance->getProtocol ()->getName (),
5180- innerIsolation,
5181- isolatedConformance->getType (),
5182- isolatedConformance->getProtocol ()->getName (),
5183- globalActorStr
5184- ).fixItInsert (conformance->getProtocolNameLoc (),
5185- globalActorStr + " " );
5186-
5187- return true ;
5188- }
5189-
5190- // The conformance is isolated, but we need it to have the same
5191- // isolation as the other isolated conformance we found.
5192- if (outerIsolation != innerIsolation) {
5193- ctx.Diags .diagnose (
5194- conformance->getLoc (),
5195- diag::isolated_conformance_mismatch_with_associated_isolation,
5196- outerIsolation,
5197- typeInContext, conformance->getProtocol ()->getName (),
5198- innerIsolation,
5199- isolatedConformance->getType (),
5200- isolatedConformance->getProtocol ()->getName ()
5201- );
5202-
5203- return true ;
5204- }
5205-
5206- return false ;
5256+ auto outerIsolation = conformance->getIsolation ();
5257+ ProtocolConformanceRef (assocConf).forEachIsolatedConformance (
5258+ [&](ProtocolConformance *isolatedConformance) {
5259+ auto innerIsolation = isolatedConformance->getIsolation ();
5260+
5261+ // If the isolation doesn't match, record an error.
5262+ if (!outerIsolation.isGlobalActor () ||
5263+ outerIsolation != innerIsolation) {
5264+ ctx.getGlobalCache ().conformanceIsolationErrors [conformance]
5265+ .push_back (
5266+ AssociatedConformanceIsolationError{isolatedConformance});
5267+ return true ;
52075268 }
5208- );
52095269
5210- diagnosedIsolatedConformanceIssue = foundIssue;
5211- }
5270+ return false ;
5271+ }
5272+ );
52125273
52135274 return false ;
52145275 });
0 commit comments