//! > Test struct folding.

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252) -> felt252 {
    let b = (a,);
    let c = (b,);
    let (d,) = c;
    let (e,) = d;
    e
}

//! > function_name
foo

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v1: (core::felt252,)) <- struct_construct(v0)
  (v2: ((core::felt252,),)) <- struct_construct(v1)
  (v3: (core::felt252,)) <- struct_destructure(v2)
  (v4: core::felt252) <- struct_destructure(v3)
End:
  Return(v4)

//! > after
Parameters: v0: core::felt252
blk0 (root):
Statements:
End:
  Return(v0)

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

//! > Test destruct after merge

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252) -> felt252 {
    let (b,) = match a {
        0 => (0,),
        _ => (a,),
    };
    b
}

//! > function_name
foo

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252
blk0 (root):
Statements:
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v1) => blk2,
  })

blk1:
Statements:
  (v2: core::felt252) <- 0
  (v3: (core::felt252,)) <- struct_construct(v2)
End:
  Goto(blk3, {v3 -> v4})

blk2:
Statements:
  (v5: (core::felt252,)) <- struct_construct(v0)
End:
  Goto(blk3, {v5 -> v4})

blk3:
Statements:
  (v6: core::felt252) <- struct_destructure(v4)
End:
  Return(v6)

//! > after
Parameters: v0: core::felt252
blk0 (root):
Statements:
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v1) => blk2,
  })

blk1:
Statements:
  (v2: core::felt252) <- 0
End:
  Goto(blk3, {v2 -> v7})

blk2:
Statements:
End:
  Goto(blk3, {v0 -> v7})

blk3:
Statements:
End:
  Return(v7)

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

//! > Test use in one arm

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252) -> felt252 {
    let arr = array![10, 11, 12];

    let c = (a, arr);
    if a == 0 {
        let (a, _arr) = c;
        return a;
    } else {
        consume(c);
        return 1;
    }
}

//! > function_name
foo

//! > module_code
extern fn consume(arg: (felt252, Array<felt252>)) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v1: core::array::Array::<core::felt252>) <- core::array::array_new::<core::felt252>()
  (v2: core::felt252) <- 10
  (v3: core::array::Array::<core::felt252>) <- core::array::array_append::<core::felt252>(v1, v2)
  (v5: core::felt252) <- 11
  (v6: core::array::Array::<core::felt252>) <- core::array::array_append::<core::felt252>(v3, v5)
  (v8: core::felt252) <- 12
  (v9: core::array::Array::<core::felt252>) <- core::array::array_append::<core::felt252>(v6, v8)
  (v11: (core::felt252, core::array::Array::<core::felt252>)) <- struct_construct(v0, v9)
  (v12: core::felt252, v13: @core::felt252) <- snapshot(v0)
  (v17: core::felt252) <- desnap(v13)
End:
  Match(match core::felt252_is_zero(v17) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v18) => blk2,
  })

blk1:
Statements:
  (v19: ()) <- struct_construct()
  (v20: core::bool) <- bool::True(v19)
End:
  Goto(blk3, {v20 -> v21})

blk2:
Statements:
  (v22: ()) <- struct_construct()
  (v23: core::bool) <- bool::False(v22)
End:
  Goto(blk3, {v23 -> v21})

blk3:
Statements:
End:
  Match(match_enum(v21) {
    bool::False(v24) => blk4,
    bool::True(v25) => blk5,
  })

blk4:
Statements:
  () <- test::consume(v11)
  (v26: core::felt252) <- 1
End:
  Return(v26)

blk5:
Statements:
  (v27: core::felt252, v28: core::array::Array::<core::felt252>) <- struct_destructure(v11)
End:
  Return(v27)

//! > after
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v1: core::array::Array::<core::felt252>) <- core::array::array_new::<core::felt252>()
  (v2: core::felt252) <- 10
  (v3: core::array::Array::<core::felt252>) <- core::array::array_append::<core::felt252>(v1, v2)
  (v5: core::felt252) <- 11
  (v6: core::array::Array::<core::felt252>) <- core::array::array_append::<core::felt252>(v3, v5)
  (v8: core::felt252) <- 12
  (v9: core::array::Array::<core::felt252>) <- core::array::array_append::<core::felt252>(v6, v8)
  (v12: core::felt252, v13: @core::felt252) <- snapshot(v0)
  (v17: core::felt252) <- desnap(v13)
