// RUN: %target-typecheck-verify-swift -swift-version 4

func needsSameType<T>(_: T.Type, _: T.Type) {}

protocol Foo {}
func needsFoo<T: Foo>(_: T.Type) {}

protocol Foo2 {}
func needsFoo2<T: Foo2>(_: T.Type) {}

extension Int: Foo, Foo2 {}
extension Float: Foo {}

protocol Conforms {
    associatedtype T where T: Foo
}
func needsConforms<X: Conforms>(_: X.Type) {
    needsFoo(X.T.self)
}
struct ConcreteConforms: Conforms { typealias T = Int }
struct ConcreteConforms2: Conforms { typealias T = Int }
struct ConcreteConformsNonFoo2: Conforms { typealias T = Float }

protocol NestedConforms {
    associatedtype U where U: Conforms, U.T: Foo2 // expected-note{{protocol requires nested type 'U'; do you want to add it?}}

    func foo(_: U)
}
extension NestedConforms { func foo(_: U) {} }
func needsNestedConforms<X: NestedConforms>(_: X.Type) {
    needsConforms(X.U.self)
    needsFoo(X.U.T.self)
    needsFoo2(X.U.T.self)
}
struct ConcreteNestedConforms: NestedConforms {
    typealias U = ConcreteConforms
}
struct ConcreteNestedConformsInfer: NestedConforms {
    func foo(_: ConcreteConforms) {}
}
struct BadConcreteNestedConforms: NestedConforms {
    // expected-error@-1 {{type 'ConcreteConformsNonFoo2.T' (aka 'Float') does not conform to protocol 'Foo2'}}
    typealias U = ConcreteConformsNonFoo2
}
struct BadConcreteNestedConformsInfer: NestedConforms {
    // expected-error@-1 {{type 'BadConcreteNestedConformsInfer' does not conform to protocol 'NestedConforms'}}
    func foo(_: ConcreteConformsNonFoo2) {}
}

protocol NestedConformsDefault {
    associatedtype U = ConcreteConforms where U: Conforms, U.T: Foo2
}
struct ConcreteNestedConformsDefaultDefaulted: NestedConformsDefault {}
struct ConcreteNestedConformsDefault: NestedConformsDefault {
    typealias U = ConcreteConforms2
}
func needsNestedConformsDefault<X: NestedConformsDefault>(_: X.Type) {
    needsConforms(X.U.self)
    needsFoo(X.U.T.self)
    needsFoo2(X.U.T.self)
}

protocol NestedSameType {
    associatedtype U: Conforms where U.T == Int // expected-note{{protocol requires nested type 'U'; do you want to add it?}}

    func foo(_: U)
}
extension NestedSameType { func foo(_: U) {} }
func needsNestedSameType<X: NestedSameType>(_: X.Type) {
    needsConforms(X.U.self)
    needsSameType(X.U.T.self, Int.self)
}
struct BadConcreteNestedSameType: NestedSameType {
    // expected-error@-1 {{'NestedSameType' requires the types 'ConcreteConformsNonFoo2.T' (aka 'Float') and 'Int' be equivalent}}
    // expected-note@-2 {{requirement specified as 'Self.U.T' == 'Int' [with Self = BadConcreteNestedSameType]}}
    typealias U = ConcreteConformsNonFoo2
}
struct BadConcreteNestedSameTypeInfer: NestedSameType {
    // expected-error@-1 {{type 'BadConcreteNestedSameTypeInfer' does not conform to protocol 'NestedSameType'}}
    func foo(_: ConcreteConformsNonFoo2) {}
}

protocol NestedSameTypeDefault {
    associatedtype U: Conforms = ConcreteConforms where U.T == Int

    func foo(_: U)
}
extension NestedSameTypeDefault { func foo(_: U) {} }
func needsNestedSameTypeDefault<X: NestedSameTypeDefault>(_: X.Type) {
    needsConforms(X.U.self)
    needsSameType(X.U.T.self, Int.self)
}
struct ConcreteNestedSameTypeDefaultDefaulted: NestedSameTypeDefault {}
struct ConcreteNestedSameTypeDefault: NestedSameTypeDefault {
    typealias U = ConcreteConforms2
}
struct ConcreteNestedSameTypeDefaultInfer: NestedSameTypeDefault {
    func foo(_: ConcreteConforms2) {}
}

protocol Inherits: NestedConforms {
    associatedtype X: Conforms where X.T == U.T

    func bar(_: X)
}
extension Inherits { func bar(_: X) {} }
func needsInherits<X: Inherits>(_: X.Type) {
    needsConforms(X.U.self)
    needsConforms(X.X.self)
    needsSameType(X.U.T.self, X.X.T.self)
}
struct ConcreteInherits: Inherits {
    typealias U = ConcreteConforms
    typealias X = ConcreteConforms
}
struct ConcreteInheritsDiffer: Inherits {
    typealias U = ConcreteConforms
    typealias X = ConcreteConforms2
}
/*
FIXME: the sametype requirement gets dropped from the requirement signature
(enumerateRequirements doesn't yield it), so this doesn't error as it should.
struct BadConcreteInherits: Inherits {
    typealias U = ConcreteConforms
    typealias X = ConcreteConformsNonFoo2
}
*/

struct X { }

protocol P {
	associatedtype P1 where P1 == X
	// expected-note@-1{{same-type constraint 'Self.P1' == 'X' written here}}
	associatedtype P2 where P2 == P1, P2 == X
	// expected-warning@-1{{redundant same-type constraint 'Self.P2' == 'X'}}
}

// Lookup of same-named associated types aren't ambiguous in this context.
protocol P1 {
  associatedtype A
}

protocol P2: P1 {
  associatedtype A
  associatedtype B where A == B
}

protocol P3: P1 {
  associatedtype A
}

protocol P4 {
  associatedtype A
}

protocol P5: P3, P4 {
  associatedtype B where B == A?
}

// Associated type inference should account for where clauses.
protocol P6 {
  associatedtype A
}

struct X1 { }

struct X2 { }

struct Y1 : P6 {
  typealias A = X1
}

struct Y2 : P6 {
  typealias A = X2
}

protocol P7 {
  associatedtype B: P6 // expected-note{{ambiguous inference of associated type 'B': 'Y1' vs. 'Y2'}}
  associatedtype C: P6 where B.A == C.A

  func getB() -> B
  func getC() -> C
}

struct Z1 : P7 {
  func getB() -> Y1 { return Y1() }
  func getB() -> Y2 { return Y2() }

  func getC() -> Y1 { return Y1() }
}

func testZ1(z1: Z1) {
  let _: Z1.C = Y1()
}


struct Z2 : P7 { // expected-error{{type 'Z2' does not conform to protocol 'P7'}}
  func getB() -> Y1 { return Y1() } // expected-note{{matching requirement 'getB()' to this declaration inferred associated type to 'Y1'}}
  func getB() -> Y2 { return Y2() } // expected-note{{matching requirement 'getB()' to this declaration inferred associated type to 'Y2'}}

  func getC() -> Y1 { return Y1() }
  func getC() -> Y2 { return Y2() }
}
