//! > Test extern function calling.

//! > test_function_name
test_function_lowering

//! > function
fn foo(ref a: felt, b: felt) {
    f(a,b);
    g(a,b);
    h(a,b);
    i(a,b);
}

//! > function_name
foo

//! > module_code
extern fn f(ref a: felt, b: felt) -> felt nopanic;
extern fn g(ref a: felt, b: felt) -> (felt,) nopanic;
extern fn h(ref a: felt, b: felt) -> (felt,felt) nopanic;
extern fn i(ref a: felt, b: felt) -> ((felt,),) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0 (root):
Inputs: v0[r0]: core::felt, v1: core::felt
Statements:
  (v3[r0]: core::felt, v2: core::felt) <- test::f(v0, v1)
  (v5[r0]: core::felt, v4: core::felt) <- test::g(v3, v1)
  (v8[r0]: core::felt, v6: core::felt, v7: core::felt) <- test::h(v5, v1)
  (v10[r0]: core::felt, v9: (core::felt,)) <- test::i(v8, v1)
  (v11: ()) <- struct_construct()
Drops: v0, v1, v2, v3, v4, v5, v6, v7, v8, v9
End:
  Callsite(v10, v11)

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

//! > Test extern function enum calling.

//! > test_function_name
test_function_lowering

//! > function
fn foo(ref a: felt, b: felt) {
  let x = f(a, b);
  match x {
    MyEnum::A(y) => (),
    MyEnum::B(y) => (),
    MyEnum::C(y) => (),
  }
}

//! > function_name
foo

//! > module_code
enum MyEnum {
  A: felt,
  B: (felt,),
  C: (felt, felt),
}
extern fn f(ref a: felt, b: felt) -> MyEnum nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v2[r0]: core::felt, v3: core::felt
Statements:
  (v4: test::MyEnum) <- MyEnum::A(v3)
Drops: v3
End:
  Callsite(v2, v4)

blk1:
Inputs: v5[r0]: core::felt, v6: core::felt
Statements:
  (v7: (core::felt,)) <- struct_construct(v6)
  (v8: test::MyEnum) <- MyEnum::B(v7)
Drops: v7, v6
End:
  Callsite(v5, v8)

blk2:
Inputs: v9[r0]: core::felt, v10: core::felt, v11: core::felt
Statements:
  (v12: (core::felt, core::felt)) <- struct_construct(v10, v11)
  (v13: test::MyEnum) <- MyEnum::C(v12)
Drops: v12, v10, v11
End:
  Callsite(v9, v13)

blk3:
Inputs: v16: core::felt
Statements:
Drops: v16
End:
  Callsite()

blk4:
Inputs: v17: (core::felt,)
Statements:
Drops: v17
End:
  Callsite()

blk5:
Inputs: v18: (core::felt, core::felt)
Statements:
Drops: v18
End:
  Callsite()

blk6 (root):
Inputs: v0[r0]: core::felt, v1: core::felt
Statements:
  (v15[r0]: core::felt, v14: test::MyEnum) <- match test::f(v0, v1) {
    (v2, v3) => blk0,
    (v5, v6) => blk1,
    (v9, v10, v11) => blk2,
  }
  () <- match_enum(v14) {
    MyEnum::A => blk3,
    MyEnum::B => blk4,
    MyEnum::C => blk5,
  }
  (v19: ()) <- struct_construct()
Drops: v0, v1
End:
  Callsite(v15, v19)

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

//! > Test extern function enum calling with optimization.

//! > test_function_name
test_function_lowering

//! > function
fn foo(a: felt, b: felt) -> felt {
  match f(a, b) {
    MyEnum::A(y) => y,
    MyEnum::B((y,)) => y,
    MyEnum::C((y,_)) => y,
  }
}

//! > function_name
foo

//! > module_code
enum MyEnum {
  A: felt,
  B: (felt,),
  C: (felt, felt),
}
extern fn f(a: felt, b: felt) -> MyEnum nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v2: core::felt
Statements:
Drops:
End:
  Callsite(v2)

blk1:
Inputs: v3: core::felt
Statements:
Drops:
End:
  Callsite(v3)

blk2:
Inputs: v4: core::felt, v5: core::felt
Statements:
Drops: v5
End:
  Callsite(v4)