End:
  Match(match core::felt252_is_zero(v17) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v18) => blk2,
  })

blk1:
Statements:
  (v19: ()) <- struct_construct()
  (v20: core::bool) <- bool::True(v19)
End:
  Goto(blk3, {v20 -> v21})

blk2:
Statements:
  (v22: ()) <- struct_construct()
  (v23: core::bool) <- bool::False(v22)
End:
  Goto(blk3, {v23 -> v21})

blk3:
Statements:
End:
  Match(match_enum(v21) {
    bool::False(v24) => blk4,
    bool::True(v25) => blk5,
  })

blk4:
Statements:
  (v29: (core::felt252, core::array::Array::<core::felt252>)) <- struct_construct(v0, v9)
  () <- test::consume(v29)
  (v26: core::felt252) <- 1
End:
  Return(v26)

blk5:
Statements:
End:
  Return(v0)

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

//! > Test destructure after merge.

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252) -> felt252 {
    let tuple = match a {
        0 => (1, (2, 3)),
        _ => {
            let tuple = (a, (2, a));
            use_tuple(tuple);
            tuple
        },
    };
    let (b, (c, d)) = tuple;
    b + c + d
}

//! > function_name
foo

//! > module_code
extern fn use_tuple<T>(a: (felt252, T)) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252
blk0 (root):
Statements:
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v1) => blk2,
  })

blk1:
Statements:
  (v2: core::felt252) <- 1
  (v3: core::felt252) <- 2
  (v4: core::felt252) <- 3
  (v5: (core::felt252, core::felt252)) <- struct_construct(v3, v4)
  (v6: (core::felt252, (core::felt252, core::felt252))) <- struct_construct(v2, v5)
End:
  Goto(blk3, {v6 -> v7})

blk2:
Statements:
  (v8: core::felt252) <- 2
  (v9: (core::felt252, core::felt252)) <- struct_construct(v8, v0)
  (v10: (core::felt252, (core::felt252, core::felt252))) <- struct_construct(v0, v9)
  () <- test::use_tuple::<(core::felt252, core::felt252)>(v10)
End:
  Goto(blk3, {v10 -> v7})

blk3:
Statements:
  (v11: core::felt252, v12: (core::felt252, core::felt252)) <- struct_destructure(v7)
  (v13: core::felt252, v14: core::felt252) <- struct_destructure(v12)
  (v15: core::felt252) <- core::felt252_add(v11, v13)
  (v16: core::felt252) <- core::felt252_add(v15, v14)
End:
  Return(v16)

//! > after
Parameters: v0: core::felt252
blk0 (root):
Statements:
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v1) => blk2,
  })

blk1:
Statements:
  (v2: core::felt252) <- 1
  (v3: core::felt252) <- 2
  (v4: core::felt252) <- 3
End:
  Goto(blk3, {v2 -> v17, v3 -> v19, v4 -> v20})

blk2:
Statements:
  (v8: core::felt252) <- 2
  (v9: (core::felt252, core::felt252)) <- struct_construct(v8, v0)
  (v10: (core::felt252, (core::felt252, core::felt252))) <- struct_construct(v0, v9)
  () <- test::use_tuple::<(core::felt252, core::felt252)>(v10)
End:
  Goto(blk3, {v0 -> v17, v8 -> v19, v0 -> v20})

blk3:
Statements:
  (v15: core::felt252) <- core::felt252_add(v17, v19)
  (v16: core::felt252) <- core::felt252_add(v15, v20)
End:
  Return(v16)

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

//! > Test construct after destruct.

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252) -> (felt252, felt252) {
    let b = (a, a);
    let tuple = match a {
        0 => { b },
        _ => {
            let (c, _) = b;
            (c, c)
        },
    };
    tuple
}

//! > function_name
foo

//! > module_code
extern fn use_tuple<T>(a: (felt252, T)) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v1: (core::felt252, core::felt252)) <- struct_construct(v0, v0)
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v2) => blk2,
  })

blk1:
Statements:
End:
  Goto(blk3, {v1 -> v3})

blk2:
Statements:
  (v4: core::felt252, v5: core::felt252) <- struct_destructure(v1)
  (v6: (core::felt252, core::felt252)) <- struct_construct(v4, v4)
End:
  Goto(blk3, {v6 -> v3})

blk3:
Statements:
End:
  Return(v3)

