// RUN: %target-run-simple-swift | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime
// REQUIRES: executable_test

// Note: JIT mode is checked in Interpreter/protocol_lookup_jit.swift.

protocol Fooable {
  func foo()
}

struct S: Fooable {
  func foo() { print("S") }
}

class C: Fooable {
  func foo() { print("C") }
}

class D: C {
  override func foo() { print("D") }
}

class E: D {
  override func foo() { print("E") }
}

struct X {}

extension Int: Fooable {
  func foo() { print("Int") }
}

func fooify<T>(x: T) {
  if let foo = x as? Fooable {
    foo.foo()
  } else {
    print("not fooable")
  }
}

struct G<T>: Fooable {
  func foo() { print("G") }
}

struct H<T> {}

fooify(1)   // CHECK:      Int
fooify(2)   // CHECK-NEXT: Int
fooify(S()) // CHECK-NEXT: S
fooify(S()) // CHECK-NEXT: S
fooify(C()) // CHECK-NEXT: C
fooify(C()) // CHECK-NEXT: C
fooify(D()) // CHECK-NEXT: D
fooify(D()) // CHECK-NEXT: D
fooify(E()) // CHECK-NEXT: E
fooify(E()) // CHECK-NEXT: E
fooify(X()) // CHECK-NEXT: not fooable
fooify(X()) // CHECK-NEXT: not fooable
fooify(G<Int>()) // CHECK-NEXT: G
fooify(G<Float>()) // CHECK-NEXT: G
fooify(G<Int>()) // CHECK-NEXT: G
fooify(H<Int>()) // CHECK-NEXT: not fooable
fooify(H<Float>()) // CHECK-NEXT: not fooable
fooify(H<Int>()) // CHECK-NEXT: not fooable

// TODO: generics w/ dependent witness tables

// Check casts from existentials.

fooify(1 as Any)   // CHECK:      Int
fooify(2 as Any)   // CHECK-NEXT: Int
fooify(S() as Any) // CHECK-NEXT: S
fooify(S() as Any) // CHECK-NEXT: S
fooify(C() as Any) // CHECK-NEXT: C
fooify(C() as Any) // CHECK-NEXT: C
fooify(D() as Any) // CHECK-NEXT: D
fooify(D() as Any) // CHECK-NEXT: D
fooify(E() as Any) // CHECK-NEXT: E
fooify(E() as Any) // CHECK-NEXT: E
fooify(X() as Any) // CHECK-NEXT: not fooable
fooify(X() as Any) // CHECK-NEXT: not fooable
fooify(G<Int>() as Any) // CHECK-NEXT: G
fooify(G<Float>() as Any) // CHECK-NEXT: G
fooify(G<Int>() as Any) // CHECK-NEXT: G
fooify(H<Int>() as Any) // CHECK-NEXT: not fooable
fooify(H<Float>() as Any) // CHECK-NEXT: not fooable
fooify(H<Int>() as Any) // CHECK-NEXT: not fooable

protocol Barrable {
  func bar()
}
extension Int: Barrable {
  func bar() { print("Int.bar") }
}

let foo: Fooable = 2
if let bar = foo as? Barrable {
  bar.bar() // CHECK-NEXT: Int.bar
} else {
  print("not barrable")
}

let foo2: Fooable = S()
if let bar2 = foo2 as? Barrable {
  bar2.bar()
} else {
  print("not barrable") // CHECK-NEXT: not barrable
}

protocol Runcible: class {
  func runce()
}

extension C: Runcible {
  func runce() { print("C") }
}

let c1: AnyObject = C()
let c2: Any = C()
if let fruncible = c1 as? protocol<Fooable, Runcible> {
  fruncible.foo() // CHECK-NEXT: C
  fruncible.runce() // CHECK-NEXT: C
} else {
  print("not fooable and runcible")
}
if let fruncible = c2 as? protocol<Fooable, Runcible> {
  fruncible.foo() // CHECK-NEXT: C
  fruncible.runce() // CHECK-NEXT: C
} else {
  print("not fooable and runcible")
}

// Protocol lookup for metatypes.
protocol StaticFoo {
  static func foo() -> String
}

class StaticBar {
  class func mightHaveFoo() -> String {
    if let selfAsFoo = self as? StaticFoo.Type {
      return selfAsFoo.foo()
    } else {
      return "no Foo for you"
    }
  }
}

class StaticWibble : StaticBar, StaticFoo {
  static func foo() -> String { return "StaticWibble.foo" }
}

// CHECK: no Foo for you
print(StaticBar.mightHaveFoo())

// CHECK: StaticWibble.foo
print(StaticWibble.mightHaveFoo())
