// RUN: %target-swift-frontend -emit-silgen-ossa -o /dev/null -sil-verify-all -enable-sil-opaque-values %s -sil-verify-none -verify
// RUN:%target-swift-frontend -emit-silgen %s -sil-verify-none -verify


public class Klass {
  var id: Int = 0
}

func get_k() -> Klass {
  return Klass()
}

public struct S {
  var _k: Klass

  var borrow_k: Klass {
    borrow {
      return _k
    }
  }
  var get_k: Klass {
    get {
      return _k
    }
  }
  var read_k: Klass {
    _read {
      yield _k
    }
  }
}

var global_var = Klass()

public struct Wrapper {
  var _k: Klass
  var _s: S

  var k: Klass {
    borrow {
      return _k
    }
  }

  var s: S {
    borrow {
      return _s
    }
  }

  var s_get: S {
    get {
      return _s
    }
  }

  var s_read: S {
    _read {
      yield _s
    }
  }

  var nested_get1: Klass {
    borrow {
      return _s.get_k // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var nested_get2: Klass {
    borrow {
      return s_get.borrow_k // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var nested_get3: Int {
    borrow {
      return _s.get_k.id  // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var nested_get4: Int {
    borrow {
      return s_get.borrow_k.id  // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var nested_read1: Klass {
    borrow {
      return _s.read_k // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var nested_read2: Klass {
    borrow {
      return s_read.read_k // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var owned_value_direct: Klass {
    borrow {
      return get_k() // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var owned_value_projected: Klass {
    borrow {
      let w = Wrapper(_k: Klass(), _s: S(_k: Klass()))
      return w.k // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var if_klass: Klass {
    borrow {
      if Int.random(in: 1..<100) == 0 {
        return _k
      }
      return _k // expected-error{{multiple return statements in borrow accessors are not yet supported}}
    }
  }

  var tuple_klass: (Klass, Klass) {
    borrow {
      return (_k, _k) // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var opt_klass: Klass? {
    borrow {
      return _k // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var global_var_prop: Klass {
    borrow {
      return global_var // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var class_var: Int {
    borrow {
      return _k.id // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }
}

public struct SimpleWrapper<T> {
  var _prop: T

  var borrow_prop: T {
    borrow {
      return _prop
    }
  }

  var get_prop: T {
    get {
      return _prop
    }
  }

  var read_prop: T {
    _read {
      yield _prop
    }
  }
}

public struct GenWrapper<T> {
  var _prop: T
  var _w: SimpleWrapper<T>

  public var prop: T {
    borrow {
      return _prop
    }
  }

  var get_prop: T {
    borrow {
      return _w.get_prop // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}

    }
  }

  var read_prop: T {
    borrow {
      return _w.read_prop // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var if_prop: T {
    borrow {
      if Int.random(in: 1..<100) == 0 {
        return _prop
      }
      return _prop // expected-error{{multiple return statements in borrow accessors are not yet supported}}
    }
  }

  var tuple_prop: (T, T) {
    borrow {
      return (_prop, _prop) // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }

  var opt_T: T? {
    borrow {
      return _prop // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}

    }
  }
}

public struct SimpleNCWrapper<T : ~Copyable> : ~Copyable {
  var _prop: T

  var borrow_prop: T {
    borrow {
      return _prop
    }
  }

  var get_prop: T {
    get {
      return _prop
    }
  }

  var read_prop: T {
    _read {
      yield _prop
    }
  }
}

public struct GenNCWrapper<T : ~Copyable> : ~Copyable {
  var _prop: T
  var _w: SimpleNCWrapper<T>

  public var prop: T {
    borrow {
      return _prop
    }
  }

  var nested_get: T {
    borrow {
      return _w.get_prop // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}

    }
  }

  var nested_read: T {
    borrow {
      return _w.read_prop // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}

    }
  }

  var if_prop: T {
    borrow {
      if Int.random(in: 1..<100) == 0 {
        return _prop
      }
      return _prop // expected-error{{multiple return statements in borrow accessors are not yet supported}}
    }
  }

  var opt_T: T? {
    borrow {
      return _prop // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
  }
}

// Borrow and mutate accessor using lazy properties
struct ExampleWithLazyProp {
  private lazy var _cachedResult: Int = {
    return expensiveComputation()
  }()

  var result: Int {
    mutating borrow {
      return _cachedResult // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
    mutate {
      return &_cachedResult // expected-error{{invalid return value from a mutate accessor}} // expected-note{{mutate accessors can return stored properties, computed properties with mutate accessors or global 'let' declarations}}
    }
  }

  private func expensiveComputation() -> Int {
    return (1...1000).reduce(0, +)
  }
}

public struct Container<Element: ~Copyable >: ~Copyable {
  var _storage1: UnsafeMutableBufferPointer<Element>
  var _storage2: UnsafeMutableBufferPointer<Element>

  var first: Element {
    @_unsafeSelfDependentResult
    borrow {
      if (Int.random(in: 1...1000) % 2 == 0) {
        return unsafe _storage1.baseAddress.unsafelyUnwrapped.pointee
      }
      return unsafe _storage2.baseAddress.unsafelyUnwrapped.pointee // expected-error {{multiple return statements in borrow accessors are not yet supported}}
    }
    @_unsafeSelfDependentResult
    mutate {
      if (Int.random(in: 1...1000) % 2 == 0) {
        return unsafe &_storage1.baseAddress.unsafelyUnwrapped.pointee
      }
      return unsafe &_storage2.baseAddress.unsafelyUnwrapped.pointee // expected-error {{multiple return statements in borrow accessors are not yet supported}}
    }
  }
}

enum OrderStatus: ~Copyable {
  case processing(trackingNumber: String)
  case cancelled(reason: String)

  var description: String {
    borrow {
      switch self {
        case .processing(let trackingNumber):
          return trackingNumber // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}

        case .cancelled(let reason):
          return reason // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}

      }
    }
  }
}

class KlassWrapper {
  var _k: Klass

  init(_ k: Klass) {
    self._k = k
  }

  var k: Klass {
    borrow {
      return _k // expected-error{{invalid return value from a borrow accessor}} // expected-note{{borrow accessors can return stored properties, computed properties with borrow accessors or global 'let' declarations}}
    }
    mutate {
      return &_k // expected-error{{invalid return value from a mutate accessor}} // expected-note{{mutate accessors can return stored properties, computed properties with mutate accessors or global 'let' declarations}}
    }
  }
}