//! > after
Parameters: v0: core::felt252
blk0 (root):
Statements:
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v2) => blk2,
  })

blk1:
Statements:
End:
  Goto(blk3, {v0 -> v7, v0 -> v8})

blk2:
Statements:
End:
  Goto(blk3, {v0 -> v7, v0 -> v8})

blk3:
Statements:
  (v3: (core::felt252, core::felt252)) <- struct_construct(v7, v8)
End:
  Return(v3)

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

//! > Test split var used in two arms (Optimization does not apply.)

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252) {
    let b = (a, a);
    match a {
        0 => {
            let (_, _) = b;
            use_tuple(b);
        },
        _ => {
            let (_, _) = b;
            use_tuple(b);
        },
    };
}

//! > function_name
foo

//! > module_code
extern fn use_tuple<T>(a: (felt252, T)) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v1: (core::felt252, core::felt252)) <- struct_construct(v0, v0)
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v2) => blk2,
  })

blk1:
Statements:
  () <- test::use_tuple::<core::felt252>(v1)
End:
  Goto(blk3, {})

blk2:
Statements:
  () <- test::use_tuple::<core::felt252>(v1)
End:
  Goto(blk3, {})

blk3:
Statements:
End:
  Return()

//! > after
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v1: (core::felt252, core::felt252)) <- struct_construct(v0, v0)
End:
  Match(match core::felt252_is_zero(v0) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v2) => blk2,
  })

blk1:
Statements:
  () <- test::use_tuple::<core::felt252>(v1)
End:
  Goto(blk3, {})

blk2:
Statements:
  () <- test::use_tuple::<core::felt252>(v1)
End:
  Goto(blk3, {})

blk3:
Statements:
End:
  Return()

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

//! > Test tuple constructed only in one arm.

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252) -> felt252 {
    let b = (a, a);
    let c = if a == 0 {
        (a, b)
    } else {
        get_tuple()
    };
    if a == 0 {
        let (d, e) = c;
        if d == 0 {
            let (f, g) = e;
            return f + g;
        }
    }
    return a;
}

//! > function_name
foo

//! > module_code
extern fn use_tuple<T>(a: (felt252, T)) nopanic;

extern fn get_tuple<T>() -> (felt252, T) nopanic;

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v1: (core::felt252, core::felt252)) <- struct_construct(v0, v0)
  (v2: core::felt252, v3: @core::felt252) <- snapshot(v0)
  (v7: core::felt252) <- desnap(v3)
End:
  Match(match core::felt252_is_zero(v7) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v8) => blk2,
  })

blk1:
Statements:
  (v9: ()) <- struct_construct()
  (v10: core::bool) <- bool::True(v9)
End:
  Goto(blk3, {v10 -> v11})

blk2:
Statements:
  (v12: ()) <- struct_construct()
  (v13: core::bool) <- bool::False(v12)
End:
  Goto(blk3, {v13 -> v11})

blk3:
Statements:
End:
  Match(match_enum(v11) {
    bool::False(v14) => blk4,
    bool::True(v15) => blk5,
  })

blk4:
Statements:
  (v16: core::felt252, v17: (core::felt252, core::felt252)) <- test::get_tuple::<(core::felt252, core::felt252)>()
  (v18: (core::felt252, (core::felt252, core::felt252))) <- struct_construct(v16, v17)
End:
  Goto(blk6, {v18 -> v19})

blk5:
Statements:
  (v20: (core::felt252, (core::felt252, core::felt252))) <- struct_construct(v2, v1)
End:
  Goto(blk6, {v20 -> v19})

blk6:
Statements:
  (v21: core::felt252, v22: @core::felt252) <- snapshot(v2)
  (v26: core::felt252) <- desnap(v22)
End:
  Match(match core::felt252_is_zero(v26) {
    IsZeroResult::Zero => blk7,
    IsZeroResult::NonZero(v27) => blk8,
  })

blk7:
Statements:
  (v28: ()) <- struct_construct()
  (v29: core::bool) <- bool::True(v28)
End:
  Goto(blk9, {v29 -> v30})

blk8:
Statements:
  (v31: ()) <- struct_construct()
  (v32: core::bool) <- bool::False(v31)
End:
  Goto(blk9, {v32 -> v30})

blk9:
Statements:
End:
  Match(match_enum(v30) {
    bool::False(v33) => blk10,
    bool::True(v34) => blk11,
  })

blk10:
Statements:
End:
  Goto(blk16, {})

