@@ -541,8 +541,10 @@ std::optional<SemanticAvailableAttr> Decl::getUnavailableAttr() const {
541541 return std::nullopt ;
542542}
543543
544+ // / Returns the mutually exclusive root platform domains that must all be
545+ // / unavailable in order for a declaration to be unavailable at runtime.
544546static llvm::SmallSetVector<AvailabilityDomain, 2 >
545- availabilityDomainsForABICompatibility (const ASTContext &ctx) {
547+ getRootTargetDomains (const ASTContext &ctx) {
546548 llvm::SmallSetVector<AvailabilityDomain, 2 > domains;
547549
548550 // Regardless of target platform, binaries built for Embedded do not require
@@ -551,83 +553,132 @@ availabilityDomainsForABICompatibility(const ASTContext &ctx) {
551553 return domains;
552554
553555 if (auto targetDomain = AvailabilityDomain::forTargetPlatform (ctx))
554- domains.insert (targetDomain->getABICompatibilityDomain ());
556+ domains.insert (targetDomain->getRootDomain ());
555557
556558 if (auto variantDomain = AvailabilityDomain::forTargetVariantPlatform (ctx))
557- domains.insert (variantDomain->getABICompatibilityDomain ());
559+ domains.insert (variantDomain->getRootDomain ());
558560
559561 return domains;
560562}
561563
562564static bool constraintIndicatesRuntimeUnavailability (
563565 const AvailabilityConstraint &constraint, const ASTContext &ctx) {
566+ std::optional<CustomAvailabilityDomain::Kind> customDomainKind;
567+ if (auto customDomain = constraint.getDomain ().getCustomDomain ())
568+ customDomainKind = customDomain->getKind ();
569+
564570 switch (constraint.getReason ()) {
565- case AvailabilityConstraint::Reason::UnconditionallyUnavailable: {
571+ case AvailabilityConstraint::Reason::UnconditionallyUnavailable:
572+ if (customDomainKind)
573+ return customDomainKind == CustomAvailabilityDomain::Kind::Enabled;
566574 return true ;
567- }
568- case AvailabilityConstraint::Reason::UnavailableForDeployment:
569575 case AvailabilityConstraint::Reason::Obsoleted:
576+ case AvailabilityConstraint::Reason::UnavailableForDeployment:
577+ return false ;
570578 case AvailabilityConstraint::Reason::PotentiallyUnavailable:
579+ if (customDomainKind)
580+ return customDomainKind == CustomAvailabilityDomain::Kind::Disabled;
571581 return false ;
572582 }
573583}
574584
575- // / Computes the `DeclRuntimeAvailability` value for `decl`.
576- static DeclRuntimeAvailability getDeclRuntimeAvailability (const Decl *decl) {
585+ // / Returns true if a decl that is unavailable in the given domain must still be
586+ // / emitted to preserve load time ABI compatibility.
587+ static bool
588+ domainRequiresABICompatibleUnavailableDecls (AvailabilityDomain domain,
589+ const ASTContext &ctx) {
590+ // FIXME: [availability] Restrict ABI compatible unavailable decls to modules
591+ // compiled with macOS, iOS, watchOS, tvOS, or visionOS target triples. For
592+ // other targets, unavailable code should always be stripped from binaries.
593+ return domain.isUniversal () || domain.isPlatform ();
594+ }
595+
596+ // / Computes the `DeclRuntimeAvailability` value for `decl` in isolation.
597+ static DeclRuntimeAvailability
598+ computeDeclRuntimeAvailability (const Decl *decl) {
577599 // Don't trust unavailability on declarations from Clang modules.
578600 if (isa<ClangModuleUnit>(decl->getDeclContext ()->getModuleScopeContext ()))
579601 return DeclRuntimeAvailability::PotentiallyAvailable;
580602
581- // Check whether the decl is unavailable at all.
582- if (!decl->isUnavailable ())
583- return DeclRuntimeAvailability::PotentiallyAvailable;
584-
585603 auto &ctx = decl->getASTContext ();
586- auto compatibilityDomains = availabilityDomainsForABICompatibility (ctx);
587- auto potentiallyAvailableDomains = compatibilityDomains ;
604+ auto rootTargetDomains = getRootTargetDomains (ctx);
605+ auto remainingTargetDomains = rootTargetDomains ;
588606
589607 AvailabilityConstraintFlags flags;
590608
591609 // Semantic availability was already computed separately for any enclosing
592610 // extension.
593611 flags |= AvailabilityConstraintFlag::SkipEnclosingExtension;
594612
595- // FIXME: [availability] Inactive domains have to be included because iOS
596- // availability is considered inactive when compiling a zippered module.
613+ // FIXME: [availability] Replace IncludeAllDomains with a RuntimeAvailability
614+ // flag that includes the target variant constraints and keeps all constraints
615+ // from active platforms.
597616 flags |= AvailabilityConstraintFlag::IncludeAllDomains;
598617
599618 auto constraints = getAvailabilityConstraintsForDecl (
600619 decl, AvailabilityContext::forInliningTarget (ctx), flags);
601620
621+ // First, collect the unavailable domains from the constraints.
622+ llvm::SmallVector<AvailabilityDomain, 8 > unavailableDomains;
602623 for (auto constraint : constraints) {
603- if (!constraintIndicatesRuntimeUnavailability (constraint, ctx))
624+ if (constraintIndicatesRuntimeUnavailability (constraint, ctx))
625+ unavailableDomains.push_back (constraint.getDomain ());
626+ }
627+
628+ // Check whether there are any available attributes that would make the
629+ // decl available in descendants of the unavailable domains.
630+ for (auto attr :
631+ decl->getSemanticAvailableAttrs (/* includingInactive=*/ false )) {
632+ auto domain = attr.getDomain ();
633+ if (llvm::is_contained (unavailableDomains, domain))
604634 continue ;
605635
636+ llvm::erase_if (unavailableDomains, [domain](auto unavailableDomain) {
637+ return unavailableDomain.contains (domain);
638+ });
639+ }
640+
641+ // Check the remaining unavailable domains to see if the requirements for
642+ // runtime unreachability are met.
643+ auto result = DeclRuntimeAvailability::PotentiallyAvailable;
644+ for (auto domain : unavailableDomains) {
606645 // Check whether the constraint is from a relevant domain.
607- auto domain = constraint.getDomain ();
608- bool isCompabilityDomainDescendant =
609- llvm::find_if (compatibilityDomains,
610- [&domain](AvailabilityDomain compatibilityDomain) {
611- return compatibilityDomain.contains (domain);
612- }) != compatibilityDomains.end ();
613-
614- if (!domain.isActive (ctx) && !isCompabilityDomainDescendant)
646+ bool isTargetDomain = rootTargetDomains.contains (domain);
647+ if (!domain.isActive (ctx) && !isTargetDomain)
615648 continue ;
616649
617- if (isCompabilityDomainDescendant) {
618- // If the decl is still potentially available in some compatibility
619- // domain, keep looking at the remaining constraints.
620- potentiallyAvailableDomains.remove (domain);
621- if (!potentiallyAvailableDomains.empty ())
622- continue ;
650+ if (!domain.isRoot ())
651+ continue ;
652+
653+ // We've found an unavailable target domain. If all the target domains are
654+ // unavailable then the decl is unreachable at runtime.
655+ if (isTargetDomain) {
656+ remainingTargetDomains.remove (domain);
657+ if (remainingTargetDomains.empty ())
658+ result = DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
659+
660+ continue ;
623661 }
624662
625- // Either every compatibility domain has been proven unavailable or this
626- // constraint proves runtime unavailability on its own.
627- return DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
663+ // We've found a single unavailable domain that alone proves the decl is
664+ // unreachable at runtime. It may still be required at load time, though.
665+ if (domainRequiresABICompatibleUnavailableDecls (domain, ctx)) {
666+ result = DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
667+ continue ;
668+ }
669+
670+ return DeclRuntimeAvailability::AlwaysUnavailable;
628671 }
629672
630- return DeclRuntimeAvailability::PotentiallyAvailable;
673+ return result;
674+ }
675+
676+ // / Determines the `DeclRuntimeAvailability` value for `decl` via
677+ // / `DeclRuntimeAvailabilityRequest`.
678+ static DeclRuntimeAvailability getDeclRuntimeAvailability (const Decl *decl) {
679+ return evaluateOrDefault (decl->getASTContext ().evaluator ,
680+ DeclRuntimeAvailabilityRequest{decl},
681+ DeclRuntimeAvailability::PotentiallyAvailable);
631682}
632683
633684DeclRuntimeAvailability
@@ -636,24 +687,20 @@ DeclRuntimeAvailabilityRequest::evaluate(Evaluator &evaluator,
636687 auto inherited = DeclRuntimeAvailability::PotentiallyAvailable;
637688 if (auto *parent =
638689 AvailabilityInference::parentDeclForInferredAvailability (decl)) {
639- inherited = evaluateOrDefault (
640- evaluator, DeclRuntimeAvailabilityRequest{parent}, inherited);
690+ inherited = getDeclRuntimeAvailability (parent);
641691 }
642692
643- // If the inherited semantic availability is already maximally unavailable
693+ // If the inherited runtime availability is already maximally unavailable
644694 // then skip computing unavailability for this declaration.
645- if (inherited == DeclRuntimeAvailability::AlwaysUnavailableABICompatible )
646- return DeclRuntimeAvailability::AlwaysUnavailableABICompatible ;
695+ if (inherited == DeclRuntimeAvailability::AlwaysUnavailable )
696+ return DeclRuntimeAvailability::AlwaysUnavailable ;
647697
648- auto availability = getDeclRuntimeAvailability (decl);
698+ auto availability = computeDeclRuntimeAvailability (decl);
649699 return std::max (inherited, availability);
650700}
651701
652702bool Decl::isUnreachableAtRuntime () const {
653- auto availability = evaluateOrDefault (
654- getASTContext ().evaluator , DeclRuntimeAvailabilityRequest{this },
655- DeclRuntimeAvailability::PotentiallyAvailable);
656- return availability ==
703+ return getDeclRuntimeAvailability (this ) >=
657704 DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
658705}
659706
@@ -666,13 +713,15 @@ getEffectiveUnavailableDeclOptimization(ASTContext &ctx) {
666713}
667714
668715bool Decl::isAvailableDuringLowering () const {
669- // Unconditionally unavailable declarations should be skipped during lowering
670- // when -unavailable-decl-optimization=complete is specified.
716+ auto availability = getDeclRuntimeAvailability ( this );
717+
671718 if (getEffectiveUnavailableDeclOptimization (getASTContext ()) !=
672719 UnavailableDeclOptimization::Complete)
673- return true ;
720+ return availability < DeclRuntimeAvailability::AlwaysUnavailable ;
674721
675- return !isUnreachableAtRuntime ();
722+ // All unreachable declarations should be skipped during lowering
723+ // when -unavailable-decl-optimization=complete is specified.
724+ return availability < DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
676725}
677726
678727bool Decl::requiresUnavailableDeclABICompatibilityStubs () const {
0 commit comments