// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -emit-silgen %s | FileCheck %s

public protocol P1 {
  func reqP1a()
  subscript(i: Int) -> Int { get set }
}

struct Box {
  var number: Int
}

extension P1 {
  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_2P16extP1a{{.*}} : $@convention(method) <Self where Self : P1> (@in_guaranteed Self) -> () {
  // CHECK: bb0([[SELF:%[0-9]+]] : $*Self):
  final func extP1a() {
    // CHECK: [[WITNESS:%[0-9]+]] = witness_method $Self, #P1.reqP1a!1 : $@convention(witness_method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> ()
    // CHECK-NEXT: apply [[WITNESS]]<Self>([[SELF]]) : $@convention(witness_method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> ()
    reqP1a()
    // CHECK: return
  }

  // CHECK-LABEL: sil @_TFE19protocol_extensionsPS_2P16extP1b{{.*}} : $@convention(method) <Self where Self : P1> (@in_guaranteed Self) -> () {
  // CHECK: bb0([[SELF:%[0-9]+]] : $*Self):
  public final func extP1b() {
    // CHECK: [[FN:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P16extP1a{{.*}} : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> ()
    // CHECK-NEXT: apply [[FN]]<Self>([[SELF]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> ()
    extP1a()
    // CHECK: return
  }

  subscript(i: Int) -> Int {
    // materializeForSet can do static dispatch to peer accessors (tested later, in the emission of the concrete conformance)
    get {
      return 0
    }
    set {}
  }

  final func callSubscript() -> Int {
    // But here we have to do a witness method call:

    // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_2P113callSubscript{{.*}}
    // CHECK: bb0(%0 : $*Self):
    // CHECK: witness_method $Self, #P1.subscript!getter.1
    // CHECK: return
    return self[0]
  }

  static var staticReadOnlyProperty: Int {
    return 0
  }

  static var staticReadWrite1: Int {
    get { return 0 }
    set { }
  }

  static var staticReadWrite2: Box {
    get { return Box(number: 0) }
    set { }
  }
}

// ----------------------------------------------------------------------------
// Using protocol extension members with concrete types
// ----------------------------------------------------------------------------
class C : P1 {
  func reqP1a() { }
}

//   (materializeForSet test from above)
// CHECK-LABEL: sil [transparent] [thunk] @_TTWC19protocol_extensions1CS_2P1S_FS1_m9subscriptFSiSi
// CHECK: bb0(%0 : $Builtin.RawPointer, %1 : $*Builtin.UnsafeValueBuffer, %2 : $Int, %3 : $*C):
// CHECK: function_ref @_TFE19protocol_extensionsPS_2P1g9subscriptFSiSi
// CHECK: return

class D : C { }

struct S : P1 {
  func reqP1a() { }
}

struct G<T> : P1 {
  func reqP1a() { }
}

struct MetaHolder {
  var d: D.Type = D.self
  var s: S.Type = S.self
}

struct GenericMetaHolder<T> {
  var g: G<T>.Type = G<T>.self
}

func inout_func(inout n: Int) {}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions5testDFTVS_10MetaHolder2ddMCS_1D1dS1__T_ : $@convention(thin) (MetaHolder, @thick D.Type, @owned D) -> ()
// CHECK: bb0([[M:%[0-9]+]] : $MetaHolder, [[DD:%[0-9]+]] : $@thick D.Type, [[D:%[0-9]+]] : $D):
func testD(m: MetaHolder, dd: D.Type, d: D) {
  // CHECK: [[D2:%[0-9]+]] = alloc_box $D
  // CHECK: [[RESULT:%.*]] = project_box [[D2]]
  // CHECK: [[FN:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P111returnsSelf{{.*}}
  // CHECK: [[DCOPY:%[0-9]+]] = alloc_stack $D
  // CHECK: store [[D]] to [[DCOPY]] : $*D
  // CHECK: apply [[FN]]<D>([[RESULT]], [[DCOPY]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> @out τ_0_0
  var d2: D = d.returnsSelf()

  // CHECK: metatype $@thick D.Type
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  let _ = D.staticReadOnlyProperty

  // CHECK: metatype $@thick D.Type
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  D.staticReadWrite1 = 1

  // CHECK: metatype $@thick D.Type
  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  D.staticReadWrite1 += 1

  // CHECK: metatype $@thick D.Type
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  D.staticReadWrite2 = Box(number: 2)

  // CHECK: metatype $@thick D.Type
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  D.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: metatype $@thick D.Type
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&D.staticReadWrite2.number)

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  let _ = dd.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  dd.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  dd.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  dd.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  dd.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&dd.staticReadWrite2.number)

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  let _ = m.d.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  m.d.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  m.d.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  m.d.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  m.d.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&m.d.staticReadWrite2.number)

  // CHECK: return
}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions5testSFTVS_10MetaHolder2ssMVS_1S_T_
func testS(m: MetaHolder, ss: S.Type) {
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  // CHECK: metatype $@thick S.Type
  let _ = S.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: metatype $@thick S.Type
  S.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  S.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  S.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  S.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&S.staticReadWrite2.number)

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  // CHECK: metatype $@thick S.Type
  let _ = ss.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: metatype $@thick S.Type
  ss.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  ss.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  ss.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  ss.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&ss.staticReadWrite2.number)

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  // CHECK: metatype $@thick S.Type
  let _ = m.s.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: metatype $@thick S.Type
  m.s.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  m.s.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  m.s.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  m.s.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick S.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&m.s.staticReadWrite2.number)

  // CHECK: return
}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions5testG
func testG<T>(m: GenericMetaHolder<T>, gg: G<T>.Type) {
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  // CHECK: metatype $@thick G<T>.Type
  let _ = G<T>.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: metatype $@thick G<T>.Type
  G<T>.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  G<T>.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  G<T>.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  G<T>.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&G<T>.staticReadWrite2.number)

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  // CHECK: metatype $@thick G<T>.Type
  let _ = gg.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: metatype $@thick G<T>.Type
  gg.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  gg.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  gg.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  gg.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&gg.staticReadWrite2.number)

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g22staticReadOnlyPropertySi
  // CHECK: metatype $@thick G<T>.Type
  let _ = m.g.staticReadOnlyProperty

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: metatype $@thick G<T>.Type
  m.g.staticReadWrite1 = 1

  // CHECK: alloc_stack $Int
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite1Si
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite1Si
  // CHECK: dealloc_stack
  m.g.staticReadWrite1 += 1

  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  m.g.staticReadWrite2 = Box(number: 2)

  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  m.g.staticReadWrite2.number += 5

  // CHECK: function_ref @_TF19protocol_extensions10inout_funcFRSiT_
  // CHECK: alloc_stack $Box
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1g16staticReadWrite2VS_3Box
  // CHECK: metatype $@thick G<T>.Type
  // CHECK: store
  // CHECK: load
  // CHECK: function_ref @_TZFE19protocol_extensionsPS_2P1s16staticReadWrite2VS_3Box
  // CHECK: dealloc_stack
  inout_func(&m.g.staticReadWrite2.number)

  // CHECK: return
}