blk11:
Statements:
  (v35: core::felt252, v36: (core::felt252, core::felt252)) <- struct_destructure(v19)
  (v37: core::felt252, v38: @core::felt252) <- snapshot(v35)
  (v42: core::felt252) <- desnap(v38)
End:
  Match(match core::felt252_is_zero(v42) {
    IsZeroResult::Zero => blk12,
    IsZeroResult::NonZero(v43) => blk13,
  })

blk12:
Statements:
  (v44: ()) <- struct_construct()
  (v45: core::bool) <- bool::True(v44)
End:
  Goto(blk14, {v45 -> v46})

blk13:
Statements:
  (v47: ()) <- struct_construct()
  (v48: core::bool) <- bool::False(v47)
End:
  Goto(blk14, {v48 -> v46})

blk14:
Statements:
End:
  Match(match_enum(v46) {
    bool::False(v49) => blk15,
    bool::True(v50) => blk17,
  })

blk15:
Statements:
End:
  Goto(blk16, {})

blk16:
Statements:
End:
  Return(v21)

blk17:
Statements:
  (v51: core::felt252, v52: core::felt252) <- struct_destructure(v36)
  (v53: core::felt252) <- core::felt252_add(v51, v52)
End:
  Return(v53)

//! > after
Parameters: v0: core::felt252
blk0 (root):
Statements:
  (v2: core::felt252, v3: @core::felt252) <- snapshot(v0)
  (v7: core::felt252) <- desnap(v3)
End:
  Match(match core::felt252_is_zero(v7) {
    IsZeroResult::Zero => blk1,
    IsZeroResult::NonZero(v8) => blk2,
  })

blk1:
Statements:
  (v9: ()) <- struct_construct()
  (v10: core::bool) <- bool::True(v9)
End:
  Goto(blk3, {v10 -> v11})

blk2:
Statements:
  (v12: ()) <- struct_construct()
  (v13: core::bool) <- bool::False(v12)
End:
  Goto(blk3, {v13 -> v11})

blk3:
Statements:
End:
  Match(match_enum(v11) {
    bool::False(v14) => blk4,
    bool::True(v15) => blk5,
  })

blk4:
Statements:
  (v16: core::felt252, v17: (core::felt252, core::felt252)) <- test::get_tuple::<(core::felt252, core::felt252)>()
  (v58: core::felt252, v59: core::felt252) <- struct_destructure(v17)
End:
  Goto(blk6, {v16 -> v54, v58 -> v56, v59 -> v57})

blk5:
Statements:
End:
  Goto(blk6, {v2 -> v54, v0 -> v56, v0 -> v57})

blk6:
Statements:
  (v21: core::felt252, v22: @core::felt252) <- snapshot(v2)
  (v26: core::felt252) <- desnap(v22)
End:
  Match(match core::felt252_is_zero(v26) {
    IsZeroResult::Zero => blk7,
    IsZeroResult::NonZero(v27) => blk8,
  })

blk7:
Statements:
  (v28: ()) <- struct_construct()
  (v29: core::bool) <- bool::True(v28)
End:
  Goto(blk9, {v29 -> v30})

blk8:
Statements:
  (v31: ()) <- struct_construct()
  (v32: core::bool) <- bool::False(v31)
End:
  Goto(blk9, {v32 -> v30})

blk9:
Statements:
End:
  Match(match_enum(v30) {
    bool::False(v33) => blk10,
    bool::True(v34) => blk11,
  })

blk10:
Statements:
End:
  Goto(blk16, {})

blk11:
Statements:
  (v37: core::felt252, v38: @core::felt252) <- snapshot(v54)
  (v42: core::felt252) <- desnap(v38)
End:
  Match(match core::felt252_is_zero(v42) {
    IsZeroResult::Zero => blk12,
    IsZeroResult::NonZero(v43) => blk13,
  })

blk12:
Statements:
  (v44: ()) <- struct_construct()
  (v45: core::bool) <- bool::True(v44)
End:
  Goto(blk14, {v45 -> v46})

blk13:
Statements:
  (v47: ()) <- struct_construct()
  (v48: core::bool) <- bool::False(v47)
End:
  Goto(blk14, {v48 -> v46})

blk14:
Statements:
End:
  Match(match_enum(v46) {
    bool::False(v49) => blk15,
    bool::True(v50) => blk17,
  })

