Adopt @unsafe throughout the standard library

Annotate all of the `Unsafe*` types and `unsafe` functions in the standard
library (including concurrency, synchronization, etc.) as `@unsafe`. Add a
few tests to ensure that we detect uses of these types in clients that
have disabled unsafe code.
This commit is contained in:
Doug Gregor
2024-07-23 09:43:03 -07:00
parent 39f1e97bf9
commit 8378562e12
18 changed files with 48 additions and 1 deletions

View File

@@ -628,6 +628,7 @@ function(_compile_swift_files
list(APPEND swift_flags "-enable-experimental-feature" "NoncopyableGenerics2")
list(APPEND swift_flags "-enable-experimental-feature" "SuppressedAssociatedTypes")
list(APPEND swift_flags "-enable-experimental-feature" "SE427NoInferenceOnExtension")
list(APPEND swift_flags "-enable-experimental-feature" "AllowUnsafeAttribute")
if(SWIFT_ENABLE_EXPERIMENTAL_NONESCAPABLE_TYPES)
list(APPEND swift_flags "-enable-experimental-feature" "NonescapableTypes")

View File

@@ -64,7 +64,7 @@ endif()
list(APPEND SWIFT_RUNTIME_CONCURRENCY_SWIFT_FLAGS
"-enable-experimental-feature"
"IsolatedAny"
)
)
list(APPEND SWIFT_RUNTIME_CONCURRENCY_C_FLAGS
"-D__STDC_WANT_LIB_EXT1__=1")

View File

