Clone a ClosureExpr and change all decl contexts within the body

Hmm, I get warning when defining the init: initializer for struct 'X' must use "self.init(...)" or "self = ..." because it is not in module 'REPL_0'

That’s an artifact of using the REPL. Try compiling that in one file

What exactly am I supposed to be looking for? This compiles and executes fine. :thinking:

public struct X {
  public var y: (Int) -> Int = { $0 }
}

extension X {
  @inlinable init(y: Int) { print(y) }
}

_ = X()
_ = X(y: 6) // 6

I suppose the motivating example isn't a huge problem, but it's definitely something that you'll have to look out for when implementing this.

The place where this transformation is not valid is in @_fixed_layout types in resilient modules, where a property initializer need to only reference public or @usableFromInline things.

@_fixed_layout
public struct X {
  public var x: (Int) -> Int = { $0 }
  public var y: (Int) -> Int = X.y_initialValue()
  static func y_initialValue() -> (Int) -> Int {
    return { $0 }
  }
}

Here, x is fine because the closure is directly written. But y calls something that's not @usableFromInline, meaning:

test.swift:4:34: error: static method 'y_initialValue()' is internal and cannot be referenced from a property initializer in a '@_fixed_layout' type
  public var y: (Int) -> Int = X.y_initialValue()
                                 ^
test.swift:5:15: note: static method 'y_initialValue()' is not '@usableFromInline' or public
  static func y_initialValue() -> (Int) -> Int {

Also, if the static function is going to be @inlinable or @_transparent, it may need to show up in parseable interface files, which would mean it would need a name other than "varName.initialValue"

I've opened https://github.com/apple/swift/pull/19743 where we can discuss implementation more.

I added a comment: [Sema] Synthesize default values for memberwise init by Azoy · Pull Request #19743 · apple/swift · GitHub

I suspect there's a better way of doing this, basically by sinking this down into the SILGen level.

Let's look at the SIL for a struct with a property that has an initial value:

struct S {
  var x: Int = 0
}

sil hidden [transparent] @$S7initial1SV1xSivpfi : $@convention(thin) () -> Int {
bb0:
  %0 = metatype $@thin Int.Type                   // user: %3
  %1 = integer_literal $Builtin.Int2048, 0        // user: %3
  // function_ref Int.init(_builtinIntegerLiteral:)
  %2 = function_ref @$SSi22_builtinIntegerLiteralSiBi2048__tcfC : $@convention(method) (Builtin.Int2048, @thin Int.Type) -> Int // user: %3
  %3 = apply %2(%1, %0) : $@convention(method) (Builtin.Int2048, @thin Int.Type) -> Int // user: %4
  return %3 : $Int                                // id: %4
} // end sil function '$S7initial1SV1xSivpfi'

Your helper function effectively already exists. What you need to do then is to have the default argument generator emitted by SIL call it.