// RUN: %target-typecheck-verify-swift
// RUN: %target-swift-ide-test -print-ast-typechecked -source-filename=%s -disable-objc-attr-requires-foundation-module | %FileCheck %s

struct S<T> {}

public protocol P {
}
extension Int: P {
}

public protocol ProtocolWithDep {
  associatedtype Element
}

public class C1 {
}

class Base {}
class Sub : Base {}
class NonSub {}

// Specialize freestanding functions with the correct number of concrete types.
// ----------------------------------------------------------------------------

// CHECK: @_specialize(exported: false, kind: full, where T == Int)
@_specialize(where T == Int)
// CHECK: @_specialize(exported: false, kind: full, where T == S<Int>)
@_specialize(where T == S<Int>)
@_specialize(where T == Int, U == Int) // expected-error{{use of undeclared type 'U'}},
@_specialize(where T == T1) // expected-error{{use of undeclared type 'T1'}}
public func oneGenericParam<T>(_ t: T) -> T {
  return t
}

// CHECK: @_specialize(exported: false, kind: full, where T == Int, U == Int)
@_specialize(where T == Int, U == Int)
@_specialize(where T == Int) // expected-error{{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error{{Missing constraint for 'U' in '_specialize' attribute}}
public func twoGenericParams<T, U>(_ t: T, u: U) -> (T, U) {
  return (t, u)
}

@_specialize(where T == Int) // expected-error{{trailing 'where' clause in '_specialize' attribute of non-generic function 'nonGenericParam'}}
func nonGenericParam(x: Int) {}

// Specialize contextual types.
// ----------------------------

class G<T> {
  // CHECK: @_specialize(exported: false, kind: full, where T == Int)
  @_specialize(where T == Int)
  @_specialize(where T == T) // expected-error{{Only concrete type same-type requirements are supported by '_specialize' attribute}}
	// expected-warning@-1{{redundant same-type constraint 'T' == 'T'}}
  @_specialize(where T == S<T>) // expected-error{{Only concrete type same-type requirements are supported by '_specialize' attribute}}
	// expected-error@-1{{same-type constraint 'T' == 'S<T>' is recursive}}
  @_specialize(where T == Int, U == Int) // expected-error{{use of undeclared type 'U'}}
  func noGenericParams() {}

  // CHECK: @_specialize(exported: false, kind: full, where T == Int, U == Float)
  @_specialize(where T == Int, U == Float)
  // CHECK: @_specialize(exported: false, kind: full, where T == Int, U == S<Int>)
  @_specialize(where T == Int, U == S<Int>)
  @_specialize(where T == Int) // expected-error{{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error {{Missing constraint for 'U' in '_specialize' attribute}}
  func oneGenericParam<U>(_ t: T, u: U) -> (U, T) {
    return (u, t)
  }
}

// Specialize with requirements.
// -----------------------------

protocol Thing {}

struct AThing : Thing {}

// CHECK: @_specialize(exported: false, kind: full, where T == AThing)
@_specialize(where T == AThing)
@_specialize(where T == Int) // expected-error{{same-type constraint type 'Int' does not conform to required protocol 'Thing'}}
func oneRequirement<T : Thing>(_ t: T) {}

protocol HasElt {
  associatedtype Element
}
struct IntElement : HasElt {
  typealias Element = Int
}
struct FloatElement : HasElt {
  typealias Element = Float
}
@_specialize(where T == FloatElement)
@_specialize(where T == IntElement) // expected-error{{'T.Element' cannot be equal to both 'IntElement.Element' (aka 'Int') and 'Float'}}
func sameTypeRequirement<T : HasElt>(_ t: T) where T.Element == Float {}

@_specialize(where T == Sub)
@_specialize(where T == NonSub) // expected-error{{'T' requires that 'NonSub' inherit from 'Base'}}
func superTypeRequirement<T : Base>(_ t: T) {}

@_specialize(where X:_Trivial(8), Y == Int) // expected-error{{trailing 'where' clause in '_specialize' attribute of non-generic function 'requirementOnNonGenericFunction'}}
public func requirementOnNonGenericFunction(x: Int, y: Int) {
}

@_specialize(where Y == Int) // expected-error{{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error{{Missing constraint for 'X' in '_specialize' attribute}}
public func missingRequirement<X:P, Y>(x: X, y: Y) {
}

@_specialize(where) // expected-error{{expected identifier for type name}}
@_specialize() // expected-error{{expected a parameter label or a where clause in '_specialize' attribute}} expected-error{{expected declaration}}
public func funcWithEmptySpecializeAttr<X: P, Y>(x: X, y: Y) {
}