// ----------------------------------------------------------------------------
// Using protocol extension members with existentials
// ----------------------------------------------------------------------------
extension P1 {
  final func f1() { }

  final subscript (i: Int64) -> Bool {
    get { return true }
  }

  final var prop: Bool {
    get { return true }
  }

  final func returnsSelf() -> Self { return self }

  final var prop2: Bool {
    get { return true }
    set { }
  }

  final subscript (b: Bool) -> Bool {
    get { return b }
    set { }
  }
}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions17testExistentials1
// CHECK: bb0([[P:%[0-9]+]] : $*P1, [[B:%[0-9]+]] : $Bool, [[I:%[0-9]+]] : $Int64):
func testExistentials1(p1: P1, b: Bool, i: Int64) {
  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[P]] : $*P1 to $*@opened([[UUID:".*"]])
  // CHECK: [[F1:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P12f1{{.*}}
  // CHECK: apply [[F1]]<@opened([[UUID]]) P1>([[POPENED]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> ()
  p1.f1()

  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[P]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: copy_addr [[POPENED]] to [initialization] [[POPENED_COPY:%.*]] :
  // CHECK: [[GETTER:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P1g9subscriptFVs5Int64Sb
  // CHECK: apply [[GETTER]]<@opened([[UUID]]) P1>([[I]], [[POPENED_COPY]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (Int64, @in_guaranteed τ_0_0) -> Bool
  // CHECK: destroy_addr [[POPENED_COPY]]
  // CHECK: store{{.*}} : $*Bool
  // CHECK: dealloc_stack [[POPENED_COPY]]
  var b2 = p1[i]

  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[P]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: copy_addr [[POPENED]] to [initialization] [[POPENED_COPY:%.*]] :
  // CHECK: [[GETTER:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P1g4propSb
  // CHECK: apply [[GETTER]]<@opened([[UUID]]) P1>([[POPENED_COPY]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> Bool
  // CHECK: store{{.*}} : $*Bool
  // CHECK: dealloc_stack [[POPENED_COPY]]
  var b3 = p1.prop
}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions17testExistentials2
// CHECK: bb0([[P:%[0-9]+]] : $*P1):
func testExistentials2(p1: P1) {
  // CHECK: [[P1A:%[0-9]+]] = alloc_box $P1
  // CHECK: [[PB:%.*]] = project_box [[P1A]]
  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[P]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: [[P1AINIT:%[0-9]+]] = init_existential_addr [[PB]] : $*P1, $@opened([[UUID2:".*"]]) P1
  // CHECK: [[FN:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P111returnsSelf{{.*}}
  // CHECK: apply [[FN]]<@opened([[UUID]]) P1>([[P1AINIT]], [[POPENED]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> @out τ_0_0
  var p1a: P1 = p1.returnsSelf()
}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions23testExistentialsGetters
// CHECK: bb0([[P:%[0-9]+]] : $*P1):
func testExistentialsGetters(p1: P1) {
  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[P]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: copy_addr [[POPENED]] to [initialization] [[POPENED_COPY:%.*]] :
  // CHECK: [[FN:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P1g5prop2Sb
  // CHECK: [[B:%[0-9]+]] = apply [[FN]]<@opened([[UUID]]) P1>([[POPENED_COPY]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> Bool
  let b: Bool = p1.prop2

  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[P]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: copy_addr [[POPENED]] to [initialization] [[POPENED_COPY:%.*]] :
  // CHECK: [[GETTER:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P1g9subscriptFSbSb
  // CHECK: apply [[GETTER]]<@opened([[UUID]]) P1>([[B]], [[POPENED_COPY]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (Bool, @in_guaranteed τ_0_0) -> Bool
  let b2: Bool = p1[b]
}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions22testExistentialSetters
// CHECK: bb0([[P:%[0-9]+]] : $*P1, [[B:%[0-9]+]] : $Bool):
func testExistentialSetters(p1: P1, b: Bool) {
  var p1 = p1
  // CHECK: [[PBOX:%[0-9]+]] = alloc_box $P1
  // CHECK: [[PBP:%[0-9]+]] = project_box [[PBOX]]
  // CHECK-NEXT: copy_addr [[P]] to [initialization] [[PBP]] : $*P1
  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[PBP]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: [[GETTER:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P1s5prop2Sb
  // CHECK: apply [[GETTER]]<@opened([[UUID]]) P1>([[B]], [[POPENED]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (Bool, @inout τ_0_0) -> ()
  // CHECK-NOT: deinit_existential_addr
  p1.prop2 = b

  // CHECK: [[POPENED:%[0-9]+]] = open_existential_addr [[PBP]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: [[SUBSETTER:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P1s9subscriptFSbSb
  // CHECK: apply [[SUBSETTER]]<@opened([[UUID]]) P1>([[B]], [[B]], [[POPENED]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (Bool, Bool, @inout τ_0_0) -> ()
  // CHECK-NOT: deinit_existential_addr [[PB]] : $*P1
  p1[b] = b

  // CHECK: return
}

struct HasAP1 {
  var p1: P1

  var someP1: P1 {
    get { return p1 }
    set { p1 = newValue }
  }
}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions29testLogicalExistentialSetters
// CHECK: bb0([[HASP1:%[0-9]+]] : $*HasAP1, [[B:%[0-9]+]] : $Bool)
func testLogicalExistentialSetters(hasAP1: HasAP1, _ b: Bool) {
  var hasAP1 = hasAP1
  // CHECK: [[HASP1_BOX:%[0-9]+]] = alloc_box $HasAP1
  // CHECK: [[PBHASP1:%[0-9]+]] = project_box [[HASP1_BOX]]
  // CHECK-NEXT: copy_addr [[HASP1]] to [initialization] [[PBHASP1]] : $*HasAP1
  // CHECK: [[P1_COPY:%[0-9]+]] = alloc_stack $P1
  // CHECK-NEXT: [[HASP1_COPY:%[0-9]+]] = alloc_stack $HasAP1
  // CHECK-NEXT: copy_addr [[PBHASP1]] to [initialization] [[HASP1_COPY]] : $*HasAP1
  // CHECK: [[SOMEP1_GETTER:%[0-9]+]] = function_ref @_TFV19protocol_extensions6HasAP1g6someP1PS_2P1_ : $@convention(method) (@in_guaranteed HasAP1) -> @out P1
  // CHECK: [[RESULT:%[0-9]+]] = apply [[SOMEP1_GETTER]]([[P1_COPY]], %8) : $@convention(method) (@in_guaranteed HasAP1) -> @out P1
  // CHECK: [[P1_OPENED:%[0-9]+]] = open_existential_addr [[P1_COPY]] : $*P1 to $*@opened([[UUID:".*"]]) P1
  // CHECK: [[PROP2_SETTER:%[0-9]+]] = function_ref @_TFE19protocol_extensionsPS_2P1s5prop2Sb : $@convention(method) <τ_0_0 where τ_0_0 : P1> (Bool, @inout τ_0_0) -> ()
  // CHECK: apply [[PROP2_SETTER]]<@opened([[UUID]]) P1>([[B]], [[P1_OPENED]]) : $@convention(method) <τ_0_0 where τ_0_0 : P1> (Bool, @inout τ_0_0) -> ()
  // CHECK: [[SOMEP1_SETTER:%[0-9]+]] = function_ref @_TFV19protocol_extensions6HasAP1s6someP1PS_2P1_ : $@convention(method) (@in P1, @inout HasAP1) -> ()
  // CHECK: apply [[SOMEP1_SETTER]]([[P1_COPY]], [[PBHASP1]]) : $@convention(method) (@in P1, @inout HasAP1) -> ()
  // CHECK-NOT: deinit_existential_addr
  hasAP1.someP1.prop2 = b
  // CHECK: return
}

func plusOneP1() -> P1 {}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions38test_open_existential_semantics_opaque
func test_open_existential_semantics_opaque(guaranteed: P1,
                                            immediate: P1) {
  var immediate = immediate
  // CHECK: [[IMMEDIATE_BOX:%.*]] = alloc_box $P1
  // CHECK: [[PB:%.*]] = project_box [[IMMEDIATE_BOX]]
  // CHECK: [[VALUE:%.*]] = open_existential_addr %0
  // CHECK: [[METHOD:%.*]] = function_ref
  // CHECK: apply [[METHOD]]<{{.*}}>([[VALUE]])

  guaranteed.f1()
  
  // -- Need a guaranteed copy because it's immutable
  // CHECK: copy_addr [[PB]] to [initialization] [[IMMEDIATE:%.*]] :
  // CHECK: [[VALUE:%.*]] = open_existential_addr [[IMMEDIATE]]
  // CHECK: [[METHOD:%.*]] = function_ref
  // -- Can consume the value from our own copy
  // CHECK: apply [[METHOD]]<{{.*}}>([[VALUE]])
  // CHECK: deinit_existential_addr [[IMMEDIATE]]
  // CHECK: dealloc_stack [[IMMEDIATE]]
  immediate.f1()

  // CHECK: [[PLUS_ONE:%.*]] = alloc_stack $P1
  // CHECK: [[VALUE:%.*]] = open_existential_addr [[PLUS_ONE]]
  // CHECK: [[METHOD:%.*]] = function_ref
  // -- Can consume the value from our own copy
  // CHECK: apply [[METHOD]]<{{.*}}>([[VALUE]])
  // CHECK: deinit_existential_addr [[PLUS_ONE]]
  // CHECK: dealloc_stack [[PLUS_ONE]]
  plusOneP1().f1()
}

protocol CP1: class {}

extension CP1 {
  final func f1() { }
}

func plusOneCP1() -> CP1 {}

// CHECK-LABEL: sil hidden @_TF19protocol_extensions37test_open_existential_semantics_class
func test_open_existential_semantics_class(guaranteed: CP1,
                                           immediate: CP1) {
  var immediate = immediate
  // CHECK: [[IMMEDIATE_BOX:%.*]] = alloc_box $CP1
  // CHECK: [[PB:%.*]] = project_box [[IMMEDIATE_BOX]]

  // CHECK-NOT: strong_retain %0
  // CHECK: [[VALUE:%.*]] = open_existential_ref %0
  // CHECK: [[METHOD:%.*]] = function_ref
  // CHECK: apply [[METHOD]]<{{.*}}>([[VALUE]])
  // CHECK-NOT: strong_release [[VALUE]]
  // CHECK-NOT: strong_release %0
  guaranteed.f1()

  // CHECK: [[IMMEDIATE:%.*]] = load [[PB]]
  // CHECK: strong_retain [[IMMEDIATE]]
  // CHECK: [[VALUE:%.*]] = open_existential_ref [[IMMEDIATE]]
  // CHECK: [[METHOD:%.*]] = function_ref
  // CHECK: apply [[METHOD]]<{{.*}}>([[VALUE]])
  // CHECK: strong_release [[VALUE]]
  // CHECK-NOT: strong_release [[IMMEDIATE]]
  immediate.f1()

  // CHECK: [[F:%.*]] = function_ref {{.*}}plusOneCP1
  // CHECK: [[PLUS_ONE:%.*]] = apply [[F]]()
  // CHECK: [[VALUE:%.*]] = open_existential_ref [[PLUS_ONE]]
  // CHECK: [[METHOD:%.*]] = function_ref
  // CHECK: apply [[METHOD]]<{{.*}}>([[VALUE]])
  // CHECK: strong_release [[VALUE]]
  // CHECK-NOT: strong_release [[PLUS_ONE]]
  plusOneCP1().f1()
}

protocol InitRequirement {
  init(c: C)
}

extension InitRequirement {
  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_15InitRequirementC{{.*}} : $@convention(thin) <Self where Self : InitRequirement> (@owned D, @thick Self.Type) -> @out Self
  // CHECK:       bb0([[OUT:%.*]] : $*Self, [[ARG:%.*]] : $D, [[SELF_TYPE:%.*]] : $@thick Self.Type):
  init(d: D) {
  // CHECK:         [[DELEGATEE:%.*]] = witness_method $Self, #InitRequirement.init!allocator.1 : $@convention(witness_method) <τ_0_0 where τ_0_0 : InitRequirement> (@owned C, @thick τ_0_0.Type) -> @out τ_0_0
  // CHECK:         [[ARG_UP:%.*]] = upcast [[ARG]]
  // CHECK:         apply [[DELEGATEE]]<Self>({{%.*}}, [[ARG_UP]], [[SELF_TYPE]])
    self.init(c: d)
  }

  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_15InitRequirementC{{.*}}
  // CHECK:         function_ref @_TFE19protocol_extensionsPS_15InitRequirementC{{.*}}
  init(d2: D) {
    self.init(d: d2)
  }
}

protocol ClassInitRequirement: class {
  init(c: C)
}

extension ClassInitRequirement {
  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_20ClassInitRequirementC{{.*}} : $@convention(thin) <Self where Self : ClassInitRequirement> (@owned D, @thick Self.Type) -> @owned Self
  // CHECK:       bb0([[ARG:%.*]] : $D, [[SELF_TYPE:%.*]] : $@thick Self.Type):
  // CHECK:         [[DELEGATEE:%.*]] = witness_method $Self, #ClassInitRequirement.init!allocator.1 : $@convention(witness_method) <τ_0_0 where τ_0_0 : ClassInitRequirement> (@owned C, @thick τ_0_0.Type) -> @owned τ_0_0
  // CHECK:         [[ARG_UP:%.*]] = upcast [[ARG]]
  // CHECK:         apply [[DELEGATEE]]<Self>([[ARG_UP]], [[SELF_TYPE]])
  init(d: D) {
    self.init(c: d)
  }
}

@objc class OC {}
@objc class OD: OC {}

@objc protocol ObjCInitRequirement {
  init(c: OC, d: OC)
}

func foo(t: ObjCInitRequirement.Type, c: OC) -> ObjCInitRequirement {
  return t.init(c: OC(), d: OC())
}

extension ObjCInitRequirement {
  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_19ObjCInitRequirementC{{.*}} : $@convention(thin) <Self where Self : ObjCInitRequirement> (@owned OD, @thick Self.Type) -> @owned Self
  // CHECK:       bb0([[ARG:%.*]] : $OD, [[SELF_TYPE:%.*]] : $@thick Self.Type):
  // CHECK:         [[OBJC_SELF_TYPE:%.*]] = thick_to_objc_metatype [[SELF_TYPE]]
  // CHECK:         [[SELF:%.*]] = alloc_ref_dynamic [objc] [[OBJC_SELF_TYPE]] : $@objc_metatype Self.Type, $Self
  // CHECK:         [[WITNESS:%.*]] = witness_method [volatile] $Self, #ObjCInitRequirement.init!initializer.1.foreign : $@convention(objc_method) <τ_0_0 where τ_0_0 : ObjCInitRequirement> (OC, OC, @owned τ_0_0) -> @owned τ_0_0
  // CHECK:         [[UPCAST1:%.*]] = upcast [[ARG]]
  // CHECK:         [[UPCAST2:%.*]] = upcast [[ARG]]
  // CHECK:         apply [[WITNESS]]<Self>([[UPCAST1]], [[UPCAST2]], [[SELF]])
  init(d: OD) {
    self.init(c: d, d: d)
  }
}

// rdar://problem/21370992 - delegation from an initializer in a
// protocol extension to an @objc initializer in a class.
class ObjCInitClass {
  @objc init() { }
}

protocol ProtoDelegatesToObjC { }

extension ProtoDelegatesToObjC where Self : ObjCInitClass {
  // CHECK-LABEL: sil hidden @_TFe19protocol_extensionsRxCS_13ObjCInitClassxS_20ProtoDelegatesToObjCrS1_C{{.*}}
  // CHECK: bb0([[STR:%[0-9]+]] : $String, [[SELF_META:%[0-9]+]] : $@thick Self.Type):
  init(string: String) {
    // CHECK:   [[SELF_BOX:%[0-9]+]] = alloc_box $Self
    // CHECK:   [[PB:%.*]] = project_box [[SELF_BOX]]
    // CHECK:   [[SELF:%[0-9]+]] = mark_uninitialized [delegatingself] [[PB]] : $*Self
    // CHECK:   [[SELF_META_OBJC:%[0-9]+]] = thick_to_objc_metatype [[SELF_META]] : $@thick Self.Type to $@objc_metatype Self.Type
    // CHECK:   [[SELF_ALLOC:%[0-9]+]] = alloc_ref_dynamic [objc] [[SELF_META_OBJC]] : $@objc_metatype Self.Type, $Self
    // CHECK:   [[SELF_ALLOC_C:%[0-9]+]] = upcast [[SELF_ALLOC]] : $Self to $ObjCInitClass
    // CHECK:   [[OBJC_INIT:%[0-9]+]] = class_method [[SELF_ALLOC_C]] : $ObjCInitClass, #ObjCInitClass.init!initializer.1 : ObjCInitClass.Type -> () -> ObjCInitClass , $@convention(method) (@owned ObjCInitClass) -> @owned ObjCInitClass
    // CHECK:   [[SELF_RESULT:%[0-9]+]] = apply [[OBJC_INIT]]([[SELF_ALLOC_C]]) : $@convention(method) (@owned ObjCInitClass) -> @owned ObjCInitClass
    // CHECK:   [[SELF_RESULT_AS_SELF:%[0-9]+]] = unchecked_ref_cast [[SELF_RESULT]] : $ObjCInitClass to $Self
    // CHECK:   assign [[SELF_RESULT_AS_SELF]] to [[SELF]] : $*Self
    self.init()
  }
}

// Delegating from an initializer in a protocol extension where Self
// has a superclass to a required initializer of that class.
class RequiredInitClass {
  required init() { }
}

protocol ProtoDelegatesToRequired { }

extension ProtoDelegatesToRequired where Self : RequiredInitClass {
  // CHECK-LABEL: sil hidden @_TFe19protocol_extensionsRxCS_17RequiredInitClassxS_24ProtoDelegatesToRequiredrS1_C{{.*}} 
  // CHECK: bb0([[STR:%[0-9]+]] : $String, [[SELF_META:%[0-9]+]] : $@thick Self.Type):
  init(string: String) {
  // CHECK:   [[SELF_BOX:%[0-9]+]] = alloc_box $Self
  // CHECK:   [[PB:%.*]] = project_box [[SELF_BOX]]
  // CHECK:   [[SELF:%[0-9]+]] = mark_uninitialized [delegatingself] [[PB]] : $*Self
  // CHECK:   [[SELF_META_AS_CLASS_META:%[0-9]+]] = upcast [[SELF_META]] : $@thick Self.Type to $@thick RequiredInitClass.Type
  // CHECK:   [[INIT:%[0-9]+]] = class_method [[SELF_META_AS_CLASS_META]] : $@thick RequiredInitClass.Type, #RequiredInitClass.init!allocator.1 : RequiredInitClass.Type -> () -> RequiredInitClass , $@convention(thin) (@thick RequiredInitClass.Type) -> @owned RequiredInitClass
  // CHECK:   [[SELF_RESULT:%[0-9]+]] = apply [[INIT]]([[SELF_META_AS_CLASS_META]]) : $@convention(thin) (@thick RequiredInitClass.Type) -> @owned RequiredInitClass
  // CHECK:   [[SELF_RESULT_AS_SELF:%[0-9]+]] = unchecked_ref_cast [[SELF_RESULT]] : $RequiredInitClass to $Self
  // CHECK:   assign [[SELF_RESULT_AS_SELF]] to [[SELF]] : $*Self
    self.init()
  }
}

// ----------------------------------------------------------------------------
// Default implementations via protocol extensions
// ----------------------------------------------------------------------------

protocol P2 {
  typealias A
  func f1(a: A)
  func f2(a: A)
  var x: A { get }
}

extension P2 {
  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_2P22f1{{.*}}
  // CHECK: witness_method $Self, #P2.f2!1
  // CHECK: function_ref @_TFE19protocol_extensionsPS_2P22f3{{.*}}
  // CHECK: return
  func f1(a: A) {
    f2(a)
    f3(a)
  }

  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_2P22f2{{.*}}
  // CHECK: witness_method $Self, #P2.f1!1
  // CHECK: function_ref @_TFE19protocol_extensionsPS_2P22f3{{.*}}
  // CHECK: return
  func f2(a: A) {
    f1(a)
    f3(a)
  }

  func f3(a: A) {}

  // CHECK-LABEL: sil hidden @_TFE19protocol_extensionsPS_2P22f4{{.*}}
  // CHECK: witness_method $Self, #P2.f1!1
  // CHECK: witness_method $Self, #P2.f2!1
  // CHECK: return
  func f4() {
    f1(x)
    f2(x)
  }
}