blk15:
Statements:
End:
  Goto(blk16, {})

blk16:
Statements:
End:
  Return(v21)

blk17:
Statements:
  (v53: core::felt252) <- core::felt252_add(v56, v57)
End:
  Return(v53)

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

//! > Test split a struct with a non-dup members.

//! > test_runner_name
test_split_structs

//! > function_code
fn foo(a: felt252, e: ThreeCases) -> (felt252, NonDupStruct) {
    let b = (a, NonDupStruct { a });
    match e {
        ThreeCases::One => {
            let (_, non_dup) = b;
            use_struct(non_dup);
            (a, NonDupStruct { a: 0 })
        },
        ThreeCases::Two => {
            let (_, non_dup) = b;
            use_struct(non_dup);
            (a, NonDupStruct { a: 1 })
        },
        ThreeCases::Three => {
            use_tuple(b);
            (a, NonDupStruct { a: 0 })
        },
    }
}

//! > function_name
foo

//! > module_code
extern fn use_tuple<T>(a: (felt252, T)) nopanic;
extern fn use_struct(a: NonDupStruct) nopanic;

struct NonDupStruct {
    a: felt252,
}

enum ThreeCases {
    One,
    Two,
    Three,
}

//! > semantic_diagnostics

//! > lowering_diagnostics

//! > before
Parameters: v0: core::felt252, v1: test::ThreeCases
blk0 (root):
Statements:
  (v2: test::NonDupStruct) <- struct_construct(v0)
  (v3: (core::felt252, test::NonDupStruct)) <- struct_construct(v0, v2)
End:
  Match(match_enum(v1) {
    ThreeCases::One(v4) => blk1,
    ThreeCases::Two(v5) => blk2,
    ThreeCases::Three(v6) => blk3,
  })

blk1:
Statements:
  (v7: core::felt252, v8: test::NonDupStruct) <- struct_destructure(v3)
  () <- test::use_struct(v8)
  (v9: core::felt252) <- 0
  (v10: test::NonDupStruct) <- struct_construct(v9)
  (v11: (core::felt252, test::NonDupStruct)) <- struct_construct(v0, v10)
End:
  Goto(blk4, {v11 -> v12})

blk2:
Statements:
  (v13: core::felt252, v14: test::NonDupStruct) <- struct_destructure(v3)
  () <- test::use_struct(v14)
  (v15: core::felt252) <- 1
  (v16: test::NonDupStruct) <- struct_construct(v15)
  (v17: (core::felt252, test::NonDupStruct)) <- struct_construct(v0, v16)
End:
  Goto(blk4, {v17 -> v12})

blk3:
Statements:
  () <- test::use_tuple::<test::NonDupStruct>(v3)
  (v18: core::felt252) <- 0
  (v19: test::NonDupStruct) <- struct_construct(v18)
  (v20: (core::felt252, test::NonDupStruct)) <- struct_construct(v0, v19)
End:
  Goto(blk4, {v20 -> v12})

blk4:
Statements:
End:
  Return(v12)

//! > after
Parameters: v0: core::felt252, v1: test::ThreeCases
blk0 (root):
Statements:
End:
  Match(match_enum(v1) {
    ThreeCases::One(v4) => blk1,
    ThreeCases::Two(v5) => blk2,
    ThreeCases::Three(v6) => blk3,
  })

blk1:
Statements:
  (v27: test::NonDupStruct) <- struct_construct(v0)
  () <- test::use_struct(v27)
  (v9: core::felt252) <- 0
End:
  Goto(blk4, {v0 -> v21, v9 -> v23})

blk2:
Statements:
  (v26: test::NonDupStruct) <- struct_construct(v0)
  () <- test::use_struct(v26)
  (v15: core::felt252) <- 1
End:
  Goto(blk4, {v0 -> v21, v15 -> v23})

blk3:
Statements:
  (v24: test::NonDupStruct) <- struct_construct(v0)
  (v25: (core::felt252, test::NonDupStruct)) <- struct_construct(v0, v24)
  () <- test::use_tuple::<test::NonDupStruct>(v25)
  (v18: core::felt252) <- 0
End:
  Goto(blk4, {v0 -> v21, v18 -> v23})

blk4:
Statements:
  (v22: test::NonDupStruct) <- struct_construct(v23)
  (v12: (core::felt252, test::NonDupStruct)) <- struct_construct(v21, v22)
End:
  Return(v12)
