Noncopyable type has deinit called before init

Hi all, I’m experiencing just the weirdest bug. It seems like maybe a miscompile to me (or I've totally misunderstood vDSP.clear), but it’s at the crossroads of non-copyable types, the Accelerate framework, and thread sanitizer. So I’m not really sure where I should be filing this. Maybe you can help?

I have a test case where a struct’s deinit is called twice, leading to memory corruption. This only happens when compiled for release with thread sanitizer. Simplified version below:

// Run this with swiftc -sanitize=thread test.swift && ./test
import Accelerate

struct Testable: ~Copyable {
    var foo = 999
    init() { print("Initing...") }
    func honk() {
        print(foo)
    }
    deinit { print("Deinit!") }
}

struct Thing: ~Copyable {
    var buffer: UnsafeMutableBufferPointer<Float>
    var t: Testable

    init() {
        buffer = .allocate(capacity: 100)
        vDSP.clear(&buffer)
        t = Testable()
    }

    func test() {
        t.honk()
    }

    deinit {
        buffer.deallocate()
    }
}


Thing().test()

This prints

Deinit!
Initing...
999
Deinit!

I think the first "Deinit" line shouldn't appear. If I remove either of the following, the first "Deinit" goes away:

  1. vDSP.clear(&buffer)
  2. -sanitize=thread from the swiftc invocation

Address sanitizer shows no issue (and doesn't reproduce the issue).

Swift information:

$ swiftc --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

Run on macOS Sonoma 14.5.

3 Likes

SOS, please! @Joe_Groff, @John_McCall, @Douglas_Gregor, @Slava_Pestov, or et al. in the core team.

Good news is that this doesn't appear to miscompile with the Swift 6.0 nightly and -sanitize=thread. Slightly less good news is that it doesn't appear to compile at all with -sanitize=thread:

error: compile command failed due to signal 6 (use -v to see invocation)
Found a write classified as a liveness use?!
Use:   %18 = builtin "tsanInoutAccess"(%17 : $*UnsafeMutableBufferPointer<Float>) : $()
standard failure
UNREACHABLE executed at /Users/ec2-user/jenkins/workspace/oss-swift-6.0-package-macos/swift/lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp:2730!
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.

(Apple Swift version 6.0-dev (LLVM 4b0b1f9e1a69523, Swift 91a3abcf816bc36))

3 Likes

Well at least the compiler’s internal checks are getting better!

From the error message I guess it’s just that the call has an inout parameter — nothing to do with Accelerate after all.

I’ll file on GitHub then

2 Likes

Filed Noncopyable deinit causes compiler crash or miscompile with tsan · Issue #74042 · apple/swift · GitHub

4 Likes

MoveOnlyAddressChecker: Ignore tsan markers. by jckarter · Pull Request #74064 · apple/swift · GitHub should fix this.

4 Likes

Thanks for the quick turnaround! I’ll try and check it out when the change makes its way to a nightly

Edit: Confirmed the June 5 release/6.0 nightly fixes this. Thanks @Joe_Groff !

3 Likes