// RUN: %empty-directory(%t)
// RUN: %target-build-swift -Xfrontend -enable-experimental-property-behaviors %s -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out
// REQUIRES: executable_test

import StdlibUnittest

/// A lazily-initialized, mutable, unsynchronized property. The property's
/// parameter closure is evaluated the first time the property is read, if
/// it has not been written to beforehand. No synchronization is provided
/// for the lazy initialization.
protocol lazy {
  associatedtype Value
  var storage: Value? { get set }
  func parameter() -> Value
}
extension lazy {
  var value: Value {
    mutating get {
      if let existing = storage {
        return existing
      }
      let value = parameter()
      storage = value
      return value
    }
    
    set {
      storage = newValue
    }
  }

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

var lazyEvaluated = false
func evaluateLazy() -> Int {
  lazyEvaluated = true
  return 1738
}

class Foo {
  var y: Int __behavior lazy { evaluateLazy() }

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

var Lazy = TestSuite("Lazy")

Lazy.test("usage") {
  let foo = Foo()

  expectFalse(lazyEvaluated)
  expectEqual(foo.y, 1738)
  expectTrue(lazyEvaluated)

  lazyEvaluated = false
  expectEqual(foo.y, 1738)
  expectFalse(lazyEvaluated)

  foo.y = 36
  expectEqual(foo.y, 36)
  expectFalse(lazyEvaluated)

  let foo2 = Foo()
  expectFalse(lazyEvaluated)
  foo2.y = 36
  expectEqual(foo2.y, 36)
  expectFalse(lazyEvaluated)

  let foo3 = Foo()
  expectFalse(lazyEvaluated)
  expectEqual(foo3.y, 1738)
  expectTrue(lazyEvaluated)
}

runAllTests()
