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

func f0(_ x: inout Int) {}
func f1<T>(_ x: inout T) {}
func f2(_ x: inout X) {}
func f2(_ x: inout Double) {}

class Reftype {
  var property: Double { get {} set {} }
}

struct X {
  subscript(i: Int) -> Float { get {} set {} }

  var property: Double { get {} set {} }

  func genuflect() {}
}

struct Y {
  subscript(i: Int) -> Float { get {} set {} }
  subscript(f: Float) -> Int { get {} set {} }
}

var i : Int
var f : Float
var x : X
var y : Y

func +=(lhs: inout X, rhs : X) {}
prefix operator +++
prefix func +++(rhs: inout X) {}

f0(&i)
f1(&i)
f1(&x[i])
f1(&x.property)
f1(&y[i])

// Missing '&'
f0(i) // expected-error{{passing value of type 'Int' to an inout parameter requires explicit '&'}}{{4-4=&}}
f1(y[i]) // expected-error{{passing value of type 'Float' to an inout parameter requires explicit '&'}} {{4-4=&}}

// Assignment operators
x += x
+++x

var yi = y[i]

// Non-settable lvalues

var non_settable_x : X {
  return x
}

struct Z {
  var non_settable_x: X { get {} }
  var non_settable_reftype: Reftype { get {} }
  var settable_x : X

  subscript(i: Int) -> Double { get {} }
  subscript(_: (i: Int, j: Int)) -> X { get {} }
}

var z : Z

func fz() -> Z {}
func fref() -> Reftype {}

