TLDR: I'm using a @resultBuilder closure as a parameter of a method in a protocol extension. When I call the method, Swift is unable to infer the type in the closure, despite it being an associatedtype of the protocol.
For a minimum example:
I have a ChefProtocol with an associated type of Ingredient that conforms to the IngredientProtocol.
protocol IngredientProtocol { }
protocol ChefProtocol {
associatedtype Ingredient: IngredientProtocol
}
I'm using a @resultBuilder to make a sandwich from a variadic Ingredient parameter. All components will be the same type, which conforms to the IngredientProtocol.
@resultBuilder
struct SandwichMaker<Ingredient: IngredientProtocol> {
static func buildBlock(_ components: Ingredient...) -> [Ingredient] {
Array(components)
}
}
Anything conforming to ChefProtocol should be able to using this makeSandwich(ingredients:) method to turn a closure of 0+ Ingredient instances into an array. It's my understanding that Ingredient here refers to Chef.Ingredient, an associatedtype of Chef.
extension ChefProtocol {
func makeSandwich(@SandwichMaker<Ingredient> ingredients: () -> [Ingredient]) { }
}
However, when I make a custom type conforming to this protocol, and specify its associatedtype, Swift can't figure out what types to expect in the closure for chef.makeSandwich. Why?
struct Chef: ChefProtocol {
enum Ingredient: IngredientProtocol {
case bread, cheese
}
}
let chef = Chef()
chef.makeSandwich {
.bread // Error: Cannot infer contextual base in reference to member 'bread'
.cheese
.bread
}
If I specify the type explicitly, it compiles. But this is annoying to have to reference the nested type, when it seems like Swift should know that the closure expects Chef's associatedtype of Ingredient.
let chef = Chef()
chef.makeSandwich {
Chef.Ingredient.bread
Chef.Ingredient.cheese
Chef.Ingredient.bread
}
Moving the associatedtype out of Chef doesn't work either. (Same error).
struct Chef: ChefProtocol {
typealias Ingredient = Ingredient1
}
enum Ingredient1: IngredientProtocol {
case bread, cheese
}
And if I put something random in the closure, NOW Swift knows what is supposed to go there.
let chef = Chef()
chef.makeSandwich {
"not an ingredient" // Error: Cannot convert value of type 'String' to expected argument type 'Chef.Ingredient'
}
Please let me know if there is a way to help Swift understand what I'm trying to do here. I'm also interested in WHY this is happening. Thanks!