29
29
#include " TypeCheckObjC.h"
30
30
#include " TypeCheckUnsafe.h"
31
31
#include " swift/AST/ASTContext.h"
32
+ #include " swift/AST/ASTContextGlobalCache.h"
32
33
#include " swift/AST/ASTMangler.h"
33
34
#include " swift/AST/ASTPrinter.h"
34
35
#include " swift/AST/AccessScope.h"
@@ -2352,6 +2353,10 @@ static bool hasRuntimeConformanceInfo(ProtocolDecl *proto) {
2352
2353
|| proto->isSpecificProtocol (KnownProtocolKind::Escapable);
2353
2354
}
2354
2355
2356
+ static void diagnoseConformanceIsolationErrors (
2357
+ const NormalProtocolConformance *conformance
2358
+ );
2359
+
2355
2360
static void ensureRequirementsAreSatisfied (ASTContext &ctx,
2356
2361
NormalProtocolConformance *conformance);
2357
2362
@@ -2685,6 +2690,8 @@ checkIndividualConformance(NormalProtocolConformance *conformance) {
2685
2690
diagnoseUnsafeUse (unsafeUse);
2686
2691
}
2687
2692
}
2693
+
2694
+ diagnoseConformanceIsolationErrors (conformance);
2688
2695
}
2689
2696
2690
2697
// / Add the next associated type deduction to the string representation
@@ -3543,61 +3550,14 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement,
3543
3550
}
3544
3551
}
3545
3552
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
+ });
3601
3561
3602
3562
return std::nullopt;
3603
3563
}
@@ -4829,6 +4789,140 @@ void ConformanceChecker::resolveSingleWitness(ValueDecl *requirement) {
4829
4789
}
4830
4790
}
4831
4791
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
+
4832
4926
// / FIXME: It feels like this could be part of findExistentialSelfReferences().
4833
4927
static std::optional<Requirement>
4834
4928
hasInvariantSelfRequirement (const ProtocolDecl *proto,
@@ -5136,8 +5230,6 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
5136
5230
if (where.isImplicit ())
5137
5231
return ;
5138
5232
5139
- bool diagnosedIsolatedConformanceIssue = false ;
5140
-
5141
5233
conformance->forEachAssociatedConformance (
5142
5234
[&](Type depTy, ProtocolDecl *proto, unsigned index ) {
5143
5235
auto assocConf = conformance->getAssociatedConformance (depTy, proto);
@@ -5161,54 +5253,23 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
5161
5253
where.withRefinedAvailability (availability), depTy, replacementTy);
5162
5254
}
5163
5255
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 ;
5207
5268
}
5208
- );
5209
5269
5210
- diagnosedIsolatedConformanceIssue = foundIssue;
5211
- }
5270
+ return false ;
5271
+ }
5272
+ );
5212
5273
5213
5274
return false ;
5214
5275
});
0 commit comments