Files
swift-mirror/test/SILOptimizer/simplify_try_apply_resilient_error.swift
Ben Cohen f8d211057e [SILOptimizer] Fold try_apply with an uninhabited error type (#92174)
A `try_apply` whose callee's error result is uninhabited can never take
its error branch. Turn it into `apply [nothrow]` and let the dead-block
cleanup remove the error block.

This mostly shows up in Embedded Swift, which specializes generics at
-Onone: a `rethrows`-shaped `throws(E)` function specialized for `E ==
Never` keeps the rethrow, and IRGen has to emit that as an unconditional
trap ("Never can't be initialized") reached by an equally dead test of
the error register. Calling `withTemporaryAllocation` or `Optional.map`
was enough to get one per call site.

Enums which are resilient from the caller's point of view are excluded,
since a case-less non-frozen enum can gain cases in a future version of
its module.

`TryApplyInst` is both `OnoneSimplifiable` (so
`MandatoryPerformanceOptimizations` gets it, i.e. Embedded `-Onone`) and
`SILCombineSimplifiable` (so `-O` gets it without depending on the
inliner).

The predicate is `FunctionConvention.hasUninhabitedErrorResult(in:)`,
written in Swift over low-level bridges
(`TypeProperties.isStructurallyUninhabited`,
`EnumDecl.hasCasesUnavailableDuringLowering`,
`EnumDecl.isEffectivelyExhaustive`). `ResultInfo.getReturnValueType` now
takes the function type the result belongs to; it used to hardcode the
enclosing function's lowered type, which is wrong for a result of an
apply site's substituted callee type (it had no prior callers).

Two existing tests change behaviour because their error enums are
genuinely case-less: closure_lifetime_fixup's `Optional.map<_, Never,
_>` call is now an `apply`, and devirt_class_witness_method's local
`enum Error` gets a case so that 'caller2' keeps testing a throwing
witness call.
2026-09-22 20:03:31 -07:00

39 lines
1.5 KiB
Swift

// RUN: %empty-directory(%t)
// RUN: split-file %s %t
/// Lib built with library evolution: `NoCases` can gain cases in a future version.
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -swift-version 6 \
// RUN: -module-name Lib %t/Lib.swift -emit-module-path %t/Lib.swiftmodule
// RUN: %target-swift-frontend -emit-sil -O -swift-version 6 -I %t -module-name Client \
// RUN: %t/Client.swift | %FileCheck %s -check-prefix=RESILIENT
/// The same Lib without library evolution: `NoCases` is exhaustive, so it stays uninhabited.
// RUN: %target-swift-frontend -emit-module -swift-version 6 \
// RUN: -module-name Lib %t/Lib.swift -emit-module-path %t/Lib.swiftmodule
// RUN: %target-swift-frontend -emit-sil -O -swift-version 6 -I %t -module-name Client \
// RUN: %t/Client.swift | %FileCheck %s -check-prefix=EXHAUSTIVE
//--- Lib.swift
public enum NoCases: Error {}
public func mightThrow<R>(_ body: () throws(NoCases) -> R) throws(NoCases) -> R {
try body()
}
//--- Client.swift
import Lib
// RESILIENT-LABEL: sil @$s6Client4callSiy3Lib7NoCasesOYKF :
// RESILIENT: try_apply
// RESILIENT: } // end sil function '$s6Client4callSiy3Lib7NoCasesOYKF'
// EXHAUSTIVE-LABEL: sil @$s6Client4callSiy3Lib7NoCasesOYKF :
// EXHAUSTIVE: apply [nothrow]
// EXHAUSTIVE-NOT: try_apply
// EXHAUSTIVE: } // end sil function '$s6Client4callSiy3Lib7NoCasesOYKF'
public func call() throws(NoCases) -> Int {
try mightThrow { 42 }
}