@@ -480,6 +480,7 @@ extension JobPriority: Comparable {
/// without making other changes.
@available(SwiftStdlib 5.1, *)
@frozen
@unsafe
public struct UnsafeContinuation<T, E: Error>: Sendable {
@usableFromInline internal var context: Builtin.RawUnsafeContinuation
@@ -683,6 +684,7 @@ internal func _resumeUnsafeThrowingContinuationWithError<T>(
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@unsafe
public func withUnsafeContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
_ fn: (UnsafeContinuation<T, Never>) -> Void
@@ -719,6 +721,7 @@ public func withUnsafeContinuation<T>(
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@unsafe
public func withUnsafeThrowingContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
_ fn: (UnsafeContinuation<T, Error>) -> Void

View File

@@ -1033,6 +1033,7 @@ public func withUnsafeCurrentTask<T>(body: (UnsafeCurrentTask?) async throws ->
/// [concurrency]: https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html
/// [tspl]: https://docs.swift.org/swift-book/
@available(SwiftStdlib 5.1, *)
@unsafe
public struct UnsafeCurrentTask {
internal let _task: Builtin.NativeObject

View File

@@ -175,6 +175,7 @@ extension _MutexHandle {
@available(SwiftStdlib 6.0, *)
@_alwaysEmitIntoClient
@_transparent
@unsafe
public borrowing func unsafeLock() {
_lock()
}
@@ -182,6 +183,7 @@ extension _MutexHandle {
@available(SwiftStdlib 6.0, *)
@_alwaysEmitIntoClient
@_transparent
@unsafe
public borrowing func unsafeTryLock() -> Bool {
_tryLock()
}
@@ -189,6 +191,7 @@ extension _MutexHandle {
@available(SwiftStdlib 6.0, *)
@_alwaysEmitIntoClient
@_transparent
@unsafe
public borrowing func unsafeUnlock() {
_unlock()
}

View File

@@ -29,6 +29,7 @@ public struct VolatileMappedRegister<Pointee> {
let _rawPointer: Builtin.RawPointer
@_transparent
@unsafe
public init(unsafeBitPattern: UInt) {
self._rawPointer = Builtin.inttoptr_Word(unsafeBitPattern._builtinWordValue)
}

View File

@@ -91,6 +91,7 @@ func _canBeClass<T>(_: T.Type) -> Int8 {
/// Returns: A new instance of type `U`, cast from `x`.
@inlinable // unsafe-performance
@_transparent
@unsafe
public func unsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U {
_precondition(MemoryLayout<T>.size == MemoryLayout<U>.size,
"Can't unsafeBitCast between types of different sizes")
@@ -242,6 +243,7 @@ internal func _isClassOrObjCExistential<T>(_ x: T.Type) -> Bool {
/// be either a class or a class protocol. Either T, U, or both may be
/// optional references.
@_transparent
@unsafe
public func _unsafeReferenceCast<T, U>(_ x: T, to: U.Type) -> U {
return Builtin.castReference(x)
}
@@ -265,12 +267,14 @@ public func _unsafeReferenceCast<T, U>(_ x: T, to: U.Type) -> U {
/// - type: The type `T` to which `x` is cast.
/// - Returns: The instance `x`, cast to type `T`.
@_transparent
@unsafe
public func unsafeDowncast<T: AnyObject>(_ x: AnyObject, to type: T.Type) -> T {
_debugPrecondition(x is T, "invalid unsafeDowncast")
return Builtin.castReference(x)
}
@_transparent
@unsafe
public func _unsafeUncheckedDowncast<T: AnyObject>(_ x: AnyObject, to type: T.Type) -> T {
_internalInvariant(x is T, "invalid unsafeDowncast")
return Builtin.castReference(x)
@@ -331,6 +335,7 @@ public func _onFastPath() {
// Optimizer hint that the condition is true. The condition is unchecked.
// The builtin acts as an opaque instruction with side-effects.
@usableFromInline @_transparent
@unsafe
func _uncheckedUnsafeAssume(_ condition: Bool) {
_ = Builtin.assume_Int1(condition._value)
}

View File

@@ -150,6 +150,7 @@ public typealias CBool = Bool
/// Opaque pointers are used to represent C pointers to types that
/// cannot be represented in Swift, such as incomplete struct types.
@frozen
@unsafe
public struct OpaquePointer {
@usableFromInline
internal var _rawValue: Builtin.RawPointer

View File

@@ -155,6 +155,7 @@ public func _getTypeByMangledNameInContext(
/// Prevents performance diagnostics in the passed closure.
@_alwaysEmitIntoClient
@_semantics("no_performance_analysis")
@unsafe
public func _unsafePerformance<T>(_ c: () -> T) -> T {
return c()
}

View File

@@ -338,6 +338,7 @@ extension Optional {
/// will never be equal to `nil` and only after you've tried using the
/// postfix `!` operator.
@inlinable
@unsafe
public var unsafelyUnwrapped: Wrapped {
@inline(__always)
get {

View File

@@ -15,6 +15,7 @@
/// When you use this type, you become partially responsible for
/// keeping the object alive.
@frozen
@unsafe
public struct Unmanaged<Instance: AnyObject> {
@usableFromInline
internal unowned(unsafe) var _value: Instance

View File

@@ -30,6 +30,7 @@
/// collection with an `${Self}` instance copies the instances out of the
/// referenced memory and into the new collection.
@frozen // unsafe-performance
@unsafe
public struct Unsafe${Mutable}BufferPointer<Element: ~Copyable>: Copyable {
@usableFromInline

View File

@@ -204,6 +204,7 @@
/// let numberPointer = UnsafePointer<Int>(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen // unsafe-performance
@unsafe
public struct UnsafePointer<Pointee: ~Copyable>: Copyable {
/// The underlying raw (untyped) pointer.
@@ -655,6 +656,7 @@ extension UnsafePointer where Pointee: ~Copyable {
/// let numberPointer = UnsafeMutablePointer<Int>(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen // unsafe-performance
@unsafe
public struct UnsafeMutablePointer<Pointee: ~Copyable>: Copyable {
/// The underlying raw (untyped) pointer.
@_preInverseGenerics

View File

@@ -95,6 +95,7 @@
/// destBytes[0..<n] = someBytes[n..<(n + n)]
% end
@frozen
@unsafe
public struct Unsafe${Mutable}RawBufferPointer {
@usableFromInline
internal let _position, _end: Unsafe${Mutable}RawPointer?

View File

@@ -168,6 +168,7 @@
/// let numberPointer = UnsafeRawPointer(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen
@unsafe
public struct UnsafeRawPointer: _Pointer {
public typealias Pointee = UInt8
@@ -773,6 +774,7 @@ extension UnsafeRawPointer {
/// let numberPointer = UnsafeMutableRawPointer(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen
@unsafe
public struct UnsafeMutableRawPointer: _Pointer {
public typealias Pointee = UInt8

View File

@@ -3,3 +3,5 @@ void unsafe_c_function(void) __attribute__((swift_attr("unsafe")));
struct __attribute__((swift_attr("unsafe"))) UnsafeType {
int field;
};
void print_ints(int *ptr, int count);

View File

@@ -4,4 +4,8 @@ import unsafe_decls
func testUnsafe(_ ut: UnsafeType) { // expected-error{{reference to unsafe struct 'UnsafeType'}}
unsafe_c_function() // expected-error{{call to unsafe global function 'unsafe_c_function'}}
var array: [CInt] = [1, 2, 3, 4, 5]
print_ints(&array, CInt(array.count))
// expected-error@-1{{call to global function 'print_ints' involves unsafe type 'UnsafeMutablePointer<Int32>'}}
}

View File

@@ -0,0 +1,17 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature AllowUnsafeAttribute -enable-experimental-feature DisallowUnsafe
// Make sure everything compiles without error when unsafe code is allowed.
// RUN: %target-swift-frontend -typecheck -enable-experimental-feature AllowUnsafeAttribute %s
func test(
x: OpaquePointer, // expected-error{{reference to unsafe struct 'OpaquePointer'}}
other: UnsafeMutablePointer<Int> // expected-error{{reference to unsafe generic struct 'UnsafeMutablePointer'}}
) {
var array = [1, 2, 3]
// expected-error@+1{{call to instance method 'withUnsafeBufferPointer' involves unsafe type 'UnsafeBufferPointer<Int>'}}
array.withUnsafeBufferPointer{ buffer in // expected-note{{'buffer' declared here}}
print(buffer) // expected-error{{reference to parameter 'buffer' involves unsafe type 'UnsafeBufferPointer<Int>'}}
}
array.append(4)
_ = array
}