mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The `_diagnoseUnavailableCodeReached()` function was introduced in the Swift 5.9 standard library and employs `@backDeployed` to support compilation of binaries that target OS releases aligned with earlier Swift releases. Unfortunately, though, this backdeployment strategy doesn't work well for some unusual build environments. Specifically, in some configurations code may be built with a compiler from a recent Swift toolchain and then linked against the dylibs in an older toolchain. When linking against the older dylibs, the `_diagnoseUnavailableCodeReached()` function does not exist but the `@backDeployed` thunks emitted into the binary reference that function and therefore linking fails. The idea of building with one toolchain and then linking to the dylibs in a different, older toolchain is extremely dubious. However, it exists and for now we need to support it. This PR introduces an alternative `_diagnoseUnavailableCodeReached()` function that is annotated with `@_alwaysEmitIntoClient`. Calls to the AEIC variant are now emitted by the compiler when the deployment target is before Swift 5.9. Once these unusual build environments upgrade and start linking against a Swift 5.9 toolchain or later we can revert all of this. Resolves rdar://119046537
346 lines
9.8 KiB
Swift
346 lines
9.8 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftShims
|
|
|
|
// Implementation Note: this file intentionally uses very LOW-LEVEL
|
|
// CONSTRUCTS, so that assert and fatal may be used liberally in
|
|
// building library abstractions without fear of infinite recursion.
|
|
//
|
|
// FIXME: We could go farther with this simplification, e.g. avoiding
|
|
// UnsafeMutablePointer
|
|
|
|
@_transparent
|
|
public // @testable
|
|
func _isDebugAssertConfiguration() -> Bool {
|
|
// The values for the assert_configuration call are:
|
|
// 0: Debug
|
|
// 1: Release
|
|
// 2: Fast
|
|
return Int32(Builtin.assert_configuration()) == 0
|
|
}
|
|
|
|
@_transparent
|
|
public // @testable, used in _Concurrency executor preconditions
|
|
func _isReleaseAssertConfiguration() -> Bool {
|
|
// The values for the assert_configuration call are:
|
|
// 0: Debug
|
|
// 1: Release
|
|
// 2: Fast
|
|
return Int32(Builtin.assert_configuration()) == 1
|
|
}
|
|
|
|
@_transparent
|
|
public // @testable
|
|
func _isFastAssertConfiguration() -> Bool {
|
|
// The values for the assert_configuration call are:
|
|
// 0: Debug
|
|
// 1: Release
|
|
// 2: Fast
|
|
return Int32(Builtin.assert_configuration()) == 2
|
|
}
|
|
|
|
@_transparent
|
|
public // @testable
|
|
func _isStdlibInternalChecksEnabled() -> Bool {
|
|
#if INTERNAL_CHECKS_ENABLED
|
|
return true
|
|
#else
|
|
return false
|
|
#endif
|
|
}
|
|
|
|
@_transparent
|
|
@_alwaysEmitIntoClient // Introduced in 5.7
|
|
public // @testable
|
|
func _isStdlibDebugChecksEnabled() -> Bool {
|
|
#if SWIFT_STDLIB_ENABLE_DEBUG_PRECONDITIONS_IN_RELEASE
|
|
return !_isFastAssertConfiguration()
|
|
#else
|
|
return _isDebugAssertConfiguration()
|
|
#endif
|
|
}
|
|
|
|
@usableFromInline @_transparent
|
|
internal func _fatalErrorFlags() -> UInt32 {
|
|
// The current flags are:
|
|
// (1 << 0): Report backtrace on fatal error
|
|
#if os(iOS) || os(tvOS) || os(watchOS)
|
|
return 0
|
|
#else
|
|
return _isDebugAssertConfiguration() ? 1 : 0
|
|
#endif
|
|
}
|
|
|
|
/// This function should be used only in the implementation of user-level
|
|
/// assertions.
|
|
///
|
|
/// This function should not be inlined because it is cold and inlining just
|
|
/// bloats code.
|
|
@usableFromInline
|
|
@inline(never)
|
|
@_semantics("programtermination_point")
|
|
internal func _assertionFailure(
|
|
_ prefix: StaticString, _ message: StaticString,
|
|
file: StaticString, line: UInt,
|
|
flags: UInt32
|
|
) -> Never {
|
|
#if !$Embedded
|
|
prefix.withUTF8Buffer {
|
|
(prefix) -> Void in
|
|
message.withUTF8Buffer {
|
|
(message) -> Void in
|
|
file.withUTF8Buffer {
|
|
(file) -> Void in
|
|
_swift_stdlib_reportFatalErrorInFile(
|
|
prefix.baseAddress!, CInt(prefix.count),
|
|
message.baseAddress!, CInt(message.count),
|
|
file.baseAddress!, CInt(file.count), UInt32(line),
|
|
flags)
|
|
Builtin.int_trap()
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
Builtin.int_trap()
|
|
}
|
|
|
|
/// This function should be used only in the implementation of user-level
|
|
/// assertions.
|
|
///
|
|
/// This function should not be inlined because it is cold and inlining just
|
|
/// bloats code.
|
|
@usableFromInline
|
|
@inline(never)
|
|
@_semantics("programtermination_point")
|
|
@_unavailableInEmbedded
|
|
internal func _assertionFailure(
|
|
_ prefix: StaticString, _ message: String,
|
|
file: StaticString, line: UInt,
|
|
flags: UInt32
|
|
) -> Never {
|
|
prefix.withUTF8Buffer {
|
|
(prefix) -> Void in
|
|
var message = message
|
|
message.withUTF8 {
|
|
(messageUTF8) -> Void in
|
|
file.withUTF8Buffer {
|
|
(file) -> Void in
|
|
_swift_stdlib_reportFatalErrorInFile(
|
|
prefix.baseAddress!, CInt(prefix.count),
|
|
messageUTF8.baseAddress!, CInt(messageUTF8.count),
|
|
file.baseAddress!, CInt(file.count), UInt32(line),
|
|
flags)
|
|
}
|
|
}
|
|
}
|
|
|
|
Builtin.int_trap()
|
|
}
|
|
|
|
/// This function should be used only in the implementation of user-level
|
|
/// assertions.
|
|
///
|
|
/// This function should not be inlined because it is cold and inlining just
|
|
/// bloats code.
|
|
@usableFromInline
|
|
@inline(never)
|
|
@_semantics("programtermination_point")
|
|
@_unavailableInEmbedded
|
|
internal func _assertionFailure(
|
|
_ prefix: StaticString, _ message: String,
|
|
flags: UInt32
|
|
) -> Never {
|
|
prefix.withUTF8Buffer {
|
|
(prefix) -> Void in
|
|
var message = message
|
|
message.withUTF8 {
|
|
(messageUTF8) -> Void in
|
|
_swift_stdlib_reportFatalError(
|
|
prefix.baseAddress!, CInt(prefix.count),
|
|
messageUTF8.baseAddress!, CInt(messageUTF8.count),
|
|
flags)
|
|
}
|
|
}
|
|
|
|
Builtin.int_trap()
|
|
}
|
|
|
|
#if $Embedded
|
|
@usableFromInline
|
|
@inline(never)
|
|
@_semantics("programtermination_point")
|
|
internal func _assertionFailure(
|
|
_ prefix: StaticString, _ message: StaticString,
|
|
flags: UInt32
|
|
) -> Never {
|
|
Builtin.int_trap()
|
|
}
|
|
#endif
|
|
|
|
/// This function should be used only in the implementation of stdlib
|
|
/// assertions.
|
|
///
|
|
/// This function should not be inlined because it is cold and it inlining just
|
|
/// bloats code.
|
|
@usableFromInline
|
|
@inline(never)
|
|
@_semantics("programtermination_point")
|
|
internal func _fatalErrorMessage(
|
|
_ prefix: StaticString, _ message: StaticString,
|
|
file: StaticString, line: UInt,
|
|
flags: UInt32
|
|
) -> Never {
|
|
_assertionFailure(prefix, message, file: file, line: line, flags: flags)
|
|
}
|
|
|
|
/// Prints a fatal error message when an unimplemented initializer gets
|
|
/// called by the Objective-C runtime.
|
|
@_transparent
|
|
public // COMPILER_INTRINSIC
|
|
func _unimplementedInitializer(className: StaticString,
|
|
initName: StaticString = #function,
|
|
file: StaticString = #file,
|
|
line: UInt = #line,
|
|
column: UInt = #column
|
|
) -> Never {
|
|
// This function is marked @_transparent so that it is inlined into the caller
|
|
// (the initializer stub), and, depending on the build configuration,
|
|
// redundant parameter values (#file etc.) are eliminated, and don't leak
|
|
// information about the user's source.
|
|
|
|
#if !$Embedded
|
|
if _isDebugAssertConfiguration() {
|
|
className.withUTF8Buffer {
|
|
(className) in
|
|
initName.withUTF8Buffer {
|
|
(initName) in
|
|
file.withUTF8Buffer {
|
|
(file) in
|
|
_swift_stdlib_reportUnimplementedInitializerInFile(
|
|
className.baseAddress!, CInt(className.count),
|
|
initName.baseAddress!, CInt(initName.count),
|
|
file.baseAddress!, CInt(file.count),
|
|
UInt32(line), UInt32(column),
|
|
/*flags:*/ 0)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
className.withUTF8Buffer {
|
|
(className) in
|
|
initName.withUTF8Buffer {
|
|
(initName) in
|
|
_swift_stdlib_reportUnimplementedInitializer(
|
|
className.baseAddress!, CInt(className.count),
|
|
initName.baseAddress!, CInt(initName.count),
|
|
/*flags:*/ 0)
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Builtin.int_trap()
|
|
}
|
|
|
|
#if !$Embedded
|
|
|
|
/// Used to evaluate editor placeholders.
|
|
public // COMPILER_INTRINSIC
|
|
func _undefined<T>(
|
|
_ message: @autoclosure () -> String = String(),
|
|
file: StaticString = #file, line: UInt = #line
|
|
) -> T {
|
|
_assertionFailure("Fatal error", message(), file: file, line: line, flags: 0)
|
|
}
|
|
|
|
#else
|
|
|
|
/// Used to evaluate editor placeholders.
|
|
public // COMPILER_INTRINSIC
|
|
func _undefined<T>(
|
|
_ message: @autoclosure () -> StaticString = StaticString(),
|
|
file: StaticString = #file, line: UInt = #line
|
|
) -> T {
|
|
_assertionFailure("Fatal error", message(), file: file, line: line, flags: 0)
|
|
}
|
|
|
|
#endif
|
|
|
|
/// Called when falling off the end of a switch and the type can be represented
|
|
/// as a raw value.
|
|
///
|
|
/// This function should not be inlined because it is cold and inlining just
|
|
/// bloats code. It doesn't take a source location because it's most important
|
|
/// in release builds anyway (old apps that are run on new OSs).
|
|
@inline(never)
|
|
@usableFromInline // COMPILER_INTRINSIC
|
|
internal func _diagnoseUnexpectedEnumCaseValue<SwitchedValue, RawValue>(
|
|
type: SwitchedValue.Type,
|
|
rawValue: RawValue
|
|
) -> Never {
|
|
#if !$Embedded
|
|
_assertionFailure("Fatal error",
|
|
"unexpected enum case '\(type)(rawValue: \(rawValue))'",
|
|
flags: _fatalErrorFlags())
|
|
#else
|
|
Builtin.int_trap()
|
|
#endif
|
|
}
|
|
|
|
/// Called when falling off the end of a switch and the value is not safe to
|
|
/// print.
|
|
///
|
|
/// This function should not be inlined because it is cold and inlining just
|
|
/// bloats code. It doesn't take a source location because it's most important
|
|
/// in release builds anyway (old apps that are run on new OSs).
|
|
@inline(never)
|
|
@usableFromInline // COMPILER_INTRINSIC
|
|
internal func _diagnoseUnexpectedEnumCase<SwitchedValue>(
|
|
type: SwitchedValue.Type
|
|
) -> Never {
|
|
#if !$Embedded
|
|
_assertionFailure(
|
|
"Fatal error",
|
|
"unexpected enum case while switching on value of type '\(type)'",
|
|
flags: _fatalErrorFlags())
|
|
#else
|
|
Builtin.int_trap()
|
|
#endif
|
|
}
|
|
|
|
/// Called when a function marked `unavailable` with `@available` is invoked
|
|
/// and the module containing the unavailable function was compiled with
|
|
/// `-unavailable-decl-optimization=stub`.
|
|
///
|
|
/// This function should not be inlined because it is cold and inlining just
|
|
/// bloats code.
|
|
@backDeployed(before: SwiftStdlib 5.9)
|
|
@inline(never)
|
|
@_semantics("unavailable_code_reached")
|
|
@usableFromInline // COMPILER_INTRINSIC
|
|
internal func _diagnoseUnavailableCodeReached() -> Never {
|
|
_diagnoseUnavailableCodeReached_aeic()
|
|
}
|
|
|
|
// FIXME: Remove this with rdar://119892482
|
|
/// An `@_alwaysEmitIntoClient` variant of `_diagnoseUnavailableCodeReached()`.
|
|
/// This is temporarily needed by the compiler to reference from back deployed
|
|
/// clients.
|
|
@_alwaysEmitIntoClient
|
|
@inline(never)
|
|
@_semantics("unavailable_code_reached")
|
|
internal func _diagnoseUnavailableCodeReached_aeic() -> Never {
|
|
_assertionFailure(
|
|
"Fatal error", "Unavailable code reached", flags: _fatalErrorFlags())
|
|
}
|