Skip to content

[SR-9176] Object Over-Released when enum case with associated value T is passed as closure to function operating over a generic specializing T #51667

@swift-ci

Description

@swift-ci
Previous ID SR-9176
Radar rdar://problem/45962437
Original Reporter pacheco (JIRA User)
Type Bug

Attachment: Download

Environment

Xcode 10.1 (10B61)

Swift 4.0, 4.2

macOS 10.13.6, 10.14

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, 4.2Regression, RunTimeCrash
Assignee None
Priority Medium

md5: e3edd86a852ba61276c0ea3eb81b3582

Issue Description:

Sorry if my description is not the most accurate, but it's not an easy one to describe 😛

When passing an enum's case with associated value T as a closure to another function (i.e. to "wrap" a T and create a new enum instance), if the type T is an associatedtype on a protocol (specialized in the generic of the function's parent type), an over-release is made, causing a EXC_BAD_ACCESS runtime crash.

I've only observed this issue since Xcode 10 (also present in 10.1), in both Swift 4 and Swift 4.2 modes. On Xcode 9 the same code worked without any issues, and since the code is valid Swift I am led to believe that this is most likely a compiler regression.

When this issue started occurring I analyzed the app with the Zombies Instrument and discovered that an additional release was being generated when the enum's case is used as a parameter.

I've managed to create a reduced example which demonstrates the issue:

enum Result<T, E: Error> {
    case success(T)
    case failure(E)

    func mapError<E2: Error>(_ f: (E) -> E2) -> Result<T, E2> {
        switch self {
        case .success(let value): return .success(value)
        case .failure(let error): return .failure(f(error))
        }
    }
}

protocol Store {
    associatedtype E: Error

    func fetch(_ completion: (Result<Void, E>) -> Void)
}

// specializing using a value type doesn't crash
enum EnumError: Error { case error }
struct StructError: Error {}

// only specializing using a reference type crashes
class ClassError: Error {}

final class MyStore: Store {
    typealias E = ClassError

    func fetch(_ completion: (Result<Void, ClassError>) -> Void) {
        completion(.failure(ClassError()))
    }
}

enum ServiceError: Error {
    case fetch(Error)
}

struct MyService<S: Store> {
    let store: S

    func fetch(_ completion: (Result<Void, ServiceError>) -> Void) {
        store.fetch { result in
            completion(result.mapError(ServiceError.fetch))
        }
    }

    func explicitFetch(_ completion: (Result<Void, ServiceError>) -> Void) {
        store.fetch { result in
            completion(result.mapError { ServiceError.fetch($0) })
        }
    }
}

// test

let store = MyStore()
let service = MyService(store: store)

// doesn't crash
// store.fetch { print($0) }

// crashes
service.fetch { print($0) }

// doesn't crash
// service.explicitFetch { print($0) }

I've atached a demo app demonstrating the issue, but it's also manifested on a playground (even though it may require a small change so that the result lives a bit longer).

Metadata

Metadata

Assignees

No one assigned

    Labels

    access controlFeature → modifiers: Access control and access levelsbugA deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfcrashBug: A crash, i.e., an abnormal termination of softwareregressionrun-time crashBug → crash: Swift code crashed during executionswift 4.2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions