From 3e1cd094454c828cef39b55ad1eec40ac533a4a7 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 3 Oct 2025 15:16:18 -0700 Subject: [PATCH 1/7] [stdlib] Unsafe[Mutable]RawPointer aligment for ~Escapable types --- stdlib/public/core/UnsafeRawPointer.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index f3c4d16e04380..0f35ed8db4059 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -559,7 +559,7 @@ extension UnsafeRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedUp(for type: T.Type) -> Self { + public func alignedUp(for type: T.Type) -> Self { let mask = UInt(Builtin.alignof(T.self)) &- 1 let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask _debugPrecondition(bits != 0, "Overflow in pointer arithmetic") @@ -577,7 +577,7 @@ extension UnsafeRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedDown(for type: T.Type) -> Self { + public func alignedDown(for type: T.Type) -> Self { let mask = UInt(Builtin.alignof(T.self)) &- 1 let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask _debugPrecondition(bits != 0, "Overflow in pointer arithmetic") @@ -1563,7 +1563,7 @@ extension UnsafeMutableRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedUp(for type: T.Type) -> Self { + public func alignedUp(for type: T.Type) -> Self { let mask = UInt(Builtin.alignof(T.self)) &- 1 let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask _debugPrecondition(bits != 0, "Overflow in pointer arithmetic") @@ -1581,7 +1581,7 @@ extension UnsafeMutableRawPointer { /// - Returns: a pointer properly aligned to store a value of type `T`. @inlinable @_alwaysEmitIntoClient - public func alignedDown(for type: T.Type) -> Self { + public func alignedDown(for type: T.Type) -> Self { let mask = UInt(Builtin.alignof(T.self)) &- 1 let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask _debugPrecondition(bits != 0, "Overflow in pointer arithmetic") From b05aad3b52908791083b3c1b63b04c9524c876d2 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sat, 4 Oct 2025 17:38:53 -0700 Subject: [PATCH 2/7] [stdlib] UnsafeMutableRawPointer: load[Unaligned] ~Escapable loadUnaligned only supports loading non-BitwiseCopyable values. The generic loadUnaligned should also be supported but it depends on UnsafePointer. --- stdlib/public/core/UnsafeRawPointer.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index 0f35ed8db4059..c31a6b10f763f 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -442,7 +442,8 @@ extension UnsafeRawPointer { /// `offset`. The returned instance is memory-managed and unassociated /// with the value in the memory referenced by this pointer. @inlinable - public func load( + @lifetime(borrow self) + public func load( fromByteOffset offset: Int = 0, as type: T.Type ) -> T { @@ -488,7 +489,8 @@ extension UnsafeRawPointer { /// with the value in the range of memory referenced by this pointer. @inlinable @_alwaysEmitIntoClient - public func loadUnaligned( + @lifetime(borrow self) + public func loadUnaligned( fromByteOffset offset: Int = 0, as type: T.Type ) -> T { From 5c480c78f26f39fb1e1473303abd541d675f4277 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 5 Oct 2025 17:45:18 -0700 Subject: [PATCH 3/7] [stdlib] UnsafeMutableRawPointer: storeBytes of ~Escapable Only supports non-BitwiseCopyable values. The generic storeBytes should also be supported but it depends on UnsafePointer. --- stdlib/public/core/UnsafeRawPointer.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index c31a6b10f763f..a565a22b3a77f 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -1418,10 +1418,11 @@ extension UnsafeMutableRawPointer { /// - type: The type of `value`. @inlinable @_alwaysEmitIntoClient - public func storeBytes( + public func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { - unsafe Builtin.storeRaw(value, (self + offset)._rawValue) + let immortalValue = unsafe _overrideLifetime(value, borrowing: ()) + unsafe Builtin.storeRaw(immortalValue, (self + offset)._rawValue) } /// Stores the given value's bytes into raw memory at the specified offset. From 969f7ca2d26de27b5444cebc16e616604421aa0c Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sat, 4 Oct 2025 17:52:04 -0700 Subject: [PATCH 4/7] Test UnsafeRawPointer load of ~Escapable --- stdlib/public/core/UnsafeRawPointer.swift | 2 + .../lifetime_dependence/rawpointer_span.swift | 76 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 test/SILOptimizer/lifetime_dependence/rawpointer_span.swift diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index a565a22b3a77f..5e08d9302b6cf 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -442,6 +442,7 @@ extension UnsafeRawPointer { /// `offset`. The returned instance is memory-managed and unassociated /// with the value in the memory referenced by this pointer. @inlinable + @_preInverseGenerics @lifetime(borrow self) public func load( fromByteOffset offset: Int = 0, @@ -489,6 +490,7 @@ extension UnsafeRawPointer { /// with the value in the range of memory referenced by this pointer. @inlinable @_alwaysEmitIntoClient + @_preInverseGenerics @lifetime(borrow self) public func loadUnaligned( fromByteOffset offset: Int = 0, diff --git a/test/SILOptimizer/lifetime_dependence/rawpointer_span.swift b/test/SILOptimizer/lifetime_dependence/rawpointer_span.swift new file mode 100644 index 0000000000000..175c658ec1503 --- /dev/null +++ b/test/SILOptimizer/lifetime_dependence/rawpointer_span.swift @@ -0,0 +1,76 @@ +// RUN: %target-swift-frontend -primary-file %s -parse-as-library -emit-sil \ +// RUN: -o /dev/null \ +// RUN: -verify \ +// RUN: -sil-verify-all \ +// RUN: -module-name test \ +// RUN: -define-availability "Span 0.1:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999" \ +// RUN: -strict-memory-safety \ +// RUN: -enable-experimental-feature Lifetimes + +// REQUIRES: swift_in_compiler +// REQUIRES: swift_feature_Lifetimes + +@safe +@_silgen_name("getRawPointer") +func getRawPointer() -> UnsafeRawPointer + +@safe +@_silgen_name("getMutRawPointer") +func getMutRawPointer() -> UnsafeMutableRawPointer + +func useSpan(_: RawSpan) {} + +//===----------------------------------------------------------------------===// +// raw pointer .load() +//===----------------------------------------------------------------------===// + +@available(Span 0.1, *) +@_lifetime(immortal) +func badLoad() -> RawSpan { + let p = getRawPointer() + return unsafe p.load(as: RawSpan.self) // expected-error{{lifetime-dependent value escapes its scope}} + // expected-note@-2{{it depends on the lifetime of variable 'p'}} + // expected-note@-2{{this use causes the lifetime-dependent value to escape}} +} + +@available(Span 0.1, *) +@_lifetime(borrow p) +func goodLoad(p: UnsafeRawPointer) -> RawSpan { + unsafe useSpan(p.load(as: RawSpan.self)) + return unsafe p.load(as: RawSpan.self) +} + +//===----------------------------------------------------------------------===// +// raw pointer .loadUnaligned() +// +// TODO: test non-BitwiseCopyable loadUnaligned +//===----------------------------------------------------------------------===// + +@available(Span 0.1, *) +@_lifetime(immortal) +func badBitwiseLoadUnaligned() -> RawSpan { + let p = getRawPointer() + return unsafe p.loadUnaligned(as: RawSpan.self) // expected-error{{lifetime-dependent value escapes its scope}} + // expected-note@-2{{it depends on the lifetime of variable 'p'}} + // expected-note@-2{{this use causes the lifetime-dependent value to escape}} +} + +@available(Span 0.1, *) +@_lifetime(borrow p) +func goodBitwiseLoadUnaligned(p: UnsafeRawPointer) -> RawSpan { + unsafe useSpan(p.loadUnaligned(as: RawSpan.self)) + return unsafe p.loadUnaligned(as: RawSpan.self) +} + +/* TODO: support loadUnaligned +@_lifetime(immortal) +func badGenericLoadUnaligned(p: UnsafeRawPointer, _: T.Type) -> T { + let p = getRawPointer() + return unsafe p.loadUnaligned(as: T.self) // ERROR +} + +@_lifetime(borrow p) +func goodGenericLoadUnaligned(p: UnsafeRawPointer, _: T.Type) -> T { + return unsafe p.loadUnaligned(as: T.self) // OK +} +*/ From d50d116c0f941bd8da91dd4463e909bf39b8d44d Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 5 Oct 2025 19:41:31 -0700 Subject: [PATCH 5/7] Test UnsafeRawPointer store of ~Escapable --- .../lifetime_dependence/rawpointer_span.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/SILOptimizer/lifetime_dependence/rawpointer_span.swift b/test/SILOptimizer/lifetime_dependence/rawpointer_span.swift index 175c658ec1503..d786130f5b6c3 100644 --- a/test/SILOptimizer/lifetime_dependence/rawpointer_span.swift +++ b/test/SILOptimizer/lifetime_dependence/rawpointer_span.swift @@ -74,3 +74,20 @@ func goodGenericLoadUnaligned(p: UnsafeRawPointer, _: T.Type) -> return unsafe p.loadUnaligned(as: T.self) // OK } */ + +//===----------------------------------------------------------------------===// +// raw pointer .storeBytes() +//===----------------------------------------------------------------------===// + +@available(Span 0.1, *) +func storeSpan(span: RawSpan) { + let p = getMutRawPointer() + unsafe p.storeBytes(of: span, as: RawSpan.self) +} + +/* TODO: support storeBytes +func storeGeneric(value: T) { + let p = getMutRawPointer() + unsafe p.storeBytes(of: value, as: T.self) +} +*/ From 64e4fbbd98a9e93daad556ec9fc3c507abba0c9e Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 5 Oct 2025 19:29:40 -0700 Subject: [PATCH 6/7] Update stability-stdlib-source-base.swift.expected for URP.load --- .../Outputs/stability-stdlib-source-base.swift.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/test/api-digester/Outputs/stability-stdlib-source-base.swift.expected b/test/api-digester/Outputs/stability-stdlib-source-base.swift.expected index 4b8e01dec4137..2d0efddb719ac 100644 --- a/test/api-digester/Outputs/stability-stdlib-source-base.swift.expected +++ b/test/api-digester/Outputs/stability-stdlib-source-base.swift.expected @@ -293,6 +293,7 @@ Func UnsafePointer.deallocate() has generic signature change from to < Func UnsafeRawBufferPointer.bindMemory(to:) has generic signature change from to Func UnsafeRawPointer.assumingMemoryBound(to:) has generic signature change from to Func UnsafeRawPointer.bindMemory(to:capacity:) has generic signature change from to +Func UnsafeRawPointer.load(fromByteOffset:as:) has generic signature change from to Func swap(_:_:) has generic signature change from to Func withExtendedLifetime(_:_:) has parameter 0 changing from Default to Shared Func withUnsafeBytes(of:_:) has generic signature change from to From 0da6783fcdd2f35b341eb4a5836e25d0bc6ba67e Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 6 Oct 2025 17:24:39 -0700 Subject: [PATCH 7/7] Update stability-stdlib-abi-without-asserts.test for URP.load --- test/api-digester/stability-stdlib-abi-without-asserts.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/api-digester/stability-stdlib-abi-without-asserts.test b/test/api-digester/stability-stdlib-abi-without-asserts.test index 1f3d391db78a3..97b3c05a5ad29 100644 --- a/test/api-digester/stability-stdlib-abi-without-asserts.test +++ b/test/api-digester/stability-stdlib-abi-without-asserts.test @@ -684,6 +684,9 @@ Func UnsafeRawPointer.assumingMemoryBound(to:) is now with @_preInverseGenerics Func UnsafeRawPointer.bindMemory(to:capacity:) has generic signature change from to Func UnsafeRawPointer.bindMemory(to:capacity:) has mangled name changing from 'Swift.UnsafeRawPointer.bindMemory(to: A.Type, capacity: Swift.Int) -> Swift.UnsafePointer' to 'Swift.UnsafeRawPointer.bindMemory(to: A.Type, capacity: Swift.Int) -> Swift.UnsafePointer' Func UnsafeRawPointer.bindMemory(to:capacity:) is now with @_preInverseGenerics +Func UnsafeRawPointer.load(fromByteOffset:as:) has generic signature change from to +Func UnsafeRawPointer.load(fromByteOffset:as:) has mangled name changing from 'Swift.UnsafeRawPointer.load(fromByteOffset: Swift.Int, as: A.Type) -> A' to 'Swift.UnsafeRawPointer.load(fromByteOffset: Swift.Int, as: A.Type) -> A' +Func UnsafeRawPointer.load(fromByteOffset:as:) is now with @_preInverseGenerics Func _fixLifetime(_:) has generic signature change from to Func _fixLifetime(_:) has mangled name changing from 'Swift._fixLifetime(A) -> ()' to 'Swift._fixLifetime(A) -> ()' Func _fixLifetime(_:) has parameter 0 changing from Default to Shared