//! > Basic borrow checking valid.

//! > test_runner_name
test_borrow_check

//! > function
fn foo(x: ACopy, y: ADrop) {
  if true {
    use_a_copy(x);
    use_a_drop(y);
  } else {
    use_a_drop(y);
  }
  use_a_copy(x);
}

//! > function_name
foo

//! > module_code
extern type ACopy;
impl ACopyCopy of Copy::<ACopy>;
extern type ADrop;
impl ADropDrop of Drop::<ADrop>;

extern fn use_a_copy(x: ACopy) nopanic;
extern fn use_a_drop(x: ADrop) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering
Parameters: v0: test::ACopy, v1: test::ADrop
blk0 (root):
Statements:
  (v2: ()) <- struct_construct()
  (v3: core::bool) <- bool::True(v2)
End:
  Match(match_enum(v3) {
    bool::False(v5) => blk2,
    bool::True(v4) => blk1,
  })

blk1:
Statements:
  () <- test::use_a_copy(v0)
  () <- test::use_a_drop(v1)
End:
  Goto(blk3, {})

blk2:
Statements:
  () <- test::use_a_drop(v1)
End:
  Goto(blk3, {})

blk3:
Statements:
  () <- test::use_a_copy(v0)
  (v6: ()) <- struct_construct()
End:
  Return(v6)

//! > ==========================================================================

//! > Basic borrow checking failures.

//! > test_runner_name
test_borrow_check

//! > function
fn foo(x: ACopy, y: ADrop) -> ADrop {
  if true {
    use_a_copy(x);
    use_a_drop(y);
  } else {
  }
  y
}

//! > function_name
foo

//! > module_code
extern type ACopy;
impl ACopyCopy of Copy::<ACopy>;
extern type ADrop;
impl ADropDrop of Drop::<ADrop>;

extern fn use_a_copy(x: ACopy) nopanic;
extern fn use_a_drop(x: ADrop) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics
error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<test::ADrop>
 --> lib.cairo:14:3
  y
  ^

error: Variable not dropped. Trait has no implementation in context: core::traits::Drop::<test::ACopy>. Trait has no implementation in context: core::traits::Destruct::<test::ACopy>.
 --> lib.cairo:8:8