blk3 (root):
Inputs: v0: core::felt, v1: core::felt
Statements:
  (v6: core::felt) <- match test::f(v0, v1) {
    (v2) => blk0,
    (v3) => blk1,
    (v4, v5) => blk2,
  }
Drops: v0, v1
End:
  Callsite(v6)

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

//! > Test extern function enum calling without optimization (no optimization with ref params).

//! > test_function_name
test_function_lowering

//! > function
fn foo(ref a: felt, b: felt) -> felt {
  match f(a, b) {
    MyEnum::A(y) => y,
    MyEnum::B((y,)) => y,
    MyEnum::C((y,_)) => y,
  }
}

//! > function_name
foo

//! > module_code
enum MyEnum {
  A: felt,
  B: (felt,),
  C: (felt, felt),
}
extern fn f(ref a: felt, b: felt) -> MyEnum nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v2[r0]: core::felt, v3: core::felt
Statements:
  (v4: test::MyEnum) <- MyEnum::A(v3)
Drops: v3
End:
  Callsite(v2, v4)

blk1:
Inputs: v5[r0]: core::felt, v6: core::felt
Statements:
  (v7: (core::felt,)) <- struct_construct(v6)
  (v8: test::MyEnum) <- MyEnum::B(v7)
Drops: v7, v6
End:
  Callsite(v5, v8)

blk2:
Inputs: v9[r0]: core::felt, v10: core::felt, v11: core::felt
Statements:
  (v12: (core::felt, core::felt)) <- struct_construct(v10, v11)
  (v13: test::MyEnum) <- MyEnum::C(v12)
Drops: v12, v10, v11
End:
  Callsite(v9, v13)

blk3:
Inputs: v16: core::felt
Statements:
Drops:
End:
  Callsite(v16)

blk4:
Inputs: v17: (core::felt,)
Statements:
  (v18: core::felt) <- struct_destructure(v17)
Drops: v17
End:
  Callsite(v18)

blk5:
Inputs: v19: (core::felt, core::felt)
Statements:
  (v20: core::felt, v21: core::felt) <- struct_destructure(v19)
Drops: v19, v21
End:
  Callsite(v20)

blk6 (root):
Inputs: v0[r0]: core::felt, v1: core::felt
Statements:
  (v15[r0]: core::felt, v14: test::MyEnum) <- match test::f(v0, v1) {
    (v2, v3) => blk0,
    (v5, v6) => blk1,
    (v9, v10, v11) => blk2,
  }
  (v22: core::felt) <- match_enum(v14) {
    MyEnum::A => blk3,
    MyEnum::B => blk4,
    MyEnum::C => blk5,
  }
Drops: v0, v1
End:
  Callsite(v15, v22)

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

//! > Test extern function enum calling with implicits.

//! > test_function_name
test_function_lowering

//! > function
fn foo(ref a: felt, b: felt) {
  let x = f(a, b);
  match x {
    MyEnum::A(y) => (),
    MyEnum::B(y) => (),
    MyEnum::C(y) => (),
  }
}

//! > function_name
foo

//! > module_code
enum MyEnum {
  A: felt,
  B: (felt,),
  C: (felt, felt),
}
extern fn f(ref a: felt, b: felt) -> MyEnum implicits(RangeCheck) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v3[r0]: core::RangeCheck, v4[r1]: core::felt, v5: core::felt
Statements:
  (v6: test::MyEnum) <- MyEnum::A(v5)
Drops: v5
End:
  Callsite(v3, v4, v6)

blk1:
Inputs: v7[r0]: core::RangeCheck, v8[r1]: core::felt, v9: core::felt
Statements:
  (v10: (core::felt,)) <- struct_construct(v9)
  (v11: test::MyEnum) <- MyEnum::B(v10)
Drops: v10, v9
End:
  Callsite(v7, v8, v11)

blk2:
Inputs: v12[r0]: core::RangeCheck, v13[r1]: core::felt, v14: core::felt, v15: core::felt
Statements:
  (v16: (core::felt, core::felt)) <- struct_construct(v14, v15)
  (v17: test::MyEnum) <- MyEnum::C(v16)
Drops: v16, v15, v14
End:
  Callsite(v12, v13, v17)

blk3:
Inputs: v21: core::felt
Statements:
Drops: v21
End:
  Callsite()

blk4:
Inputs: v22: (core::felt,)
Statements:
Drops: v22
End:
  Callsite()

