You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a type argument to the ABI accessor function signature. (#939)
We access the test content record section directly instead of using some
(non-existent) type-safe Swift API. On some platforms (Darwin in
particular), it is possible for two copies of the testing library to be
loaded at runtime, and for them to have incompatible definitions of
types like `Test`. That means one library's `@Test` macro could produce
a test content record that is then read by the other library's discovery
logic, resulting in a stack smash or worse.
We can resolve this issue by adding a `type` argument to the accessor
function we define for test content records; the body of the accessor
can then compare the value of this argument against the expected Swift
type of its result and, if they don't match, bail early.
The new argument is defined as a pointer _to_ a Swift type rather than
as a Swift type directly because `@convention(c)` functions cannot
directly reference Swift types. The value of the new argument can be
safely ignored if the type of the test content record's value is and
always has been `@frozen`.
### 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.
@@ -105,42 +117,59 @@ If `accessor` is `nil`, the test content record is ignored. The testing library
105
117
may, in the future, define record kinds that do not provide an accessor function
106
118
(that is, they represent pure compile-time information only.)
107
119
108
-
The second argument to this function, `hint`, is an optional input that can be
120
+
The second argument to this function, `type`, is a pointer to the type[^mightNotBeSwift]
121
+
(not a bitcast Swift type) of the value expected to be written to `outValue`.
122
+
This argument helps to prevent memory corruption if two copies of Swift Testing
123
+
or a third-party library are inadvertently loaded into the same process. If the
124
+
value at `type` does not match the test content record's expected type, the
125
+
accessor function must return `false` and must not modify `outValue`.
126
+
127
+
<!-- TODO: discuss this argument's value in Embedded Swift (no metatypes) -->
128
+
129
+
[^mightNotBeSwift]: Although this document primarily deals with Swift, the test
130
+
content record section is generally language-agnostic. The use of languages
131
+
other than Swift is beyond the scope of this document. With that in mind, it
132
+
is _technically_ feasible for a test content accessor to be written in (for
133
+
example) C++, expect the `type` argument to point to a C++ value of type
134
+
`std::type_info`, and write a C++ class instance to `outValue`.
135
+
136
+
The third argument to this function, `hint`, is an optional input that can be
109
137
passed to help the accessor function determine if its corresponding test content
110
138
record matches what the caller is looking for. If the caller passes `nil` as the
111
139
`hint` argument, the accessor behaves as if it matched (that is, no additional
112
140
filtering is performed.)
113
141
114
-
The concrete Swift type of the value written to `outValue` and the value pointed
115
-
to by `hint` depend on the kind of record:
142
+
The concrete Swift type of the value written to `outValue`, the type pointed to
143
+
by `type`, and the value pointed to by `hint` depend on the kind of record:
116
144
117
145
- For test or suite declarations (kind `0x74657374`), the accessor produces an
118
-
asynchronous Swift function that returns an instance of `Test`:
146
+
asynchronous Swift function[^notAccessorSignature] that returns an instance of
147
+
`Testing.Test`:
119
148
120
149
```swift
121
150
@Sendable () async -> Test
122
151
```
123
152
124
-
This signature is not the signature of `accessor`, but of the Swift function
125
-
reference it writes to `outValue`. This level of indirection is necessary
126
-
because loading a test or suite declaration is an asynchronous operation, but
127
-
C functions cannot be `async`.
153
+
[^notAccessorSignature]: This signature is not the signature of `accessor`,
154
+
but of the Swift function reference it writes to `outValue`. This level of
155
+
indirection is necessary because loading a test or suite declaration is an
156
+
asynchronous operation, but C functions cannot be `async`.
128
157
129
158
Test content records of this kind do not specify a type for `hint`. Always
130
159
pass `nil`.
131
160
132
161
- For exit test declarations (kind `0x65786974`), the accessor produces a
133
-
structure describing the exit test (of type `__ExitTest`.)
162
+
structure describing the exit test (of type `Testing.__ExitTest`.)
134
163
135
-
Test content records of this kind accept a `hint` of type `__ExitTest.ID`.
164
+
Test content records of this kind accept a `hint` of type `Testing.__ExitTest.ID`.
136
165
They only produce a result if they represent an exit test declared with the
137
166
same ID (or if `hint` is `nil`.)
138
167
139
168
> [!WARNING]
140
169
> Calling code should use [`withUnsafeTemporaryAllocation(of:capacity:_:)`](https://developer.apple.com/documentation/swift/withunsafetemporaryallocation(of:capacity:_:))
141
-
> and [`withUnsafePointer(to:_:)`](https://developer.apple.com/documentation/swift/withunsafepointer(to:_:)-35wrn),
142
-
> respectively, to ensure the pointers passed to `accessor` are large enough and
143
-
> are well-aligned. If they are not large enough to contain values of the
0 commit comments