Improved Result Builder Implementation in Swift 5.8

Method withUnsafeMutableResultPointer exists only on LavaBuilder, protocol LVPath does not define it.

This is how the transformed code looks like:

try LavaBuilder<VkPresentInfoKHR>.init({
  let $__builder0 = LavaBuilder<VkPresentInfoKHR>.buildExpression((\.waitSemaphoreCount, \.pWaitSemaphores) <- waitSemaphores)
  let $__buider1 = LavaBuilder<VkPresentInfoKHR>.buildExpression((\.swapchainCount, \.pSwapchains) <- swapchains)
  let $__builder2 = LavaBuilder<VkPresentInfoKHR>.buildExpression(\.pImageIndices <- imageIndices)
  let $__builder3 = LavaBuilder<VkPresentInfoKHR>.buildExpression(\.pResults <- nil)

  let $__buildBlock0 = LavaBuilder<VkPresentInfoKHR>.buildPartialBlock(first: $__builder0)
  let $__buildBlock1 = LavaBuilder<VkPresentInfoKHR>.buildPartialBlock(accumulated: $__buildBlock0, next: $__builder1)
  let $__buildBlock2 = LavaBuilder<VkPresentInfoKHR>.buildPartialBlock(accumulated: $__buildBlock1, next: $__builder2)
  let $__buildBlock3 = LavaBuilder<VkPresentInfoKHR>.buildPartialBlock(accumulated: $__buildBlock2, next: $__builder3)

  return LavaBuilder<VkPresentInfoKHR>.buildFinalResult($__buildBlock3)
})
.withUnsafeMutableResultPointer { info in
  try lock.synchronized {
    try vulkanInvoke {
      device.vkQueuePresentKHR(pointer, info)
    }
  }
}

I added .init to explicitly delimit where initializer reference begins and ends to show that .withUnsafeMutableResultPointer is always chained on it and therefore its base type is LavaBuilder<VkPresentInfoKHR> always.

The actual issue here is that return buildFinalResult($__buildBlock3) is a result of the @LavaBuilder<Struct> _ content: () throws -> (Path) closure which means that it's type-checked separately from parent context and the type-checker currently doesn't propagate any generic requirements into the body of the multi-statement closure (return doesn't know that result type should conform to LVPath protocol) which makes that bit of code ambiguous.

Luckily, all overloads buildFinalResult overloads besides:

    @inlinable @_transparent
    public static func buildFinalResult(_ component: some LVPath<Struct>) -> some LVPath<Struct> {
        component
    }

are redundant because buildPartialBlock(...) would only be able to produce one type - some LVPath<Struct>, so you can safely delete them and it should fix this situation.

3 Likes