@_specialize(where X:_Trivial(8), Y:_Trivial(32), Z == Int) // expected-error{{use of undeclared type 'Z'}}
@_specialize(where X:_Trivial(8), Y:_Trivial(32, 4))
@_specialize(where X == Int) // expected-error{{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error{{Missing constraint for 'Y' in '_specialize' attribute}}
@_specialize(where Y:_Trivial(32)) // expected-error {{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error{{Missing constraint for 'X' in '_specialize' attribute}}
@_specialize(where Y: P) // expected-error{{Only same-type and layout requirements are supported by '_specialize' attribute}} expected-error{{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error{{Missing constraint for 'X' in '_specialize' attribute}}
@_specialize(where Y: MyClass) // expected-error{{use of undeclared type 'MyClass'}} expected-error{{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error{{Missing constraint for 'X' in '_specialize' attribute}}
@_specialize(where X:_Trivial(8), Y == Int)
@_specialize(where X == Int, Y == Int)
@_specialize(where X == Int, X == Int) // expected-error{{too few type parameters are specified in '_specialize' attribute (got 1, but expected 2)}} expected-error{{Missing constraint for 'Y' in '_specialize' attribute}}
// expected-warning@-1{{redundant same-type constraint 'X' == 'Int'}}
// expected-note@-2{{same-type constraint 'X' == 'Int' written here}}
@_specialize(where Y:_Trivial(32), X == Float)
@_specialize(where X1 == Int, Y1 == Int) // expected-error{{use of undeclared type 'X1'}} expected-error{{use of undeclared type 'Y1'}} expected-error{{too few type parameters are specified in '_specialize' attribute (got 0, but expected 2)}} expected-error{{Missing constraint for 'X' in '_specialize' attribute}} expected-error{{Missing constraint for 'Y' in '_specialize' attribute}}
public func funcWithTwoGenericParameters<X, Y>(x: X, y: Y) {
}

@_specialize(where X == Int, Y == Int)
@_specialize(exported: true, where X == Int, Y == Int)
@_specialize(exported: false, where X == Int, Y == Int)
@_specialize(exported: false where X == Int, Y == Int) // expected-error{{missing ',' in '_specialize' attribute}}
@_specialize(exported: yes, where X == Int, Y == Int) // expected-error{{expected a boolean true or false value in '_specialize' attribute}}
@_specialize(exported: , where X == Int, Y == Int) // expected-error{{expected a boolean true or false value in '_specialize' attribute}}

@_specialize(kind: partial, where X == Int, Y == Int)
@_specialize(kind: partial, where X == Int)
@_specialize(kind: full, where X == Int, Y == Int)
@_specialize(kind: any, where X == Int, Y == Int) // expected-error{{expected 'partial' or 'full' as values of the 'kind' parameter in '_specialize' attribute}}
@_specialize(kind: false, where X == Int, Y == Int) // expected-error{{expected 'partial' or 'full' as values of the 'kind' parameter in '_specialize' attribute}}
@_specialize(kind: partial where X == Int, Y == Int) // expected-error{{missing ',' in '_specialize' attribute}}
@_specialize(kind: partial, where X == Int, Y == Int)
@_specialize(kind: , where X == Int, Y == Int)

@_specialize(exported: true, kind: partial, where X == Int, Y == Int)
@_specialize(exported: true, exported: true, where X == Int, Y == Int) // expected-error{{parameter 'exported' was already defined in '_specialize' attribute}}
@_specialize(kind: partial, exported: true, where X == Int, Y == Int)
@_specialize(kind: partial, kind: partial, where X == Int, Y == Int) // expected-error{{parameter 'kind' was already defined in '_specialize' attribute}}

@_specialize(where X == Int, Y == Int, exported: true, kind: partial) // expected-error{{use of undeclared type 'exported'}} expected-error{{use of undeclared type 'kind'}} expected-error{{use of undeclared type 'partial'}} expected-error{{expected identifier for type name}}
public func anotherFuncWithTwoGenericParameters<X: P, Y>(x: X, y: Y) {
}

@_specialize(where T: P) // expected-error{{Only same-type and layout requirements are supported by '_specialize' attribute}}
@_specialize(where T: Int) // expected-error{{Only conformances to protocol types are supported by '_specialize' attribute}} expected-error{{Only same-type and layout requirements are supported by '_specialize' attribute}}
// expected-error@-1{{type 'T' constrained to non-protocol type 'Int'}}

@_specialize(where T: S1) // expected-error{{Only conformances to protocol types are supported by '_specialize' attribute}} expected-error{{Only same-type and layout requirements are supported by '_specialize' attribute}}
// expected-error@-1{{type 'T' constrained to non-protocol type 'S1'}}
@_specialize(where T: C1) // expected-error{{Only conformances to protocol types are supported by '_specialize' attribute}} expected-error{{Only same-type and layout requirements are supported by '_specialize' attribute}}
@_specialize(where Int: P) // expected-error{{type 'Int' in conformance requirement does not refer to a generic parameter or associated type}} expected-error{{Only same-type and layout requirements are supported by '_specialize' attribute}} expected-error{{too few type parameters are specified in '_specialize' attribute (got 0, but expected 1)}} expected-error{{Missing constraint for 'T' in '_specialize' attribute}}
func funcWithForbiddenSpecializeRequirement<T>(_ t: T) {
}