blk5:
Inputs: v23: (core::felt, core::felt)
Statements:
Drops: v23
End:
  Callsite()

blk6 (root):
Inputs: v0[r0]: core::RangeCheck, v1[r1]: core::felt, v2: core::felt
Statements:
  (v19[r0]: core::RangeCheck, v20[r1]: core::felt, v18: test::MyEnum) <- match test::f(v0, v1, v2) {
    (v3, v4, v5) => blk0,
    (v7, v8, v9) => blk1,
    (v12, v13, v14, v15) => blk2,
  }
  () <- match_enum(v18) {
    MyEnum::A => blk3,
    MyEnum::B => blk4,
    MyEnum::C => blk5,
  }
  (v24: ()) <- struct_construct()
Drops: v2, v1
End:
  Callsite(v19, v20, v24)

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

//! > Test extern function enum calling with optimization with implicits.

//! > test_function_name
test_function_lowering

//! > function
fn foo(ref a: felt, b: felt) -> felt {
  match f(a, b) {
    MyEnum::A(y) => y,
    MyEnum::B((y,)) => y,
    MyEnum::C((y,_)) => y,
  }
}

//! > function_name
foo

//! > module_code
enum MyEnum {
  A: felt,
  B: (felt,),
  C: (felt, felt),
}
extern fn f(ref a: felt, b: felt) -> MyEnum implicits(RangeCheck) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v3[r0]: core::RangeCheck, v4[r1]: core::felt, v5: core::felt
Statements:
  (v6: test::MyEnum) <- MyEnum::A(v5)
Drops: v5
End:
  Callsite(v3, v4, v6)

blk1:
Inputs: v7[r0]: core::RangeCheck, v8[r1]: core::felt, v9: core::felt
Statements:
  (v10: (core::felt,)) <- struct_construct(v9)
  (v11: test::MyEnum) <- MyEnum::B(v10)
Drops: v10, v9
End:
  Callsite(v7, v8, v11)

blk2:
Inputs: v12[r0]: core::RangeCheck, v13[r1]: core::felt, v14: core::felt, v15: core::felt
Statements:
  (v16: (core::felt, core::felt)) <- struct_construct(v14, v15)
  (v17: test::MyEnum) <- MyEnum::C(v16)
Drops: v16, v15, v14
End:
  Callsite(v12, v13, v17)

blk3:
Inputs: v21: core::felt
Statements:
Drops:
End:
  Callsite(v21)

blk4:
Inputs: v22: (core::felt,)
Statements:
  (v23: core::felt) <- struct_destructure(v22)
Drops: v22
End:
  Callsite(v23)

blk5:
Inputs: v24: (core::felt, core::felt)
Statements:
  (v25: core::felt, v26: core::felt) <- struct_destructure(v24)
Drops: v26, v24
End:
  Callsite(v25)

blk6 (root):
Inputs: v0[r0]: core::RangeCheck, v1[r1]: core::felt, v2: core::felt
Statements:
  (v19[r0]: core::RangeCheck, v20[r1]: core::felt, v18: test::MyEnum) <- match test::f(v0, v1, v2) {
    (v3, v4, v5) => blk0,
    (v7, v8, v9) => blk1,
    (v12, v13, v14, v15) => blk2,
  }
  (v27: core::felt) <- match_enum(v18) {
    MyEnum::A => blk3,
    MyEnum::B => blk4,
    MyEnum::C => blk5,
  }
Drops: v2, v1
End:
  Callsite(v19, v20, v27)

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

//! > Test match on libfunc call that uses the refs of the libfunc in one of the arms.

//! > test_function_name
test_function_lowering

//! > function
fn foo(mut arr: Array::<felt>, mut b: (felt,)) -> Array::<felt> {
    let y = match f(arr, b) {
        MyEnum::A(x) => arr
    };
    y
}

//! > function_name
foo

//! > module_code
enum MyEnum {
  A: felt,
}
extern fn f(ref arr: Array::<felt>, ref b: (felt,)) -> MyEnum nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v2: core::array::Array::<core::felt>, v3: (core::felt,), v4: core::felt
Statements:
  (v5: test::MyEnum) <- MyEnum::A(v4)
Drops: v4
End:
  Callsite(v2, v3, v5)

blk1:
Inputs: v9: core::felt
Statements:
Drops: v9
End:
  Callsite(v7)

