Passing a parameter pack to a function call fails to compile

Playing around with parameter packs I was trying to compile the following but failed. Now this feels like it's either a bug in the compiler or I'm doing something wrong using the parameter pack feature :) Would love to understand if I'm missing something:

The following code is a reproducer I made from the larger swift-parsing package codebase.
This fails to compile with the following error:
Value pack expansion can only appear inside a function argument list, tuple element, or as the expression of a for-in loop

public protocol Parser<Input, Output> {
    associatedtype Input
    associatedtype Output
    
    func parse(_ input: inout Input) -> Output
}

public protocol ParserPrinter<Input, Output>: Parser {
    func print(_ output: Output, into input: inout Input)
}

@available(macOS 14.0.0, *)
public struct Take<P0: Parser, P1: Parser, each Outputs>: Parser
where P0.Input == P1.Input, P0.Output == (repeat each Outputs) {
    let p0: P0
    let p1: P1
    
    init(_ p0: P0, _ p1: P1) {
      self.p0 = p0
      self.p1 = p1
    }
    
    public func parse(_ input: inout P0.Input) -> (repeat each Outputs, P1.Output) {
      let outputs = self.p0.parse(&input)
      return (repeat each outputs, self.p1.parse(&input))
    }
}

@available(macOS 14.0.0, *)
extension Take: ParserPrinter where P0: ParserPrinter, P1: ParserPrinter {
    public func print(_ output: (repeat each Outputs, P1.Output), into input: inout P0.Input) {
        self.p1.print(output.1, into: &input)
        self.p0.print(output.0, into: &input) // Value pack expansion can only appear inside a function argument list, tuple element, or as the expression of a for-in loop
    }
}

when I put repeat each output.0 inside the print(:) function call I get a second error as well:
Cannot pass value pack expansion to non-pack parameter of type 'Self.Output'
and the original:
Value pack expansion can only appear inside a function argument list, tuple element, or as the expression of a for-in loop
Which feels strange since that call shouldn't expect Self.Output but P0.Output which is constrained to equal to the parameter pack each Outputs

2 Likes

Unfortunately this is a known limitation today. We don't have a good way to express this:

func uncons<each T, U>(_ t: (repeat each T, U)) -> (repeat each T) {
  // ?
} 

Here is an attempt that crashes sadly:

func uncons2<each T, U>(_ t: repeat each T, u: U) -> (repeat each T) {
  return (repeat each t)
}

func untuple<each T, U>(_ fn: (repeat each T) -> U) -> ((repeat each T)) -> U {
  return { tuple in fn(repeat each tuple) }
}

func uncons<each T, U>(_ t: (repeat each T, U)) -> (repeat each T) {
  let fn: (repeat each T, U) -> (repeat each T) = uncons2
  let fn2: ((repeat each T, U)) -> (repeat each T) = untuple(fn)
  return fn2(t)
}
1 Like

Thanks for the insight! You mention that it crashes, is that due to a compiler bug? Or does the compiler simply not support it yet? Or was it an oversight at the original design of the syntax?
I'm asking to understand what would be needed to get this kind of functionality :) a bug fix, an evolution proposal, etc etc.

My example crashes due to a bug that just needs to be fixed. It would be nice if there was a cleaner way to express it, with pattern matching for example. I suspect pattern matching with packs and enums might need a proposal to spell out the details.

We could also allow .0/.1 on tuples that contain pack expansions (it should be diagnosed today because you saw it’s not fully implemented) but the semantics would be a bit odd, I think.

What do you mean with pattern matching with enums in this case? I don't fully understand how they're related.
I'd love to but feel I'm not experienced enough with the parameter pack apis and have a clear vision on what problems the pattern matching should cover to put together a full proposal.
Would love to help though if you think it's something that needs to be addressed! Let me know