You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SR-14041 Protocol allows to create a nested enum without explicit indirect keyword resulting in a runtime crash
Issue Description:
In the attached code, there are two isomorphic single-case enums using a closure as an associated value. Both of them conform to a protocol ConsumerHolder with an associated type; SafeEnum conforms by using a separate static function to conform to the protocol, while CrashingEnum conforms through its enum case.
When the two enums are used with a generic function, which calls the protocol requirement (using the associated type) and passes a trivial closure, the call using CrashingEnum results in a double-free (presumably of the closure).
If the associated type is replaced with a fixed type, the crash does not occur.
If the generic function is replaced with a non-generic equivalent, the crash does not occur.
Reproduced in Xcode 12.3, Xcode 12.4, Xcode 12.5b1, and Development Snapshot 2021-01-27 (a).
// This doesn’t crash
enum SafeEnum: ConsumerHolder {
case consume((ConcreteModel) -> Void)
static func wrappedConsumer(_ event: @escaping (ConcreteModel) -> Void) -> SafeEnum {
return .consume(event)
}
}
performTest(SafeEnum.self) {
if case .consume(let consumer) = $0 {
print("Safe test starting")
consumer(1)
print("Safe test completed")
}
}
// This crashes – note that the static func protocol constraint is fulfilled by an enum case
enum CrashingEnum: ConsumerHolder {
case wrappedConsumer((ConcreteModel) -> Void)
}
performTest(CrashingEnum.self) {
if case .wrappedConsumer(let consumer) = $0 {
print("Crashing test starting")
consumer(1)
print("Crashing test completed")
}
}
protocol ConsumerHolder {
// If this associated type is replaced with a typealias, it no longer crashes
#if true
associatedtype Model
#else
typealias Model = ConcreteModel
#endif
static func wrappedConsumer(_: @escaping (Model) -> Void) -> Self
}
typealias ConcreteModel = Int
// If performTest is not generic, it no longer crashes
#if true
func performTest<Holder: ConsumerHolder> (
_ type: Holder.Type,
consume: @escaping (Holder) -> Void
) where Holder.Model == ConcreteModel {
let wrapped = Holder.wrappedConsumer { _ in }
consume(wrapped)
}
#else
func performTest(
_ type: SafeEnum.Type,
consume: @escaping (SafeEnum) -> Void
) {
let wrapped = SafeEnum.wrappedConsumer { _ in }
consume(wrapped)
}
func performTest(
_ type: CrashingEnum.Type,
consume: @escaping (CrashingEnum) -> Void
) {
let wrapped = CrashingEnum.wrappedConsumer { _ in }
consume(wrapped)
}
#endif
The text was updated successfully, but these errors were encountered:
Additional Detail from JIRA
md5: 5b68d4e65166f40e8133c99e5eb7d87b
duplicates:
indirect
keyword resulting in a runtime crashIssue Description:
In the attached code, there are two isomorphic single-case enums using a closure as an associated value. Both of them conform to a protocol
ConsumerHolder
with an associated type;SafeEnum
conforms by using a separate static function to conform to the protocol, whileCrashingEnum
conforms through its enum case.When the two enums are used with a generic function, which calls the protocol requirement (using the associated type) and passes a trivial closure, the call using
CrashingEnum
results in a double-free (presumably of the closure).If the associated type is replaced with a fixed type, the crash does not occur.
If the generic function is replaced with a non-generic equivalent, the crash does not occur.
Reproduced in Xcode 12.3, Xcode 12.4, Xcode 12.5b1, and Development Snapshot 2021-01-27 (a).
The text was updated successfully, but these errors were encountered: