// RUN: %target-typecheck-verify-swift -enable-experimental-property-behaviors -module-name Main
// REQUIRES: property_behavior_value_substitution

protocol behavior {
  associatedtype Value
}
extension behavior {
  var value: Value { fatalError("") }
}

struct NotABehavior {}

var test1: Int __behavior behavior
var test2: String __behavior Main.behavior

// expected-error@+1{{property behavior name must refer to a protocol}}
var test5: String __behavior NotABehavior

struct Concrete {
  var test: String __behavior behavior
  static var test: String __behavior behavior
}

protocol Proto { }
extension Proto {
  var test: String __behavior behavior
  static var test: String __behavior behavior
}

struct Generic<T> {
  var test: String __behavior behavior
  static var test: String __behavior behavior
}

protocol SelfRequirement {}
protocol ValueRequirement {}

protocol constraints: SelfRequirement {
  associatedtype Value: ValueRequirement
}
extension constraints {
  var value: Value { fatalError("") }
}

struct VR: ValueRequirement {}
struct GVR<T>: ValueRequirement {}
struct NVR {}

// expected-error@+3{{property behavior 'constraints' can only be used on instance properties because it has 'Self' requirements}}
// expected-error@+2{{type 'NVR' does not conform to protocol 'ValueRequirement'}}
// expected-note@+1{{behavior 'constraints' requires property type to conform to protocol 'ValueRequirement'}}
var a: NVR __behavior constraints

// expected-error@+1{{property behavior 'constraints' can only be used on instance properties because it has 'Self' requirements}}
var b: VR __behavior constraints

struct SR: SelfRequirement {
  // expected-error@+2{{type 'NVR' does not conform to protocol 'ValueRequirement'}}
  // expected-note@+1{{behavior 'constraints' requires property type to conform to protocol 'ValueRequirement'}}
  var a: NVR __behavior constraints
  var b: VR __behavior constraints

  // expected-error@+3{{property behavior 'constraints' can only be used on instance properties because it has 'Self' requirements}}
  // expected-error@+2{{type 'NVR' does not conform to protocol 'ValueRequirement'}}
  // expected-note@+1{{behavior 'constraints' requires property type to conform to protocol 'ValueRequirement'}}
  static var a: NVR __behavior constraints
  // expected-error@+1{{property behavior 'constraints' can only be used on instance properties because it has 'Self' requirements}}
  static var b: VR __behavior constraints
}

// expected-error@+1 * {{type 'NSR' does not conform to protocol 'SelfRequirement'}}
struct NSR {
  // expected-note@+3{{conformance to 'SelfRequirement' required to contain an instance property with behavior 'constraints'}}
  // expected-error@+2{{type 'NVR' does not conform to protocol 'ValueRequirement'}}
  // expected-note@+1{{behavior 'constraints' requires property type to conform to protocol 'ValueRequirement'}}
  var a: NVR __behavior constraints
  // expected-note@+1{{conformance to 'SelfRequirement' required to contain an instance property with behavior 'constraints'}}
  var b: VR __behavior constraints
}

struct GSR<T>: SelfRequirement {
  var a: VR __behavior constraints
  var b: GVR<T> __behavior constraints
}

extension GSR where T: ValueRequirement {
  var c: T __behavior constraints
}

protocol nonBehaviorReqt {
  associatedtype Value

  func notBehaviorRelated() // expected-note{{declared here}}
}
extension nonBehaviorReqt {
  var value: Value { fatalError("") }
}

// expected-error@+1{{behavior protocol 'nonBehaviorReqt' has non-behavior requirement 'notBehaviorRelated'}}
var x: Int __behavior nonBehaviorReqt

protocol hasStorage {
  associatedtype Value

  var storage: Value? { get set }
}
extension hasStorage {
  static func initStorage() -> Value? {
    return nil
  }

  // Overloads that should be disregarded
  static func initStorage() -> Value {
    fatalError("")
  }
  static func initStorage(_: String) -> Value? {
    fatalError("")
  }

  var value: Value { fatalError("") }
}

var tuple = (0,0)

var storage1: Int __behavior hasStorage // expected-error {{not supported}}
struct Foo<T> {
  static var staticStorage1: T __behavior hasStorage // expected-error{{static stored properties not supported in generic types}}
  var storage2: T __behavior hasStorage

  // FIXME: Hack because we can't find the synthesized associated type witness
  // during witness matching.
  typealias Value = T

  func foo<U>(_: U) {
    var storage1: T __behavior hasStorage // expected-error {{not supported}}
    var storage2: U __behavior hasStorage // expected-error {{not supported}}

    _ = storage1
    _ = storage2
  }
}

struct Foo2<T> {
  var storage3: Int = 0 // expected-error {{initializer expression provided, but property behavior 'hasStorage' does not use it}}
    __behavior hasStorage

  var (storage4, storage5) = tuple // expected-error* {{initializer expression provided, but property behavior 'hasStorage' does not use it}}
    __behavior hasStorage

  // FIXME: Hack because we can't find the synthesized associated type witness
  // during witness matching.
  typealias Value = Int
}


extension Foo {
  static var y: T __behavior hasStorage // expected-error{{static stored properties not supported in generic types}}
  var y: T __behavior hasStorage // expected-error {{extensions must not contain stored properties}}
}

protocol storageWithoutInit {
  associatedtype Value

  var storage: Value { get set }
}
extension storageWithoutInit {
  var value: Value { fatalError("") }

  func initStorage() -> Int { fatalError("signature is wrong") } // expected-note * {{found this candidate}}
}

struct Bar {
  var x: Int __behavior storageWithoutInit // expected-error {{property behavior protocol has a 'storage' requirement but does not have a static 'initStorage' method with the expected type '() -> Self.Value'}}
}
class Bas {
  var x: Int __behavior storageWithoutInit // expected-error {{property behavior protocol has a 'storage' requirement but does not have a static 'initStorage' method with the expected type '() -> Self.Value'}}
}

protocol valueTypeMismatch {
  associatedtype Value
}
extension valueTypeMismatch {
  var value: Value? { fatalError("") } // expected-note {{'value' property declared here}}
}

var x: Int __behavior valueTypeMismatch // expected-error {{property behavior 'valueTypeMismatch' provides 'value' property implementation with type 'Int?' that doesn't match type 'x' of declared property 'Int'}}

protocol getset {
  associatedtype Value
}
extension getset {
  var value: Value { get { } set { } }
}

var testGetset: Int __behavior getset
class C<T> {
  var testGetset: T __behavior getset
  static var testGetset: T __behavior getset
}
struct S<T> {
  var testGetset: T __behavior getset
  static var testGetset: T __behavior getset
}

protocol parameterized {
  associatedtype Value
  func parameter() -> Value
}
extension parameterized {
  var value: Value {
    get { }
    set { }
  }
}

struct TestParameters {
  var hasParameter: Int __behavior parameterized { 0 }
  var (sharedParameter1, sharedParameter2): (Int,Int) 
    __behavior parameterized { 0 }  // expected-error {{multiple variables is not supported}} expected-error{{sharedParameter2}}
  var missingParameter: Int __behavior parameterized // expected-error{{requires a parameter}}
  var invalidParameter: Int __behavior parameterized { 5.5 } // expected-error{{cannot convert return expression of type 'Double' to return type 'Int'}}
}

// TODO
var globalParameter: Int __behavior parameterized { 0 } // expected-error{{not supported}}

protocol noParameter {
  associatedtype Value
}
extension noParameter {
  var value: Value {
    get { }
    set { }
  }
}

var hasNoParameter: Int __behavior noParameter
var hasUnwantedParameter: Int __behavior noParameter { 0 } // expected-error{{parameter expression provided, but property behavior 'noParameter' does not use it}}

protocol storageWithInitialValue {
  associatedtype Value
  var storage: Value? { get set }
}
extension storageWithInitialValue {
  var value: Value { get { } }

  static func initStorage(_ x: Value) -> Value? {
    return nil
  }
}

struct TestStorageWithInitialValue {
  var x: Int __behavior storageWithInitialValue // Would require DI
  var y = 0 __behavior storageWithInitialValue
  var z: Int = 5.5 __behavior storageWithInitialValue // expected-error {{cannot convert value of type 'Double' to type 'Int' in coercion}}
  var (a, b) = tuple __behavior storageWithInitialValue // expected-error* {{do not support destructuring}}

  // FIXME: Hack because we can't find the synthesized associated type witness
  // during witness matching.
  typealias Value = Int
}
