From 707c4aa52c7ed0f274689e702cc8beb28e81c0c2 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Thu, 28 Aug 2025 11:30:17 -0700 Subject: [PATCH 1/2] AST: Factor out a utility for computing runtime unavailability. In anticipation of needing to compute runtime unavailability to determine whether declarations should be printed in swiftinterfaces, factor out the code that computes runtime unavailability into a shared utility based on `DeclAvailabilityConstraints`. NFC. --- include/swift/AST/AvailabilityConstraint.h | 8 ++++++ lib/AST/Availability.cpp | 26 +------------------ lib/AST/AvailabilityConstraint.cpp | 30 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/AvailabilityConstraint.h b/include/swift/AST/AvailabilityConstraint.h index 495f63204eb1c..807078f729e90 100644 --- a/include/swift/AST/AvailabilityConstraint.h +++ b/include/swift/AST/AvailabilityConstraint.h @@ -240,6 +240,14 @@ std::optional getAvailabilityConstraintForDeclInDomain( const Decl *decl, const AvailabilityContext &context, AvailabilityDomain domain, AvailabilityConstraintFlags flags = std::nullopt); + +/// Computes the set of constraints that indicate whether a decl is "runtime +/// unavailable" (can never be reached at runtime) and adds the domain for each +/// of those constraints to the \p domains vector. +void getRuntimeUnavailableDomains( + const DeclAvailabilityConstraints &constraints, + llvm::SmallVectorImpl &domains, const ASTContext &ctx); + } // end namespace swift namespace llvm { diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 8641c30fbb1c5..f5eca52812b49 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -574,27 +574,6 @@ getRootTargetDomains(const ASTContext &ctx) { return domains; } -static bool constraintIndicatesRuntimeUnavailability( - const AvailabilityConstraint &constraint, const ASTContext &ctx) { - std::optional customDomainKind; - if (auto customDomain = constraint.getDomain().getCustomDomain()) - customDomainKind = customDomain->getKind(); - - switch (constraint.getReason()) { - case AvailabilityConstraint::Reason::UnavailableUnconditionally: - if (customDomainKind) - return customDomainKind == CustomAvailabilityDomain::Kind::Enabled; - return true; - case AvailabilityConstraint::Reason::UnavailableObsolete: - case AvailabilityConstraint::Reason::UnavailableUnintroduced: - return false; - case AvailabilityConstraint::Reason::Unintroduced: - if (customDomainKind) - return customDomainKind == CustomAvailabilityDomain::Kind::Disabled; - return false; - } -} - /// Returns true if a decl that is unavailable in the given domain must still be /// emitted to preserve load time ABI compatibility. static bool @@ -633,10 +612,7 @@ computeDeclRuntimeAvailability(const Decl *decl) { // First, collect the unavailable domains from the constraints. llvm::SmallVector unavailableDomains; - for (auto constraint : constraints) { - if (constraintIndicatesRuntimeUnavailability(constraint, ctx)) - unavailableDomains.push_back(constraint.getDomain()); - } + getRuntimeUnavailableDomains(constraints, unavailableDomains, ctx); // Check whether there are any available attributes that would make the // decl available in descendants of the unavailable domains. diff --git a/lib/AST/AvailabilityConstraint.cpp b/lib/AST/AvailabilityConstraint.cpp index e75b7dfbaea39..0eb542a339d2e 100644 --- a/lib/AST/AvailabilityConstraint.cpp +++ b/lib/AST/AvailabilityConstraint.cpp @@ -322,3 +322,33 @@ swift::getAvailabilityConstraintForDeclInDomain( return std::nullopt; } + +static bool constraintIndicatesRuntimeUnavailability( + const AvailabilityConstraint &constraint, const ASTContext &ctx) { + std::optional customDomainKind; + if (auto customDomain = constraint.getDomain().getCustomDomain()) + customDomainKind = customDomain->getKind(); + + switch (constraint.getReason()) { + case AvailabilityConstraint::Reason::UnavailableUnconditionally: + if (customDomainKind) + return customDomainKind == CustomAvailabilityDomain::Kind::Enabled; + return true; + case AvailabilityConstraint::Reason::UnavailableObsolete: + case AvailabilityConstraint::Reason::UnavailableUnintroduced: + return false; + case AvailabilityConstraint::Reason::Unintroduced: + if (customDomainKind) + return customDomainKind == CustomAvailabilityDomain::Kind::Disabled; + return false; + } +} + +void swift::getRuntimeUnavailableDomains( + const DeclAvailabilityConstraints &constraints, + llvm::SmallVectorImpl &domains, const ASTContext &ctx) { + for (auto constraint : constraints) { + if (constraintIndicatesRuntimeUnavailability(constraint, ctx)) + domains.push_back(constraint.getDomain()); + } +} From e390cce66240ade1e3bd1838bb50ebbf245cd259 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Thu, 28 Aug 2025 16:36:24 -0700 Subject: [PATCH 2/2] AST: Fix runtime unavailability for universally unavailable decls. The logic that computes whether a decl is unavailable at runtime was allowing `@available` attributes for specific availability domains override `@available(*, unavailable)`. Universally unavailable decls are always unavailable, regardless of their other `@available` attributes. --- lib/AST/Availability.cpp | 4 +++ .../unavailable_decl_custom_domain.swift | 34 +++++++++++++------ .../unavailable_decl_optimization_stub.swift | 8 +++++ ...ailable_decl_optimization_stub_macos.swift | 8 +++++ 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index f5eca52812b49..413cea4ab3d2e 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -623,6 +623,10 @@ computeDeclRuntimeAvailability(const Decl *decl) { continue; llvm::erase_if(unavailableDomains, [domain](auto unavailableDomain) { + // Unavailability in '*' cannot be superseded by an @available attribute + // for a more specific availability domain. + if (unavailableDomain.isUniversal()) + return false; return unavailableDomain.contains(domain); }); } diff --git a/test/SILGen/unavailable_decl_custom_domain.swift b/test/SILGen/unavailable_decl_custom_domain.swift index d4ed9b3f7c9ce..08462dd11d4fd 100644 --- a/test/SILGen/unavailable_decl_custom_domain.swift +++ b/test/SILGen/unavailable_decl_custom_domain.swift @@ -3,15 +3,7 @@ // RUN: -define-enabled-availability-domain EnabledDomain \ // RUN: -define-disabled-availability-domain DisabledDomain \ // RUN: -define-dynamic-availability-domain DynamicDomain \ -// RUN: | %FileCheck %s - -// RUN: %target-swift-emit-silgen -module-name Test %s -verify \ -// RUN: -enable-experimental-feature CustomAvailability \ -// RUN: -define-enabled-availability-domain EnabledDomain \ -// RUN: -define-disabled-availability-domain DisabledDomain \ -// RUN: -define-dynamic-availability-domain DynamicDomain \ -// RUN: -unavailable-decl-optimization=stub \ -// RUN: | %FileCheck %s +// RUN: | %FileCheck %s --check-prefixes=CHECK,CHECK-NOOPT // RUN: %target-swift-emit-silgen -module-name Test %s -verify \ // RUN: -enable-experimental-feature CustomAvailability \ @@ -19,7 +11,7 @@ // RUN: -define-disabled-availability-domain DisabledDomain \ // RUN: -define-dynamic-availability-domain DynamicDomain \ // RUN: -unavailable-decl-optimization=complete \ -// RUN: | %FileCheck %s +// RUN: | %FileCheck %s --check-prefixes=CHECK,CHECK-OPT // REQUIRES: swift_feature_CustomAvailability @@ -84,3 +76,25 @@ public func availableInEnabledAndDisabledDomain() { } @available(DisabledDomain) @available(EnabledDomain) public func availableInDisabledAndEnabledDomain() { } + +// CHECK-NOOPT: s4Test49availableInEnabledDomainAndUnavailableUniversallyyyF +// CHECK-OPT-NOT: s4Test49availableInEnabledDomainAndUnavailableUniversallyyyF +@available(*, unavailable) +@available(EnabledDomain) +public func availableInEnabledDomainAndUnavailableUniversally() { } + +// CHECK-NOT: s4Test40unavailableInEnabledDomainAndUniversallyyyF +@available(*, unavailable) +@available(EnabledDomain, unavailable) +public func unavailableInEnabledDomainAndUniversally() { } + +// CHECK-NOT: s4Test50availableInDisabledDomainAndUnavailableUniversallyyyF +@available(*, unavailable) +@available(DisabledDomain) +public func availableInDisabledDomainAndUnavailableUniversally() { } + +// CHECK-NOOPT: s4Test41unavailableInDisabledDomainAndUniversallyyyF +// CHECK-OPT-NOT: s4Test41unavailableInDisabledDomainAndUniversallyyyF +@available(*, unavailable) +@available(DisabledDomain, unavailable) +public func unavailableInDisabledDomainAndUniversally() { } diff --git a/test/SILGen/unavailable_decl_optimization_stub.swift b/test/SILGen/unavailable_decl_optimization_stub.swift index e0467bb57864d..ee5f53f9dfb28 100644 --- a/test/SILGen/unavailable_decl_optimization_stub.swift +++ b/test/SILGen/unavailable_decl_optimization_stub.swift @@ -12,6 +12,14 @@ public func unavailableFunc() -> S { return S() } +// CHECK-LABEL: sil{{.*}}@$s4Test33unavailableFuncIntroducedInSwift5yyF +// CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss31_diagnoseUnavailableCodeReacheds5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyFTwb)]] : $@convention(thin) () -> Never +// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]() +// CHECK: } // end sil function '$s4Test33unavailableFuncIntroducedInSwift5yyF' +@available(*, unavailable) +@available(swift 5) +public func unavailableFuncIntroducedInSwift5() { } + // CHECK-LABEL: sil{{.*}}@$s4Test025unavailableFuncWithNestedC0yyF // CHECK: [[FNREF:%.*]] = function_ref @$[[DIAGNOSEFN:(ss31_diagnoseUnavailableCodeReacheds5NeverOyF|ss31_diagnoseUnavailableCodeReacheds5NeverOyFTwb)]] : $@convention(thin) () -> Never // CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]() diff --git a/test/SILGen/unavailable_decl_optimization_stub_macos.swift b/test/SILGen/unavailable_decl_optimization_stub_macos.swift index 2ce1bc611bce7..8ca4c1241f6d6 100644 --- a/test/SILGen/unavailable_decl_optimization_stub_macos.swift +++ b/test/SILGen/unavailable_decl_optimization_stub_macos.swift @@ -24,6 +24,14 @@ public func unavailableFunc() {} @available(macOS, unavailable) public func unavailableOnMacOSFunc() {} +// CHECK-LABEL: sil{{.*}}@$s4Test36unavailableFuncWithMacOSIntroductionyyF +// CHECK: [[FNREF:%.*]] = function_ref @$ss31_diagnoseUnavailableCodeReacheds5NeverOyFTwb : $@convention(thin) () -> Never +// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]() +// CHECK: } // end sil function '$s4Test36unavailableFuncWithMacOSIntroductionyyF' +@available(*, unavailable) +@available(macOS 12.0, *) +public func unavailableFuncWithMacOSIntroduction() {} + // CHECK-LABEL: sil{{.*}}@$s4Test31unavailableOnMacOSExtensionFuncyyF // CHECK-NOT: _diagnoseUnavailableCodeReached // CHECK: } // end sil function '$s4Test31unavailableOnMacOSExtensionFuncyyF'