I go into this extensively in Issue #85924, but TL;DR...
These two statements type-check differently:
func merge<each A, B>(_ a: (repeat each A), _ b: B) -> (repeat each A, B) {
return (repeat each a, b)
}
// The `var` tuple in `a` is not flattened and remains intact in the output tuple:
let mergedVar/*: ((String, Int), String) */ = {
var a = ("a", 1) // @lvalue (String, Int)
let b = "c" // String
let c = merge(a, b) /*: ((String, Int), String) <- */
return c
}()
// The `let` value of `a` is flattened into a single-level tuple:
let mergedLet /*: (String, Int, String) */ = {
let a = ("a", 1) // (String, Int)
let b = "c" // String
let c = merge(a, b) /*: (String, Int, String) <- */
return c
}()
It seems like the culprit is passingvar a into merge causes parameter a to be of type @lvalue (String, Int), resulting in a nested packing. let a is @lValue-free and packs as expected (or as I'd expect?).
I found this randomly investigating #85837 where ResultBuilders were not optimally packing parameters, with a fix option here.
Questions
What parameter pack shapes are valid
-
What are the actual intended shape options for parameter packs?
-
Given that my first experience with parameter packs is also coming across this bug, I'm not sure.
Based on the proposal I can't figure out if these are equally valid:
let flat: (String, Int, String) = merge(("A", 2), "C")
let nested: ((String, Int), String) = merge(("A", 2), "C")
Mainly is the var a case's outcome a vaild result to begin with, or is it a bug that it results/packs like this?
What does lValue do here?
What purpose does @lValue serve in the type system here. Is the intent to signal that the var value could be mutable memory in some way, even if it's not something you could mutate within a function call?
If it's being considered within parameter packing, is it being considered correctly in this case?
Why @lValue?
What is the reasoning behind having the @lValue designation on these parameters?
Does this have something to do with supporting inout parameters? As in, if var a is passed in as inout, the compiler would avoid changing the type/shape of a?
If it is about inout and not generically about var parameters, is this a general bug that just hasn't been an issue in the past?
edit: On searching again I found this, which seems to indicate this might be indended.
Bonus - ABI/Reverse Compatability
If a fix is made for #85837 or #85924 , are there ABI or runtime implications?
As in are callees of a generic function using parameter packs impacted if substantive changes to the result of calls to func merge<each A, B> occur, or is this a safe type of fix to make overall?