Can we get consistent assertion behavior in the latest release Swift compiler across host platforms?

In my opinion, the right behaviour here is that development snapshots should have assertions enabled and official point releases should have them disabled across all platforms; this is how it effectively works on macOS (the official Xcode toolchain, which most macOS developers use, is assertions disabled; Swift.org snapshots are assertions enabled). If you’re downloading a development snapshot you’re expecting less stability, and if you’re on a release compiler you expect it to be stable and as fast as possible (since assertions carry some runtime overhead). It would be nice if the Windows toolchains could align with this pattern.

3 Likes

Might be worth considering here: have an extra conditional variable (environment variable?) that controls whether assert crashes or not (or equally make assert a function pointer that points to either normal assert implementation or another implementation that doesn't crash, just logs). The conditional variable / function pointer will be set early and never changed again to simplify thread safety angle.


Test implementation:

private let trappingAssert = ProcessInfo.processInfo.environment["DONT_TRAP_ON_ASSERTS"] != "1"

public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = String(), file: StaticString = #fileID, line: UInt = #line) {
    if !condition() {
        print("\(file): \(line) \(message())") // log, etc
        if trappingAssert {
            _real_assert(false) // underlying assert
        }
    }
}

Unfortunately, we don't have enough code to throw at the compiler to ensure correctness. On macOS and Linux, the regular validation will force a significant amount of code to be compiled with the compiler to help find issues. I barely got ~2 reports of issues with the new support for static linking, with the remainder of the issues being found by me later during use. I would say that Windows currently has no viable path to align with this and should remain assertions only.

The middle ground approach would be to bundle an additional NoAsserts toolchain that the user is able to opt-in to. But, I don't think that would be reasonable to do without some additional work to improve the infrastructure to support that. This does work well enough in practice - the BCNY toolchains do this - and still default to the Asserts variant.

What's the right way to bring this more officially to the @platform-steering-group to get an official word? I appreciate we as individuals have opinions, but it'd be good, considering how it's affecting real-world maintenance of packages in the wild, for this to be discussed and driven to resolution by the right entities in the Swift project. Maybe the @ecosystem-steering-group should also chime in, since this is a usability pain.

5 Likes

IMO this is more for the PSG than the ESG as it pertains to the details of the platform. While I can appreciate that the platforms impact the ecosystem (Windows not being supported would be a massive hit to the ecosystem), this is really a PSG issue IMO.

We can certainly put this on the PSG backlog though.

3 Likes

The OSS Darwin toolchain has these assertions enabled. If I install Swift on my computer using swiftly, there are dozens of populat packages that have crashed the compiler for a year, due to running into assertions.

As an application developer thats sharing code across multiple platforms, this issue causes a ton of headaches. Not only do I have to be careful not to introduce code that will crash the compiler, I also can’t use libraries that trip these assertions. And bitrot is always on the horizon, if any of my dependencies introduce perfectly valid swift code, I have to pin it and no longer receive updates to that library.

Is there anything we can do to get this moving through the @platform-steering-group?

This bug has some minimum reproducable code in it surrounding parameter packs.

2 Likes

I ran into a similar situation with this small piece of code: on Xcode built-in and Linux (swift 6.2+ toolchains) the code compiles fine, while on downloaded open-sourced macOS toolchains it fires the asserts (e.g. Assertion failed: (claimed.size() == primaryAssociatedTypes.size()), function createConstrainedExistentialType at ASTDemangler.cpp:85, with swift-6.2.3-RELEASE.xctoolchain )

typealias FullProtocolWithU<U> = FullProtocol & ProtocolGenericU<U>
typealias FullProtocolWithT<T> = FullProtocol & ProtocolGenericT<T>
protocol FullProtocol<U, T>: ProtocolGenericU, ProtocolGenericT {}
protocol ProtocolGenericU<U>: BaseProtocol {}
protocol ProtocolGenericT<T>: BaseProtocol {}
protocol BaseProtocol {
    associatedtype U
    associatedtype T
    var t: T { get }
}
struct S: FullProtocol {
    typealias U = Double
    typealias T = Int
    let t = 1
}
func crashOpenSourceSwift6_2_MacOS_Toolchain<T>(_ value: any FullProtocolWithT<T>) {
    print("Here's the T: ", value.t)
}
let value = S()
crashOpenSourceSwift6_2_MacOS_Toolchain(value)

I want to use this pattern so that I can conform my types to just one protocol (FullProtocol) and then in some existential contexts require only the type U to be constrained, in other existential contexts, only require the type T to be constrained, and in some existential context require both U and T to be constrained. This compiles and works without issue on Linux, macOS, and iOS, with swift 6.2+, provided that I'm not using the open-source toolchains on macOS.

2 Likes

It appears this has been fixed for the OSS macOS release toolchain, which now turns off assertions too:

> swiftly list
Installed release toolchains
----------------------------
Swift 6.3.1 (in use) (default)
Swift 6.2.4
-- snip ---

> swift -version
Apple Swift version 6.3.1 (swift-6.3.1-RELEASE)
Target: arm64-apple-macosx26.0

Trying it out with a recent SIL assertion crasher reported on GitHub causes no problems, whereas it would crash the prior 6.2.4 release with assertions enabled:

>swift prop.swift

> swiftly use 6.2.4
The global default toolchain has been set to `Swift 6.2.4` (was 6.3.1)

> swift -version
Apple Swift version 6.2.4 (swift-6.2.4-RELEASE)
Target: arm64-apple-macosx26.0
Build config: +assertions

> swift prop.swift
Assertion failed: ((AccessKind == SGFAccessKind::BorrowedObjectRead || AccessKind == SGFAccessKind::BorrowedAddressRead) && "non-borrow component requires an address base"), function emitUsingStorage, file SILGenLValue.cpp, line 3497.

Thanks to the Swift team for changing this; the only remaining outlier is the Windows release toolchain, which I think still has asserts enabled.

3 Likes

Windows ships both variants, encourages the use of the assets toolchain, but allows you to opt into the noasserts build. We still require that issues being filed use the asserts toolchain.

1 Like