// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST1
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST2
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST3
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST4
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST5
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST6
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST7
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST8
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST9
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST10
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST11
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST12
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST13
// REQUIRES: asserts,swift_stdlib_no_asserts,optimized_stdlib

// XFAIL: *

// TEST1-LABEL: COW Array Opts in Func {{.*}}inoutarr{{.*}}
// TEST1: Hoisting make_mutable
// TEST1: COW Array Opts

func inoutarr(a: inout [Int]) {
  for i in 0..<a.count {
    a[i] = 0
  }
}

struct S {
  var a: [Int]
}

// TEST2-LABEL: COW Array Opts in Func {{.*}}arrelt{{.*}}
// TEST2: Hoisting make_mutable
// TEST2: COW Array Opts
func arrelt(s: inout S) {
  for i in 0..<s.a.count {
    s.a[i] = 0
  }
}


class Array_in_class {
  final var A : [Int]
  final var B : [Int]
  final var C : [[Int]]

  init() {
    A = []
    B = []
    C = [[]]
  }

  // TEST3-LABEL: COW Array Opts in Func {{.*}}hoistInClass{{.*}}
  // TEST3: Hoisting make_mutable
  // TEST3: COW Array Opts
  func hoistInClass() {
    for i in 0..<A.count {
      A[i] = 0
    }
  }

  // TEST4-LABEL: COW Array Opts in Func {{.*}}hoistInClass2Arr{{.*}}
  // TEST4: Hoisting make_mutable
  // TEST4: COW Array Opts
  func hoistInClass2Arr() {
    for i in 0..<A.count {
      A[i] = 0
      B[i] = 2
    }
  }

  // TEST5-LABEL: COW Array Opts in Func {{.*}}dontHoistInClassAppend{{.*}}
  // TEST5-NOT: Hoisting make_mutable
  // TEST5: COW Array Opts in Func
  func dontHoistInClassAppend() {
    for i in 0..<A.count {
      A[i] = 0
      C.append(A)
    }
  }
}

struct Array2d {
  var A : [Int]
  var cols : Int
  var rows : Int
}

// TEST6-LABEL: COW Array Opts in Func {{.*}}test2DArrayLoop{{.*}}
// TEST6:        Array Opts in Loop Loop at depth 2
// TEST6-NOT:   COW Array Opts in
// TEST6:        Hoisting make_mutable
// TEST6:        Array Opts in Loop Loop at depth 1
// TEST6-NOT:   COW Array Opts in
// TEST6:        Hoisting make_mutable
// TEST6:   COW Array Opts in

func test2DArrayLoop(A: inout Array2d) {
  for r in 0 ..< A.rows {
    for c in 0 ..< A.cols {
      A.A[r*A.cols+c] += 1
    }
  }
}

class AClass {}

// TEST7-LABEL: COW Array Opts in Func {{.*}}hoistArrayOfClasses{{.*}}
// TEST7: Hoisting make_mutable
// TEST7: COW Array Opts
func hoistArrayOfClasses(A: inout [AClass], x: AClass) {
  for i in 0 ..< A.count {
    A[i] = x
  }
}

// TEST8-LABEL: COW Array Opts in Func {{.*}}hoistMutableOfAppend{{.*}}
// TEST8: Hoisting make_mutable
// TEST8: COW Array Opts

func hoistMutableOfAppend(A: inout [Int]) {
  for i in 0 ..< 10 {
    A.append(i)
  }
}

@inline(never)
func use(_ a: [Int]) {
}

class ArrayHolder {
  final var A : [[Int]]
  init() { A = [] }

// TEST9-LABEL: COW Array Opts in Func {{.*}}hoist2DArrayInClass
// TEST9: Hoisting make_mutable
// TEST9: COW Array Opts

  func hoist2DArrayInClass() {
    for i in 0 ..< 10 {
      for y in 0 ..< 10 {
        A[i][y] = A[i][y] * 2
      }
    }
  }

// TEST10-LABEL: COW Array Opts in Func {{.*}}dontHoist2DArray
// TEST10-NOT: Hoisting make_mutable
// TEST10: COW Array Opts

  func dontHoist2DArray(){
    var escape : [Int] = []
    for i in 0 ..< A.count {
      for y in 0 ..< A[i].count {
        escape = A[i]
        A[i][y] = A[i][y] * 2
      }
    }
    use(escape)
  }

// TEST11-LABEL: COW Array Opts in Func {{.*}}dontHoist2DArray2
// TEST11-NOT: Hoisting make_mutable
// TEST11: COW Array Opts

  func dontHoist2DArray2(){
    let b = [0]
    for i in 0 ..< A.count {
      for y in 0 ..< A[i].count {
        A[1] = b
        A[i][y] = A[i][y] * 2
      }
    }
  }

// TEST12-LABEL: COW Array Opts in Func {{.*}}dontHoist2DArray3
// TEST12-NOT: Hoisting make_mutable
// TEST12: COW Array Opts

  func dontHoist2DArray3() {
    for i in 0 ..< A.count {
      for y in 0 ..< A[i].count {
        A[i].append(2)
        A[i][y] = A[i][y] * 2
      }
    }
  }
}

// TEST13-LABEL: COW Array Opts in Func {{.*}}hoist2DArray
// TEST13: Hoisting make_mutable
// TEST13: COW Array Opts

func hoist2DArray(a: inout [[Int]]) {
  for i in 0 ..< a.count {
    for y in 0 ..< a[i].count {
      a[i][y] = a[i][y] * 2
    }
  }
}
