Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-14171] Double free when using enum case as protocol witness #56549

Closed
swift-ci opened this issue Feb 9, 2021 · 0 comments
Closed

[SR-14171] Double free when using enum case as protocol witness #56549

swift-ci opened this issue Feb 9, 2021 · 0 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Feb 9, 2021

Previous ID SR-14171
Radar None
Original Reporter jayton (JIRA User)
Type Bug
Status Resolved
Resolution Duplicate
Additional Detail from JIRA
Votes 0
Component/s
Labels Bug
Assignee None
Priority Medium

md5: 5b68d4e65166f40e8133c99e5eb7d87b

duplicates:

  • 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
@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.
Projects
None yet
Development

No branches or pull requests

1 participant