From 3ec7b0d7b7c9980bfbfdc6dd0b459323cf45b42b Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Sat, 3 Jan 2026 13:16:20 -0800 Subject: [PATCH 1/3] Add proposal to support inferring generic arguments of result builder attributes --- .../xxxx-infer-result-builder-generics.md | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 proposals/xxxx-infer-result-builder-generics.md diff --git a/proposals/xxxx-infer-result-builder-generics.md b/proposals/xxxx-infer-result-builder-generics.md new file mode 100644 index 0000000000..30a2f784f6 --- /dev/null +++ b/proposals/xxxx-infer-result-builder-generics.md @@ -0,0 +1,210 @@ +# Infer generic arguments of result builder attributes + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Cal Stephens](https://github.com/calda) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [#86209](https://github.com/swiftlang/swift/pull/86209) +* Pitch: [1](https://forums.swift.org/t/add-arraybuilder-to-the-standard-library/83811/3) + +## Introduction + +We should enable generic result builders to be used as attributes without needing to explicitly specify generic arguments, instead allowing them to be inferred from the return type of the attached declaration. + +## Motivation + +Take this simple generic `ArrayBuilder` type that a project may choose to define: + +```swift +@resultBuilder +enum ArrayBuilder { + static func buildBlock(_ elements: Element...) -> [Element] { + elements + } + + // ... +} +``` + +At call sites, the result builder must be fully spelled out as `@ArrayBuilder`, explicitly specifying the generic argument for `Element`: + +```swift +/// An invocation for the swift-format command line tool +struct SwiftFormatInvocation { + @ArrayBuilder let arguments: [String] +} +``` + +```swift +@ArrayBuilder +var arguments: [String] { + "format" + "--in-place" + + if recursive { + "--recursive" + } +} +``` + +```swift +extension Array { + init(@ArrayBuilder _ build: () -> Self) { + self = build() + } +} +``` + +In all of these cases, specifying the generic arguments of the `ArrayBuilder` adds additional boilerplate without adding much value, because the generic arguments are already obvious from context. This is also inconsistent with most other areas of the language, where generic arguments for types can typically be inferred. + +## Proposed solution + +We should improve the ergonomics of generic result builders by allowing generic arguments to be inferred from the return type of the attached declaration. + +This allows us to omit the generic arguments in all of these examples, simplifying the code: + +```swift +// Inferred to be `@ArrayBuilder` +struct SwiftFormatInvocation { + @ArrayBuilder let arguments: [String] +} +``` + +```swift +// Inferred to be `@ArrayBuilder` +@ArrayBuilder +var arguments: [String] { + "format" + "--in-place" + + if recursive { + "--recursive" + } +} +``` + +```swift +// Inferred to be `@ArrayBuilder` +extension Array { + init(@ArrayBuilder _ build: () -> Self) { + self = build() + } +} +``` + +## Detailed design + +When not specified explicitly, the generic arguments for a generic result builder attribute will be inferred from the return type of the attached declaration. + +We can infer that the return type of the attached declaration should be equal to one of the potential result types of the result builder. The potential result types of the result builder are defined by the types returned from the `buildFinalResult`, `buildPartialBlock`, and `buildBlock` methods. + +For example, take this result builder: + +```swift +@resultBuilder +enum CollectionBuilder { + static func buildBlock(_ component: Element...) -> [Element] { + component + } + + static func buildFinalResult(_ component: [Element]) -> [Element] { + component + } + + static func buildFinalResult(_ component: [Element]) -> Set where Element: Hashable { + Set(component) + } +} +``` + +with these call sites: + +```swift +@CollectionBuilder +var array: [String] { + "a" + "b" +} + +@CollectionBuilder +var set: Set { + "c" + "d" +} +``` + +The valid result types of `CollectionBuilder` are `[Element]` and `Set`. This gives us simple constraints (`[Element] == [String]`, `Set == Set`) which are trivial to solve: `Element` is inferred to be `String`. + +This design supports arbitrarily long lists of generic parameters and arbitrarily complex result types, as long as the generic arguments are unambiguously solvable. In this more complex example, the generic result builder is inferred to be `@DictionaryBuilder`, since that solves `[Key: [Value]] == [String: [Int]]`: + +```swift +@resultBuilder +enum DictionaryBuilder { + static func buildBlock(_ component: (key: Key, value: Value)...) -> [Key: [Value]] { + // ... + } +} + +@DictionaryBuilder +var dictionary: [String: [Int]] { + (key: "a", value: 42) + (key: "b", value: 100) +} +``` + +Type inference is also supported for non-generic result builders namespaced within generic types. In this example, the result builder is inferred to be `@Array.Builder`: + +```swift +extension Array { + @resultBuilder + enum Builder { + static func buildBlock(_ elements: Element...) -> [Element] { + elements + } + } +} + +@Array.Builder +var array: [String] { + "a" + "b" +} +``` + +This will be supported in all valid result builder use cases, including function parameters, computed properties, functions results, and struct properties: + +```swift +init(@ArrayBuilder arguments: () -> [String]) { ... } + +@ArrayBuilder +var arguments: [String] { ... } + +@ArrayBuilder +func arguments() -> [String] { ... } + +struct SwiftFormatInvocation { + @ArrayBuilder let arguments: [String] +} +``` + +## Source compatibility + +Inferring result builder generic parameters has no source compatibility impact, since this simply allows code that was previously rejected with an error. + +## ABI compatibility + +This proposal simply enables new callsite syntax for existing result builder declarations and has no ABI impacts. + +## Implications on adoption + +This proposal simply enables new callsite syntax for existing declarations and has no adoption implications. + +## Future directions + +### Add an `@ArrayBuilder` to the standard library + +We could eventually add an `@ArrayBuilder` (or similar) to the standard library, or a core package like swift-collections. In the meantime, these ergonomic improvements will be valuable for community-defined generic result builders. + +## Alternatives considered + +The primary alternative would be to do nothing and preserve the status-quo. However, these ergonomic improvements provide value for codebases using generic result builders, so seem to carry their weight. From a126433c142a92ca0b095f5342f1ec09adda9784 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Tue, 10 Mar 2026 19:40:27 -0700 Subject: [PATCH 2/3] Update pitch URL --- proposals/xxxx-infer-result-builder-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/xxxx-infer-result-builder-generics.md b/proposals/xxxx-infer-result-builder-generics.md index 30a2f784f6..d9b0fe7c26 100644 --- a/proposals/xxxx-infer-result-builder-generics.md +++ b/proposals/xxxx-infer-result-builder-generics.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: **Awaiting review** * Implementation: [#86209](https://github.com/swiftlang/swift/pull/86209) -* Pitch: [1](https://forums.swift.org/t/add-arraybuilder-to-the-standard-library/83811/3) +* Pitch: [1](https://forums.swift.org/t/pitch-infer-generic-arguments-of-result-builder-attributes/85272) ## Introduction From ac7d41de4f9cfb9f3038c2cb921268f5ff700e05 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 13 Mar 2026 10:16:57 -0700 Subject: [PATCH 3/3] Clarify that placeholder types are supported --- proposals/xxxx-infer-result-builder-generics.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/proposals/xxxx-infer-result-builder-generics.md b/proposals/xxxx-infer-result-builder-generics.md index d9b0fe7c26..689805fc03 100644 --- a/proposals/xxxx-infer-result-builder-generics.md +++ b/proposals/xxxx-infer-result-builder-generics.md @@ -171,6 +171,15 @@ var array: [String] { } ``` +Placeholder types are also supported, like in other generic contexts, allowing you to only specify a subset of the generic arguments and have the rest be inferred automatically: + +```swift +@DictionaryBuilder +var dictionary: [String: (a: Int, b: Int, c: Int)] { + (key: "a", value: (a: 1, b: 2, c: 3)) +} +``` + This will be supported in all valid result builder use cases, including function parameters, computed properties, functions results, and struct properties: ```swift