//! > Test extern function calling.

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

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

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::felt252, v1: core::felt252
blk0 (root):
Statements:
  (v2: core::felt252, v3: core::felt252) <- test::f(v0, v1)
  (v4: core::felt252, v5: core::felt252) <- test::g(v2, v1)
  (v6: core::felt252, v7: core::felt252, v8: core::felt252) <- test::h(v4, v1)
  (v9: core::felt252, v10: (core::felt252,)) <- test::i(v6, v1)
End:
  Return(v9)

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

//! > Test extern function enum calling.

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

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

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::felt252, v1: core::felt252
blk0 (root):
Statements:
End:
  Match(match test::f(v0, v1) {
    MyEnum::A(v2, v3) => blk1,
    MyEnum::B(v4, v5) => blk2,
    MyEnum::C(v6, v7, v8) => blk3,
  })

blk1:
Statements:
  (v9: test::MyEnum) <- MyEnum::A(v3)
End:
  Goto(blk4, {v2 -> v10, v9 -> v11})

blk2:
Statements:
  (v12: (core::felt252,)) <- struct_construct(v5)
  (v13: test::MyEnum) <- MyEnum::B(v12)
End:
  Goto(blk4, {v4 -> v10, v13 -> v11})

blk3:
Statements:
  (v14: (core::felt252, core::felt252)) <- struct_construct(v7, v8)
  (v15: test::MyEnum) <- MyEnum::C(v14)
End:
  Goto(blk4, {v6 -> v10, v15 -> v11})

blk4:
Statements:
End:
  Match(match_enum(v11) {
    MyEnum::A(v16) => blk5,
    MyEnum::B(v17) => blk6,
    MyEnum::C(v18) => blk7,
  })

blk5:
Statements:
End:
  Return(v10)

blk6:
Statements:
End:
  Return(v10)

blk7:
Statements:
End:
  Return(v10)

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

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

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

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

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::felt252, v1: core::felt252
blk0 (root):
Statements:
End:
  Match(match test::f(v0, v1) {
    MyEnum::A(v2) => blk1,
    MyEnum::B(v3) => blk2,
    MyEnum::C(v4, v5) => blk3,
  })

blk1:
Statements:
End:
  Return(v2)

blk2:
Statements:
End:
  Return(v3)

blk3:
Statements:
End:
  Return(v4)

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

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

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

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

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::felt252, v1: core::felt252
blk0 (root):
Statements:
End:
  Match(match test::f(v0, v1) {
    MyEnum::A(v2, v3) => blk1,
    MyEnum::B(v4, v5) => blk2,
    MyEnum::C(v6, v7, v8) => blk3,
  })

blk1:
Statements:
End:
  Return(v2, v3)

blk2:
Statements:
End:
  Return(v4, v5)

blk3:
Statements:
End:
  Return(v6, v7)

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

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

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

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

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::RangeCheck, v1: core::felt252, v2: core::felt252
blk0 (root):
Statements:
End:
  Match(match test::f(v0, v1, v2) {
    MyEnum::A(v3, v4, v5) => blk1,
    MyEnum::B(v6, v7, v8) => blk2,
    MyEnum::C(v9, v10, v11, v12) => blk3,
  })

blk1:
Statements:
  (v13: test::MyEnum) <- MyEnum::A(v5)
End:
  Goto(blk4, {v3 -> v14, v4 -> v15, v13 -> v16})

blk2:
Statements:
  (v17: (core::felt252,)) <- struct_construct(v8)
  (v18: test::MyEnum) <- MyEnum::B(v17)
End:
  Goto(blk4, {v6 -> v14, v7 -> v15, v18 -> v16})

blk3:
Statements:
  (v19: (core::felt252, core::felt252)) <- struct_construct(v11, v12)
  (v20: test::MyEnum) <- MyEnum::C(v19)
End:
  Goto(blk4, {v9 -> v14, v10 -> v15, v20 -> v16})

blk4:
Statements:
End:
  Match(match_enum(v16) {
    MyEnum::A(v21) => blk5,
    MyEnum::B(v22) => blk6,
    MyEnum::C(v23) => blk7,
  })

blk5:
Statements:
End:
  Return(v14, v15)

blk6:
Statements:
End:
  Return(v14, v15)

blk7:
Statements:
End:
  Return(v14, v15)

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

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

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

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

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::RangeCheck, v1: core::felt252, v2: core::felt252
blk0 (root):
Statements:
End:
  Match(match test::f(v0, v1, v2) {
    MyEnum::A(v3, v4, v5) => blk1,
    MyEnum::B(v6, v7, v8) => blk2,
    MyEnum::C(v9, v10, v11, v12) => blk3,
  })

blk1:
Statements:
End:
  Return(v3, v4, v5)

blk2:
Statements:
End:
  Return(v6, v7, v8)

blk3:
Statements:
End:
  Return(v9, v10, v11)

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

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

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

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

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::array::Array::<core::felt252>, v1: (core::felt252,)
blk0 (root):
Statements:
End:
  Match(match test::f(v0, v1) {
    MyEnum::A(v2, v3, v4) => blk1,
  })

blk1:
Statements:
End:
  Return(v2)

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

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

//! > test_runner_name
test_function_lowering

//! > function
fn foo() {
    let _unused = gas::withdraw_gas();
}

//! > function_name
foo

//! > module_code

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::RangeCheck, v1: core::gas::GasBuiltin
blk0 (root):
Statements:
End:
  Match(match core::gas::withdraw_gas(v0, v1) {
    Option::Some(v2, v3) => blk1,
    Option::None(v4, v5) => blk2,
  })

blk1:
Statements:
End:
  Return(v2, v3)

blk2:
Statements:
End:
  Return(v4, v5)

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

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

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

//! > module_code

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::RangeCheck, v1: core::gas::GasBuiltin
blk0 (root):
Statements:
End:
  Match(match core::gas::withdraw_gas(v0, v1) {
    Option::Some(v2, v3) => blk1,
    Option::None(v4, v5) => blk2,
  })

blk1:
Statements:
  (v6: ()) <- struct_construct()
  (v7: core::option::Option::<()>) <- Option::Some(v6)
End:
  Return(v2, v3, v7)

blk2:
Statements:
  (v8: ()) <- struct_construct()
  (v9: core::option::Option::<()>) <- Option::None(v8)
End:
  Return(v4, v5, v9)

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

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

//! > test_runner_name
test_function_lowering

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

//! > function_name
foo

//! > module_code

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::RangeCheck, v1: core::gas::GasBuiltin
blk0 (root):
Statements:
End:
  Match(match core::gas::withdraw_gas(v0, v1) {
    Option::Some(v2, v3) => blk1,
    Option::None(v4, v5) => blk2,
  })

blk1:
Statements:
  (v6: ()) <- struct_construct()
  (v7: core::option::Option::<()>) <- Option::Some(v6)
End:
  Return(v2, v3, v7)

blk2:
Statements:
  (v8: ()) <- struct_construct()
  (v9: core::option::Option::<()>) <- Option::None(v8)
End:
  Return(v4, v5, v9)
