Skip to content

Commit f66ef03

Browse files
authored
Add an unreachable marker function. (#1150)
This PR adds a wrapper around `__builtin_unreachable()` (`Builtin.unreachable()` when building the Swift standard library) that we can use in place of `fatalError()`. The benefit is that the generated code size for unreachable paths is significantly reduced. For example, given the following function compiled with `-O`: ```swift @available(*, unavailable) func f() { fatalError("Unreachable") } ``` The compiler currently produces: ```asm sub sp, sp, #0x20 stp x29, x30, [sp, #0x10] add x29, sp, #0x10 mov w8, #0x1 ; =1 str w8, [sp, #0x8] mov w8, #0xc ; =12 str x8, [sp] adrp x0, 0 add x0, x0, #0x6a8 ; "Fatal error" adrp x5, 0 add x5, x5, #0x690 ; "UnreachableTest/S.swift" mov x3, #0x6e55 ; =28245 movk x3, #0x6572, lsl #16 movk x3, #0x6361, lsl #32 movk x3, #0x6168, lsl #48 mov x4, #0x6c62 ; =27746 movk x4, #0x65, lsl #16 movk x4, #0xeb00, lsl #48 mov w1, #0xb ; =11 mov w2, #0x2 ; =2 mov w6, #0x17 ; =23 mov w7, #0x2 ; =2 bl 0x100000680 ; symbol stub for: Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never brk #0x1 ``` But with this change: ```swift @available(*, unavailable) func f() { swt_unreachable() } ``` It instead compiles to simply: ```asm brk #0x1 ``` ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent e7851c5 commit f66ef03

File tree

6 files changed

+32
-17
lines changed

6 files changed

+32
-17
lines changed

Sources/Testing/ExitTests/ExitTest.CapturedValue.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11+
private import _TestingInternals
12+
1113
@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
1214
#if SWT_NO_EXIT_TESTS
1315
@available(*, unavailable, message: "Exit tests are not available on this platform.")
@@ -82,7 +84,7 @@ extension ExitTest {
8284
}
8385
return nil
8486
#else
85-
fatalError("Unsupported")
87+
swt_unreachable()
8688
#endif
8789
}
8890

@@ -101,7 +103,7 @@ extension ExitTest {
101103
_kind = .typeOnly(type)
102104
}
103105
#else
104-
fatalError("Unsupported")
106+
swt_unreachable()
105107
#endif
106108
}
107109
}
@@ -119,7 +121,7 @@ extension ExitTest {
119121
type
120122
}
121123
#else
122-
fatalError("Unsupported")
124+
swt_unreachable()
123125
#endif
124126
}
125127
}

Sources/Testing/ExitTests/ExitTest.Condition.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ extension ExitTest.Condition {
136136
#if !SWT_NO_EXIT_TESTS
137137
Self(.exitCode(exitCode))
138138
#else
139-
fatalError("Unsupported")
139+
swt_unreachable()
140140
#endif
141141
}
142142

@@ -169,7 +169,7 @@ extension ExitTest.Condition {
169169
#if !SWT_NO_EXIT_TESTS
170170
Self(.signal(signal))
171171
#else
172-
fatalError("Unsupported")
172+
swt_unreachable()
173173
#endif
174174
}
175175
}
@@ -192,7 +192,7 @@ extension ExitTest.Condition: CustomStringConvertible {
192192
String(describing: exitStatus)
193193
}
194194
#else
195-
fatalError("Unsupported")
195+
swt_unreachable()
196196
#endif
197197
}
198198
}

