-
Notifications
You must be signed in to change notification settings - Fork 10.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Combination of ~Copyable
+ optional chaining + assignment crashes the compiler.
#80261
Comments
CC: @meg-gupta @eeckstein |
This is a problem in the MoveOnlyChecker |
I think It may be limited to globals e.g. this doesn't crash: struct S: ~Copyable {
var x: Int
}
func test() {
var s: S? = S(x: 0)
s?.x = 1
}
test() |
Interesting and weird. The compiler still crashes when it's a stored property of a struct NonCopyable: ~Copyable {
var value: Int = 0
}
final class Holder { // The compiler doesn't crash if this is a noncopyable struct.
var nc: NonCopyable? = NonCopyable()
func f() {
nc?.value = 1
}
} Stack dumpSIL verification failed: instruction isn't dominated by its operand: properlyDominates(valueI, I)
Verifying instruction:
%9 = unchecked_take_enum_data_addr %3 : $*Optional<NonCopyable>, #Optional.some!enumelt // users: %8, %12
-> destroy_addr %9 : $*NonCopyable // id: %8
In function:
// Holder.f()
// Isolation: unspecified
sil hidden [ossa] @$s4main6HolderC1fyyF : $@convention(method) (@guaranteed Holder) -> () {
// %0 "self" // users: %2, %1
bb0(%0 : @guaranteed $Holder):
debug_value %0, let, name "self", argno 1 // id: %1
%2 = ref_element_addr %0, #Holder.nc // user: %3
%3 = begin_access [modify] [dynamic] %2 // users: %6, %9, %14, %21
%4 = integer_literal $Builtin.Int1, -1 // user: %6
%5 = integer_literal $Builtin.Int1, 0 // user: %6
%6 = select_enum_addr %3, case #Optional.some!enumelt: %4, default %5 : $Builtin.Int1 // user: %7
cond_br %6, bb1, bb3 // id: %7
bb1: // Preds: bb0
destroy_addr %9 // id: %8
%9 = unchecked_take_enum_data_addr %3, #Optional.some!enumelt // users: %8, %12
%10 = integer_literal $Builtin.Int64, 1 // user: %11
%11 = struct $Int (%10) // user: %13
%12 = struct_element_addr %9, #NonCopyable.value // user: %13
store %11 to [trivial] %12 // id: %13
end_access %3 // id: %14
%15 = tuple () // user: %16
%16 = enum $Optional<()>, #Optional.some!enumelt, %15 // user: %17
br bb2(%16) // id: %17
bb2(%18 : $Optional<()>): // Preds: bb3 bb1
%19 = tuple () // user: %20
return %19 // id: %20
bb3: // Preds: bb0
end_access %3 // id: %21
%22 = enum $Optional<()>, #Optional.none!enumelt // user: %23
br bb2(%22) // id: %23
} // end sil function '$s4main6HolderC1fyyF'
Stack dump:
0. Program arguments: /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-03-17-a.xctoolchain/usr/bin/swift-frontend -frontend -interpret GHI-80261_NonCopyable-OptionalChaining-Assignment_StoredProperty.swift -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -color-diagnostics -empty-abi-descriptor -resource-dir /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-03-17-a.xctoolchain/usr/lib/swift -no-auto-bridging-header-chaining -module-name main -in-process-plugin-server-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-03-17-a.xctoolchain/usr/lib/swift/host/libSwiftInProcPluginServer.dylib -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-03-17-a.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-03-17-a.xctoolchain/usr/local/lib/swift/host/plugins -target-sdk-version 15.2 -target-sdk-name macosx15.2 -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
1. Apple Swift version 6.2-dev (LLVM 21406e90d7382d7, Swift 90340a069a706c3)
2. Compiling with effective version 5.10
3. While verifying SIL function "@$s4main6HolderC1fyyF".
for 'f()' (at GHI-80261_NonCopyable-OptionalChaining-Assignment_StoredProperty.swift:8:3)
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 0x0000000105bc0184 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1 swift-frontend 0x0000000105bbe8b0 llvm::sys::RunSignalHandlers() + 112
2 swift-frontend 0x0000000105bc07cc SignalHandler(int, __siginfo*, void*) + 296
3 libsystem_platform.dylib 0x000000018f996de4 _sigtramp + 56
4 libsystem_pthread.dylib 0x000000018f95ff70 pthread_kill + 288
5 libsystem_c.dylib 0x000000018f86c908 abort + 128
6 swift-frontend 0x000000010136f828 swift::SILModule::print(llvm::raw_ostream&, swift::ModuleDecl*, swift::SILOptions const&, bool) const + 0
7 swift-frontend 0x000000010139187c (anonymous namespace)::SILVerifier::visitSILInstruction(swift::SILInstruction*) + 1584
8 swift-frontend 0x000000010137c568 swift::SILVisitorBase<(anonymous namespace)::SILVerifier, void>::visitSILBasicBlock(swift::SILBasicBlock*) + 22648
9 swift-frontend 0x0000000101376c30 (anonymous namespace)::SILVerifier::visitSILBasicBlock(swift::SILBasicBlock*) + 24
10 swift-frontend 0x0000000101375590 (anonymous namespace)::SILVerifier::visitSILFunction(swift::SILFunction*) + 10464
11 swift-frontend 0x000000010136f9e0 swift::SILFunction::verify(swift::CalleeCache*, bool, bool, bool) const + 224
12 swift-frontend 0x00000001013726f4 swift::SILModule::verify(swift::CalleeCache*, bool, bool) const + 192
13 swift-frontend 0x00000001013725cc swift::SILModule::verify(bool, bool) const + 140
14 swift-frontend 0x0000000100723228 swift::CompilerInstance::performSILProcessing(swift::SILModule*) + 148
15 swift-frontend 0x00000001004ea340 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*) + 820
16 swift-frontend 0x00000001004e9cf8 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1320
17 swift-frontend 0x00000001004f5f48 withSemanticAnalysis(swift::CompilerInstance&, swift::FrontendObserver*, llvm::function_ref<bool (swift::CompilerInstance&)>, bool) + 164
18 swift-frontend 0x00000001004eb6f4 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 716
19 swift-frontend 0x00000001004eaf10 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2244
20 swift-frontend 0x00000001002a2c8c swift::mainEntry(int, char const**) + 3060
21 dyld 0x000000018f5e0274 start + 2840 |
@YOCKOW From my understanding it looks the nc?.value = 1 Similar error happens when doing: var nc1: NonCopyable? = NonCopyable()
nc1?.value = 1
func f(nc1: consuming NonCopyable?) {
var nc2: NonCopyable? = nc1
} Or even: var nc1: NonCopyable? = NonCopyable()
nc1?.value = 1
func f(nc1: consuming NonCopyable?) {
var nc2: NonCopyable? = NonCopyable(value: nc1!.value)
} Same issue happens with Specifically in these passes is where the dominator tree seems broken: Pass 72: *** SIL function after #72, stage Mandatory Diagnostic Passes + Enabling Optimization Passes, pass 18: LowerTupleAddrConstructor (lower-tuple-addr-constructor)
// main
// Isolation: unspecified
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @$s4main2ncAA11NonCopyableVSgvp // id: %2
%3 = global_addr @$s4main2ncAA11NonCopyableVSgvp : $*Optional<NonCopyable> // users: %9, %8
%4 = metatype $@thin NonCopyable.Type // user: %6
// function_ref NonCopyable.init()
%5 = function_ref @$s4main11NonCopyableVACycfC : $@convention(method) (@thin NonCopyable.Type) -> @owned NonCopyable // user: %6
%6 = apply %5(%4) : $@convention(method) (@thin NonCopyable.Type) -> @owned NonCopyable // user: %7
%7 = enum $Optional<NonCopyable>, #Optional.some!enumelt, %6 // user: %8
store %7 to [init] %3 // id: %8
%9 = begin_access [modify] [dynamic] %3 // users: %24, %15, %10
%10 = mark_unresolved_non_copyable_value [assignable_but_not_consumable] %9 // users: %17, %13
%11 = integer_literal $Builtin.Int1, -1 // user: %13
%12 = integer_literal $Builtin.Int1, 0 // user: %13
%13 = select_enum_addr %10, case #Optional.some!enumelt: %11, default %12 : $Builtin.Int1 // user: %14
cond_br %13, bb2, bb1 // id: %14
bb1: // Preds: bb0
end_access %9 // id: %15
br bb4 // id: %16
bb2: // Preds: bb0
%17 = unchecked_take_enum_data_addr %10, #Optional.some!enumelt // user: %22
%18 = integer_literal $Builtin.IntLiteral, 1 // user: %21
%19 = metatype $@thin Int.Type // user: %21
// function_ref Int.init(_builtinIntegerLiteral:)
%20 = function_ref @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int // user: %21
%21 = apply %20(%18, %19) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int // user: %23
%22 = struct_element_addr %17, #NonCopyable.value // user: %23
store %21 to [trivial] %22 // id: %23
end_access %9 // id: %24
%25 = tuple () // user: %26
%26 = enum $Optional<()>, #Optional.some!enumelt, %25 // user: %27
br bb3(%26) // id: %27
bb3(%28 : $Optional<()>): // Preds: bb4 bb2
%29 = integer_literal $Builtin.Int32, 0 // user: %30
%30 = struct $Int32 (%29) // user: %31
return %30 // id: %31
bb4: // Preds: bb1
%32 = enum $Optional<()>, #Optional.none!enumelt // user: %33
br bb3(%32) // id: %33
} // end sil function 'main' And then we destroy Pass 89: *** SIL function after #89, stage Mandatory Diagnostic Passes + Enabling Optimization Passes, pass 21: MoveOnlyChecker (sil-move-only-checker)
// main
// Isolation: unspecified
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @$s4main3nc1AA11NonCopyableVSgvp // id: %2
%3 = global_addr @$s4main3nc1AA11NonCopyableVSgvp : $*Optional<NonCopyable> // users: %9, %8
%4 = metatype $@thin NonCopyable.Type // user: %6
// function_ref NonCopyable.init()
%5 = function_ref @$s4main11NonCopyableVACycfC : $@convention(method) (@thin NonCopyable.Type) -> @owned NonCopyable // user: %6
%6 = apply %5(%4) : $@convention(method) (@thin NonCopyable.Type) -> @owned NonCopyable // user: %7
%7 = enum $Optional<NonCopyable>, #Optional.some!enumelt, %6 // user: %8
store %7 to [init] %3 // id: %8
%9 = begin_access [modify] [dynamic] %3 // users: %12, %17, %24, %14
%10 = integer_literal $Builtin.Int1, -1 // user: %12
%11 = integer_literal $Builtin.Int1, 0 // user: %12
%12 = select_enum_addr %9, case #Optional.some!enumelt: %10, default %11 : $Builtin.Int1 // user: %13
cond_br %12, bb2, bb1 // id: %13
bb1: // Preds: bb0
end_access %9 // id: %14
br bb4 // id: %15
bb2: // Preds: bb0
destroy_addr %17 // id: %16
%17 = unchecked_take_enum_data_addr %9, #Optional.some!enumelt // users: %16, %22
%18 = integer_literal $Builtin.IntLiteral, 1 // user: %21
%19 = metatype $@thin Int.Type // user: %21
// function_ref Int.init(_builtinIntegerLiteral:)
%20 = function_ref @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int // user: %21
%21 = apply %20(%18, %19) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int // user: %23
%22 = struct_element_addr %17, #NonCopyable.value // user: %23
store %21 to [trivial] %22 // id: %23
end_access %9 // id: %24
%25 = tuple () // user: %26
%26 = enum $Optional<()>, #Optional.some!enumelt, %25 // user: %27
br bb3(%26) // id: %27
bb3(%28 : $Optional<()>): // Preds: bb4 bb2
%29 = integer_literal $Builtin.Int32, 0 // user: %30
%30 = struct $Int32 (%29) // user: %31
return %30 // id: %31
bb4: // Preds: bb1
%32 = enum $Optional<()>, #Optional.none!enumelt // user: %33
br bb3(%32) // id: %33
} // end sil function 'main' It would be great if you provide right pointers to it in the codebase which handle this to at least give it a try. |
Description
The compiler crashes when you try to assign a value to a property of optional-chained non-copyable type.
Reproduction
Stack dump
Expected behavior
The compiler doesn't crash.
Environment
Additional information
This may be related to #75999, but this issue occurs even if you use
!
instead of?
.(Related post: https://forums.swift.org/t/how-can-i-borrow-with-conditional-binding/78759/11)
The text was updated successfully, but these errors were encountered: