//@compile-flags: -Zmiri-tree-borrows

// These tests fail Stacked Borrows, but pass Tree Borrows.

// The first four have in common that in SB a mutable reborrow is enough to produce
// write access errors, but in TB an actual write is needed.
// A modified version of each is also available that fails Tree Borrows.

mod fnentry_invalidation {
    // Copied directly from fail/stacked_borrows/fnentry_invalidation.rs
    // Version that fails TB: fail/tree_borrows/fnentry_invalidation.rs
    pub fn main() {
        let mut x = 0i32;
        let z = &mut x as *mut i32;
        x.do_bad();
        unsafe {
            let _oof = *z;
            // In SB this is an error, but in TB the mutable reborrow did
            // not invalidate z for reading.
        }
    }

    trait Bad {
        fn do_bad(&mut self) {
            // who knows
        }
    }

    impl Bad for i32 {}
}

mod pass_invalid_mut {
    // Copied directly from fail/stacked_borrows/pass_invalid_mut.rs
    // Version that fails TB: fail/tree_borrows/pass_invalid_mut.rs
    fn foo(_: &mut i32) {}

    pub fn main() {
        let x = &mut 42;
        let xraw = x as *mut _;
        let xref = unsafe { &mut *xraw };
        let _val = unsafe { *xraw }; // In SB this invalidates xref...
        foo(xref); // ...which then cannot be reborrowed here.
        // But in TB xref is Reserved and thus still writeable.
    }
}

mod return_invalid_mut {
    // Copied directly from fail/stacked_borrows/return_invalid_mut.rs
    // Version that fails TB: fail/tree_borrows/return_invalid_mut.rs
    fn foo(x: &mut (i32, i32)) -> &mut i32 {
        let xraw = x as *mut (i32, i32);
        let ret = unsafe { &mut (*xraw).1 };
        let _val = unsafe { *xraw }; // In SB this invalidates ret...
        ret // ...which then cannot be reborrowed here.
        // But in TB ret is Reserved and thus still writeable.
    }

    pub fn main() {
        foo(&mut (1, 2));
    }
}

mod static_memory_modification {
    // Copied directly from fail/stacked_borrows/static_memory_modification.rs
    // Version that fails TB: fail/tree_borrows/static_memory_modification.rs
    static X: usize = 5;

    #[allow(mutable_transmutes)]
    pub fn main() {
        let x = unsafe {
            std::mem::transmute::<&usize, &mut usize>(&X) // In SB this mutable reborrow fails.
            // But in TB we are allowed to transmute as long as we don't write.
        };
        assert_eq!(*&*x, 5);
    }
}

// This one is about direct writes to local variables not being in conflict
// with interior mutable reborrows.
#[allow(unused_assignments)] // spurious warning
fn interior_mut_reborrow() {
    use std::cell::UnsafeCell;

    let mut c = UnsafeCell::new(42);
    let ptr = c.get(); // first create interior mutable ptr
    c = UnsafeCell::new(13); // then write to parent
    assert_eq!(unsafe { ptr.read() }, 13); // then read through previous ptr
}

fn main() {
    fnentry_invalidation::main();
    pass_invalid_mut::main();
    return_invalid_mut::main();
    static_memory_modification::main();
    interior_mut_reborrow();
}