Sources/Testing/Issues/Confirmation.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11+
private import _TestingInternals
12+
1113
/// A type that can be used to confirm that an event occurs zero or more times.
1214
public struct Confirmation: Sendable {
1315
/// The number of times ``confirm(count:)`` has been called.
@@ -202,7 +204,7 @@ public func confirmation<R>(
202204
sourceLocation: SourceLocation = #_sourceLocation,
203205
_ body: (Confirmation) async throws -> R
204206
) async rethrows -> R {
205-
fatalError("Unsupported")
207+
swt_unreachable()
206208
}
207209

208210
/// An overload of ``confirmation(_:expectedCount:isolation:sourceLocation:_:)-l3il``
@@ -218,7 +220,7 @@ public func confirmation<R>(
218220
sourceLocation: SourceLocation = #_sourceLocation,
219221
_ body: (Confirmation) async throws -> R
220222
) async rethrows -> R {
221-
fatalError("Unsupported")
223+
swt_unreachable()
222224
}
223225

224226
/// An overload of ``confirmation(_:expectedCount:isolation:sourceLocation:_:)-l3il``
@@ -234,5 +236,5 @@ public func confirmation<R>(
234236
sourceLocation: SourceLocation = #_sourceLocation,
235237
_ body: (Confirmation) async throws -> R
236238
) async rethrows -> R {
237-
fatalError("Unsupported")
239+
swt_unreachable()
238240
}

Sources/Testing/Traits/TimeLimitTrait.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11+
private import _TestingInternals
12+
1113
/// A type that defines a time limit to apply to a test.
1214
///
1315
/// To add this trait to a test, use ``Trait/timeLimit(_:)-4kzjp``.
@@ -126,55 +128,55 @@ extension TimeLimitTrait.Duration {
126128
/// This function is unavailable and is provided for diagnostic purposes only.
127129
@available(*, unavailable, message: "Time limit must be specified in minutes")
128130
public static func seconds(_ seconds: some BinaryInteger) -> Self {
129-
fatalError("Unsupported")
131+
swt_unreachable()
130132
}
131133

132134
/// Construct a time limit duration given a number of seconds.
133135
///
134136
/// This function is unavailable and is provided for diagnostic purposes only.
135137
@available(*, unavailable, message: "Time limit must be specified in minutes")
136138
public static func seconds(_ seconds: Double) -> Self {
137-
fatalError("Unsupported")
139+
swt_unreachable()
138140
}
139141

140142
/// Construct a time limit duration given a number of milliseconds.
141143
///
142144
/// This function is unavailable and is provided for diagnostic purposes only.
143145
@available(*, unavailable, message: "Time limit must be specified in minutes")
144146
public static func milliseconds(_ milliseconds: some BinaryInteger) -> Self {
145-
fatalError("Unsupported")
147+
swt_unreachable()
146148
}
147149

148150
/// Construct a time limit duration given a number of milliseconds.
149151
///
150152
/// This function is unavailable and is provided for diagnostic purposes only.
151153
@available(*, unavailable, message: "Time limit must be specified in minutes")
152154
public static func milliseconds(_ milliseconds: Double) -> Self {
153-
fatalError("Unsupported")
155+
swt_unreachable()
154156
}
155157

156158
/// Construct a time limit duration given a number of microseconds.
157159
///
158160
/// This function is unavailable and is provided for diagnostic purposes only.
159161
@available(*, unavailable, message: "Time limit must be specified in minutes")
160162
public static func microseconds(_ microseconds: some BinaryInteger) -> Self {
161-
fatalError("Unsupported")
163+
swt_unreachable()
162164
}
163165

164166
/// Construct a time limit duration given a number of microseconds.
165167
///
166168
/// This function is unavailable and is provided for diagnostic purposes only.
167169
@available(*, unavailable, message: "Time limit must be specified in minutes")
168170
public static func microseconds(_ microseconds: Double) -> Self {
169-
fatalError("Unsupported")
171+
swt_unreachable()
170172
}
171173

172174
/// Construct a time limit duration given a number of nanoseconds.
173175
///
174176
/// This function is unavailable and is provided for diagnostic purposes only.
175177
@available(*, unavailable, message: "Time limit must be specified in minutes")
176178
public static func nanoseconds(_ nanoseconds: some BinaryInteger) -> Self {
177-
fatalError("Unsupported")
179+
swt_unreachable()
178180
}
179181
}
180182

Sources/_TestingInternals/include/Stubs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616

1717
SWT_ASSUME_NONNULL_BEGIN
1818

19+
/// Mark a code path as unreachable.
20+
///
21+
/// This function is necessary because Swift does not have an equivalent of
22+
/// `__builtin_unreachable()`.
23+
__attribute__((always_inline, noreturn))
24+
static inline void swt_unreachable(void) {
25+
__builtin_unreachable();
26+
}
27+
1928
#if !SWT_NO_FILE_IO
2029
/// The C file handle type.
2130
///

Tests/TestingTests/ExitTestTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ private import _TestingInternals
306306

307307
await Test {
308308
try await #require(processExitsWith: .success) {}
309-
fatalError("Unreachable")
309+
Issue.record("#require(processExitsWith:) should have thrown an error")
310310
}.run(configuration: configuration)
311311
}
312312
}

0 commit comments

Comments
 (0)