@_specialize(where T: _Trivial(32), T: _Trivial(64), T: _Trivial, T: _RefCountedObject)
// expected-error@-1{{generic parameter 'T' has conflicting layout constraints '_Trivial(64)' and '_Trivial(32)'}}
// expected-error@-2{{generic parameter 'T' has conflicting layout constraints '_RefCountedObject' and '_Trivial(32)'}}
// expected-warning@-3{{redundant layout constraint 'T' : '_Trivial'}}
// expected-note@-4 3{{layout constraint constraint 'T' : '_Trivial(32)' written here}}
@_specialize(where T: _Trivial, T: _Trivial(64))
// expected-warning@-1{{redundant layout constraint 'T' : '_Trivial'}}
// expected-note@-2 1{{layout constraint constraint 'T' : '_Trivial(64)' written here}}
@_specialize(where T: _RefCountedObject, T: _NativeRefCountedObject)
// expected-warning@-1{{redundant layout constraint 'T' : '_RefCountedObject'}}
// expected-note@-2 1{{layout constraint constraint 'T' : '_NativeRefCountedObject' written here}}
@_specialize(where Array<T> == Int) // expected-error{{Only requirements on generic parameters are supported by '_specialize' attribute}}
// expected-error@-1{{generic signature requires types 'Array<T>' and 'Int' to be the same}}
@_specialize(where T.Element == Int) // expected-error{{Only requirements on generic parameters are supported by '_specialize' attribute}}
public func funcWithComplexSpecializeRequirements<T: ProtocolWithDep>(t: T) -> Int {
  return 55555
}

public protocol Proto: class {
}

@_specialize(where T: _RefCountedObject)
// expected-warning@-1{{redundant layout constraint 'T' : '_RefCountedObject'}}
@_specialize(where T: _Trivial)
// expected-error@-1{{generic parameter 'T' has conflicting layout constraints '_Trivial' and '_NativeClass'}}
@_specialize(where T: _Trivial(64))
// expected-error@-1{{generic parameter 'T' has conflicting layout constraints '_Trivial(64)' and '_NativeClass'}}
public func funcWithABaseClassRequirement<T>(t: T) -> Int where T: C1 {
  return 44444
}

public struct S1 {
}

@_specialize(exported: false, where T == Int64)
public func simpleGeneric<T>(t: T) -> T {
  return t
}


@_specialize(exported: true, where S: _Trivial(64))
// Check that any bitsize size is OK, not only powers of 8.
@_specialize(where S: _Trivial(60))
@_specialize(exported: true, where S: _RefCountedObject)
@inline(never)
public func copyValue<S>(_ t: S, s: inout S) -> Int64 where S: P{
  return 1
}

@_specialize(exported: true, where S: _Trivial)
@_specialize(exported: true, where S: _Trivial(64))
@_specialize(exported: true, where S: _Trivial(32))
@_specialize(exported: true, where S: _RefCountedObject)
@_specialize(exported: true, where S: _NativeRefCountedObject)
@_specialize(exported: true, where S: _Class)
@_specialize(exported: true, where S: _NativeClass)
@inline(never)
public func copyValueAndReturn<S>(_ t: S, s: inout S) -> S where S: P{
  return s
}

struct OuterStruct<S> {
  struct MyStruct<T> {
    @_specialize(where T == Int, U == Float) // expected-error{{too few type parameters are specified in '_specialize' attribute (got 2, but expected 3)}} expected-error{{Missing constraint for 'S' in '_specialize' attribute}}
    public func foo<U>(u : U) {
    }

    @_specialize(where T == Int, U == Float, S == Int)
    public func bar<U>(u : U) {
    }
  }
}

// Check _TrivialAtMostN constraints.
@_specialize(exported: true, where S: _TrivialAtMost(64))
@inline(never)
public func copy2<S>(_ t: S, s: inout S) -> S where S: P{
  return s
}

// Check missing alignment.
@_specialize(where S: _Trivial(64, )) // expected-error{{expected non-negative alignment to be specified in layout constraint}}
// Check non-numeric size.
@_specialize(where S: _Trivial(Int)) // expected-error{{expected non-negative size to be specified in layout constraint}}
// Check non-numeric alignment.
@_specialize(where S: _Trivial(64, X)) // expected-error{{expected non-negative alignment to be specified in layout constraint}}
@inline(never)
public func copy3<S>(_ s: S) -> S {
  return s
}

public func funcWithWhereClause<T>(t: T) where T:P, T: _Trivial(64) { // expected-error{{layout constraints are only allowed inside '_specialize' attributes}}
}

// rdar://problem/29333056
public protocol P1 {
  associatedtype DP1
  associatedtype DP11
}

public protocol P2 {
  associatedtype DP2 : P1
}

public struct H<T> {
}

public struct MyStruct3 : P1 {
  public typealias DP1 = Int
  public typealias DP11 = H<Int>
}

public struct MyStruct4 : P2 {
  public typealias DP2 = MyStruct3
}

@_specialize(where T==MyStruct4)
public func foo<T: P2>(_ t: T) where T.DP2.DP11 == H<T.DP2.DP1> {
}
