Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b0cc9ad
Allow Builtin.Take -> ~Escapable
atrick Sep 13, 2025
56611c3
Allow Builtin.Load -> ~Escapable
atrick Oct 3, 2025
6233108
[stdlib] Unsafe[Mutable]RawPointer aligment for ~Escapable types
atrick Oct 3, 2025
9305c97
[stdlib] UnsafeMutableRawPointer: load[Unaligned] ~Escapable
atrick Oct 5, 2025
07b326f
[stdlib] UnsafeMutableRawPointer: storeBytes of ~Escapable
atrick Oct 6, 2025
74aab42
Test UnsafeRawPointer load of ~Escapable
atrick Oct 5, 2025
53cc167
Test UnsafeRawPointer store of ~Escapable
atrick Oct 6, 2025
18ba2c8
Update stability-stdlib-source-base.swift.expected for URP.load
atrick Oct 6, 2025
1a1f49e
SwiftCompilerSources: bridge Type.unsafePointerElementType
atrick Sep 16, 2025
5d8d458
SwiftCompilerSources: bridge Function.isAddressor()
atrick Oct 3, 2025
0f5c158
LifetimeDependenceDefUseWalker: store to unsafeMutableAddress
atrick Oct 3, 2025
71ff1cc
[stdlib] Unsafe[Mutable]Pointer: make Pointee ~Escapable
atrick Sep 16, 2025
f6eb945
[stdlib] Unsafe[Mutable]BufferPointer: make Pointee ~Escapable
atrick Sep 17, 2025
52dea90
[stdlib] adjust 'unsafe' annotation for UnsafePointer<T: ~Escapable>
atrick Sep 16, 2025
4c00cf9
[stdlib] UnsafeMutablePointer: '@'lifetime for move & subscript
atrick Sep 16, 2025
c61bd80
[stdlib] Fix UnsafeMutablePointer.initialize() for lifetimes.
atrick Sep 16, 2025
5efbb03
[stdlib] UnsafeMutableBufferPointer: '@'lifetime for move & subscript
atrick Sep 17, 2025
ca43397
[stdlib] Unsafe[Mutable]RawPointer APIs for UP<T: ~Escapable>
atrick Oct 3, 2025
f6168ef
[stdlib] UnsafeMutableRawPointer: '@'lifetime for initializeMemory
atrick Oct 3, 2025
b14cd1f
Test UnsafePointer<~Escapable> lifetimes
atrick Sep 26, 2025
813fe4c
SIL test: update FileCheck output for ~Escapable types
atrick Sep 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions SwiftCompilerSources/Sources/AST/Type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,8 @@ extension TypeProperties {
public var isDynamicSelf: Bool { rawType.bridged.isDynamicSelf()}
public var isBox: Bool { rawType.bridged.isBox() }

/// True if this is the type which represents an integer literal used in a type position.
/// For example `N` in `struct T<let N: Int> {}`
public var isInteger: Bool { rawType.bridged.isInteger() }

public var canBeClass: Type.TraitResult { rawType.bridged.canBeClass().result }

/// True if this the nominal type `Swift.Optional`.
public var isOptional: Bool { rawType.bridged.isOptional() }

/// True if this type is a value type (struct/enum) that defines a `deinit`.
public var isValueTypeWithDeinit: Bool {
if let nominal = nominal, nominal.valueTypeDestructor != nil {
Expand All @@ -164,6 +157,34 @@ extension TypeProperties {
return false
}

//===--------------------------------------------------------------------===//
// Checks for stdlib types
//===--------------------------------------------------------------------===//

/// True if this is the type which represents an integer literal used in a type position.
/// For example `N` in `struct T<let N: Int> {}`
public var isInteger: Bool { rawType.bridged.isInteger() }

/// True if this the nominal type `Swift.Optional`.
public var isOptional: Bool { rawType.bridged.isOptional() }

/// A non-nil result type implies isUnsafe[Raw][Mutable]Pointer. A raw
/// pointer has a `void` element type.
public var unsafePointerElementType: Type? {
Type(bridgedOrNil: rawType.bridged.getAnyPointerElementType())
}

public var isAnyUnsafePointer: Bool {
unsafePointerElementType != nil
}

public var isAnyUnsafeBufferPointer: Bool {
rawType.bridged.isUnsafeBufferPointerType()
|| rawType.bridged.isUnsafeMutableBufferPointerType()
|| rawType.bridged.isUnsafeRawBufferPointerType()
|| rawType.bridged.isUnsafeMutableRawBufferPointerType()
}

//===--------------------------------------------------------------------===//
// Properties of lowered `SILFunctionType`s
//===--------------------------------------------------------------------===//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,25 @@ extension LifetimeDependenceDefUseWalker {
break
case .yield:
return storeToYieldDependence(address: address, of: operand)
case .global, .class, .tail, .storeBorrow, .pointer, .index, .unidentified:
case let .pointer(p2a):
if let base = p2a.isResultOfUnsafeAddressor() {
let selfValue = base.value
if selfValue.type.isAddress {
// Normally an unsafeMutableAddressor is mutating, so this is the common case (address-type
// 'selfValue'). Treat the store to this pointer-to-address projection just like any store to the local
// variable holding 'selfValue'.
return visitStoredUses(of: operand, into: selfValue)
}
// A nonmutating unsafeMutableAddress is only expected to happen for UnsafeMutable[Buffer]Pointer, in which case
// 'selfValue' is trivial so all uses can safely by ignored.
if selfValue.type.isTrivial(in: selfValue.parentFunction) {
return .continueWalk
}
// Otherwise conservatively a store to indirect memory as escaping.
return escapingDependence(on: operand)
}
break
case .global, .class, .tail, .storeBorrow, .index, .unidentified:
// An address produced by .storeBorrow should never be stored into.
//
// TODO: allow storing an immortal value into a global.
Expand Down
2 changes: 2 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash

public var isConvertPointerToPointerArgument: Bool { bridged.isConvertPointerToPointerArgument() }

public var isAddressor: Bool { bridged.isAddressor() }

public var specializationLevel: Int { bridged.specializationLevel() }

public var isSpecialization: Bool { bridged.isSpecialization() }
Expand Down
16 changes: 16 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,22 @@ public extension PointerToAddressInst {
}
return nil
}

// If this address is the result of a call to unsafe[Mutable]Address, return the 'self' operand of the apply. This
// represents the base value into which this address projects.
func isResultOfUnsafeAddressor() -> Operand? {
if isStrict,
let extract = pointer as? StructExtractInst,
extract.`struct`.type.isAnyUnsafePointer,
let addressorApply = extract.`struct` as? ApplyInst,
let addressorFunc = addressorApply.referencedFunction,
addressorFunc.isAddressor
{
let selfArgIdx = addressorFunc.selfArgumentIndex!
return addressorApply.argumentOperands[selfArgIdx]
}
return nil
}
}

/// TODO: migrate AccessBase to use this instead of GlobalVariable because many utilities need to get back to a
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -2955,6 +2955,11 @@ struct BridgedASTType {
BRIDGED_INLINE bool isBuiltinFixedWidthInteger(SwiftInt width) const;
BRIDGED_INLINE bool isOptional() const;
BRIDGED_INLINE bool isBuiltinType() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getAnyPointerElementType() const;
BRIDGED_INLINE bool isUnsafeBufferPointerType() const;
BRIDGED_INLINE bool isUnsafeMutableBufferPointerType() const;
BRIDGED_INLINE bool isUnsafeRawBufferPointerType() const;
BRIDGED_INLINE bool isUnsafeMutableRawBufferPointerType() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getNominalOrBoundGenericNominal() const;
BRIDGED_INLINE TraitResult canBeClass() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getAnyNominal() const;
Expand Down
20 changes: 20 additions & 0 deletions include/swift/AST/ASTBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,26 @@ bool BridgedASTType::isBuiltinType() const {
return unbridged()->isBuiltinType();
}

BridgedASTType BridgedASTType::getAnyPointerElementType() const {
return {unbridged()->getCanonicalType()->getAnyPointerElementType().getPointer()};
}

bool BridgedASTType::isUnsafeBufferPointerType() const {
return unbridged()->isUnsafeBufferPointer();
}

bool BridgedASTType::isUnsafeMutableBufferPointerType() const {
return unbridged()->isUnsafeMutableBufferPointer();
}

bool BridgedASTType::isUnsafeRawBufferPointerType() const {
return unbridged()->isUnsafeRawBufferPointer();
}

bool BridgedASTType::isUnsafeMutableRawBufferPointerType() const {
return unbridged()->isUnsafeMutableRawBufferPointer();
}

OptionalBridgedDeclObj BridgedASTType::getNominalOrBoundGenericNominal() const {
return {unbridged()->getNominalOrBoundGenericNominal()};
}
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ BUILTIN_SIL_OPERATION(LoadRaw, "loadRaw", Special)
BUILTIN_SIL_OPERATION(LoadInvariant, "loadInvariant", Special)

/// Take has type (Builtin.RawPointer) -> T
/// where T: ~Copyable & ~Escapable
BUILTIN_SIL_OPERATION(Take, "take", Special)

/// Destroy has type (T.Type, Builtin.RawPointer) -> ()
Expand Down
1 change: 1 addition & 0 deletions include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ struct BridgedFunction {
BRIDGED_INLINE bool isSpecialization() const;
bool isTrapNoReturn() const;
bool isConvertPointerToPointerArgument() const;
bool isAddressor() const;
bool isAutodiffVJP() const;
SwiftInt specializationLevel() const;
SWIFT_IMPORT_UNSAFE BridgedSubstitutionMap getMethodSubstitutions(BridgedSubstitutionMap contextSubs,
Expand Down
6 changes: 2 additions & 4 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,16 +923,14 @@ static ValueDecl *getRefCountingOperation(ASTContext &ctx, Identifier id) {
static ValueDecl *getLoadOperation(ASTContext &ctx, Identifier id) {
return getBuiltinFunction(ctx, id, _thin,
_generics(_unrestricted,
_conformsTo(_typeparam(0), _copyable),
_conformsTo(_typeparam(0), _escapable)),
_conformsTo(_typeparam(0), _copyable)),
_parameters(_rawPointer),
_typeparam(0));
}

static ValueDecl *getTakeOperation(ASTContext &ctx, Identifier id) {
return getBuiltinFunction(ctx, id, _thin,
_generics(_unrestricted,
_conformsTo(_typeparam(0), _escapable)),
_generics(_unrestricted),
_parameters(_rawPointer),
_typeparam(0));
}
Expand Down
7 changes: 7 additions & 0 deletions lib/SILOptimizer/Utils/OptimizerBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,13 @@ bool BridgedFunction::isConvertPointerToPointerArgument() const {
return false;
}

bool BridgedFunction::isAddressor() const {
if (auto declRef = dyn_cast_or_null<AccessorDecl>(getFunction()->getDeclRef().getDecl())) {
return declRef->isAnyAddressor();
}
return false;
}

bool BridgedFunction::isAutodiffVJP() const {
return swift::isDifferentiableFuncComponent(
getFunction(), swift::AutoDiffFunctionComponent::VJP);
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/Pointer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public protocol _Pointer:
/// A type that represents the distance between two pointers.
typealias Distance = Int

associatedtype Pointee: ~Copyable
associatedtype Pointee: ~Copyable & ~Escapable

/// The underlying raw pointer value.
var _rawValue: Builtin.RawPointer { get }
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StringBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ extension String {
_connectOrphanedFoundationSubclassesIfNeeded()

if _guts.isSmall {
return unsafe _guts.asSmall.withUTF8 { bufPtr in
return _guts.asSmall.withUTF8 { bufPtr in
// Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
if _guts.isSmallASCII, let result = unsafe _createCFString(
bufPtr.baseAddress._unsafelyUnwrappedUnchecked,
Expand Down
37 changes: 21 additions & 16 deletions stdlib/public/core/UnsafeBufferPointer.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
/// referenced memory and into the new collection.
@frozen // unsafe-performance
@unsafe
public struct Unsafe${Mutable}BufferPointer<Element: ~Copyable>: Copyable {
public struct Unsafe${Mutable}BufferPointer<Element: ~Copyable & ~Escapable>: Copyable, Escapable {

@usableFromInline
@_preInverseGenerics
Expand Down Expand Up @@ -116,12 +116,12 @@ public struct Unsafe${Mutable}BufferPointer<Element: ~Copyable>: Copyable {

// FIXME: The ASTPrinter should print this synthesized conformance.
// rdar://140291657
extension Unsafe${Mutable}BufferPointer: BitwiseCopyable where Element: ~Copyable {}
extension Unsafe${Mutable}BufferPointer: BitwiseCopyable where Element: ~Copyable & ~Escapable {}

@available(*, unavailable)
extension Unsafe${Mutable}BufferPointer: Sendable where Element: ~Copyable {}
extension Unsafe${Mutable}BufferPointer: Sendable where Element: ~Copyable & ~Escapable {}

extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable & ~Escapable {
/// A pointer to the first element of the buffer.
///
/// If the `baseAddress` of this buffer is `nil`, the count is zero. However,
Expand Down Expand Up @@ -225,7 +225,7 @@ extension Unsafe${Mutable}BufferPointer: @unsafe Sequence {
}
}

extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable & ~Escapable {
public typealias Index = Int

/// A Boolean value indicating whether the buffer is empty.
Expand Down Expand Up @@ -418,6 +418,7 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
/// - Parameter i: The position of the element to access. `i` must be in the
/// range `0..<count`.
@_alwaysEmitIntoClient
@lifetime(borrow self)
public subscript(i: Int) -> Element {
@_transparent
unsafeAddress {
Expand All @@ -437,6 +438,7 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {

// Skip all debug and runtime checks
@_alwaysEmitIntoClient
@lifetime(borrow self)
internal subscript(_unchecked i: Int) -> Element {
@_transparent
unsafeAddress {
Expand Down Expand Up @@ -479,7 +481,7 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {

}

extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable & ~Escapable {
/// Constructs a standalone buffer pointer over the items within the supplied
/// range of positions in the memory region addressed by this buffer pointer.
///
Expand Down Expand Up @@ -574,6 +576,7 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
}
}

// TODO: Support Span<Element: ~Escapable>
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
Expand Down Expand Up @@ -795,7 +798,7 @@ extension Unsafe${Mutable}BufferPointer:
% end
}

extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable & ~Escapable {
@safe
@_alwaysEmitIntoClient
public func _isWellAligned() -> Bool {
Expand Down Expand Up @@ -872,7 +875,7 @@ extension Unsafe${Mutable}BufferPointer {
}
}

extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable & ~Escapable {
/// Deallocates the memory block previously allocated at this buffer pointer’s
/// base address.
///
Expand All @@ -890,7 +893,7 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
}

% if Mutable:
extension UnsafeMutableBufferPointer where Element: ~Copyable {
extension UnsafeMutableBufferPointer where Element: ~Copyable & ~Escapable {
/// Allocates uninitialized memory for the specified number of instances of
/// type `Element`.
///
Expand Down Expand Up @@ -1141,7 +1144,7 @@ extension UnsafeMutableBufferPointer {
}
}

extension UnsafeMutableBufferPointer where Element: ~Copyable {
extension UnsafeMutableBufferPointer where Element: ~Copyable & ~Escapable {
/// Moves every element of an initialized source buffer into the uninitialized
/// memory referenced by this buffer, leaving the source memory uninitialized
/// and this buffer's memory initialized.
Expand Down Expand Up @@ -1220,7 +1223,7 @@ extension UnsafeMutableBufferPointer {
}
}

extension UnsafeMutableBufferPointer where Element: ~Copyable {
extension UnsafeMutableBufferPointer where Element: ~Copyable & ~Escapable {
/// Updates this buffer's initialized memory by moving
/// every element from the source buffer, leaving the source memory
/// uninitialized.
Expand Down Expand Up @@ -1292,7 +1295,7 @@ extension UnsafeMutableBufferPointer {
}
}

extension UnsafeMutableBufferPointer where Element: ~Copyable {
extension UnsafeMutableBufferPointer where Element: ~Copyable & ~Escapable {
/// Deinitializes every instance in this buffer.
///
/// The region of memory underlying this buffer must be fully initialized.
Expand Down Expand Up @@ -1340,9 +1343,11 @@ extension UnsafeMutableBufferPointer where Element: ~Copyable {
/// - index: The index of the buffer element to retrieve and deinitialize.
/// - Returns: The instance referenced by this index in this buffer.
@_alwaysEmitIntoClient
@lifetime(borrow self)
public func moveElement(from index: Index) -> Element {
_debugPrecondition(startIndex <= index && index < endIndex)
return unsafe baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index).move()
let p = unsafe baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index)
return unsafe _overrideLifetime(p.move(), copying: self)
}

/// Deinitializes the memory underlying the element at `index`.
Expand All @@ -1362,7 +1367,7 @@ extension UnsafeMutableBufferPointer where Element: ~Copyable {
}
% end

extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable & ~Escapable {
/// Executes the given closure while temporarily binding the memory referenced
/// by this buffer to the given type.
///
Expand Down Expand Up @@ -1419,7 +1424,7 @@ extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
/// - buffer: The buffer temporarily bound to `T`.
/// - Returns: The return value, if any, of the `body` closure parameter.
@_alwaysEmitIntoClient @_transparent
public func withMemoryRebound<T: ~Copyable, E: Error, Result: ~Copyable>(
public func withMemoryRebound<T: ~Copyable & ~Escapable, E: Error, Result: ~Copyable>(
to type: T.Type,
_ body: (_ buffer: ${Self}<T>) throws(E) -> Result
) throws(E) -> Result {
Expand Down Expand Up @@ -1473,7 +1478,7 @@ extension Unsafe${Mutable}BufferPointer {
@_unavailableInEmbedded
@_preInverseGenerics
extension Unsafe${Mutable}BufferPointer: CustomDebugStringConvertible
where Element: ~Copyable {
where Element: ~Copyable & ~Escapable {
/// A textual representation of the buffer, suitable for debugging.
@_preInverseGenerics
@safe
Expand Down
Loading