Repository navigation
OSSACompleteLifetime crash at -O: SILCombine leaks a copy of a borrowed class reference stored in two noncopyable fields #93409
Description
Description
Compiling the program below with -O crashes the optimizer with Invalid SIL provided to OSSACompleteLifetime?!. It compiles cleanly at -Onone.
make passes its borrowed owner to Context.init(owner:), which stores the reference in two noncopyable fields. After inlining, SILCombine removes the move_value [lexical] on the copy passed to the initializer, and leaves that copy without a consuming use (details under Additional information).
The program also compiles at -O when:
maketakesownerasconsuming, or- either
FilesorPermitis Copyable.
Reproduction
public final class Instance {}
struct Files: ~Copyable {
let owner: Instance
}
struct Permit: ~Copyable {
let instance: Instance
let tag: Int
}
public struct Context: ~Copyable {
let files: Files
let permit: Permit
init(owner: Instance) {
files = Files(owner: owner)
permit = Permit(instance: owner, tag: 1)
}
}
public func make(_ owner: Instance, _ body: (borrowing Context) -> Int) -> Int {
body(Context(owner: owner))
}swiftc -O -parse-as-library -c repro.swift
Stack dump
Invalid SIL provided to OSSACompleteLifetime?! {{
OSSACompleteLifetime is visiting the availability boundary of %4 = copy_value %0 : $Instance // user: %11
When walking forward from the def to the availability boundary a non-dead-end successor-less block was encountered: bb0
Its terminator must be an unreachable but is actually instead return %17 : $Int // id: %25
The walk stops at consumes, so reaching such a block means the value was leaked. The function with the leak is as follows:
// make(_:_:)
// Isolation: unspecified
sil [ossa] @$s5repro4makeySiAA8InstanceC_SiAA7ContextVXEtF : $@convention(thin) (@guaranteed Instance, @guaranteed @noescape @callee_guaranteed (@guaranteed Context) -> Int) -> Int {
[%0: read c*.v**, write c*.v**, copy **, destroy c*.v**]
[%1: read v**.c*.v**, write v**.c*.v**, copy v**.**, destroy v**.c*.v**]
[global: read,write,copy,destroy,allocate,deinit_barrier]
// %0 "owner" // users: %5, %6, %9, %2, %13, %4
// %1 "body" // users: %17, %3
bb0(%0 : @guaranteed $Instance, %1 : @guaranteed $@noescape @callee_guaranteed (@guaranteed Context) -> Int):
debug_value %0, let, name "owner", argno 1 // id: %2
debug_value %1, let, name "body", argno 2 // id: %3
%4 = copy_value %0 // user: %11
debug_value %0, let, name "owner", argno 1 // id: %5
debug_value %0, var, name "self", type $Context, expr op_fragment:#Context.files:op_fragment:#Files.owner // id: %6
%7 = integer_literal $Builtin.Int64, 1 // user: %8
%8 = struct $Int (%7) // users: %14, %10
debug_value %0, var, name "self", type $Context, expr op_fragment:#Context.permit:op_fragment:#Permit.instance // id: %9
debug_value %8, var, name "self", type $Context, expr op_fragment:#Context.permit:op_fragment:#Permit.tag // id: %10
%11 = copy_value %4 // user: %12
%12 = struct $Files (%11) // user: %15
%13 = copy_value %0 // user: %14
%14 = struct $Permit (%13, %8) // user: %15
%15 = struct $Context (%12, %14) // users: %18, %17
debug_value undef : $*Context, var, name "self", expr op_deref // id: %16
%17 = apply %1(%15) : $@noescape @callee_guaranteed (@guaranteed Context) -> Int // user: %25
(%18, %19) = destructure_struct %15 // users: %20, %22
%20 = destructure_struct %18 // user: %21
destroy_value %20 // id: %21
(%22, %23) = destructure_struct %19 // user: %24
destroy_value %22 // id: %24
return %17 // id: %25
} // end sil function '$s5repro4makeySiAA8InstanceC_SiAA7ContextVXEtF'
Invalid SIL provided to OSSACompleteLifetime?! }}
Something that ran before OSSACompleteLifetime (the current pass, an earlier pass, SILGen) has introduced a leak of this value.
Please rerun the crashing swift-frontend command with the following flags added and file a bug with the output:
-sil-ownership-verify-all -Xllvm '-sil-print-function=$s5repro4makeySiAA8InstanceC_SiAA7ContextVXEtF' -Xllvm -sil-print-types -Xllvm -sil-print-module-on-error
Use the -disable-sil-ownership-verifier frontend flag to disable this check.
<unknown>:0: error: fatal error encountered during compilation; please submit a bug report (https://swift.org/contributing/#reporting-bugs)
<unknown>:0: note: Invalid lifetime of value whose availability boundary is being visited.
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0. Program arguments: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file repro.swift -target arm64-apple-macosx27.2.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -stack-check -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX27.0.sdk -no-color-diagnostics -Xcc -fno-color-diagnostics -O -new-driver-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-driver -empty-abi-descriptor -no-auto-bridging-header-chaining -module-name repro -disable-clang-spi -target-sdk-version 27.0 -target-sdk-name macosx27.0 -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -in-process-plugin-server-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/libSwiftInProcPluginServer.dylib -plugin-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins -enable-default-cmo -parse-as-library -o repro.o
1. Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)
2. Compiling with effective version 5.10
3. While evaluating request ExecuteSILPipelineRequest(Run pipelines { PrepareOptimizationPasses, EarlyModulePasses, HighLevel,Function+EarlyLoopOpt, HighLevel,Module+StackPromote, MidLevel,Function, ClosureSpecialize, LowLevel,Function, LateLoopOpt, SIL Debug Info Generator } on SIL for repro)
4. While running pass #912 SILFunctionTransform "SemanticARCOpts" on SILFunction "@$s5repro4makeySiAA8InstanceC_SiAA7ContextVXEtF".
for 'make(_:_:)' (at repro.swift:22:8)
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0 swift-frontend 0x0000000106a93bd0 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1 swift-frontend 0x0000000106a91358 llvm::sys::RunSignalHandlers() + 172
2 swift-frontend 0x0000000106a941c8 SignalHandler(int, __siginfo*, void*) + 360
3 libsystem_platform.dylib 0x000000019dc21144 _sigtramp + 56
4 libsystem_pthread.dylib 0x0000000198c50960 pthread_kill + 296
5 libsystem_c.dylib 0x0000000198ce4480 abort + 148
6 swift-frontend 0x00000001009c0ff8 swift::DiagnosticHelper::~DiagnosticHelper() + 0
7 swift-frontend 0x0000000106a02460 llvm::report_fatal_error(llvm::Twine const&, bool) + 292
8 swift-frontend 0x0000000106a0233c llvm::report_fatal_error(llvm::Twine const&, bool) + 0
9 swift-frontend 0x0000000100fc11ec visitUsersOutsideLinearLivenessBoundary(swift::SILValue, swift::SSAPrunedLiveness const&, llvm::function_ref<void (swift::SILInstruction*)>) + 0
10 swift-frontend 0x0000000101550d40 swift::semanticarc::OwnershipLiveRange::OwnershipLiveRange(swift::SILValue, llvm::ArrayRef<std::__1::pair<swift::Operand*, swift::SILValue>>) + 1188
11 swift-frontend 0x0000000101551f34 swift::semanticarc::SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(swift::CopyValueInst*) + 116
12 swift-frontend 0x00000001015533e4 swift::semanticarc::SemanticARCOptVisitor::visitCopyValueInst(swift::CopyValueInst*) + 96
13 swift-frontend 0x000000010155766c swift::semanticarc::SemanticARCOptVisitor::processWorklist() + 1060
14 swift-frontend 0x000000010154f454 (anonymous namespace)::SemanticARCOpts::run() + 2640
15 swift-frontend 0x00000001015087b8 swift::SILPassManager::runFunctionPasses(unsigned int, unsigned int) + 3564
16 swift-frontend 0x0000000101500cb0 swift::SILPassManager::executePassPipelinePlan(swift::SILPassPipelinePlan const&) + 364
17 swift-frontend 0x0000000101523b0c swift::SimpleRequest<swift::ExecuteSILPipelineRequest, std::__1::tuple<> (swift::SILPipelineExecutionDescriptor), (swift::RequestFlags)1>::evaluateRequest(swift::ExecuteSILPipelineRequest const&, swift::Evaluator&) + 52
18 swift-frontend 0x000000010150b6d4 swift::ExecuteSILPipelineRequest::OutputType swift::Evaluator::getResultUncached<swift::ExecuteSILPipelineRequest, swift::ExecuteSILPipelineRequest::OutputType swift::evaluateOrFatal<swift::ExecuteSILPipelineRequest>(swift::Evaluator&, swift::ExecuteSILPipelineRequest)::'lambda'()>(swift::ExecuteSILPipelineRequest const&, swift::ExecuteSILPipelineRequest::OutputType swift::evaluateOrFatal<swift::ExecuteSILPipelineRequest>(swift::Evaluator&, swift::ExecuteSILPipelineRequest)::'lambda'()) + 388
19 swift-frontend 0x000000010150dd58 swift::runSILOptimizationPasses(swift::SILModule&) + 464
20 swift-frontend 0x00000001009e19c4 swift::CompilerInstance::performSILProcessing(swift::SILModule*) + 1616
21 swift-frontend 0x00000001005a4860 performCompileStepsPostSILGen(swift::CompilerInstance&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>>, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::PrimarySpecificPaths const&, int&, swift::FrontendObserver*, llvm::ArrayRef<char const*>, llvm::ArrayRef<swift::PrimarySpecificPaths>) + 1584
22 swift-frontend 0x00000001005a3248 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*, llvm::ArrayRef<char const*>) + 992
23 swift-frontend 0x00000001005a8c0c performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*, llvm::ArrayRef<char const*>) + 1312
24 swift-frontend 0x00000001005a5dc4 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 3776
25 swift-frontend 0x0000000100525008 swift::mainEntry(int, char const**) + 5600
26 dyld 0x0000000198b7cd7c start + 6652
Expected behavior
The program compiles at -O, as it does at -Onone.
Environment
$ swiftc -version
swift-driver version: 1.168.6 Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)
Target: arm64-apple-macosx27.2.0
Xcode 27.1 (27A9275), macOS 27.0 SDK.
Additional information
Rerunning with the flags the crash message asks for (-sil-ownership-verify-all -Xllvm -sil-print-function=… -Xllvm -sil-print-types -Xllvm -sil-print-module-on-error) moves the failure to the end of pass #902 SILCombine (stage HighLevel,Function+EarlyLoopOpt): "Found a leaked owned value that was never consumed. Value: %4 = copy_value %0 : $Instance".
make before that SILCombine run (debug_values omitted):
bb0(%0 : @guaranteed $Instance, %1 : @guaranteed $@noescape @callee_guaranteed (@guaranteed Context) -> Int):
%4 = copy_value %0
%5 = move_value [lexical] %4
%8 = integer_literal $Builtin.Int64, 1
%9 = struct $Int (%8)
%12 = copy_value %5
%13 = struct $Files (%12)
%14 = struct $Permit (%5, %9)
%15 = struct $Context (%13, %14)
%17 = apply %1(%15) : $@noescape @callee_guaranteed (@guaranteed Context) -> Int
(%18, %19) = destructure_struct %15
%20 = destructure_struct %18
destroy_value %20
(%22, %23) = destructure_struct %19
destroy_value %22
return %17
After:
bb0(%0 : @guaranteed $Instance, %1 : @guaranteed $@noescape @callee_guaranteed (@guaranteed Context) -> Int):
%4 = copy_value %0
%7 = integer_literal $Builtin.Int64, 1
%8 = struct $Int (%7)
%11 = copy_value %4
%12 = struct $Files (%11)
%13 = copy_value %0
%14 = struct $Permit (%13, %8)
%15 = struct $Context (%12, %14)
%17 = apply %1(%15) : $@noescape @callee_guaranteed (@guaranteed Context) -> Int
(%18, %19) = destructure_struct %15
%20 = destructure_struct %18
destroy_value %20
(%22, %23) = destructure_struct %19
destroy_value %22
return %17
Removing %5 = move_value [lexical] %4 does two things:
- It gives the consuming use (
struct $Permit) a freshcopy_value %0. - It redirects the non-consuming use to
%4.
After that, nothing consumes %4.
The same crash happens when the borrowed reference is a closure capture, e.g. { body(Context(owner: owner)) } in an escaping closure that captures owner. It came up in a real project, where a whole-module -O build crashed on that shape. The workaround there was to pass the initializer an owned reference.

Activity
fix: Mint the operation context at admission, so Release builds compi…