@@ -79,6 +79,19 @@ bool swift::isExported(const ValueDecl *VD) {
7979 return false ;
8080}
8181
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+
8295bool swift::isExported (const ExtensionDecl *ED) {
8396 // An extension can only be exported if it extends an exported type.
8497 if (auto *NTD = ED->getExtendedNominal ()) {
@@ -94,14 +107,8 @@ bool swift::isExported(const ExtensionDecl *ED) {
94107
95108 // If the extension declares a conformance to a public protocol then the
96109 // 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 ;
105112
106113 return false ;
107114}
@@ -313,7 +320,7 @@ static bool hasActiveAvailableAttribute(Decl *D,
313320 return getActiveAvailableAttribute (D, AC);
314321}
315322
316- static bool bodyIsResilienceBoundary (Decl *D) {
323+ static bool shouldConstrainBodyToDeploymentTarget (Decl *D) {
317324 // The declaration contains code...
318325 if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
319326 // And it has a location so we can check it...
@@ -526,9 +533,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
526533 AvailabilityContext DeclInfo = ExplicitDeclInfo;
527534 DeclInfo.intersectWith (getCurrentTRC ()->getAvailabilityInfo ());
528535
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))
532537 DeclInfo.intersectWith (AvailabilityContext::forDeploymentTarget (Context));
533538
534539 SourceRange Range = refinementSourceRangeForDecl (D);
@@ -562,9 +567,10 @@ class TypeRefinementContextBuilder : private ASTWalker {
562567 }
563568
564569 // 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.
566572 if (!hasActiveAvailableAttribute (D, Context) &&
567- !signatureIsResilienceBoundary (D)) {
573+ !shouldConstrainSignatureToDeploymentTarget (D)) {
568574 return false ;
569575 }
570576
@@ -581,12 +587,23 @@ class TypeRefinementContextBuilder : private ASTWalker {
581587 return true ;
582588 }
583589
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);
590607 }
591608
592609 // / Returns the source range which should be refined by declaration. This
@@ -633,14 +650,14 @@ class TypeRefinementContextBuilder : private ASTWalker {
633650 }
634651
635652 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.
638655 if (isCurrentTRCContainedByDeploymentTarget ())
639656 return false ;
640657
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.
642659 if (auto afd = dyn_cast<AbstractFunctionDecl>(D))
643- return bodyIsResilienceBoundary (afd);
660+ return shouldConstrainBodyToDeploymentTarget (afd);
644661
645662 // The only other case we care about is top-level code.
646663 return isa<TopLevelCodeDecl>(D);
@@ -4089,14 +4106,7 @@ void swift::checkExplicitAvailability(Decl *decl) {
40894106 return false ;
40904107 });
40914108
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);
41004110
41014111 if (!hasMembers && !hasProtocols) return ;
41024112
0 commit comments