blk2 (root):
Inputs: v0: core::array::Array::<core::felt>, v1: (core::felt,)
Statements:
  (v7: core::array::Array::<core::felt>, v8: (core::felt,), v6: test::MyEnum) <- match test::f(v0, v1) {
    (v2, v3, v4) => blk0,
  }
  (v10: core::array::Array::<core::felt>) <- match_enum(v6) {
    MyEnum::A => blk1,
  }
Drops: v1, v8
End:
  Callsite(v10)

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

//! > Test calling libfunc that returns enum but does nothing with the result.

//! > test_function_name
test_function_lowering

//! > function
fn foo() {
  get_gas();
}

//! > function_name
foo

//! > module_code

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v2[r0]: core::RangeCheck, v3[r1]: core::gas::GasBuiltin
Statements:
  (v4: ()) <- struct_construct()
  (v5: core::option::Option::<()>) <- Option::Some(v4)
Drops: v4
End:
  Callsite(v2, v3, v5)

blk1:
Inputs: v6[r0]: core::RangeCheck, v7[r1]: core::gas::GasBuiltin
Statements:
  (v8: ()) <- struct_construct()
  (v9: core::option::Option::<()>) <- Option::None(v8)
Drops: v8
End:
  Callsite(v6, v7, v9)

blk2 (root):
Inputs: v0[r0]: core::RangeCheck, v1[r1]: core::gas::GasBuiltin
Statements:
  (v11[r0]: core::RangeCheck, v12[r1]: core::gas::GasBuiltin, v10: core::option::Option::<()>) <- match core::gas::get_gas(v0, v1) {
    (v2, v3) => blk0,
    (v6, v7) => blk1,
  }
  (v13: ()) <- struct_construct()
Drops: v10
End:
  Callsite(v11, v12, v13)

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

//! > Test calling libfunc that returns enum and returns the result as a tail expression.

//! > test_function_name
test_function_lowering

//! > function
fn foo() -> Option::<()> {
  get_gas()
}

//! > function_name
foo

//! > module_code

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v2[r0]: core::RangeCheck, v3[r1]: core::gas::GasBuiltin
Statements:
  (v4: ()) <- struct_construct()
  (v5: core::option::Option::<()>) <- Option::Some(v4)
Drops: v4
End:
  Callsite(v2, v3, v5)

blk1:
Inputs: v6[r0]: core::RangeCheck, v7[r1]: core::gas::GasBuiltin
Statements:
  (v8: ()) <- struct_construct()
  (v9: core::option::Option::<()>) <- Option::None(v8)
Drops: v8
End:
  Callsite(v6, v7, v9)

blk2 (root):
Inputs: v0[r0]: core::RangeCheck, v1[r1]: core::gas::GasBuiltin
Statements:
  (v11[r0]: core::RangeCheck, v12[r1]: core::gas::GasBuiltin, v10: core::option::Option::<()>) <- match core::gas::get_gas(v0, v1) {
    (v2, v3) => blk0,
    (v6, v7) => blk1,
  }
Drops:
End:
  Callsite(v11, v12, v10)

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

//! > Test calling libfunc that returns enum and returns the result with `return`.

//! > test_function_name
test_function_lowering

//! > function
fn foo() -> Option::<()> {
  return get_gas();
}

//! > function_name
foo

//! > module_code

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_format
blk0:
Inputs: v2[r0]: core::RangeCheck, v3[r1]: core::gas::GasBuiltin
Statements:
  (v4: ()) <- struct_construct()
  (v5: core::option::Option::<()>) <- Option::Some(v4)
Drops: v4
End:
  Callsite(v2, v3, v5)

blk1:
Inputs: v6[r0]: core::RangeCheck, v7[r1]: core::gas::GasBuiltin
Statements:
  (v8: ()) <- struct_construct()
  (v9: core::option::Option::<()>) <- Option::None(v8)
Drops: v8
End:
  Callsite(v6, v7, v9)

blk2 (root):
Inputs: v0[r0]: core::RangeCheck, v1[r1]: core::gas::GasBuiltin
Statements:
  (v11[r0]: core::RangeCheck, v12[r1]: core::gas::GasBuiltin, v10: core::option::Option::<()>) <- match core::gas::get_gas(v0, v1) {
    (v2, v3) => blk0,
    (v6, v7) => blk1,
  }
Drops:
End:
  Return(v11, v12, v10)
