From 8181154f075c5785de392935162e8f2a65fe15dc Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 5 Dec 2025 15:19:31 +0000 Subject: [PATCH 1/3] stdlib: add `withTemporaryAllocation` using `OutputSpan` --- stdlib/public/core/TemporaryAllocation.swift | 22 ++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/TemporaryAllocation.swift b/stdlib/public/core/TemporaryAllocation.swift index 960eec28492f6..babbbe6016067 100644 --- a/stdlib/public/core/TemporaryAllocation.swift +++ b/stdlib/public/core/TemporaryAllocation.swift @@ -141,14 +141,14 @@ internal func _withUnsafeTemporaryAllocation< // Builtin.stackDealloc() will end up blowing it away (and the verifier will // notice and complain.) let result: R - + #if compiler(>=5.5) && $BuiltinStackAlloc let stackAddress = Builtin.stackAlloc( capacity._builtinWordValue, MemoryLayout.stride._builtinWordValue, alignment._builtinWordValue ) - + // The multiple calls to Builtin.stackDealloc() are because defer { } produces // a child function at the SIL layer and that conflicts with the verifier's // idea of a stack allocation's lifetime. @@ -269,6 +269,24 @@ public func withUnsafeTemporaryAllocation( return try result.get() } +@available(SwiftCompatibilitySpan 5.0, *) +@_alwaysEmitIntoClient @_transparent +public func withTemporaryAllocation( + of type: T.Type, + capacity: Int, + _ body: (inout OutputSpan) throws(E) -> R +) throws(E) -> R where T : ~Copyable, R : ~Copyable { + try withUnsafeTemporaryAllocation(of: type, capacity: capacity) { (buffer) throws(E) in + var span = OutputSpan(buffer: buffer, initializedCount: 0) + defer { + let initializedCount = span.finalize(for: buffer) + buffer.extracting(.. Date: Fri, 5 Dec 2025 15:54:02 +0000 Subject: [PATCH 2/3] `withTemporaryAllocation`: fix use of `defer`, add tests --- stdlib/public/core/TemporaryAllocation.swift | 14 +++- test/stdlib/TemporaryAllocation.swift | 68 ++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/TemporaryAllocation.swift b/stdlib/public/core/TemporaryAllocation.swift index babbbe6016067..869702a1b7277 100644 --- a/stdlib/public/core/TemporaryAllocation.swift +++ b/stdlib/public/core/TemporaryAllocation.swift @@ -278,12 +278,20 @@ public func withTemporaryAllocation( ) throws(E) -> R where T : ~Copyable, R : ~Copyable { try withUnsafeTemporaryAllocation(of: type, capacity: capacity) { (buffer) throws(E) in var span = OutputSpan(buffer: buffer, initializedCount: 0) - defer { + + do throws(E) { + let result = try body(&span) + let initializedCount = span.finalize(for: buffer) buffer.extracting(...alignment - 1 + expectEqual(pointerBits & alignmentMask, 0) + initializedCount = 0 + } + } +} + // MARK: Typed throws enum HomeworkError: Error, Equatable { case dogAteIt @@ -172,4 +229,15 @@ TemporaryAllocationTestSuite.test("typedAllocationWithThrow") { } } +TemporaryAllocationTestSuite.test("spanWithThrow") { + do throws(HomeworkError) { + try withTemporaryAllocation(of: Int.self, capacity: 1) { (span) throws(HomeworkError) -> Void in + throw HomeworkError.forgot + } + fatalError("did not throw!?!") + } catch { + expectEqual(error, .forgot) + } +} + runAllTests() From 8b68d54904fae257ebae3e254527a6ee4333ec4d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 5 Dec 2025 21:43:52 +0000 Subject: [PATCH 3/3] withTemporaryAllocation: revert back to using `defer` --- stdlib/public/core/TemporaryAllocation.swift | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/stdlib/public/core/TemporaryAllocation.swift b/stdlib/public/core/TemporaryAllocation.swift index 869702a1b7277..b1e0dfbe5b35f 100644 --- a/stdlib/public/core/TemporaryAllocation.swift +++ b/stdlib/public/core/TemporaryAllocation.swift @@ -278,20 +278,13 @@ public func withTemporaryAllocation( ) throws(E) -> R where T : ~Copyable, R : ~Copyable { try withUnsafeTemporaryAllocation(of: type, capacity: capacity) { (buffer) throws(E) in var span = OutputSpan(buffer: buffer, initializedCount: 0) - - do throws(E) { - let result = try body(&span) - + defer { let initializedCount = span.finalize(for: buffer) + span = OutputSpan() buffer.extracting(..