Is this a function builder bug or is my builder incorrect?

cc @Douglas_Gregor, I'm 90% sure the single expression worked in Xcode 11.3.x if we added wrapIntoAnyP manually as it previously had no support for buildExpression. Now in Xcode 11.4 non of the single expressions seem to work. The compiler does not want to pick up the expression convert it into AnyP and then build the block from it.

struct AnyP<T>: P {
  typealias C = T
  let value: Any
}

protocol P {
  associatedtype C
}

extension P {
  func wrapIntoAnyP() -> AnyP<C> {
    AnyP(value: self)
  }
}

extension String: P {
  typealias C = Int
}
extension Bool: P {
  typealias C = Int
}

extension Optional: P where Wrapped: P {
  typealias C = Wrapped.C
}

@_functionBuilder
enum Builder {
  static func buildExpression<T, C>(
    _ content: T
  ) -> AnyP<C> where T: P, T.C == C {
    content.wrapIntoAnyP()
  }

  static func buildIf<T, C>(
    _ content: T?
  ) -> AnyP<C> where T: P, T.C == C {
    content.wrapIntoAnyP()
  }

  static func buildBlock<C>(
    _ components: AnyP<C>...
  ) -> [AnyP<C>] {
    components
  }
}

func foo<T>(@Builder test: () -> [T]) where T: P {}

// OKAY
foo {
  true
  "swift"
}

foo { // error: Generic parameter 'T' could not be inferred
  true // error: Cannot convert value of type 'Bool' to closure result type '[T]'
}

Also the if expression seems not to work either. :thinking:

// error: Unable to infer complex closure return type; add explicit type to disambiguate
foo {
  if true {
    "swift"
  }
  "swift"
}

I assume the compiler to pick up the expression, build AnyP, wrap it into an optional, wrap that into AnyP, pick up the last string, wrap it into AnyP and finally build the block from several AnyP's.

Please file a bug report on bugs.swift.org if you suspect there is a bug here (and any information you have about past compiler versions where it was known to work). It's ok if you are not sure whether it is supposed to work or not.

1 Like