//! > Test nopanic function cycle.

//! > test_runner_name
test_function_lowering(expect_diagnostics: true)

//! > function_code
fn foo(x: felt252) nopanic {
    foo(x);
}

//! > function_name
foo

//! > semantic_diagnostics

//! > lowering_diagnostics
error[E3008]: Call cycle of `nopanic` functions is not allowed.
 --> lib.cairo:1:1-3:1
  fn foo(x: felt252) nopanic {
 _^
|     foo(x);
| }
|_^

//! > lowering_flat
<Failed lowering function - run with RUST_LOG=warn (or less) to see diagnostics>

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

//! > Test explicit gas handling.

//! > test_runner_name
test_function_lowering(expect_diagnostics: false)

//! > function_code
fn foo(x: felt252) {
    match core::gas::withdraw_gas() {
        Some(_) => foo(x),
        None => {},
    }
}

//! > function_name
foo

//! > semantic_diagnostics

//! > lowering_diagnostics

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

blk1:
Statements:
  (v7: core::gas::GasBuiltin) <- core::gas::redeposit_gas(v4)
  (v8: core::RangeCheck, v9: core::gas::GasBuiltin) <- test::foo(v3, v7, v2)
End:
  Return(v8, v9)

blk2:
Statements:
  (v10: core::gas::GasBuiltin) <- core::gas::redeposit_gas(v6)
End:
  Return(v5, v10)

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

//! > Test nopanic function cycle with withdraw gas.

//! > test_runner_name
test_function_lowering(expect_diagnostics: false)

//! > function_code
fn foo(x: felt252, costs: core::gas::BuiltinCosts) nopanic {
    match core::gas::withdraw_gas_all(costs) {
        Some(_) => foo(x, costs),
        None => {},
    }
}

//! > function_name
foo

//! > semantic_diagnostics

//! > lowering_diagnostics

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

blk1:
Statements:
  (v8: core::gas::GasBuiltin) <- core::gas::redeposit_gas(v5)
  (v9: core::RangeCheck, v10: core::gas::GasBuiltin) <- test::foo(v4, v8, v2, v3)
End:
  Return(v9, v10)

blk2:
Statements:
  (v11: core::gas::GasBuiltin) <- core::gas::redeposit_gas(v7)
End:
  Return(v6, v11)

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

//! > Test destructor basic cycle.

//! > test_runner_name
test_function_lowering(expect_diagnostics: true)

//! > function_code
fn foo() {}

//! > function_name
foo

//! > module_code
struct A {}
impl ADestruct of Destruct<A> {
    fn destruct(self: A) nopanic {
        let A { } = self;
        B {};
    }
}

struct B {}
impl BDestruct of Destruct<B> {
    fn destruct(self: B) nopanic {
        let B { } = self;
        A {};
    }
}

//! > semantic_diagnostics

//! > lowering_diagnostics
error[E3008]: Call cycle of `nopanic` functions is not allowed.
 --> lib.cairo:3:5-6:5
      fn destruct(self: A) nopanic {
 _____^
| ...
|     }
|_____^

error[E3008]: Call cycle of `nopanic` functions is not allowed.
 --> lib.cairo:11:5-14:5
      fn destruct(self: B) nopanic {
 _____^
| ...
|     }
|_____^

//! > lowering_flat
Parameters:
blk0 (root):
Statements:
End:
  Return()

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

//! > Test self-referential Destruct impl in no-gas mode does not hang (issue #9894).

//! > test_runner_name
test_function_lowering(expect_diagnostics: false, no_gas: true)

//! > function_code
fn foo() {
    let _ = D {};
}

//! > function_name
foo

//! > module_code
struct D {}
impl DDestruct of Destruct<D> {
    fn destruct(self: D) nopanic {}
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters:
blk0 (root):
Statements:
  (v0: test::D) <- struct_construct()
  () <- test::DDestruct::destruct(v0)
End:
  Return()
