// RUN: %target-swift-frontend -emit-silgen -disable-availability-checking -module-name main -enable-subst-sil-function-types-for-function-values %s | %FileCheck %s

func generic<T, U>(_ f: @escaping (T) -> U) -> (T) -> U { return f }

// CHECK-LABEL: sil {{.*}}4main{{.*}}11sameGeneric
func sameGeneric<X, Y, Z>(_: X, _: Y, _ f: @escaping (Z) -> Z) -> (Z) -> Z {
// CHECK: bb0({{.*}}, [[F:%[0-9]+]] : @guaranteed $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Z, Z>
  // Similarly generic types should be directly substitutable
  // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}7generic
  // CHECK: [[RET:%.*]] = apply [[GENERIC]]<Z, Z>([[F]])
  // CHECK: return [[RET]]
  return generic(f)
}

// CHECK-LABEL: sil {{.*}}4main{{.*}}16concreteIndirect
func concreteIndirect(_ f: @escaping (Any) -> Any) -> (Any) -> Any {
// CHECK: bb0([[F:%[0-9]+]] : @guaranteed $@callee_guaranteed (@in_guaranteed Any) -> @out Any)
  // Any is passed indirectly, but is a concrete type, so we need to convert
  // to the generic abstraction level
  // CHECK: [[F2:%.*]] = copy_value [[F]]
  // CHECK: [[GENERIC_F:%.*]] = convert_function [[F2]] : {{.*}} to $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Any, Any>
  // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}7generic
  // CHECK: [[GENERIC_RET:%.*]] = apply [[GENERIC]]<Any, Any>([[GENERIC_F]])
  // CHECK: [[RET:%.*]] = convert_function [[GENERIC_RET]] : {{.*}}
  // CHECK: return [[RET]]
  return generic(f)
}

// CHECK-LABEL: sil {{.*}}4main{{.*}}14concreteDirect
func concreteDirect(_ f: @escaping (Int) -> String) -> (Int) -> String {
// CHECK: bb0([[F:%[0-9]+]] : @guaranteed $@callee_guaranteed (Int) -> @owned String):
  // Int and String are passed and returned directly, so we need both
  // thunking and conversion to the substituted form
  // CHECK: [[F2:%.*]] = copy_value [[F]]
  // CHECK: [[REABSTRACT_F:%.*]] = partial_apply {{.*}}([[F2]])
  // CHECK: [[GENERIC_F:%.*]] = convert_function [[REABSTRACT_F]] : {{.*}} to $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Int, String>
  // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}7generic
  // CHECK: [[GENERIC_RET:%.*]] = apply [[GENERIC]]<Int, String>([[GENERIC_F]])
  // CHECK: [[REABSTRACT_RET:%.*]] = convert_function [[GENERIC_RET]] : {{.*}}
  // CHECK: [[RET:%.*]] = partial_apply {{.*}}([[REABSTRACT_RET]])
  // CHECK: return [[RET]]

  return generic(f)
}

func genericTakesFunction<T, U>(
  _ f: @escaping ((T) -> U) -> (T) -> U
) -> ((T) -> U) -> (T) -> U { return f }

func sameGenericTakesFunction<T>(
  _ f: @escaping ((T) -> T) -> (T) -> T
) -> ((T) -> T) -> (T) -> T {
  return genericTakesFunction(f)
}

// CHECK-LABEL: sil {{.*}}4main29concreteIndirectTakesFunction
func concreteIndirectTakesFunction(
  _ f: @escaping ((Any) -> Any) -> (Any) -> Any
) -> ((Any) -> Any) -> (Any) -> Any {
// CHECK: bb0([[F:%[0-9]+]] : @guaranteed $@callee_guaranteed

  // Calling convention matches callee, but the representation of the argument
  // to `f` needs to change, so we still have to thunk
  // CHECK: [[F2:%.*]] = copy_value [[F]]
  // CHECK: [[REABSTRACT_F:%.*]] = partial_apply {{.*}}([[F2]])
  // CHECK: [[GENERIC_F:%.*]] = convert_function [[REABSTRACT_F]]
  // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}20genericTakesFunction
  // CHECK: [[GENERIC_RET:%.*]] = apply [[GENERIC]]<Any, Any>([[GENERIC_F]])
  // CHECK: [[REABSTRACT_RET:%.*]] = convert_function [[GENERIC_RET]] : {{.*}}
  // CHECK: [[RET:%.*]] = partial_apply {{.*}}([[REABSTRACT_RET]])
  // CHECK: return [[RET]]
  return genericTakesFunction(f)
}

func concreteDirectTakesFunction(
  _ f: @escaping ((Int) -> String) -> (Int) -> String
) -> ((Int) -> String) -> (Int) -> String {
  // Int and String are passed and returned directly, so we need both
  // thunking and conversion to the substituted form
  return genericTakesFunction(f)
}
