-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Closed
Labels
bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.A deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfThe Swift compiler itselfcrashBug: A crash, i.e., an abnormal termination of softwareBug: A crash, i.e., an abnormal termination of softwareoptimized onlyFlag: An issue whose reproduction requires optimized compilationFlag: An issue whose reproduction requires optimized compilation
Description
| Previous ID | SR-12180 |
| Radar | rdar://problem/59496027 |
| Original Reporter | kamils (JIRA User) |
| Type | Bug |
| Status | Resolved |
| Resolution | Done |
Environment
Apple Swift version 5.1 (swiftlang-1100.0.270.13)
Additional Detail from JIRA
| Votes | 0 |
| Component/s | Compiler |
| Labels | Bug, CompilerCrash, OptimizedOnly |
| Assignee | None |
| Priority | Medium |
md5: 0edb6e6fee44600a3b53619a01df402a
Issue Description:
On production AppStore build we did face a 100% reproducible deterministic app crash which I was able reduce to be caused by following code. This happens only with `-O (Optimize for speed)`, `-Onone` is unaffected.
swiftc --version
Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)
Target: x86_64-apple-darwin19.0.0This happens both on X86-64 and ARM so clearly it's a Swift compiler issue.
import Foundation
public class UserDefaultsStore<T>: Storing where T: Codable {
public typealias Element = T
private var userDefaults: UserDefaults
@inline(__always) //this will be inlined by default, will crash
public func read(for url: String) -> T? {
print(userDefaults) //� Crash will happen here
return nil
}
//This code is essential for crash to happen (even though it's not called)
public func save(_ element: T, for url: String) {
}
public init(userDefaults: UserDefaults) {
self.userDefaults = userDefaults
}
}
public protocol Storing {
associatedtype Element
func read(for url: String) -> Element?
func save(_ element: Element, for key: String)
}
public struct AnyStoring<T>: Storing {
public typealias Element = T
private let readHandler: (String) -> T?
private let saveHandler: (T, String) -> Void
//This is essential to cause the crash
@inline(never)
public func read(for key: String) -> T? {
return readHandler(key)
}
public func save(_ element: T, for key: String) {
saveHandler(element, key)
}
public init<C: Storing>(_ cache: C) where C.Element == Element {
readHandler = cache.read
saveHandler = cache.save
}
}
//This can be class as well
public struct SomeModel: Codable {
}
//Call to crash
AnyStoring<SomeModel>(UserDefaultsStore<SomeModel>(userDefaults: UserDefaults.standard)).read(for: "")We found four possible workarounds not to trigger the crash:
- Define both `AnyStoring` & `UserDefaultsStore` as a class
- Define both `AnyStoring` & `UserDefaultsStore` as a struct
- Define `AnyStoring` as a class & `UserDefaultsStore` as a struct (so effectively exchanging their definition types)
- Mark the crashing method
public func read(for url: String) -> T? {
with `@inline(never)`
The SIL of inlined and nonline variant for crashing `public func read(for url: String) -> T? {` looks identical to me (apart from the mangled signature).
// UserDefaultsStore.read(for:)
sil [always_inline] [ossa] @$s11main_always17UserDefaultsStoreC4read3forxSgSS_tF : $@convention(method) <T where T : Decodable, T : Encodable> (@guaranteed String, @guaranteed UserDefaultsStore<T>) -> @out Optional<T> {
// %0 // user: %25
// %1 // user: %3
// %2 // users: %12, %11, %4
bb0(%0 : $*Optional<T>, %1 : @guaranteed $String, %2 : @guaranteed $UserDefaultsStore<T>):
debug_value %1 : $String, let, name "url", argno 1 // id: %3
debug_value %2 : $UserDefaultsStore<T>, let, name "self", argno 2 // id: %4
%5 = integer_literal $Builtin.Word, 1 // user: %7
// function_ref _allocateUninitializedArray<A>(_:)
%6 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %7
%7 = apply %6<Any>(%5) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %8
(%8, %9) = destructure_tuple %7 : $(Array<Any>, Builtin.RawPointer) // users: %23, %20, %10
%10 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*Any // user: %13
%11 = class_method %2 : $UserDefaultsStore<T>, #UserDefaultsStore.userDefaults!getter.1 : <T where T : Decodable, T : Encodable> (UserDefaultsStore<T>) -> () -> UserDefaults, $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %12
%12 = apply %11<T>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %14
%13 = init_existential_addr %10 : $*Any, $UserDefaults // user: %14
store %12 to [init] %13 : $*UserDefaults // id: %14
// function_ref default argument 1 of print(_:separator:terminator:)
%15 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String // user: %16
%16 = apply %15() : $@convention(thin) () -> @owned String // users: %22, %20
// function_ref default argument 2 of print(_:separator:terminator:)
%17 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String // user: %18
%18 = apply %17() : $@convention(thin) () -> @owned String // users: %21, %20
// function_ref print(_:separator:terminator:)
%19 = function_ref @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () // user: %20
%20 = apply %19(%8, %16, %18) : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> ()
destroy_value %18 : $String // id: %21
destroy_value %16 : $String // id: %22
destroy_value %8 : $Array<Any> // id: %23
%24 = metatype $@thin Optional<T>.Type
inject_enum_addr %0 : $*Optional<T>, #Optional.none!enumelt // id: %25
%26 = tuple () // user: %27
return %26 : $() // id: %27
} // end sil function '$s11main_always17UserDefaultsStoreC4read3forxSgSS_tF'// UserDefaultsStore.read(for:)
sil [noinline] [ossa] @$s4main17UserDefaultsStoreC4read3forxSgSS_tF : $@convention(method) <T where T : Decodable, T : Encodable> (@guaranteed String, @guaranteed UserDefaultsStore<T>) -> @out Optional<T> {
// %0 // user: %25
// %1 // user: %3
// %2 // users: %12, %11, %4
bb0(%0 : $*Optional<T>, %1 : @guaranteed $String, %2 : @guaranteed $UserDefaultsStore<T>):
debug_value %1 : $String, let, name "url", argno 1 // id: %3
debug_value %2 : $UserDefaultsStore<T>, let, name "self", argno 2 // id: %4
%5 = integer_literal $Builtin.Word, 1 // user: %7
// function_ref _allocateUninitializedArray<A>(_:)
%6 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %7
%7 = apply %6<Any>(%5) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %8
(%8, %9) = destructure_tuple %7 : $(Array<Any>, Builtin.RawPointer) // users: %23, %20, %10
%10 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*Any // user: %13
%11 = class_method %2 : $UserDefaultsStore<T>, #UserDefaultsStore.userDefaults!getter.1 : <T where T : Decodable, T : Encodable> (UserDefaultsStore<T>) -> () -> UserDefaults, $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %12
%12 = apply %11<T>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %14
%13 = init_existential_addr %10 : $*Any, $UserDefaults // user: %14
store %12 to [init] %13 : $*UserDefaults // id: %14
// function_ref default argument 1 of print(_:separator:terminator:)
%15 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String // user: %16
%16 = apply %15() : $@convention(thin) () -> @owned String // users: %22, %20
// function_ref default argument 2 of print(_:separator:terminator:)
%17 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String // user: %18
%18 = apply %17() : $@convention(thin) () -> @owned String // users: %21, %20
// function_ref print(_:separator:terminator:)
%19 = function_ref @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () // user: %20
%20 = apply %19(%8, %16, %18) : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> ()
destroy_value %18 : $String // id: %21
destroy_value %16 : $String // id: %22
destroy_value %8 : $Array<Any> // id: %23
%24 = metatype $@thin Optional<T>.Type
inject_enum_addr %0 : $*Optional<T>, #Optional.none!enumelt // id: %25
%26 = tuple () // user: %27
return %26 : $() // id: %27
} // end sil function '$s4main17UserDefaultsStoreC4read3forxSgSS_tF'Metadata
Metadata
Assignees
Labels
bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.A deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfThe Swift compiler itselfcrashBug: A crash, i.e., an abnormal termination of softwareBug: A crash, i.e., an abnormal termination of softwareoptimized onlyFlag: An issue whose reproduction requires optimized compilationFlag: An issue whose reproduction requires optimized compilation