16
16
17
17
#include " swift/AST/ASTContext.h"
18
18
#include " swift/AST/Attr.h"
19
+ #include " swift/AST/AvailabilityConstraint.h"
20
+ #include " swift/AST/AvailabilityContext.h"
19
21
#include " swift/AST/AvailabilityDomain.h"
20
22
#include " swift/AST/AvailabilityInference.h"
21
23
#include " swift/AST/AvailabilityRange.h"
@@ -629,27 +631,78 @@ Decl::getUnavailableAttr(bool ignoreAppExtensions) const {
629
631
return std::nullopt;
630
632
}
631
633
632
- static bool isDeclCompletelyUnavailable (const Decl *decl) {
634
+ // / Returns true if \p decl is proven to be unavailable for all platforms that
635
+ // / external modules interacting with this module could target. A declaration
636
+ // / that is not proven to be unavailable in this way could be reachable at
637
+ // / runtime, even if it is unavailable to all code in this module.
638
+ static bool isUnavailableForAllABICompatiblePlatforms (const Decl *decl) {
633
639
// Don't trust unavailability on declarations from clang modules.
634
640
if (isa<ClangModuleUnit>(decl->getDeclContext ()->getModuleScopeContext ()))
635
641
return false ;
636
642
637
- auto unavailableAttr = decl->getUnavailableAttr (/* ignoreAppExtensions=*/ true );
638
- if (!unavailableAttr)
643
+ auto &ctx = decl->getASTContext ();
644
+ llvm::SmallVector<AvailabilityDomain, 2 > compatibilityDomains;
645
+ if (auto targetDomain = AvailabilityDomain::forTargetPlatform (ctx))
646
+ compatibilityDomains.push_back (targetDomain->getABICompatibilityDomain ());
647
+
648
+ llvm::SmallSet<AvailabilityDomain, 8 > unavailablePlatformDomains;
649
+ llvm::SmallSet<AvailabilityDomain, 8 > availablePlatformDomains;
650
+
651
+ // / Returns whether the domain is either active for the compilation or is
652
+ // / contained by one of the ABI compatibility domains.
653
+ auto isRelevantDomain = [&](AvailabilityDomain domain) {
654
+ if (domain.isActive (ctx))
655
+ return true ;
656
+
657
+ for (auto compatibilityDomain : compatibilityDomains) {
658
+ if (compatibilityDomain.contains (domain))
659
+ return true ;
660
+ }
661
+
639
662
return false ;
663
+ };
664
+
665
+ // Build up the collection of relevant available and unavailable platform
666
+ // domains by looking at all the @available attributes. Along the way, we
667
+ // may find an attribute that makes the declaration universally unavailable
668
+ // in which case platform availability is irrelevant.
669
+ for (auto attr : decl->getSemanticAvailableAttrs (/* includeInactive=*/ true )) {
670
+ auto domain = attr.getDomain ();
671
+ if (!isRelevantDomain (domain))
672
+ continue ;
673
+
674
+ if (!domain.isPlatform ()) {
675
+ // This attribute is not platform-specific so if it makes the declaration
676
+ // unconditionally unavailable then we're done.
677
+ if (attr.isUnconditionallyUnavailable ())
678
+ return true ;
679
+ continue ;
680
+ }
640
681
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 ())
682
+ // Record the availability indicated by this platform-specific attribute.
683
+ // If it indicates unconditional unavailability, that overrides any previous
684
+ // attributes for the same domain.
685
+ if (attr.isUnconditionallyUnavailable ()) {
686
+ availablePlatformDomains.erase (domain);
687
+ unavailablePlatformDomains.insert (domain);
688
+ } else if (!unavailablePlatformDomains.contains (domain)) {
689
+ availablePlatformDomains.insert (domain);
690
+ }
691
+ }
692
+
693
+ // Check if the declaration could be available to clients compiling for a
694
+ // a derived platform.
695
+ if (availablePlatformDomains.size () > 0 )
646
696
return false ;
647
697
648
- // Universally unavailable declarations are always completely unavailable.
649
- if (unavailableAttr->getPlatform () == PlatformKind::none)
650
- return true ;
698
+ // Verify whether the declaration has been marked explicitly unavailable on
699
+ // each of the ABI compatibility domains.
700
+ for (auto compatibilityDomain : compatibilityDomains) {
701
+ if (!unavailablePlatformDomains.contains (compatibilityDomain))
702
+ return false ;
703
+ }
651
704
652
- // FIXME: Support zippered frameworks (rdar://125371621)
705
+ // FIXME: [availability] Support zippered frameworks (rdar://125371621)
653
706
// If we have a target variant (e.g. we're building a zippered macOS
654
707
// framework) then the decl is only unreachable if it is unavailable for both
655
708
// the primary target and the target variant.
@@ -670,7 +723,7 @@ SemanticDeclAvailabilityRequest::evaluate(Evaluator &evaluator,
670
723
}
671
724
672
725
if (inherited == SemanticDeclAvailability::CompletelyUnavailable ||
673
- isDeclCompletelyUnavailable (decl))
726
+ isUnavailableForAllABICompatiblePlatforms (decl))
674
727
return SemanticDeclAvailability::CompletelyUnavailable;
675
728
676
729
if (inherited == SemanticDeclAvailability::ConditionallyUnavailable ||
@@ -699,14 +752,6 @@ getEffectiveUnavailableDeclOptimization(ASTContext &ctx) {
699
752
if (ctx.LangOpts .UnavailableDeclOptimizationMode .has_value ())
700
753
return *ctx.LangOpts .UnavailableDeclOptimizationMode ;
701
754
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
-
710
755
return UnavailableDeclOptimization::None;
711
756
}
712
757
0 commit comments