//! > Test reboxing

//! > test_runner_name
test_reboxing_analysis

//! > function_name
main

//! > module_code
#[derive(Drop, Copy)]
struct A {
    a: felt252,
    b: felt252,
}

//! > function_code
fn main(a: Box<A>) -> Box<felt252> {
    BoxTrait::new(a.b)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates
v4

//! > before
Parameters: v0: core::box::Box::<test::A>
blk0 (root):
Statements:
  (v1: test::A) <- unbox(v0)
  (v2: core::felt252, v3: core::felt252) <- struct_destructure(v1)
  (v4: core::box::Box::<core::felt252>) <- into_box(v3)
End:
  Return(v4)

//! > after
Parameters: v0: core::box::Box::<test::A>
blk0 (root):
Statements:
  (v1: test::A) <- unbox(v0)
  (v5: core::box::Box::<core::felt252>, v6: core::box::Box::<core::felt252>) <- struct_destructure(v0)
  (v2: core::felt252, v3: core::felt252) <- struct_destructure(v1)
End:
  Return(v6)

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

//! > Test reboxing with multiple members

//! > test_runner_name
test_reboxing_analysis

//! > function_name
multi_member

//! > module_code
#[derive(Drop, Copy)]
struct Point {
    x: u32,
    y: u32,
    z: u32,
}

//! > function_code
fn multi_member(p: Box<Point>) -> (Box<u32>, Box<u32>) {
    (BoxTrait::new(p.x), BoxTrait::new(p.z))
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates
v5, v10

//! > before
Parameters: v0: core::box::Box::<test::Point>
blk0 (root):
Statements:
  (v1: test::Point) <- unbox(v0)
  (v2: core::integer::u32, v3: core::integer::u32, v4: core::integer::u32) <- struct_destructure(v1)
  (v5: core::box::Box::<core::integer::u32>) <- into_box(v2)
  (v6: test::Point) <- unbox(v0)
  (v7: core::integer::u32, v8: core::integer::u32, v9: core::integer::u32) <- struct_destructure(v6)
  (v10: core::box::Box::<core::integer::u32>) <- into_box(v9)
  (v11: (core::box::Box::<core::integer::u32>, core::box::Box::<core::integer::u32>)) <- struct_construct(v5, v10)
End:
  Return(v11)

//! > after
Parameters: v0: core::box::Box::<test::Point>
blk0 (root):
Statements:
  (v1: test::Point) <- unbox(v0)
  (v12: core::box::Box::<core::integer::u32>, v13: core::box::Box::<core::integer::u32>, v14: core::box::Box::<core::integer::u32>) <- struct_destructure(v0)
  (v2: core::integer::u32, v3: core::integer::u32, v4: core::integer::u32) <- struct_destructure(v1)
  (v6: test::Point) <- unbox(v0)
  (v15: core::box::Box::<core::integer::u32>, v16: core::box::Box::<core::integer::u32>, v17: core::box::Box::<core::integer::u32>) <- struct_destructure(v0)
  (v7: core::integer::u32, v8: core::integer::u32, v9: core::integer::u32) <- struct_destructure(v6)
  (v11: (core::box::Box::<core::integer::u32>, core::box::Box::<core::integer::u32>)) <- struct_construct(v12, v17)
End:
  Return(v11)

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

//! > Test reboxing with nested struct

//! > test_runner_name
test_reboxing_analysis

//! > function_name
nested_struct

//! > module_code
#[derive(Drop, Copy)]
struct Inner {
    value: felt252,
}

#[derive(Drop, Copy)]
struct Outer {
    inner: Inner,
    other: felt252,
}

//! > function_code
fn nested_struct(o: Box<Outer>) -> Box<felt252> {
    BoxTrait::new(o.inner.value)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates
v5

//! > before
Parameters: v0: core::box::Box::<test::Outer>
blk0 (root):
Statements:
  (v1: test::Outer) <- unbox(v0)
  (v2: test::Inner, v3: core::felt252) <- struct_destructure(v1)
  (v4: core::felt252) <- struct_destructure(v2)
  (v5: core::box::Box::<core::felt252>) <- into_box(v4)
End:
  Return(v5)

//! > after
Parameters: v0: core::box::Box::<test::Outer>
blk0 (root):
Statements:
  (v1: test::Outer) <- unbox(v0)
  (v6: core::box::Box::<test::Inner>, v7: core::box::Box::<core::felt252>) <- struct_destructure(v0)
  (v2: test::Inner, v3: core::felt252) <- struct_destructure(v1)
  (v8: core::box::Box::<core::felt252>) <- struct_destructure(v6)
  (v4: core::felt252) <- struct_destructure(v2)
End:
  Return(v8)

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

//! > Test reboxing with snapshot struct

//! > test_runner_name
test_reboxing_analysis

//! > function_name
with_snapshot

//! > module_code
#[derive(Drop)]
struct Data {
    non_copy: NonCopy,
    b: felt252,
}

#[derive(Drop)]
struct NonCopy {
    a: felt252,
}

//! > function_code
fn with_snapshot(data: Box<@Data>) -> Box<@NonCopy> {
    BoxTrait::new(data.unbox().non_copy)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates
v4

//! > before
Parameters: v0: core::box::Box::<@test::Data>
blk0 (root):
Statements:
  (v1: @test::Data) <- unbox(v0)
  (v2: @test::NonCopy, v3: @core::felt252) <- struct_destructure(v1)
  (v4: core::box::Box::<@test::NonCopy>) <- into_box(v2)
End:
  Return(v4)

//! > after
Parameters: v0: core::box::Box::<@test::Data>
blk0 (root):
Statements:
  (v1: @test::Data) <- unbox(v0)
  (v5: core::box::Box::<@test::NonCopy>, v6: core::box::Box::<@core::felt252>) <- struct_destructure(v0)
  (v2: @test::NonCopy, v3: @core::felt252) <- struct_destructure(v1)
End:
  Return(v5)

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

//! > Test reboxing without destructure (rebox whole struct)

//! > test_runner_name
test_reboxing_analysis

//! > function_name
rebox_whole

//! > module_code
struct Simple {
    value: felt252,
}

//! > function_code
fn rebox_whole(s: Box<Simple>) -> Box<Simple> {
    BoxTrait::new(s.unbox())
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates
v2

//! > before
Parameters: v0: core::box::Box::<test::Simple>
blk0 (root):
Statements:
  (v1: test::Simple) <- unbox(v0)
  (v2: core::box::Box::<test::Simple>) <- into_box(v1)
End:
  Return(v2)

//! > after
Parameters: v0: core::box::Box::<test::Simple>
blk0 (root):
Statements:
  (v1: test::Simple) <- unbox(v0)
End:
  Return(v0)

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

//! > Test no variable confusion in reboxing analysis

//! > test_runner_name
test_reboxing_analysis

//! > function_name
no_opportunity

//! > module_code
#[derive(Drop, Copy)]
struct Data {
    a: felt252,
    b: felt252,
}

//! > function_code
fn no_opportunity(d: Box<Data>, x: felt252) -> Box<felt252> {
    let _unused = d.a;
    BoxTrait::new(x)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates

//! > before
Parameters: v0: core::box::Box::<test::Data>, v1: core::felt252
blk0 (root):
Statements:
  (v5: core::box::Box::<core::felt252>) <- into_box(v1)
End:
  Return(v5)

//! > after
Parameters: v0: core::box::Box::<test::Data>, v1: core::felt252
blk0 (root):
Statements:
  (v5: core::box::Box::<core::felt252>) <- into_box(v1)
End:
  Return(v5)

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

//! > Test reboxing with enum

//! > test_runner_name
test_reboxing_analysis

//! > function_name
enum_rebox

//! > module_code
#[derive(Drop, Copy)]
enum MyEnum {
    A: u32,
    B: u32,
}

//! > function_code
fn enum_rebox(e: Box<MyEnum>) -> Box<u32> {
    let a = match e.unbox() {
        MyEnum::A(x) => x,
        MyEnum::B(x) => x,
    };
    BoxTrait::new(a)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates

//! > before
Parameters: v0: core::box::Box::<test::MyEnum>
blk0 (root):
Statements:
  (v1: test::MyEnum) <- unbox(v0)
End:
  Match(match_enum(v1) {
    MyEnum::A(v2) => blk1,
    MyEnum::B(v3) => blk2,
  })

blk1:
Statements:
End:
  Goto(blk3, {v2 -> v4})

blk2:
Statements:
End:
  Goto(blk3, {v3 -> v4})

blk3:
Statements:
  (v5: core::box::Box::<core::integer::u32>) <- into_box(v4)
End:
  Return(v5)

//! > after
Parameters: v0: core::box::Box::<test::MyEnum>
blk0 (root):
Statements:
  (v1: test::MyEnum) <- unbox(v0)
End:
  Match(match_enum(v1) {
    MyEnum::A(v2) => blk1,
    MyEnum::B(v3) => blk2,
  })

blk1:
Statements:
End:
  Goto(blk3, {v2 -> v4})

blk2:
Statements:
End:
  Goto(blk3, {v3 -> v4})

blk3:
Statements:
  (v5: core::box::Box::<core::integer::u32>) <- into_box(v4)
End:
  Return(v5)

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

//! > Test reboxing with tuple

//! > test_runner_name
test_reboxing_analysis

//! > function_name
tuple_rebox

//! > module_code

//! > function_code
fn tuple_rebox(t: Box<(u32, felt252, u64)>) -> Box<felt252> {
    let (_, mid, _) = t.unbox();
    BoxTrait::new(mid)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates
v5

//! > before
Parameters: v0: core::box::Box::<(core::integer::u32, core::felt252, core::integer::u64)>
blk0 (root):
Statements:
  (v1: (core::integer::u32, core::felt252, core::integer::u64)) <- unbox(v0)
  (v2: core::integer::u32, v3: core::felt252, v4: core::integer::u64) <- struct_destructure(v1)
  (v5: core::box::Box::<core::felt252>) <- into_box(v3)
End:
  Return(v5)

//! > after
Parameters: v0: core::box::Box::<(core::integer::u32, core::felt252, core::integer::u64)>
blk0 (root):
Statements:
  (v1: (core::integer::u32, core::felt252, core::integer::u64)) <- unbox(v0)
  (v6: core::box::Box::<core::integer::u32>, v7: core::box::Box::<core::felt252>, v8: core::box::Box::<core::integer::u64>) <- struct_destructure(v0)
  (v2: core::integer::u32, v3: core::felt252, v4: core::integer::u64) <- struct_destructure(v1)
End:
  Return(v7)

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

//! > Test reboxing with non-Copy struct

//! > test_runner_name
test_reboxing_analysis

//! > function_name
non_copy_struct_rebox

//! > TODO(eytan-starkware): Add support for non-copy structs where applicable

//! > module_code
#[derive(Drop)]
struct NonCopyStruct {
    arr: Array<felt252>,
    val: felt252,
}

//! > function_code
fn non_copy_struct_rebox(s: Box<NonCopyStruct>) -> Box<felt252> {
    BoxTrait::new(s.unbox().val)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates

//! > before
Parameters: v0: core::box::Box::<test::NonCopyStruct>
blk0 (root):
Statements:
  (v1: test::NonCopyStruct) <- unbox(v0)
  (v2: core::array::Array::<core::felt252>, v3: core::felt252) <- struct_destructure(v1)
  (v4: core::box::Box::<core::felt252>) <- into_box(v3)
End:
  Return(v4)

//! > after
Parameters: v0: core::box::Box::<test::NonCopyStruct>
blk0 (root):
Statements:
  (v1: test::NonCopyStruct) <- unbox(v0)
  (v2: core::array::Array::<core::felt252>, v3: core::felt252) <- struct_destructure(v1)
  (v4: core::box::Box::<core::felt252>) <- into_box(v3)
End:
  Return(v4)

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

//! > Test reboxing with non-Copy tuple

//! > test_runner_name
test_reboxing_analysis

//! > function_name
non_copy_tuple_rebox

//! > module_code

//! > function_code
fn non_copy_tuple_rebox(t: Box<(Array<felt252>, felt252)>) -> Box<felt252> {
    let (_, val) = t.unbox();
    BoxTrait::new(val)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates

//! > before
Parameters: v0: core::box::Box::<(core::array::Array::<core::felt252>, core::felt252)>
blk0 (root):
Statements:
  (v1: (core::array::Array::<core::felt252>, core::felt252)) <- unbox(v0)
  (v2: core::array::Array::<core::felt252>, v3: core::felt252) <- struct_destructure(v1)
  (v4: core::box::Box::<core::felt252>) <- into_box(v3)
End:
  Return(v4)

//! > after
Parameters: v0: core::box::Box::<(core::array::Array::<core::felt252>, core::felt252)>
blk0 (root):
Statements:
  (v1: (core::array::Array::<core::felt252>, core::felt252)) <- unbox(v0)
  (v2: core::array::Array::<core::felt252>, v3: core::felt252) <- struct_destructure(v1)
  (v4: core::box::Box::<core::felt252>) <- into_box(v3)
End:
  Return(v4)

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

//! > Test reboxing with branch-specific unbox assignments

//! > test_runner_name
test_reboxing_analysis

//! > function_name
branch_mut_unbox

//! > TODO(eytan-starkware): Support equality tracking and replacing the original deconstruct statement

//! > module_code
#[derive(Drop, Copy)]
struct Pair {
    left: felt252,
    right: felt252,
}

//! > function_code
fn branch_mut_unbox(flag: bool, p1: Box<Pair>, p2: Box<Pair>) -> Box<felt252> {
    let mut x = p1.unbox();
    if flag {
        x = p1.unbox();
    } else {
        x = p2.unbox();
    }
    BoxTrait::new(x.right)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates

//! > before
Parameters: v0: core::bool, v1: core::box::Box::<test::Pair>, v2: core::box::Box::<test::Pair>
blk0 (root):
Statements:
End:
  Match(match_enum(v0) {
    bool::False(v4) => blk1,
    bool::True(v5) => blk2,
  })

blk1:
Statements:
  (v6: test::Pair) <- unbox(v2)
End:
  Goto(blk3, {v6 -> v7})

blk2:
Statements:
  (v8: test::Pair) <- unbox(v1)
End:
  Goto(blk3, {v8 -> v7})

blk3:
Statements:
  (v9: core::felt252, v10: core::felt252) <- struct_destructure(v7)
  (v11: core::box::Box::<core::felt252>) <- into_box(v10)
End:
  Return(v11)

//! > after
Parameters: v0: core::bool, v1: core::box::Box::<test::Pair>, v2: core::box::Box::<test::Pair>
blk0 (root):
Statements:
End:
  Match(match_enum(v0) {
    bool::False(v4) => blk1,
    bool::True(v5) => blk2,
  })

blk1:
Statements:
  (v6: test::Pair) <- unbox(v2)
End:
  Goto(blk3, {v6 -> v7})

blk2:
Statements:
  (v8: test::Pair) <- unbox(v1)
End:
  Goto(blk3, {v8 -> v7})

blk3:
Statements:
  (v9: core::felt252, v10: core::felt252) <- struct_destructure(v7)
  (v11: core::box::Box::<core::felt252>) <- into_box(v10)
End:
  Return(v11)

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

//! > Test reboxing with non-drop struct and member

//! > test_runner_name
test_reboxing_analysis

//! > function_name
main

//! > TODO(eytan-starkware): Support non-drop by unboxing each member.

//! > module_code
#[derive(Copy)]
struct NonDrop {
    b: felt252,
}

#[derive(Copy)]
struct A {
    a: felt252,
    non_drop: NonDrop,
}

//! > function_code
fn main(a: Box<A>) -> (Box<felt252>, NonDrop) {
    let unboxed = a.unbox();
    (BoxTrait::new(unboxed.a), unboxed.non_drop)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates

//! > before
Parameters: v0: core::box::Box::<test::A>
blk0 (root):
Statements:
  (v1: test::A) <- unbox(v0)
  (v2: core::felt252, v3: test::NonDrop) <- struct_destructure(v1)
  (v4: core::box::Box::<core::felt252>) <- into_box(v2)
  (v5: (core::box::Box::<core::felt252>, test::NonDrop)) <- struct_construct(v4, v3)
End:
  Return(v5)

//! > after
Parameters: v0: core::box::Box::<test::A>
blk0 (root):
Statements:
  (v1: test::A) <- unbox(v0)
  (v2: core::felt252, v3: test::NonDrop) <- struct_destructure(v1)
  (v4: core::box::Box::<core::felt252>) <- into_box(v2)
  (v5: (core::box::Box::<core::felt252>, test::NonDrop)) <- struct_construct(v4, v3)
End:
  Return(v5)

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

//! > Test reboxing with snapshot of non-drop struct and member

//! > test_runner_name
test_reboxing_analysis

//! > function_name
main

//! > module_code
use core::box::BoxTrait;

#[derive(Copy)]
struct NonDrop {
    b: felt252,
}

#[derive(Copy)]
struct A {
    a: felt252,
    non_drop: NonDrop,
}

//! > function_code
fn main(a: Box<@A>) -> (Box<@felt252>, @NonDrop) {
    let a = a.unbox();
    (BoxTrait::new(a.a), a.non_drop)
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > candidates
v4

//! > before
Parameters: v0: core::box::Box::<@test::A>
blk0 (root):
Statements:
  (v1: @test::A) <- unbox(v0)
  (v2: @core::felt252, v3: @test::NonDrop) <- struct_destructure(v1)
  (v4: core::box::Box::<@core::felt252>) <- into_box(v2)
  (v5: @core::felt252, v6: @test::NonDrop) <- struct_destructure(v1)
  (v7: (core::box::Box::<@core::felt252>, @test::NonDrop)) <- struct_construct(v4, v6)
End:
  Return(v7)

//! > after
Parameters: v0: core::box::Box::<@test::A>
blk0 (root):
Statements:
  (v1: @test::A) <- unbox(v0)
  (v8: core::box::Box::<@core::felt252>, v9: core::box::Box::<@test::NonDrop>) <- struct_destructure(v0)
  (v2: @core::felt252, v3: @test::NonDrop) <- struct_destructure(v1)
  (v5: @core::felt252, v6: @test::NonDrop) <- struct_destructure(v1)
  (v7: (core::box::Box::<@core::felt252>, @test::NonDrop)) <- struct_construct(v8, v6)
End:
  Return(v7)

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

//! > Test mixed reboxing types in different blocks

//! > test_runner_name
test_reboxing_analysis

//! > function_name
mixed_reboxing

//! > TODO(eytan-starkware): When removing demand for Copy, check no double

//! >     into_box remain

//! > module_code
#[derive(Drop, Copy)]
struct Point {
    x: felt252,
    y: felt252,
}

//! > function_code
fn mixed_reboxing(p: Box<Point>, flag: bool) -> Box<felt252> {
    if flag {
        let unboxed = p.unbox();
        BoxTrait::new(unboxed.x)
    } else {
        let unboxed = p.unbox();
        let boxed = BoxTrait::new(unboxed);
        let unboxed2 = boxed.unbox();
        BoxTrait::new(unboxed2.y)
    }
}

//! > candidates
v5, v9, v14

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

blk1:
Statements:
  (v4: test::Point) <- unbox(v0)
  (v5: core::box::Box::<test::Point>) <- into_box(v4)
  (v6: test::Point) <- unbox(v5)
  (v7: core::felt252, v8: core::felt252) <- struct_destructure(v6)
  (v9: core::box::Box::<core::felt252>) <- into_box(v8)
End:
  Goto(blk3, {v9 -> v10})

blk2:
Statements:
  (v11: test::Point) <- unbox(v0)
  (v12: core::felt252, v13: core::felt252) <- struct_destructure(v11)
  (v14: core::box::Box::<core::felt252>) <- into_box(v12)
End:
  Goto(blk3, {v14 -> v10})

blk3:
Statements:
End:
  Return(v10)

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

blk1:
Statements:
  (v4: test::Point) <- unbox(v0)
  (v6: test::Point) <- unbox(v0)
  (v15: core::box::Box::<core::felt252>, v16: core::box::Box::<core::felt252>) <- struct_destructure(v0)
  (v7: core::felt252, v8: core::felt252) <- struct_destructure(v6)
End:
  Goto(blk3, {v16 -> v10})

blk2:
Statements:
  (v11: test::Point) <- unbox(v0)
  (v17: core::box::Box::<core::felt252>, v18: core::box::Box::<core::felt252>) <- struct_destructure(v0)
  (v12: core::felt252, v13: core::felt252) <- struct_destructure(v11)
End:
  Goto(blk3, {v17 -> v10})

blk3:
Statements:
End:
  Return(v10)
