// edition:2021

// Test that move closures drop derefs with `capture_disjoint_fields` enabled.

#![feature(rustc_attrs)]

fn simple_move_closure() {
    struct S(String);
    struct T(S);

    let t = T(S("s".into()));
    let mut c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        t.0.0 = "new S".into();
        //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> MutBorrow
        //~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue
    };
    c();
}

// Test move closure use reborrows when using references
fn simple_ref() {
    let mut s = 10;
    let ref_s = &mut s;

    let mut c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        *ref_s += 10;
        //~^ NOTE: Capturing ref_s[Deref] -> MutBorrow
        //~| NOTE: Min Capture ref_s[] -> ByValue
    };
    c();
}

// Test move closure use reborrows when using references
fn struct_contains_ref_to_another_struct_1() {
    struct S(String);
    struct T<'a>(&'a mut S);

    let mut s = S("s".into());
    let t = T(&mut s);

    let mut c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        t.0.0 = "new s".into();
        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> MutBorrow
        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
    };

    c();
}

// Test that we can use reborrows to read data of Copy types
// i.e. without truncating derefs
fn struct_contains_ref_to_another_struct_2() {
    struct S(i32);
    struct T<'a>(&'a S);

    let s = S(0);
    let t = T(&s);

    let mut c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        let _t = t.0.0;
        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
    };

    c();
}

// Test that we can use truncate to move out of !Copy types
fn struct_contains_ref_to_another_struct_3() {
    struct S(String);
    struct T<'a>(&'a S);

    let s = S("s".into());
    let t = T(&s);

    let mut c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        let _t = t.0.0;
        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
    };

    c();
}

// Test that derefs of box are truncated in move closures
fn truncate_box_derefs() {
    struct S(i32);


    // Content within the box is moved within the closure
    let b = Box::new(S(10));
    let c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        let _t = b.0;
        //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
        //~| NOTE: Min Capture b[] -> ByValue
    };

    c();

    // Content within the box is used by a shared ref and the box is the root variable
    let b = Box::new(S(10));

    let c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        println!("{}", b.0);
        //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
        //~| NOTE: Min Capture b[] -> ByValue
    };

    c();

    // Content within the box is used by a shared ref and the box is not the root variable
    let b = Box::new(S(10));
    let t = (0, b);

    let c = #[rustc_capture_analysis]
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    move || {
    //~^ ERROR: First Pass analysis includes:
    //~| ERROR: Min Capture analysis includes:
        println!("{}", t.1.0);
        //~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow
        //~| NOTE: Min Capture t[(1, 0)] -> ByValue
    };
}

struct Foo { x: i32 }

// Ensure that even in move closures, if the data is not owned by the root variable
// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
fn box_mut_1() {
    let mut foo = Foo { x: 0 } ;

    let p_foo = &mut foo;
    let box_p_foo = Box::new(p_foo);

    let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    //~| First Pass analysis includes:
    //~| NOTE: Capturing box_p_foo[Deref,Deref,(0, 0)] -> MutBorrow
    //~| Min Capture analysis includes:
    //~| NOTE: Min Capture box_p_foo[] -> ByValue
}

// Ensure that even in move closures, if the data is not owned by the root variable
// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
fn box_mut_2() {
    let foo = Foo { x: 0 } ;

    let mut box_foo = Box::new(foo);
    let p_foo = &mut box_foo;

    let c = #[rustc_capture_analysis] move || p_foo.x += 10;
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    //~| First Pass analysis includes:
    //~| NOTE: Capturing p_foo[Deref,Deref,(0, 0)] -> MutBorrow
    //~| Min Capture analysis includes:
    //~| NOTE: Min Capture p_foo[] -> ByValue
}

// Test that move closures can take ownership of Copy type
fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
    let x = 10;

    let c = #[rustc_capture_analysis] move || x;
    //~^ ERROR: attributes on expressions are experimental
    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
    //~| First Pass analysis includes:
    //~| NOTE: Capturing x[] -> ImmBorrow
    //~| Min Capture analysis includes:
    //~| NOTE: Min Capture x[] -> ByValue

    c
}

fn main() {
    simple_move_closure();
    simple_ref();
    struct_contains_ref_to_another_struct_1();
    struct_contains_ref_to_another_struct_2();
    struct_contains_ref_to_another_struct_3();
    truncate_box_derefs();
    box_mut_2();
    box_mut_1();
}
