1616
1717#include " swift/AST/ASTContext.h"
1818#include " swift/AST/Attr.h"
19+ #include " swift/AST/AvailabilityConstraint.h"
20+ #include " swift/AST/AvailabilityContext.h"
1921#include " swift/AST/AvailabilityDomain.h"
2022#include " swift/AST/AvailabilityInference.h"
2123#include " swift/AST/AvailabilityRange.h"
@@ -454,17 +456,13 @@ Decl::getSemanticAvailableAttr(const AvailableAttr *attr) const {
454456}
455457
456458std::optional<SemanticAvailableAttr>
457- Decl::getActiveAvailableAttrForCurrentPlatform (bool ignoreAppExtensions ) const {
459+ Decl::getActiveAvailableAttrForCurrentPlatform () const {
458460 std::optional<SemanticAvailableAttr> bestAttr;
459461
460462 for (auto attr : getSemanticAvailableAttrs (/* includingInactive=*/ false )) {
461463 if (!attr.isPlatformSpecific ())
462464 continue ;
463465
464- if (ignoreAppExtensions &&
465- isApplicationExtensionPlatform (attr.getPlatform ()))
466- continue ;
467-
468466 // We have an attribute that is active for the platform, but is it more
469467 // specific than our current best?
470468 if (!bestAttr || inheritsAvailabilityFromPlatform (
@@ -575,23 +573,17 @@ bool Decl::isUnavailableInCurrentSwiftVersion() const {
575573 return false ;
576574}
577575
578- std::optional<SemanticAvailableAttr>
579- getDeclUnavailableAttr (const Decl *D, bool ignoreAppExtensions) {
576+ std::optional<SemanticAvailableAttr> getDeclUnavailableAttr (const Decl *D) {
580577 auto &ctx = D->getASTContext ();
581578 std::optional<SemanticAvailableAttr> result;
582- auto bestActive =
583- D->getActiveAvailableAttrForCurrentPlatform (ignoreAppExtensions);
579+ auto bestActive = D->getActiveAvailableAttrForCurrentPlatform ();
584580
585581 for (auto attr : D->getSemanticAvailableAttrs (/* includingInactive=*/ false )) {
586582 // If this is a platform-specific attribute and it isn't the most
587583 // specific attribute for the current platform, we're done.
588584 if (attr.isPlatformSpecific () && (!bestActive || attr != bestActive))
589585 continue ;
590586
591- if (ignoreAppExtensions &&
592- isApplicationExtensionPlatform (attr.getPlatform ()))
593- continue ;
594-
595587 // Unconditional unavailable.
596588 if (attr.isUnconditionallyUnavailable ())
597589 return attr;
@@ -610,9 +602,8 @@ getDeclUnavailableAttr(const Decl *D, bool ignoreAppExtensions) {
610602 return result;
611603}
612604
613- std::optional<SemanticAvailableAttr>
614- Decl::getUnavailableAttr (bool ignoreAppExtensions) const {
615- if (auto attr = getDeclUnavailableAttr (this , ignoreAppExtensions))
605+ std::optional<SemanticAvailableAttr> Decl::getUnavailableAttr () const {
606+ if (auto attr = getDeclUnavailableAttr (this ))
616607 return attr;
617608
618609 // If D is an extension member, check if the extension is unavailable.
@@ -624,36 +615,89 @@ Decl::getUnavailableAttr(bool ignoreAppExtensions) const {
624615 // and the availability of other categories. rdar://problem/53956555
625616 if (!getClangNode ())
626617 if (auto ext = dyn_cast<ExtensionDecl>(getDeclContext ()))
627- return ext->getUnavailableAttr (ignoreAppExtensions );
618+ return ext->getUnavailableAttr ();
628619
629620 return std::nullopt ;
630621}
631622
632- static bool isDeclCompletelyUnavailable (const Decl *decl) {
633- // Don't trust unavailability on declarations from clang modules.
623+ static llvm::SmallVector<AvailabilityDomain, 2 >
624+ availabilityDomainsForABICompatibility (const ASTContext &ctx) {
625+ llvm::SmallVector<AvailabilityDomain, 2 > domains;
626+
627+ // Regardless of target platform, binaries built for Embedded do not require
628+ // compatibility.
629+ if (ctx.LangOpts .hasFeature (Feature::Embedded))
630+ return domains;
631+
632+ if (auto targetDomain = AvailabilityDomain::forTargetPlatform (ctx))
633+ domains.push_back (targetDomain->getABICompatibilityDomain ());
634+
635+ if (auto variantDomain = AvailabilityDomain::forTargetVariantPlatform (ctx))
636+ domains.push_back (variantDomain->getABICompatibilityDomain ());
637+
638+ return domains;
639+ }
640+
641+ // / Returns true if \p decl is proven to be unavailable for all platforms that
642+ // / external modules interacting with this module could target. A declaration
643+ // / that is not proven to be unavailable in this way could be reachable at
644+ // / runtime, even if it is unavailable to all code in this module.
645+ static bool isUnavailableForAllABICompatiblePlatforms (const Decl *decl) {
646+ // Don't trust unavailability on declarations from Clang modules.
634647 if (isa<ClangModuleUnit>(decl->getDeclContext ()->getModuleScopeContext ()))
635648 return false ;
636649
637- auto unavailableAttr = decl->getUnavailableAttr (/* ignoreAppExtensions=*/ true );
638- if (!unavailableAttr)
639- return false ;
650+ auto &ctx = decl->getASTContext ();
651+ llvm::SmallVector<AvailabilityDomain, 2 > compatibilityDomains =
652+ availabilityDomainsForABICompatibility (ctx);
653+
654+ llvm::SmallSet<AvailabilityDomain, 8 > unavailableDescendantDomains;
655+ llvm::SmallSet<AvailabilityDomain, 8 > availableDescendantDomains;
656+
657+ // Build up the collection of relevant available and unavailable platform
658+ // domains by looking at all the @available attributes. Along the way, we
659+ // may find an attribute that makes the declaration universally unavailable
660+ // in which case platform availability is irrelevant.
661+ for (auto attr : decl->getSemanticAvailableAttrs (/* includeInactive=*/ true )) {
662+ auto domain = attr.getDomain ();
663+ bool isCompabilityDomainDescendant =
664+ llvm::find_if (compatibilityDomains,
665+ [&domain](AvailabilityDomain compatibilityDomain) {
666+ return compatibilityDomain.contains (domain);
667+ }) != compatibilityDomains.end ();
668+
669+ if (isCompabilityDomainDescendant) {
670+ // Record the whether the descendant domain is marked available
671+ // or unavailable. Unavailability overrides availability.
672+ if (attr.isUnconditionallyUnavailable ()) {
673+ availableDescendantDomains.erase (domain);
674+ unavailableDescendantDomains.insert (domain);
675+ } else if (!unavailableDescendantDomains.contains (domain)) {
676+ availableDescendantDomains.insert (domain);
677+ }
678+ } else if (attr.isActive (ctx)) {
679+ // The declaration is always unavailable if an active attribute from a
680+ // domain outside the compatibility hierarchy indicates unavailability.
681+ if (attr.isUnconditionallyUnavailable ())
682+ return true ;
683+ }
684+ }
640685
641- // getUnavailableAttr() can return an @available attribute that is
642- // obsoleted for certain deployment targets or language modes. These decls
643- // can still be reached by code in other modules that is compiled with
644- // a different deployment target or language mode.
645- if (!unavailableAttr->isUnconditionallyUnavailable ())
686+ // If there aren't any compatibility domains to check and we didn't find any
687+ // other active attributes that make the declaration unavailable, then it must
688+ // be available.
689+ if (compatibilityDomains.empty ())
646690 return false ;
647691
648- // Universally unavailable declarations are always completely unavailable.
649- if (unavailableAttr-> getPlatform () == PlatformKind::none)
650- return true ;
651-
652- // FIXME: Support zippered frameworks (rdar://125371621)
653- // If we have a target variant (e.g. we're building a zippered macOS
654- // framework) then the decl is only unreachable if it is unavailable for both
655- // the primary target and the target variant .
656- if (decl-> getASTContext (). LangOpts . TargetVariant . has_value () )
692+ // Verify that the declaration has been marked unavailable in every
693+ // compatibility domain.
694+ for ( auto compatibilityDomain : compatibilityDomains) {
695+ if (!unavailableDescendantDomains. contains (compatibilityDomain))
696+ return false ;
697+ }
698+
699+ // Verify that there aren't any explicitly available descendant domains .
700+ if (availableDescendantDomains. size () > 0 )
657701 return false ;
658702
659703 return true ;
@@ -670,7 +714,7 @@ SemanticDeclAvailabilityRequest::evaluate(Evaluator &evaluator,
670714 }
671715
672716 if (inherited == SemanticDeclAvailability::CompletelyUnavailable ||
673- isDeclCompletelyUnavailable (decl))
717+ isUnavailableForAllABICompatiblePlatforms (decl))
674718 return SemanticDeclAvailability::CompletelyUnavailable;
675719
676720 if (inherited == SemanticDeclAvailability::ConditionallyUnavailable ||
@@ -699,14 +743,6 @@ getEffectiveUnavailableDeclOptimization(ASTContext &ctx) {
699743 if (ctx.LangOpts .UnavailableDeclOptimizationMode .has_value ())
700744 return *ctx.LangOpts .UnavailableDeclOptimizationMode ;
701745
702- // FIXME: Allow unavailable decl optimization on visionOS.
703- // visionOS must be ABI compatible with iOS. Enabling unavailable declaration
704- // optimizations naively would break compatibility since declarations marked
705- // unavailable on visionOS would be optimized regardless of whether they are
706- // available on iOS. rdar://116742214
707- if (ctx.LangOpts .Target .isXROS ())
708- return UnavailableDeclOptimization::None;
709-
710746 return UnavailableDeclOptimization::None;
711747}
712748
0 commit comments