fn foo(x: ACopy, y: ADrop) -> ADrop {
       ^

//! > lowering
Parameters: v0: test::ACopy, v1: test::ADrop
blk0 (root):
Statements:
  (v2: ()) <- struct_construct()
  (v3: core::bool) <- bool::True(v2)
End:
  Match(match_enum(v3) {
    bool::False(v5) => blk2,
    bool::True(v4) => blk1,
  })

blk1:
Statements:
  () <- test::use_a_copy(v0)
  () <- test::use_a_drop(v1)
End:
  Goto(blk3, {})

blk2:
Statements:
End:
  Goto(blk3, {})

blk3:
Statements:
End:
  Return(v1)

//! > ==========================================================================

//! > Borrow checking with panic

//! > test_runner_name
test_borrow_check

//! > function
fn foo(ref x: ADrop, y: ADrop) {
  use_a_drop(x);
  bar();
  x = y;
}

fn bar(){
  let mut data = Default::default();
  data.append(1);
  panic(data);
}

//! > function_name
foo

//! > module_code
extern type ACopy;
impl ACopyCopy of Copy::<ACopy>;
extern type ADrop;
impl ADropDrop of Drop::<ADrop>;

extern fn use_a_copy(x: ACopy) nopanic;
extern fn use_a_drop(x: ADrop) nopanic;

use array::ArrayTrait;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering
Parameters: v0: test::ADrop, v1: test::ADrop
blk0 (root):
Statements:
  () <- test::use_a_drop(v0)
  (v2: ()) <- test::bar()
  (v3: ()) <- struct_construct()
End:
  Return(v1, v3)

//! > ==========================================================================

//! > Borrow checking array

//! > test_runner_name
test_borrow_check

//! > function
fn foo(ref self: Query::<felt252>, value: felt252) {
  self.data.append(value)
}

//! > function_name
foo

//! > module_code
use array::ArrayTrait;

struct Query<T> {
  data: Array::<T>,
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering
Parameters: v0: test::Query::<core::felt252>, v1: core::felt252
blk0 (root):
Statements:
  (v2: core::array::Array::<core::felt252>) <- struct_destructure(v0)
  (v4: core::array::Array::<core::felt252>, v3: ()) <- core::array::ArrayImpl::<core::felt252>::append(v2, v1)
  (v5: test::Query::<core::felt252>) <- struct_construct(v4)
End:
  Return(v5, v3)

//! > ==========================================================================

//! > Find drops.

//! > test_runner_name
test_borrow_check

//! > function
fn foo() {
   let mut arr: Array<u256> = Default::default();
   arr.append(1.into());
}

//! > function_name
foo

//! > module_code
// Core library imports.
use array::ArrayTrait;
use traits::Into;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering
Parameters:
blk0 (root):
Statements:
  (v0: core::array::Array::<core::integer::u256>) <- core::array::ArrayDefault::<core::integer::u256>::default()
  (v1: core::felt252) <- 1u
  (v2: core::integer::u256) <- core::integer::Felt252IntoU256::into(v1)
  (v4: core::array::Array::<core::integer::u256>, v3: ()) <- core::array::ArrayImpl::<core::integer::u256>::append(v0, v2)
  (v5: ()) <- struct_construct()
End:
  Return(v5)

//! > ==========================================================================

//! > Non destructible with panicable call.

//! > test_runner_name
test_borrow_check

//! > function
fn foo(ref a: A) {
   1 + 1;
}

//! > function_name
foo

//! > module_code
struct A {}

//! > semantic_diagnostics

//! > lowering_diagnostics
error: Variable not dropped. Trait has no implementation in context: core::traits::Drop::<test::A>. Trait has no implementation in context: core::traits::Destruct::<test::A>.
 --> lib.cairo:2:12
fn foo(ref a: A) {
           ^

//! > lowering
Parameters: v0: test::A
blk0 (root):
Statements:
  (v1: core::felt252) <- 1u
  (v2: core::felt252) <- 1u
  (v3: core::felt252) <- core::Felt252Add::add(v1, v2)
  (v4: ()) <- struct_construct()
End:
  Return(v0, v4)

//! > ==========================================================================

//! > Test pushing of moved var and returning moved variable

//! > test_runner_name
test_borrow_check

//! > function
fn foo(x: ADrop, mut y: ADrop) -> ADrop {
  if true {
    use_a_drop(y);
  } else {
    y = x;
  }

  return y;
}

//! > function_name
foo

//! > module_code
extern type ADrop;
impl ADropDrop of Drop::<ADrop>;

extern fn use_a_drop(x: ADrop) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics
error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<test::ADrop>
 --> lib.cairo:12:10
  return y;
         ^

//! > lowering
Parameters: v0: test::ADrop, v1: test::ADrop
blk0 (root):
Statements:
  (v2: ()) <- struct_construct()
  (v3: core::bool) <- bool::True(v2)
End:
  Match(match_enum(v3) {
    bool::False(v5) => blk2,
    bool::True(v4) => blk1,
  })

blk1:
Statements:
  () <- test::use_a_drop(v1)
End:
  Goto(blk3, {v1 -> v6})

blk2:
Statements:
End:
  Goto(blk3, {v0 -> v6})

blk3:
Statements:
End:
  Return(v6)

//! > ==========================================================================

//! > Test panic with moved input.

//! > test_runner_name
test_borrow_check

//! > function
fn foo() {
  let arr = array!['err_code'];
  let mut b = arr;
  b.append('bla');

  panic(arr);
}

//! > function_name
foo

//! > module_code
use array::ArrayTrait;

//! > semantic_diagnostics

//! > lowering_diagnostics
error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<core::array::Array::<core::felt252>>
 --> inline_macros:10:9
  panic(arr);
        ^*^

//! > lowering
Parameters:
blk0 (root):
Statements:
  (v0: core::array::Array::<core::felt252>) <- core::array::ArrayImpl::<core::felt252>::new()
  (v1: core::felt252) <- 7310030899191440485u
  (v3: core::array::Array::<core::felt252>, v2: ()) <- core::array::ArrayImpl::<core::felt252>::append(v0, v1)
  (v4: core::felt252) <- 6450273u
  (v6: core::array::Array::<core::felt252>, v5: ()) <- core::array::ArrayImpl::<core::felt252>::append(v3, v4)
  (v7: core::panics::Panic) <- struct_construct()
  (v8: (core::panics::Panic, core::array::Array::<core::felt252>)) <- struct_construct(v7, v3)
End:
  Panic(v8)

//! > ==========================================================================

//! > Test match_extern on moved var.

//! > test_runner_name
test_borrow_check

//! > function
fn foo(x: NonCopy) -> Option<NonCopy> {
  use_non_copy(x);
  do_match_extern(x)
}

//! > function_name
foo

//! > module_code
extern type NonCopy;

extern fn use_non_copy(x: NonCopy) nopanic;

extern fn do_match_extern(x: NonCopy) -> Option<NonCopy> nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics
error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<test::NonCopy>
 --> lib.cairo:8:19
  do_match_extern(x)
                  ^

//! > lowering
Parameters: v0: test::NonCopy
blk0 (root):
Statements:
  () <- test::use_non_copy(v0)
End:
  Match(match test::do_match_extern(v0) {
    Option::Some(v1) => blk1,
    Option::None => blk2,
  })

blk1:
Statements:
  (v2: core::option::Option::<test::NonCopy>) <- Option::Some(v1)
End:
  Goto(blk3, {v2 -> v5})

blk2:
Statements:
  (v3: ()) <- struct_construct()
  (v4: core::option::Option::<test::NonCopy>) <- Option::None(v3)
End:
  Goto(blk3, {v4 -> v5})

blk3:
Statements:
End:
  Return(v5)

//! > ==========================================================================

//! > Test recomposed struct var.

//! > test_runner_name
test_borrow_check

//! > function
fn foo(mut x: MyStruct) -> MyStruct {
  x.a += 1;
  use_non_copy(x.b);
  return x;
}

//! > function_name
foo

//! > module_code
extern type NonCopy;

extern fn use_non_copy(x: NonCopy) nopanic;

struct MyStruct {
  a: u32,
  b: NonCopy,
}

//! > semantic_diagnostics

//! > lowering_diagnostics
error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<test::NonCopy>
 --> lib.cairo:12:10
  return x;
         ^

error: Variable not dropped. Trait has no implementation in context: core::traits::Drop::<test::NonCopy>. Trait has no implementation in context: core::traits::Destruct::<test::NonCopy>.
 --> lib.cairo:10:3
  x.a += 1;
  ^

//! > lowering
Parameters: v0: test::MyStruct
blk0 (root):
Statements:
  (v1: core::integer::u32) <- 1u
  (v2: core::integer::u32, v3: test::NonCopy) <- struct_destructure(v0)
  (v5: core::integer::u32, v4: ()) <- core::integer::U32AddEq::add_eq(v2, v1)
  () <- test::use_non_copy(v3)
  (v6: test::MyStruct) <- struct_construct(v5, v3)
End:
  Return(v6)

//! > ==========================================================================

//! > Test moved error on a reconstructed var.

//! > test_runner_name
test_borrow_check

//! > function
fn foo(ref s1: MyStruct, ref s2: MyStruct) {
    invalidate(s1.a);
    invalidate(s2.a);
}

//! > function_name
foo

//! > module_code
use array::ArrayTrait;

extern fn invalidate(a: Array<felt252>) nopanic;

#[derive(Drop)]
struct MyStruct {
    a: Array<felt252>,
    b: u8,
}

//! > semantic_diagnostics

//! > lowering_diagnostics
error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<core::array::Array::<core::felt252>>
 --> lib.cairo:10:30
fn foo(ref s1: MyStruct, ref s2: MyStruct) {
                             ^^

error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<core::array::Array::<core::felt252>>
 --> lib.cairo:10:12
fn foo(ref s1: MyStruct, ref s2: MyStruct) {
           ^^

//! > lowering
Parameters: v0: test::MyStruct, v1: test::MyStruct
blk0 (root):
Statements:
  (v2: core::array::Array::<core::felt252>, v3: core::integer::u8) <- struct_destructure(v0)
  () <- test::invalidate(v2)
  (v4: core::array::Array::<core::felt252>, v5: core::integer::u8) <- struct_destructure(v1)
  () <- test::invalidate(v4)
  (v6: ()) <- struct_construct()
  (v7: test::MyStruct) <- struct_construct(v2, v3)
  (v8: test::MyStruct) <- struct_construct(v4, v5)
End:
  Return(v7, v8, v6)
