//! > Test boolean if.

//! > test_runner_name
test_function_lowering(expect_diagnostics: false)

//! > function_code
fn foo(flag: bool, x: u128) -> Option<A> {
    let a = A { x };
    if flag {
        Some(a)
    } else {
        None
    }
}

//! > function_name
foo

//! > module_code
struct A {
    x: u128,
}
impl ADestruct of Destruct<A> {
    #[inline(never)]
    #[feature("corelib-internal-use")]
    fn destruct(self: A) nopanic {
        let A { x } = self;
        // Use RangeCheck, a previously unused implicit.
        match core::integer::u128_overflowing_add(x, 2_u128) {
            Ok(v) => v,
            Err(v) => v,
        };
    }
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::RangeCheck, v1: core::bool, v2: core::integer::u128
blk0 (root):
Statements:
End:
  Match(match_enum(v1) {
    bool::False(v3) => blk1,
    bool::True(v4) => blk2,
  })

blk1:
Statements:
  (v5: test::A) <- struct_construct(v2)
  (v6: core::RangeCheck) <- test::ADestruct::destruct(v0, v5)
  (v7: ()) <- struct_construct()
  (v8: core::option::Option::<test::A>) <- Option::None(v7)
End:
  Return(v6, v8)

blk2:
Statements:
  (v9: test::A) <- struct_construct(v2)
  (v10: core::option::Option::<test::A>) <- Option::Some(v9)
End:
  Return(v0, v10)

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

//! > Test dict destruct through panic.

//! > test_runner_name
test_function_lowering(expect_diagnostics: false)

//! > function_code
fn foo(d: Felt252Dict<felt252>) {
    get_total_signed_weight(d);
}

//! > function_name
foo

//! > module_code
fn get_total_signed_weight(used_keys: Felt252Dict<felt252>) -> u128 {
    1_u128
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::RangeCheck, v1: core::SegmentArena, v2: core::gas::GasBuiltin, v3: core::dict::Felt252Dict::<core::felt252>
blk0 (root):
Statements:
  (v4: core::RangeCheck, v5: core::SegmentArena, v6: core::gas::GasBuiltin, v7: core::dict::SquashedFelt252Dict::<core::felt252>) <- core::dict::Felt252DictImpl::<core::felt252, core::Felt252Felt252DictValue>::squash(v0, v1, v2, v3)
End:
  Return(v4, v5, v6)

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

//! > Test panic destruct.

//! > test_runner_name
test_function_lowering(expect_diagnostics: false)

//! > function_code
fn foo(a: A, b: B) {
    may_panic();
    may_panic();
    panic_with_felt252('123');
}

//! > function_name
foo

//! > module_code
#[inline(never)]
fn may_panic() {
    panic_with_felt252('123');
}

struct A {}
impl APanicDestruct of PanicDestruct<A> {
    #[inline(never)]
    fn panic_destruct(self: A, ref panic: Panic) nopanic {
        let A { } = self;
    }
}

struct B {
    a: A,
}
impl BPanicDestruct of PanicDestruct<B> {
    #[inline(never)]
    fn panic_destruct(self: B, ref panic: Panic) nopanic {
        let B { a: _ } = self;
    }
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: test::A, v1: test::B
blk0 (root):
Statements:
  (v2: core::panics::PanicResult::<((),)>) <- test::may_panic()
End:
  Match(match_enum(v2) {
    PanicResult::Ok(v3) => blk1,
    PanicResult::Err(v4) => blk4,
  })

blk1:
Statements:
  (v5: core::panics::PanicResult::<((),)>) <- test::may_panic()
End:
  Match(match_enum(v5) {
    PanicResult::Ok(v6) => blk2,
    PanicResult::Err(v7) => blk3,
  })

blk2:
Statements:
  (v8: (core::panics::Panic, core::array::Array::<core::felt252>)) <- core::panic_with_const_felt252::<3224115>()
  (v9: core::panics::Panic, v10: core::array::Array::<core::felt252>) <- struct_destructure(v8)
  (v11: core::panics::Panic) <- test::BPanicDestruct::panic_destruct(v1, v9)
  (v12: core::panics::Panic) <- test::APanicDestruct::panic_destruct(v0, v11)
  (v13: (core::panics::Panic, core::array::Array::<core::felt252>)) <- struct_construct(v12, v10)
  (v14: core::panics::PanicResult::<((),)>) <- PanicResult::Err(v13)
End:
  Return(v14)

blk3:
Statements:
End:
  Goto(blk5, {v7 -> v15})

blk4:
Statements:
End:
  Goto(blk5, {v4 -> v15})

blk5:
Statements:
  (v16: core::panics::Panic, v17: core::array::Array::<core::felt252>) <- struct_destructure(v15)
  (v18: core::panics::Panic) <- test::BPanicDestruct::panic_destruct(v1, v16)
  (v19: core::panics::Panic) <- test::APanicDestruct::panic_destruct(v0, v18)
  (v20: (core::panics::Panic, core::array::Array::<core::felt252>)) <- struct_construct(v19, v17)
  (v21: core::panics::PanicResult::<((),)>) <- PanicResult::Err(v20)
End:
  Return(v21)

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

//! > Test panic destruct with merge.

//! > test_runner_name
test_function_lowering(expect_diagnostics: false)

//! > function_code
fn foo(a: bool, d: A) -> A {
    if a {
        may_panic();
    }
    return d;
}

//! > function_name
foo

//! > module_code
#[inline(never)]
fn may_panic() {
    panic_with_felt252('123');
}

struct A {}
impl APanicDestruct of PanicDestruct<A> {
    #[inline(never)]
    fn panic_destruct(self: A, ref panic: Panic) nopanic {
        let A { } = self;
    }
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::bool, v1: test::A
blk0 (root):
Statements:
End:
  Match(match_enum(v0) {
    bool::False(v2) => blk1,
    bool::True(v3) => blk2,
  })

blk1:
Statements:
  (v4: (test::A,)) <- struct_construct(v1)
  (v5: core::panics::PanicResult::<(test::A,)>) <- PanicResult::Ok(v4)
End:
  Return(v5)

blk2:
Statements:
  (v6: core::panics::PanicResult::<((),)>) <- test::may_panic()
End:
  Match(match_enum(v6) {
    PanicResult::Ok(v7) => blk3,
    PanicResult::Err(v8) => blk4,
  })

blk3:
Statements:
  (v9: (test::A,)) <- struct_construct(v1)
  (v10: core::panics::PanicResult::<(test::A,)>) <- PanicResult::Ok(v9)
End:
  Return(v10)

blk4:
Statements:
  (v11: core::panics::Panic, v12: core::array::Array::<core::felt252>) <- struct_destructure(v8)
  (v13: core::panics::Panic) <- test::APanicDestruct::panic_destruct(v1, v11)
  (v14: (core::panics::Panic, core::array::Array::<core::felt252>)) <- struct_construct(v13, v12)
  (v15: core::panics::PanicResult::<(test::A,)>) <- PanicResult::Err(v14)
End:
  Return(v15)

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

//! > Test custom unit destruct (Currently broken).

//! > test_runner_name
test_function_lowering(expect_diagnostics: false)

//! > function_code
fn foo(a: Option<()>) {
    bar::<(), MyDestruct>(a.expect('Should be Some.'))
}

//! > function_name
foo

//! > module_code
#[inline]
fn bar<T, +Destruct<T>>(a: T) {}

pub impl MyDestruct of Destruct<()> {
    #[inline(always)]
    fn destruct(self: ()) nopanic {
        my_fn()
    }
}

extern fn my_fn() nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > lowering_flat
Parameters: v0: core::option::Option::<()>
blk0 (root):
Statements:
End:
  Match(match_enum(v0) {
    Option::Some(v1) => blk1,
    Option::None(v2) => blk2,
  })

blk1:
Statements:
  () <- test::my_fn()
  (v3: ()) <- struct_construct()
  (v4: ((),)) <- struct_construct(v3)
  (v5: core::panics::PanicResult::<((),)>) <- PanicResult::Ok(v4)
End:
  Return(v5)

blk2:
Statements:
  (v6: (core::panics::Panic, core::array::Array::<core::felt252>)) <- core::panic_with_const_felt252::<433078840523992521883553638820701486>()
  (v7: core::panics::PanicResult::<((),)>) <- PanicResult::Err(v6)
End:
  Return(v7)

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

//! > Test inline of self calling destruct.

//! > test_runner_name
test_function_lowering(expect_diagnostics: true)

//! > function_code
fn foo() {
    let _a = A {};
}

//! > function_name
foo

//! > module_code
struct A {}
impl ADestruct of Destruct<A> {
    #[inline(always)]
    fn destruct(self: A) nopanic {}
}

//! > semantic_diagnostics

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

error[E3005]: Cannot inline a function that might call itself.
 --> lib.cairo:3:5-4:35
      #[inline(always)]
 _____^
|     fn destruct(self: A) nopanic {}
|___________________________________^

//! > lowering_flat
Parameters:
