@@ -79,6 +79,19 @@ bool swift::isExported(const ValueDecl *VD) {
79
79
return false ;
80
80
}
81
81
82
+ static bool hasConformancesToPublicProtocols (const ExtensionDecl *ED) {
83
+ auto protocols = ED->getLocalProtocols (ConformanceLookupKind::OnlyExplicit);
84
+ for (const ProtocolDecl *PD : protocols) {
85
+ AccessScope scope =
86
+ PD->getFormalAccessScope (/* useDC*/ nullptr ,
87
+ /* treatUsableFromInlineAsPublic*/ true );
88
+ if (scope.isPublic ())
89
+ return true ;
90
+ }
91
+
92
+ return false ;
93
+ }
94
+
82
95
bool swift::isExported (const ExtensionDecl *ED) {
83
96
// An extension can only be exported if it extends an exported type.
84
97
if (auto *NTD = ED->getExtendedNominal ()) {
@@ -94,14 +107,8 @@ bool swift::isExported(const ExtensionDecl *ED) {
94
107
95
108
// If the extension declares a conformance to a public protocol then the
96
109
// extension is exported.
97
- auto protocols = ED->getLocalProtocols (ConformanceLookupKind::OnlyExplicit);
98
- for (const ProtocolDecl *PD : protocols) {
99
- AccessScope scope =
100
- PD->getFormalAccessScope (/* useDC*/ nullptr ,
101
- /* treatUsableFromInlineAsPublic*/ true );
102
- if (scope.isPublic ())
103
- return true ;
104
- }
110
+ if (hasConformancesToPublicProtocols (ED))
111
+ return true ;
105
112
106
113
return false ;
107
114
}
@@ -313,7 +320,7 @@ static bool hasActiveAvailableAttribute(Decl *D,
313
320
return getActiveAvailableAttribute (D, AC);
314
321
}
315
322
316
- static bool bodyIsResilienceBoundary (Decl *D) {
323
+ static bool shouldConstrainBodyToDeploymentTarget (Decl *D) {
317
324
// The declaration contains code...
318
325
if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
319
326
// And it has a location so we can check it...
@@ -526,9 +533,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
526
533
AvailabilityContext DeclInfo = ExplicitDeclInfo;
527
534
DeclInfo.intersectWith (getCurrentTRC ()->getAvailabilityInfo ());
528
535
529
- // If the entire declaration is surrounded by a resilience boundary, it is
530
- // also constrained by the deployment target.
531
- if (signatureIsResilienceBoundary (D))
536
+ if (shouldConstrainSignatureToDeploymentTarget (D))
532
537
DeclInfo.intersectWith (AvailabilityContext::forDeploymentTarget (Context));
533
538
534
539
SourceRange Range = refinementSourceRangeForDecl (D);
@@ -562,9 +567,10 @@ class TypeRefinementContextBuilder : private ASTWalker {
562
567
}
563
568
564
569
// No need to introduce a context if the declaration does not have an
565
- // availability attribute and the signature is not a resilience boundary.
570
+ // availability attribute and the signature does not constrain availability
571
+ // to the deployment target.
566
572
if (!hasActiveAvailableAttribute (D, Context) &&
567
- !signatureIsResilienceBoundary (D)) {
573
+ !shouldConstrainSignatureToDeploymentTarget (D)) {
568
574
return false ;
569
575
}
570
576
@@ -581,12 +587,23 @@ class TypeRefinementContextBuilder : private ASTWalker {
581
587
return true ;
582
588
}
583
589
584
- // / A declaration's signature is a resilience boundary if the entire
585
- // / declaration--not just the body--is not ABI-public and it's in a context
586
- // / where ABI-public declarations would be available below the minimum
587
- // / deployment target.
588
- bool signatureIsResilienceBoundary (Decl *D) {
589
- return !isCurrentTRCContainedByDeploymentTarget () && !::isExported (D);
590
+ // / Checks whether the entire declaration, including its signature, should be
591
+ // / constrained to the deployment target. Generally public API declarations
592
+ // / are not constrained since they appear in the interface of the module and
593
+ // / may be consumed by clients with lower deployment targets, but there are
594
+ // / some exceptions.
595
+ bool shouldConstrainSignatureToDeploymentTarget (Decl *D) {
596
+ if (isCurrentTRCContainedByDeploymentTarget ())
597
+ return false ;
598
+
599
+ // As a convenience, SPI decls and explicitly unavailable decls are
600
+ // constrained to the deployment target. There's not much benefit to
601
+ // checking these declarations at a lower availability version floor since
602
+ // neither can be used by API clients.
603
+ if (D->isSPI () || AvailableAttr::isUnavailable (D))
604
+ return true ;
605
+
606
+ return !::isExported (D);
590
607
}
591
608
592
609
// / Returns the source range which should be refined by declaration. This
@@ -633,14 +650,14 @@ class TypeRefinementContextBuilder : private ASTWalker {
633
650
}
634
651
635
652
bool bodyIntroducesNewContext (Decl *D) {
636
- // Are we already effectively in a resilience boundary ? If not, adding one
637
- // wouldn't change availability.
653
+ // Are we already constrained by the deployment target ? If not, adding a
654
+ // new context wouldn't change availability.
638
655
if (isCurrentTRCContainedByDeploymentTarget ())
639
656
return false ;
640
657
641
- // If we're in a function, is its body a resilience boundary?
658
+ // If we're in a function, check if it ought to use the deployment target.
642
659
if (auto afd = dyn_cast<AbstractFunctionDecl>(D))
643
- return bodyIsResilienceBoundary (afd);
660
+ return shouldConstrainBodyToDeploymentTarget (afd);
644
661
645
662
// The only other case we care about is top-level code.
646
663
return isa<TopLevelCodeDecl>(D);
@@ -4089,14 +4106,7 @@ void swift::checkExplicitAvailability(Decl *decl) {
4089
4106
return false ;
4090
4107
});
4091
4108
4092
- auto protocols = extension->getLocalProtocols (ConformanceLookupKind::OnlyExplicit);
4093
- auto hasProtocols = std::any_of (protocols.begin (), protocols.end (),
4094
- [](const ProtocolDecl *PD) -> bool {
4095
- AccessScope scope =
4096
- PD->getFormalAccessScope (/* useDC*/ nullptr ,
4097
- /* treatUsableFromInlineAsPublic*/ true );
4098
- return scope.isPublic ();
4099
- });
4109
+ auto hasProtocols = hasConformancesToPublicProtocols (extension);
4100
4110
4101
4111
if (!hasMembers && !hasProtocols) return ;
4102
4112
0 commit comments