mirror of
https://github.com/apple/swift.git
synced 2026-10-24 20:15:56 +02:00
When ClosureSpecialization specializes a callee that is called with both, an escaping closure (`partial_apply`) and a non-escaping on-stack closure (`partial_apply [on_stack]`), the two specializations must have distinct mangled names because they have distinct parameter ownership conventions: the escaping version takes @owned captures, the non-escaping version takes @guaranteed captures. Previously, mangleClosureProp used 'c' for both cases, causing a name collision. The second specialization would look up and reuse the first specialized function, producing a call with mismatched ownership (e.g. passing a @guaranteed value to an @owned parameter). Fix: introduce a new ARG-SPEC-KIND token 'E' (`EscapingClosureProp`) for escaping `partial_apply` closures, keeping 'c' exclusively for `partial_apply [on-stack]` (non-escaping) closures. Each specialization now gets a unique name and the correct parameter conventions, preventing the collision regardless of processing order. Mangling schemes of function specializations which are produced by the optimizer are not part of the ABI, therefore this mangling change is not ABI breaking. Fixes a compiler crash https://github.com/swiftlang/swift/issues/90011 rdar://179906047
83 lines
2.4 KiB
Swift
83 lines
2.4 KiB
Swift
// RUN: %target-swift-frontend -O -emit-sil %s
|
|
|
|
// Regression test: closure specialization crashed when an on-stack (non-escaping)
|
|
// closure's captured argument has guaranteed ownership (e.g. from a load_borrow
|
|
// in a protocol witness thunk), and an existing escaping specialization of the
|
|
// same callee — with @owned parameter convention — was reused due to mangling
|
|
// collision between on-stack and escaping partial_applys of the same function.
|
|
//
|
|
// The fix inserts a copy_value in rewriteApply when a guaranteed argument is
|
|
// passed to an @owned parameter of the (reused) specialized function.
|
|
|
|
public protocol Receiver: AnyObject {
|
|
associatedtype State
|
|
func receive(_ state: State)
|
|
}
|
|
|
|
enum Mode: Equatable {
|
|
case selected(Bool)
|
|
case typing
|
|
}
|
|
|
|
struct Input: Equatable {
|
|
var x: Int
|
|
var y: Int
|
|
}
|
|
|
|
struct Output: Equatable {
|
|
final class Box {}
|
|
var mode: Mode
|
|
var box: Box
|
|
var input: Input
|
|
|
|
static func == (lhs: Output, rhs: Output) -> Bool {
|
|
lhs.mode == rhs.mode && lhs.box === rhs.box && lhs.input == rhs.input
|
|
}
|
|
}
|
|
|
|
public final class Store {
|
|
public func add<R>(_ state: R.State, _ receiver: R) where R: Receiver {
|
|
receiver.receive(state)
|
|
}
|
|
}
|
|
|
|
final class ConcreteReceiver: Receiver {
|
|
typealias State = (String) -> Output?
|
|
private var id: String
|
|
private var output: Output?
|
|
|
|
init(id: String) { self.id = id }
|
|
|
|
func receive(_ state: (String) -> Output?) {
|
|
let old = output
|
|
let new = state(id)
|
|
if old == new { return }
|
|
output = new
|
|
}
|
|
}
|
|
|
|
// Coordinator.receive passes an owned closure to child.receive, causing the
|
|
// closure specializer to create a specialization of ConcreteReceiver.receive
|
|
// with an @owned capture parameter (processed first).
|
|
final class Coordinator: Receiver {
|
|
typealias State = Any
|
|
private let child: ConcreteReceiver
|
|
|
|
init(child: ConcreteReceiver) { self.child = child }
|
|
|
|
func receive(_ state: Any) {
|
|
guard let pair = state as? ((String) -> Output?, (String) -> Output?) else { return }
|
|
child.receive(pair.1)
|
|
}
|
|
}
|
|
|
|
// entry calls Store.add which dispatches through the protocol witness thunk for
|
|
// ConcreteReceiver.receive. The witness thunk uses load_borrow, giving the
|
|
// captured closure guaranteed ownership. The closure specializer reuses the
|
|
// existing @owned specialization and must insert a copy_value.
|
|
@inline(never)
|
|
func entry(_ state: @escaping (String) -> Output?) {
|
|
let receiver = ConcreteReceiver(id: "id")
|
|
Store().add(state, receiver)
|
|
}
|