// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify
// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify

func a() {  // expected-warning {{all paths through this function will call itself}}
  a()
}

func b(_ x : Int) {  // expected-warning {{all paths through this function will call itself}}
  if x != 0 {
    b(x)
  } else {
    b(x+1)
  }
}

func c(_ x : Int) {
  if x != 0 {
    c(5)
  }
}

func d(_ x : Int) {  // expected-warning {{all paths through this function will call itself}}
  var x = x
  if x != 0 {
    x += 1
  }
  return d(x)
}

// Doesn't warn on mutually recursive functions

func e() { f() }
func f() { e() }

func g() { // expected-warning {{all paths through this function will call itself}}
  while true { // expected-note {{condition always evaluates to true}}
    g()
  }

  g() // expected-warning {{will never be executed}}
}

func h(_ x : Int) {
  while (x < 5) {
    h(x+1)
  }
}

func i(_ x : Int) {  // expected-warning {{all paths through this function will call itself}}
  var x = x
  while (x < 5) {
    x -= 1
  }
  i(0)
}

func j() -> Int {  // expected-warning {{all paths through this function will call itself}}
  return 5 + j()
}

func k() -> Any {  // expected-warning {{all paths through this function will call itself}}
  return type(of: k())
}

@_silgen_name("exit") func exit(_: Int32) -> Never

func l() {
  guard Bool.random() else {
    exit(0) // no warning; calling 'exit' terminates the program
  }
  l()
}

func m() { // expected-warning {{all paths through this function will call itself}}
  guard Bool.random() else {
    fatalError() // we _do_ warn here, because fatalError is a programtermination_point
  }
  m()
}

enum MyNever {}

func blackHole() -> MyNever { // expected-warning {{all paths through this function will call itself}}
  blackHole()
}

@_semantics("programtermination_point")
func terminateMe() -> MyNever {
  terminateMe() // no warning; terminateMe is a programtermination_point
}

func n() -> MyNever {
  if Bool.random() {
    blackHole() // no warning; blackHole() will terminate the program
  }
  n()
}

func o() -> MyNever {
  if Bool.random() {
    o()
  }
  blackHole() // no warning; blackHole() will terminate the program
}

func mayHaveSideEffects() {}

func p() { // expected-warning {{all paths through this function will call itself}}
  if Bool.random() {
    mayHaveSideEffects() // presence of side-effects doesn't alter the check for the programtermination_point apply
    fatalError()
  }
  p()
}

class S {
  convenience init(a: Int) { // expected-warning {{all paths through this function will call itself}}
    self.init(a: a)
  }
  init(a: Int?) {}

  static func a() { // expected-warning {{all paths through this function will call itself}}
    return a()
  }

  func b() { // expected-warning {{all paths through this function will call itself}}
    var i = 0
    repeat {
      i += 1
      b()
    } while (i > 5)
  }

  var bar: String = "hi!"
}

class T: S {
  // No warning, calls super
  override func b() {
    var i = 0
    repeat {
      i += 1
      super.b()
    } while (i > 5)
  }

  override var bar: String {
    get {
      return super.bar
    }
    set { // expected-warning {{all paths through this function will call itself}}
      self.bar = newValue
    }
  }
}

func == (l: S?, r: S?) -> Bool { // expected-warning {{all paths through this function will call itself}}
  if l == nil && r == nil { return true }
  guard let l = l, let r = r else { return false }
  return l === r
}

public func == <Element>(lhs: Array<Element>, rhs: Array<Element>) -> Bool { // expected-warning {{all paths through this function will call itself}}
  return lhs == rhs
}

func factorial(_ n : UInt) -> UInt { // expected-warning {{all paths through this function will call itself}}
  return (n != 0) ? factorial(n - 1) * n : factorial(1)
}

func tr(_ key: String) -> String { // expected-warning {{all paths through this function will call itself}}
  return tr(key) ?? key // expected-warning {{left side of nil coalescing operator '??' has non-optional type}}
}
