swiftlang / swift

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

Conforming an actor type with a synchronous initializer to an async init protocol requirement crashes the compiler #76529

Open fwcd opened 1 month ago

fwcd commented 1 month ago

Description

Conforming an actor to a protocol with async initializers using synchronous initializers seems to crash the compiler using the new Swift 6/Xcode 16 toolchain.

Reproduction

protocol P: Actor {
    init() async
}

actor X: P {
    init() {}
}

Stack dump

error: compile command failed due to signal 11 (use -v to see invocation)
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0.  Program arguments: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file actor-crash.swift -target arm64-apple-macosx15.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -stack-check -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk -color-diagnostics -new-driver-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-driver -empty-abi-descriptor -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -module-name main -disable-clang-spi -target-sdk-version 15.0 -target-sdk-name macosx15.0 -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -plugin-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins -o /var/folders/bp/fvm0x9c92xb3x02w8qsg21jh0000gn/T/TemporaryDirectory.dm8nws/actor-crash-1.o
1.  Apple Swift version 6.0 (swiftlang-6.0.0.9.10 clang-1600.0.26.2)
2.  Compiling with effective version 5.10
3.  While evaluating request IRGenRequest(IR Generation for file "actor-crash.swift")
4.  While emitting IR SIL function "@$s4main1XCAA1PA2aDPxyYacfCTW".
 for 'init()' (at actor-crash.swift:6:5)
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  swift-frontend           0x0000000105e430fc llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x0000000105e41350 llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x0000000105e436c8 SignalHandler(int) + 292
3  libsystem_platform.dylib 0x0000000191404184 _sigtramp + 56
4  swift-frontend           0x0000000100e372d8 (anonymous namespace)::IRGenSILFunction::visitSILBasicBlock(swift::SILBasicBlock*) + 37740
5  swift-frontend           0x0000000100e2cc50 (anonymous namespace)::IRGenSILFunction::emitSILFunction() + 13220
6  swift-frontend           0x0000000100e29408 swift::irgen::IRGenModule::emitSILFunction(swift::SILFunction*) + 2088
7  swift-frontend           0x0000000100c75098 swift::irgen::IRGenerator::emitGlobalTopLevel(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>> const&) + 3364
8  swift-frontend           0x0000000100de0c44 swift::IRGenRequest::evaluate(swift::Evaluator&, swift::IRGenDescriptor) const + 4700
9  swift-frontend           0x0000000100e28954 swift::SimpleRequest<swift::IRGenRequest, swift::GeneratedModule (swift::IRGenDescriptor), (swift::RequestFlags)9>::evaluateRequest(swift::IRGenRequest const&, swift::Evaluator&) + 176
10 swift-frontend           0x0000000100de9b3c swift::IRGenRequest::OutputType swift::Evaluator::getResultUncached<swift::IRGenRequest, swift::IRGenRequest::OutputType swift::evaluateOrFatal<swift::IRGenRequest>(swift::Evaluator&, swift::IRGenRequest)::'lambda'()>(swift::IRGenRequest const&, swift::IRGenRequest::OutputType swift::evaluateOrFatal<swift::IRGenRequest>(swift::Evaluator&, swift::IRGenRequest)::'lambda'()) + 784
11 swift-frontend           0x0000000100de3574 swift::performIRGeneration(swift::FileUnit*, swift::IRGenOptions const&, swift::TBDGenOptions const&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>>, llvm::StringRef, swift::PrimarySpecificPaths const&, llvm::StringRef, llvm::GlobalVariable**) + 180
12 swift-frontend           0x00000001007f285c generateIR(swift::IRGenOptions const&, swift::TBDGenOptions const&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>>, swift::PrimarySpecificPaths const&, llvm::StringRef, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, llvm::GlobalVariable*&, llvm::ArrayRef<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>) + 156
13 swift-frontend           0x00000001007ee088 performCompileStepsPostSILGen(swift::CompilerInstance&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>>, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::PrimarySpecificPaths const&, int&, swift::FrontendObserver*) + 2176
14 swift-frontend           0x00000001007ecf74 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 984
15 swift-frontend           0x00000001007f0228 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1680
16 swift-frontend           0x00000001007eef58 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 3572
17 swift-frontend           0x000000010077601c swift::mainEntry(int, char const**) + 3680
18 dyld                     0x000000019104c274 start + 2840

Expected behavior

This shouldn't crash. As a workaround, X could mark the initializer async too.

Environment

swift-driver version: 1.115 Apple Swift version 6.0 (swiftlang-6.0.0.9.10 clang-1600.0.26.2) Target: arm64-apple-macosx15.0

Additional information

No response

matthewharrilal commented 1 month ago

I have a question for you: what need do you have that warrants an async initializer? I'm not questioning its importance but rather the context in which you're using it. If it's a UI-related task where an async operation helps complete the process, I would think that a synchronous initializer might be better suited, as it allows you to provide a visual indication to the user that data is loading.

fwcd commented 1 month ago

I find async initializers to be pretty useful for writing wrappers around web APIs. Authenticating within the initializer means an instance always represents an authenticated state, which is really convenient, since the methods can now operate under that assumption. That makes it more type safe too, since there is no instance to call anything on when logged out. Wrapping the instance in an optional e.g. would make sure that the logged out case is always handled when chaining some method call. Sure, in practice there's still stuff that can go wrong, tokens can expire, but those are edge cases that are perhaps better served via errors.

matthewharrilal commented 1 month ago

Oh, interesting—that makes sense! Although things can still go wrong between initialization and making authorized requests, such as token expiration, using an async initializer seems beneficial. With it, you likely avoid the need to add property wrappers to every method just to ensure the user is authorized. This way, you can avoid running authorization checks or middleware on every function call, correct?

Does this align with your thought process?