swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.49k stars 10.35k forks source link

[SR-12117] @propertyWrapper projected value access results in EXC_BAD_ACCESS #54553

Open swift-ci opened 4 years ago

swift-ci commented 4 years ago
Previous ID SR-12117
Radar rdar://problem/59132836
Original Reporter TadeasKriz (JIRA User)
Type Bug

Attachment: Download

Environment Xcode 11.3.1 (11C505) macOS 10.15.2 Running the app on iOS 13.3 and iOS 13.1.2
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: 9449fd8a8aa9b519c982b33df9b1e9e2

Issue Description:

While working on a project, accessing a projected value using `$property` suddenly started crashing with `EXC_BAD_ACCESS(code=2)`. When I stopped at a breakpoint at the same place and run `po $property`, it printed `\<uninitialized>`. Running `po property.projectedValue` returned the correct value though. When I replaced the call `$property` with `_property.projectedValue`, the crashing has disappeared.

Also, it seems that the first run after a clean build the crash doesn't occur, which is weird.

To get more information I've run the app with Address sanitizer turned on. Instead of `EXC_BAD_ACCESS(code=2)` I'm seeing a message `Stack buffer overflow` and the following output in the console.

AddressSanitizer.log

The code of the property is as follows:

import RxSwift
import RxRelay

@propertyWrapper
public struct EnableObserving<T> {
    private let observable: Observable<T>
    private let accept: (T) -> Void

    public init(wrappedValue: T) {
        self.init(wrappedValue: wrappedValue, skipFirst: false)
    }

    public init(wrappedValue: T, skipFirst: Bool) {
        self.wrappedValue = wrappedValue

        if skipFirst {
            let replaySubject = ReplaySubject<T>.create(bufferSize: 1)
            observable = replaySubject
            accept = replaySubject.onNext
        } else {
            let relay = BehaviorRelay(value: wrappedValue)
            observable = relay.asObservable()
            accept = relay.accept
        }

    }

    public var wrappedValue: T {
        didSet {
            accept(wrappedValue)
        }
    }

    public var projectedValue: Observable<T> {
        return observable
    }
}
beccadax commented 4 years ago

@swift-ci create