Result builder-annotated parameter does not contribute to a function's signature

I have a fairly simple result builder that is generic over one type. When I try to create a generic function that uses a closure annotated with my result builder type, the Swift compiler tells me that the generic parameter is not used in the function signature.

static func build<T>(@MyResultBuilder<T> _ body: @escaping () -> MyType) -> MyType {
    body()
}

The above code yields the compiler error "Generic parameter 'T' is not used in function signature".

At first glance it looks like I could get around it by using a hacky throwaway type:

static func build<T>(
    @MyResultBuilder<T> _ body: @escaping () -> MyType,
    _ hack: T? = nil
) -> MyType {
    body()
}

But doing so leads to type inference that isn't as strict as it is when I declare my result builder and parameterize the generic type explicitly.

I wasn't able to find any discussion on whether annotations should contribute to the function's signature, but it feels like allowing annotations to affect the function signature makes sense. Is there a better way to produce my desired effect?

This feels like it's working as intended; how would the compiler know what to infer for T when applying the builder transform to something like this:

Receiver.build {
  Something
  SomethingElse
}

since there's no relationship between T and the built value of MyType?

What is your desired effect; that is, where do you want T to come from? The clearest way would be to require a type parameter to the function:

static func build<T>(
  _ type: T.Type,
  @MyResultBuilder<T> _ body: @escaping () -> MyType
) -> MyType {
  body()
}

and call it like

Receiver.build(Whatever.self) {
  // ...
}

But since the build value MyType isn't generic, it's not immediately clear what purpose T serves here (unless you're wrapping a bunch of things in type erasing types or existentials).

1 Like

I see... I suppose I thought that T could be inferred by evaluating the type passed to the DSL. I see now that when I declare it directly as

@MyResultBuilder<Something> var result {
  Something
  SomethingElse 
}

because I am directly specifying the generic type there, that's why the line with SomethingElse would result in an error whereas build(_:) just doesn't give the compiler enough information.

And yes, you were right--I'm essentially using generics as phantom types in my result builder implementation to ensure that the DSL only accepts types that are valid to compose, despite the fact that my final result is not generic.

Thanks for the quick response, the explanation, and the suggestion!