mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Simplify assertion machinery in the standard library.
This change includes a number of simplifications that allow us to eliminate the type checker hack that specifically tries AssertString. Doing so provides a 25% speedup in the test/stdlib/ArrayNew.swift test (which is type-checker bound). The specific simplifications here: - User-level assert/precondition/preconditionalFailure/assertionFailer/fatalError always take an autoclosure producing a String, eliminating the need for the StaticString/AssertString dance. - Standard-library internal _precondition/_sanityCheck/etc. always take a StaticString. When we want to improve the diagnostics in the standard library, we can provide a separate overload or differently-named function. - Remove AssertString, AssertStringType, StaticStringType, which are no longer used or needed - Remove the AssertString hack from the compiler - Remove the "BooleanType" overloads of these functions, because their usefuless left when we stopped making optional types conform to BooleanType (sorry, should have been a separate patch). Swift SVN r22139
This commit is contained in:
@@ -16,25 +16,6 @@
|
||||
/// release or fast mode these checks are disabled. This means they may have no
|
||||
/// effect on program semantics, depending on the assert configuration.
|
||||
|
||||
% for (generic_params, closure_type, message_param, message_eval) in [
|
||||
% ( '',
|
||||
% 'Bool',
|
||||
% '_ message: StaticString = StaticString()',
|
||||
% 'message' ),
|
||||
% ( '<Str : AssertStringType>',
|
||||
% 'Bool',
|
||||
% 'message: @autoclosure () -> Str',
|
||||
% 'message().stringValue' ),
|
||||
% ( '<T : BooleanType, Str : StaticStringType>',
|
||||
% 'T',
|
||||
% 'message: @autoclosure () -> Str',
|
||||
% 'message().stringValue' ),
|
||||
% ( '<T : BooleanType, Str : AssertStringType>',
|
||||
% 'T',
|
||||
% 'message: @autoclosure () -> Str',
|
||||
% 'message().stringValue' ),
|
||||
% ]:
|
||||
|
||||
/// Traditional C-style assert with an optional message.
|
||||
///
|
||||
/// When assertions are enabled and `condition` is false, stop program
|
||||
@@ -45,14 +26,15 @@
|
||||
/// When assertions are turned off, the optimizer can assume that the
|
||||
/// `condition` is true.
|
||||
@transparent
|
||||
public func assert${generic_params}(
|
||||
condition: @autoclosure () -> ${closure_type}, ${message_param},
|
||||
public func assert(
|
||||
condition: @autoclosure () -> Bool,
|
||||
_ message: @autoclosure () -> String = String(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
// Only assert in debug mode.
|
||||
if _isDebugAssertConfiguration() {
|
||||
if !_branchHint(condition(), true) {
|
||||
_assertionFailed("assertion failed", ${message_eval}, file, line)
|
||||
_assertionFailed("assertion failed", message(), file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,14 +45,15 @@ public func assert${generic_params}(
|
||||
///
|
||||
/// In unchecked mode the optimizer can assume that the `condition` is true.
|
||||
@transparent
|
||||
public func precondition${generic_params}(
|
||||
condition: @autoclosure () -> ${closure_type}, ${message_param},
|
||||
public func precondition(
|
||||
condition: @autoclosure () -> Bool,
|
||||
_ message: @autoclosure () -> String = String(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
// Only check in debug and release mode. In release mode just trap.
|
||||
if _isDebugAssertConfiguration() {
|
||||
if !_branchHint(condition(), true) {
|
||||
_assertionFailed("precondition failed", ${message_eval}, file, line)
|
||||
_assertionFailed("precondition failed", message(), file, line)
|
||||
}
|
||||
} else if _isReleaseAssertConfiguration() {
|
||||
let error = !condition()
|
||||
@@ -78,25 +61,15 @@ public func precondition${generic_params}(
|
||||
}
|
||||
}
|
||||
|
||||
% end
|
||||
|
||||
% for (generic_params, message_param, message_eval) in [
|
||||
% ( '',
|
||||
% 'message: StaticString',
|
||||
% 'message' ),
|
||||
% ( '<Str : AssertStringType>',
|
||||
% 'message: Str',
|
||||
% 'message.stringValue' ),
|
||||
% ]:
|
||||
|
||||
/// A fatal error occurred and program execution should stop in debug mode. In
|
||||
/// optimized builds this is a noop.
|
||||
@transparent @noreturn
|
||||
public func assertionFailure${generic_params}(
|
||||
${message_param}, file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
public func assertionFailure(
|
||||
_ message: @autoclosure () -> String = String(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
if _isDebugAssertConfiguration() {
|
||||
_assertionFailed("fatal error", ${message_eval}, file, line)
|
||||
_assertionFailed("fatal error", message(), file, line)
|
||||
}
|
||||
_conditionallyUnreachable()
|
||||
}
|
||||
@@ -105,12 +78,13 @@ public func assertionFailure${generic_params}(
|
||||
/// in optimized mode. In unchecked builds this is a noop, but the
|
||||
/// optimizer can still assume that the call is unreachable.
|
||||
@transparent @noreturn
|
||||
public func preconditionFailure${generic_params}(
|
||||
${message_param}, file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
public func preconditionFailure(
|
||||
_ message: @autoclosure () -> String = String(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
// Only check in debug and release mode. In release mode just trap.
|
||||
if _isDebugAssertConfiguration() {
|
||||
_assertionFailed("fatal error", ${message_eval}, file, line)
|
||||
_assertionFailed("fatal error", message(), file, line)
|
||||
} else if _isReleaseAssertConfiguration() {
|
||||
Builtin.int_trap()
|
||||
}
|
||||
@@ -120,14 +94,13 @@ public func preconditionFailure${generic_params}(
|
||||
/// A fatal error occurred and program execution should stop in debug,
|
||||
/// optimized and unchecked modes.
|
||||
@transparent @noreturn
|
||||
public func fatalError${generic_params}(
|
||||
${message_param}, file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
public func fatalError(
|
||||
_ message: @autoclosure () -> String = String(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
_assertionFailed("fatal error", ${message_eval}, file, line)
|
||||
_assertionFailed("fatal error", message(), file, line)
|
||||
}
|
||||
|
||||
% end
|
||||
|
||||
/// Library precondition checks
|
||||
///
|
||||
/// Library precondition checks are enabled in debug mode and release mode. When
|
||||
@@ -149,21 +122,6 @@ public func _precondition(
|
||||
Builtin.condfail(error.value)
|
||||
}
|
||||
}
|
||||
@transparent
|
||||
public func _precondition<T : BooleanType>(
|
||||
condition: @autoclosure () -> T, _ message: StaticString = StaticString(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
// Only check in debug and release mode. In release mode just trap.
|
||||
if _isDebugAssertConfiguration() {
|
||||
if !_branchHint(condition(), true) {
|
||||
_fatalErrorMessage("fatal error", message, file, line)
|
||||
}
|
||||
} else if _isReleaseAssertConfiguration() {
|
||||
let error = !condition().boolValue
|
||||
Builtin.condfail(error.value)
|
||||
}
|
||||
}
|
||||
|
||||
@transparent @noreturn
|
||||
public func _preconditionFailure(
|
||||
@@ -215,19 +173,6 @@ public func _debugPrecondition(
|
||||
}
|
||||
}
|
||||
|
||||
@transparent
|
||||
public func _debugPrecondition<T : BooleanType>(
|
||||
condition: @autoclosure () -> T, _ message: StaticString = StaticString(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
// Only check in debug mode.
|
||||
if _isDebugAssertConfiguration() {
|
||||
if !_branchHint(condition(), true) {
|
||||
_fatalErrorMessage("fatal error", message, file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@transparent @noreturn
|
||||
public func _debugPreconditionFailure(
|
||||
_ message: StaticString = StaticString(),
|
||||
@@ -256,18 +201,6 @@ public func _sanityCheck(
|
||||
#endif
|
||||
}
|
||||
|
||||
@transparent
|
||||
public func _sanityCheck<T : BooleanType>(
|
||||
condition: @autoclosure () -> T, _ message: StaticString = StaticString(),
|
||||
file: StaticString = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
#if INTERNAL_CHECKS_ENABLED
|
||||
if !_branchHint(condition(), true) {
|
||||
_fatalErrorMessage("fatal error", message, file, line)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@transparent @noreturn
|
||||
public func _sanityCheckFailure(
|
||||
_ message: StaticString = StaticString(),
|
||||
|
||||
@@ -10,41 +10,22 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Instances of conforming types can be created from string literals
|
||||
/// and are used in assertions and precondition checks.
|
||||
///
|
||||
/// This protocol exists only for directing overload resolution and
|
||||
/// string literal type deduction in assertions.
|
||||
///
|
||||
/// See also: `StaticStringType`, `StaticString`, `AssertString`
|
||||
public protocol AssertStringType
|
||||
: UnicodeScalarLiteralConvertible,
|
||||
ExtendedGraphemeClusterLiteralConvertible,
|
||||
StringLiteralConvertible {
|
||||
/// The textual value, as a `String`
|
||||
var stringValue: String { get }
|
||||
}
|
||||
|
||||
/// This protocol exists only for directing overload resolution and
|
||||
/// string literal type deduction in assertions.
|
||||
///
|
||||
/// See also: `AssertStringType`, `StaticString`, `AssertString`
|
||||
public protocol StaticStringType : AssertStringType {}
|
||||
|
||||
// Implementation Note: Because StaticString is used in the
|
||||
// implementation of assert() and fatal(), we keep it extremely close
|
||||
// to the bare metal. In particular, because we store only Builtin
|
||||
// types, we are guaranteed that no assertions are involved in its
|
||||
// construction. This feature is crucial for preventing infinite
|
||||
// recursion even in non-asserting cases.
|
||||
// implementation of _precondition(), _fatalErrorMessage(), etc., we
|
||||
// keep it extremely close to the bare metal. In particular, because
|
||||
// we store only Builtin types, we are guaranteed that no assertions
|
||||
// are involved in its construction. This feature is crucial for
|
||||
// preventing infinite recursion even in non-asserting cases.
|
||||
|
||||
/// An extremely simple string designed to represent something
|
||||
/// "statically knowable".
|
||||
public struct StaticString
|
||||
: StaticStringType,
|
||||
_BuiltinUnicodeScalarLiteralConvertible,
|
||||
: _BuiltinUnicodeScalarLiteralConvertible,
|
||||
_BuiltinExtendedGraphemeClusterLiteralConvertible,
|
||||
_BuiltinStringLiteralConvertible,
|
||||
UnicodeScalarLiteralConvertible,
|
||||
ExtendedGraphemeClusterLiteralConvertible,
|
||||
StringLiteralConvertible,
|
||||
Printable,
|
||||
DebugPrintable {
|
||||
|
||||
@@ -202,65 +183,3 @@ public struct StaticString
|
||||
return self.stringValue.debugDescription
|
||||
}
|
||||
}
|
||||
|
||||
/// A String-like type that can be constructed from string interpolation, and
|
||||
/// is considered less specific than `StaticString` in overload resolution.
|
||||
public struct AssertString
|
||||
: AssertStringType, StringInterpolationConvertible, Printable,
|
||||
DebugPrintable {
|
||||
public var stringValue: String
|
||||
|
||||
@transparent
|
||||
public init() {
|
||||
self.stringValue = ""
|
||||
}
|
||||
|
||||
@transparent
|
||||
public init(_ value: String) {
|
||||
self.stringValue = value
|
||||
}
|
||||
|
||||
@effects(readonly)
|
||||
@transparent
|
||||
public init(unicodeScalarLiteral value: String) {
|
||||
self.stringValue = value
|
||||
}
|
||||
|
||||
@effects(readonly)
|
||||
@transparent
|
||||
public init(extendedGraphemeClusterLiteral value: String) {
|
||||
self.stringValue = value
|
||||
}
|
||||
|
||||
@effects(readonly)
|
||||
@transparent
|
||||
public init(stringLiteral value: String) {
|
||||
self.stringValue = value
|
||||
}
|
||||
|
||||
public static func convertFromStringInterpolation(
|
||||
strings: AssertString...
|
||||
) -> AssertString {
|
||||
var result = String()
|
||||
for str in strings {
|
||||
result += str.stringValue
|
||||
}
|
||||
return AssertString(result)
|
||||
}
|
||||
|
||||
@transparent
|
||||
public static func convertFromStringInterpolationSegment<T>(
|
||||
expr: T
|
||||
) -> AssertString {
|
||||
return AssertString(String.convertFromStringInterpolationSegment(expr))
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self.stringValue
|
||||
}
|
||||
|
||||
public var debugDescription: String {
|
||||
return self.stringValue.debugDescription
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -458,7 +458,12 @@ extension String : CollectionType {
|
||||
|
||||
@availability(*, unavailable, message="cannot subscript String with an Int")
|
||||
public subscript(i: Int) -> Character {
|
||||
fatalError("cannot subscript String with an Int")
|
||||
_fatalErrorMessage(
|
||||
"fatal error",
|
||||
"cannot subscript String with an Int",
|
||||
__FILE__,
|
||||
__LINE__
|
||||
)
|
||||
}
|
||||
|
||||
public func generate() -> IndexingGenerator<String> {
|
||||
|
||||
Reference in New Issue
Block a user