From 270ea8b921d280b48a54621e2823320a7a104e07 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 3 Oct 2025 14:57:32 -0700 Subject: [PATCH] SILOptimizer: Fix mandatory switch dispatch elimination for unreachable cases. https://github.com/swiftlang/swift/pull/67920 introduced a mandatory SIL optimization that eliminates switch statement dispatch for enum elements that are provably unavailable at runtime. This optimization helps eliminate unreachable code, but it's also load bearing because it removes references to enum element symbols that may not preset at link time. The optimization was checking the wrong condition to determine whether an enum element is unavailable at runtime and so the optimization wasn't working for code using custom availability domains. Resolves rdar://161846311. --- .../Mandatory/DiagnoseUnreachable.cpp | 4 +- ...enum_element_mandatory_custom_domain.swift | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 test/SILOptimizer/unavailable_enum_element_mandatory_custom_domain.swift diff --git a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp index ea9051d3156ba..f2ddc950a02f9 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp @@ -926,9 +926,7 @@ static bool eliminateSwitchDispatchOnUnavailableElements( SmallVector, 4> NewCaseBBs; for (unsigned i : range(SWI.getNumCases())) { auto CaseBB = SWI.getCase(i); - auto availableAtr = CaseBB.first->getUnavailableAttr(); - - if (availableAtr && availableAtr->isUnconditionallyUnavailable()) { + if (!CaseBB.first->isAvailableDuringLowering()) { // Mark the basic block as potentially unreachable. SILBasicBlock *UnreachableBlock = CaseBB.second; if (!State->PossiblyUnreachableBlocks.contains(UnreachableBlock)) { diff --git a/test/SILOptimizer/unavailable_enum_element_mandatory_custom_domain.swift b/test/SILOptimizer/unavailable_enum_element_mandatory_custom_domain.swift new file mode 100644 index 0000000000000..30c4bd5b5d26d --- /dev/null +++ b/test/SILOptimizer/unavailable_enum_element_mandatory_custom_domain.swift @@ -0,0 +1,63 @@ +// RUN: %target-swift-emit-sil -module-name Test %s -verify -parse-as-library \ +// RUN: -enable-experimental-feature CustomAvailability \ +// RUN: -define-enabled-availability-domain EnabledDomain \ +// RUN: -define-always-enabled-availability-domain AlwaysEnabledDomain \ +// RUN: -define-disabled-availability-domain DisabledDomain \ +// RUN: -define-dynamic-availability-domain DynamicDomain \ +// RUN: -Onone \ +// RUN: | %FileCheck %s --check-prefixes=CHECK,CHECK-NOOPT + +// REQUIRES: swift_feature_CustomAvailability + +public enum Enum { + case alwaysAvailable + + @available(*, unavailable) + case alwaysUnavailable + + @available(EnabledDomain) + case enabled + + @available(EnabledDomain, unavailable) + case enabledUnavailable + + @available(AlwaysEnabledDomain) + case alwaysEnabled + + @available(AlwaysEnabledDomain, unavailable) + case alwaysEnabledUnavailable + + @available(DisabledDomain) + case disabled + + @available(DisabledDomain, unavailable) + case disabledUnavailable + + @available(DynamicDomain) + case dynamic + + @available(DynamicDomain, unavailable) + case dynamicUnavailable +} + +// CHECK-LABEL: sil @$s4Test22testFullyCoveredSwitchyyAA4EnumOF : $@convention(thin) (Enum) -> () { +// CHECK: switch_enum %0, case #Enum.alwaysAvailable!enumelt: {{bb[0-9]+}}, case #Enum.alwaysUnavailable!enumelt: {{bb[0-9]+}}, case #Enum.enabled!enumelt: {{bb[0-9]+}}, case #Enum.alwaysEnabled!enumelt: {{bb[0-9]+}}, case #Enum.disabledUnavailable!enumelt: {{bb[0-9]+}}, case #Enum.dynamic!enumelt: {{bb[0-9]+}}, case #Enum.dynamicUnavailable!enumelt: {{bb[0-9]+}}, default [[DEFAULTBB:bb[0-9]+]] +// CHECK: [[DEFAULTBB]]: +// CHECK-NEXT: integer_literal $Builtin.Int1, -1 +// CHECK-NEXT: cond_fail {{%.*}}, "unexpected enum value" +// CHECK-NEXT: unreachable +// CHECK: } // end sil function '$s4Test22testFullyCoveredSwitchyyAA4EnumOF' +public func testFullyCoveredSwitch(_ e: Enum) { + switch e { + case .alwaysAvailable: () + case .alwaysUnavailable: () + case .enabled: () + case .enabledUnavailable: () + case .alwaysEnabled: () + case .alwaysEnabledUnavailable: () + case .disabled: () + case .disabledUnavailable: () + case .dynamic: () + case .dynamicUnavailable: () + } +}