@@ -563,28 +563,43 @@ getRootTargetDomains(const ASTContext &ctx) {
563563
564564static bool constraintIndicatesRuntimeUnavailability (
565565 const AvailabilityConstraint &constraint, const ASTContext &ctx) {
566+ std::optional<CustomAvailabilityDomain::Kind> customDomainKind;
567+ if (auto customDomain = constraint.getDomain ().getCustomDomain ())
568+ customDomainKind = customDomain->getKind ();
569+
566570 switch (constraint.getReason ()) {
567- case AvailabilityConstraint::Reason::UnconditionallyUnavailable: {
571+ case AvailabilityConstraint::Reason::UnconditionallyUnavailable:
572+ if (customDomainKind)
573+ return customDomainKind == CustomAvailabilityDomain::Kind::Enabled;
568574 return true ;
569- }
570- case AvailabilityConstraint::Reason::UnavailableForDeployment:
571575 case AvailabilityConstraint::Reason::Obsoleted:
576+ case AvailabilityConstraint::Reason::UnavailableForDeployment:
577+ return false ;
572578 case AvailabilityConstraint::Reason::PotentiallyUnavailable:
579+ if (customDomainKind)
580+ return customDomainKind == CustomAvailabilityDomain::Kind::Disabled;
573581 return false ;
574582 }
575583}
576584
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+
577596// / Computes the `DeclRuntimeAvailability` value for `decl` in isolation.
578597static DeclRuntimeAvailability
579598computeDeclRuntimeAvailability (const Decl *decl) {
580599 // Don't trust unavailability on declarations from Clang modules.
581600 if (isa<ClangModuleUnit>(decl->getDeclContext ()->getModuleScopeContext ()))
582601 return DeclRuntimeAvailability::PotentiallyAvailable;
583602
584- // Check whether the decl is unavailable at all.
585- if (!decl->isUnavailable ())
586- return DeclRuntimeAvailability::PotentiallyAvailable;
587-
588603 auto &ctx = decl->getASTContext ();
589604 auto rootTargetDomains = getRootTargetDomains (ctx);
590605 auto remainingTargetDomains = rootTargetDomains;
@@ -595,41 +610,67 @@ computeDeclRuntimeAvailability(const Decl *decl) {
595610 // extension.
596611 flags |= AvailabilityConstraintFlag::SkipEnclosingExtension;
597612
598- // FIXME: [availability] Inactive domains have to be included because iOS
599- // 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.
600616 flags |= AvailabilityConstraintFlag::IncludeAllDomains;
601617
602618 auto constraints = getAvailabilityConstraintsForDecl (
603619 decl, AvailabilityContext::forInliningTarget (ctx), flags);
604620
621+ // First, collect the unavailable domains from the constraints.
622+ llvm::SmallVector<AvailabilityDomain, 8 > unavailableDomains;
605623 for (auto constraint : constraints) {
606- 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))
607634 continue ;
608635
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) {
609645 // Check whether the constraint is from a relevant domain.
610- auto domain = constraint.getDomain ();
611646 bool isTargetDomain = rootTargetDomains.contains (domain);
612-
613647 if (!domain.isActive (ctx) && !isTargetDomain)
614648 continue ;
615649
616650 if (!domain.isRoot ())
617651 continue ;
618652
653+ // We've found an unavailable target domain. If all the target domains are
654+ // unavailable then the decl is unreachable at runtime.
619655 if (isTargetDomain) {
620- // If the decl is still potentially available in some compatibility
621- // domain, keep looking at the remaining constraints.
622656 remainingTargetDomains.remove (domain);
623- if (!remainingTargetDomains.empty ())
624- continue ;
657+ if (remainingTargetDomains.empty ())
658+ result = DeclRuntimeAvailability::AlwaysUnavailableABICompatible;
659+
660+ continue ;
625661 }
626662
627- // Either every compatibility domain has been proven unavailable or this
628- // constraint proves runtime unavailability on its own.
629- 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;
630671 }
631672
632- return DeclRuntimeAvailability::PotentiallyAvailable ;
673+ return result ;
633674}
634675
635676// / Determines the `DeclRuntimeAvailability` value for `decl` via
@@ -651,8 +692,8 @@ DeclRuntimeAvailabilityRequest::evaluate(Evaluator &evaluator,
651692
652693 // If the inherited runtime availability is already maximally unavailable
653694 // then skip computing unavailability for this declaration.
654- if (inherited == DeclRuntimeAvailability::AlwaysUnavailableABICompatible )
655- return DeclRuntimeAvailability::AlwaysUnavailableABICompatible ;
695+ if (inherited == DeclRuntimeAvailability::AlwaysUnavailable )
696+ return DeclRuntimeAvailability::AlwaysUnavailable ;
656697
657698 auto availability = computeDeclRuntimeAvailability (decl);
658699 return std::max (inherited, availability);
@@ -676,7 +717,7 @@ bool Decl::isAvailableDuringLowering() const {
676717
677718 if (getEffectiveUnavailableDeclOptimization (getASTContext ()) !=
678719 UnavailableDeclOptimization::Complete)
679- return true ;
720+ return availability < DeclRuntimeAvailability::AlwaysUnavailable ;
680721
681722 // All unreachable declarations should be skipped during lowering
682723 // when -unavailable-decl-optimization=complete is specified.
0 commit comments