// non-settable var is non-settable:
// - assignment
non_settable_x = x // expected-error{{cannot assign to value: 'non_settable_x' is a get-only property}}
// - inout (mono)
f2(&non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
// - inout (generic)
f1(&non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
// - inout assignment
non_settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
+++non_settable_x // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}

// non-settable property is non-settable:
z.non_settable_x = x // expected-error{{cannot assign to property: 'non_settable_x' is a get-only property}}
f2(&z.non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
f1(&z.non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
z.non_settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
+++z.non_settable_x // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}

// non-settable subscript is non-settable:
z[0] = 0.0 // expected-error{{cannot assign through subscript: subscript is get-only}}
f2(&z[0]) // expected-error{{cannot pass immutable value as inout argument: subscript is get-only}}
f1(&z[0]) // expected-error{{cannot pass immutable value as inout argument: subscript is get-only}}
z[0] += 0.0 // expected-error{{left side of mutating operator isn't mutable: subscript is get-only}}
+++z[0] // expected-error{{cannot pass immutable value as inout argument: subscript is get-only}}

// settable property of an rvalue value type is non-settable:
fz().settable_x = x // expected-error{{cannot assign to property: 'fz' returns immutable value}}
f2(&fz().settable_x) // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
f1(&fz().settable_x) // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
fz().settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'fz' returns immutable value}}
+++fz().settable_x // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}

// settable property of an rvalue reference type IS SETTABLE:
fref().property = 0.0
f2(&fref().property)
f1(&fref().property)
fref().property += 0.0
fref().property += 1

// settable property of a non-settable value type is non-settable:
z.non_settable_x.property = 1.0 // expected-error{{cannot assign to property: 'non_settable_x' is a get-only property}}
f2(&z.non_settable_x.property) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
f1(&z.non_settable_x.property) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
z.non_settable_x.property += 1.0 // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
+++z.non_settable_x.property // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}

// settable property of a non-settable reference type IS SETTABLE:
z.non_settable_reftype.property = 1.0
f2(&z.non_settable_reftype.property)
f1(&z.non_settable_reftype.property)
z.non_settable_reftype.property += 1.0
z.non_settable_reftype.property += 1

// regressions with non-settable subscripts in value contexts
_ = z[0] == 0
var d : Double
d = z[0]

// regressions with subscripts that return generic types
var xs:[X]
_ = xs[0].property

struct A<T> {
    subscript(i: Int) -> T { get {} }
}

struct B {
    subscript(i: Int) -> Int { get {} }
}

var a:A<B>

_ = a[0][0]

// Instance members of struct metatypes.
struct FooStruct {
  func instanceFunc0() {}
}

func testFooStruct() {
  FooStruct.instanceFunc0(FooStruct())()
}

// Don't load from explicit lvalues.
func takesInt(_ x: Int) {}
func testInOut(_ arg: inout Int) {
  var x : Int
  takesInt(&x) // expected-error{{'&' used with non-inout argument of type 'Int'}}
}

// Don't infer inout types.
var ir = &i // expected-error{{variable has type 'inout Int' which includes nested inout parameters}} \
            // expected-error{{'&' can only appear immediately in a call argument list}}
var ir2 = ((&i)) // expected-error{{variable has type 'inout Int' which includes nested inout parameters}} \
                 // expected-error{{'&' can only appear immediately in a call argument list}}

// <rdar://problem/17133089>
func takeArrayRef(_ x: inout Array<String>) { }

// rdar://22308291
takeArrayRef(["asdf", "1234"]) // expected-error{{contextual type 'inout Array<String>' cannot be used with array literal}}

// <rdar://problem/19835413> Reference to value from array changed
func rdar19835413() {
  func f1(_ p: UnsafeMutableRawPointer) {}
  func f2(_ a: [Int], i: Int, pi: UnsafeMutablePointer<Int>) {
    var a = a
    f1(&a)
    f1(&a[i])
    f1(&a[0])
    f1(pi)
    f1(pi)
  }
}

// <rdar://problem/21877598> Crash when accessing stored property without
// setter from constructor
protocol Radish {
  var root: Int { get }
}

public struct Kale : Radish {
  public let root : Int
  public init() {
    let _ = Kale().root
    self.root = 0
  }
}

func testImmutableUnsafePointer(_ p: UnsafePointer<Int>) {
  p.pointee = 1 // expected-error {{cannot assign to property: 'pointee' is a get-only property}}
  p[0] = 1 // expected-error {{cannot assign through subscript: subscript is get-only}}
}

// <https://bugs.swift.org/browse/SR-7> Inferring closure param type to 
// inout crashes compiler
let g = { x in f0(x) } // expected-error{{passing value of type 'Int' to an inout parameter requires explicit '&'}} {{19-19=&}}

// <rdar://problem/17245353> Crash with optional closure taking inout
func rdar17245353() {
  typealias Fn = (inout Int) -> ()
  func getFn() -> Fn? { return nil }

  let _: (inout UInt, UInt) -> Void = { $0 += $1 }
}

// <rdar://problem/23131768> Bugs related to closures with inout parameters
func rdar23131768() {
  func f(_ g: (inout Int) -> Void) { var a = 1; g(&a); print(a) }
  f { $0 += 1 } // Crashes compiler

  func f2(_ g: (inout Int) -> Void) { var a = 1; g(&a); print(a) }
  f2 { $0 = $0 + 1 } // previously error: Cannot convert value of type '_ -> ()' to expected type '(inout Int) -> Void'

  func f3(_ g: (inout Int) -> Void) { var a = 1; g(&a); print(a) }
  f3 { (v: inout Int) -> Void in v += 1 }
}

// <rdar://problem/23331567> Swift: Compiler crash related to closures with inout parameter.
func r23331567(_ fn: (_ x: inout Int) -> Void) {
  var a = 0
  fn(&a)
}
r23331567 { $0 += 1 }

// <rdar://problem/30685195> Compiler crash with invalid assignment
struct G<T> {
  subscript(x: Int) -> T { get { } nonmutating set { } }
  // expected-note@-1 {{'subscript' declared here}}
}

func wump<T>(to: T, _ body: (G<T>) -> ()) {}

wump(to: 0, { $0[] = 0 })
// expected-error@-1 {{missing argument for parameter #1 in call}}
