Skip to content

Instantly share code, notes, and snippets.

@momvart
Last active September 14, 2023 18:31
Show Gist options
  • Save momvart/bfe4eae7f7efb18b720aee26d9c95521 to your computer and use it in GitHub Desktop.
Save momvart/bfe4eae7f7efb18b720aee26d9c95521 to your computer and use it in GitHub Desktop.
An example of how unsafe Rust can cause memory deallocation bugs. A case of late `len` update and unsoundness.
#![feature(maybe_uninit_uninit_array)]
#![feature(extend_one)]
use std::mem::MaybeUninit;
fn main() {
let mut v = MyVec::new();
v.push(Box::new(DropReporter));
let mut dest = BadExtend(Default::default());
// let mut dest = Vec::default(); // This works fine.
let _result = std::panic::catch_unwind(move || {
v.drain_to_double_free(&mut dest);
v.drain_to_drop_uninit(&mut dest);
});
println!("No unhandled error.");
}
struct MyVec<T> {
data: [MaybeUninit<Box<T>>; 4],
len: usize,
}
impl<T> MyVec<T> {
fn new() -> Self {
let data = MaybeUninit::uninit_array();
let len = 0;
MyVec { data, len }
}
fn push(&mut self, item: T) {
if self.len == self.data.len() {
return;
}
self.data[self.len] = MaybeUninit::new(Box::new(item));
self.len += 1;
}
fn drain_to_double_free(&mut self, dest: &mut impl Extend<Box<T>>) {
for i in 0..self.len {
let mut item = MaybeUninit::uninit();
unsafe {
std::ptr::copy(self.data[i].as_ptr(), item.as_mut_ptr(), 1);
}
dest.extend_one(unsafe { item.assume_init() });
}
self.len = 0;
}
fn drain_to_drop_uninit(&mut self, dest: &mut impl Extend<Box<T>>) {
for i in 0..self.len {
let mut item = MaybeUninit::uninit();
unsafe {
std::mem::swap(self.data[i].assume_init_mut(), item.assume_init_mut());
}
dest.extend_one(unsafe { item.assume_init() });
}
self.len = 0;
}
}
impl<T> Drop for MyVec<T> {
fn drop(&mut self) {
for i in 0..self.len {
println!("Dropping item at {:?}", i);
unsafe { self.data[i].assume_init_drop() }
}
}
}
#[derive(Debug)]
struct DropReporter(&'static str);
impl Drop for DropReporter {
fn drop(&mut self) {
println!("Dropping {:?}", self.0);
}
}
#[derive(Default)]
struct BadExtend<T>(std::marker::PhantomData<T>);
impl<X> Extend<X> for BadExtend<X> {
fn extend<T: IntoIterator<Item = X>>(&mut self, iter: T) {
if iter.into_iter().next().is_some() {
panic!("BadExtend::extend called")
}
}
}
@momvart
Copy link
Author

momvart commented Sep 14, 2023

The MIR for this program:

// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn main() -> () {
    let mut _0: ();
    let mut _1: MyVec<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>;
    let _2: ();
    let mut _3: &mut MyVec<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>;
    let mut _4: std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>;
    let mut _5: std::marker::PhantomData<std::boxed::Box<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>>;
    let mut _7: [closure@./buggy_vec.rs:12:44: 12:51];
    let _8: ();
    let mut _9: std::fmt::Arguments<'_>;
    let mut _10: &[&str];
    let mut _12: bool;
    scope 1 {
        debug v => _1;
        scope 2 {
            debug dest => const BadExtend::<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>(PhantomData::<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>);
            let _6: std::result::Result<(), std::boxed::Box<dyn std::any::Any + std::marker::Send>>;
            scope 3 {
                debug _result => _6;
                let mut _11: &[&str; 1];
            }
        }
    }

    bb0: {
        _12 = const false;
        _12 = const true;
        _1 = MyVec::<Box<fn(&'static str) -> DropReporter {DropReporter}>>::new() -> bb1;
    }

    bb1: {
        _3 = &mut _1;
        _4 = Box::<fn(&'static str) -> DropReporter {DropReporter}>::new(DropReporter) -> [return: bb2, unwind: bb12];
    }

    bb2: {
        _2 = MyVec::<Box<fn(&'static str) -> DropReporter {DropReporter}>>::push(move _3, move _4) -> [return: bb3, unwind: bb12];
    }

    bb3: {
        _5 = <PhantomData<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>> as Default>::default() -> [return: bb4, unwind: bb12];
    }

    bb4: {
        _12 = const false;
        _7 = [closure@./buggy_vec.rs:12:44: 12:51] { v: move _1, dest: const BadExtend::<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>(PhantomData::<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>) };
        _6 = catch_unwind::<[closure@./buggy_vec.rs:12:44: 12:51], ()>(move _7) -> [return: bb5, unwind: bb12];
    }

    bb5: {
        _11 = const _;
        _10 = _11 as &[&str] (Pointer(Unsize));
        _9 = Arguments::<'_>::new_const(move _10) -> [return: bb6, unwind: bb9];
    }

    bb6: {
        _8 = _print(move _9) -> [return: bb7, unwind: bb9];
    }

    bb7: {
        drop(_6) -> [return: bb8, unwind: bb12];
    }

    bb8: {
        _12 = const false;
        return;
    }

    bb9 (cleanup): {
        drop(_6) -> [return: bb12, unwind terminate];
    }

    bb10 (cleanup): {
        resume;
    }

    bb11 (cleanup): {
        drop(_1) -> [return: bb10, unwind terminate];
    }

    bb12 (cleanup): {
        switchInt(_12) -> [0: bb10, otherwise: bb11];
    }
}

promoted[0] in main: &[&str; 1] = {
    let mut _0: &[&str; 1];
    let mut _1: [&str; 1];

    bb0: {
        _1 = [const "No unhandled error.\n"];
        _0 = &_1;
        return;
    }
}

fn main::{closure#0}(_1: [closure@./buggy_vec.rs:12:44: 12:51]) -> () {
    debug v => (_1.0: MyVec<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>);
    debug dest => const BadExtend::<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>(PhantomData::<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>);
    let mut _0: ();
    let _2: ();
    let mut _3: &mut MyVec<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>;
    let mut _4: &mut BadExtend<std::boxed::Box<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>>;
    let _5: ();
    let mut _6: &mut MyVec<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>;
    let mut _7: &mut BadExtend<std::boxed::Box<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>>;

    bb0: {
        _3 = &mut (_1.0: MyVec<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>);
        _4 = &mut (_1.1: BadExtend<std::boxed::Box<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>>);
        _2 = MyVec::<Box<fn(&'static str) -> DropReporter {DropReporter}>>::drain_to_double_free::<BadExtend<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>>(move _3, _4) -> [return: bb1, unwind: bb4];
    }

    bb1: {
        _6 = &mut (_1.0: MyVec<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>);
        _7 = &mut (_1.1: BadExtend<std::boxed::Box<std::boxed::Box<fn(&'static str) -> DropReporter {DropReporter}>>>);
        _5 = MyVec::<Box<fn(&'static str) -> DropReporter {DropReporter}>>::drain_to_drop_uninit::<BadExtend<Box<Box<fn(&'static str) -> DropReporter {DropReporter}>>>>(move _6, _7) -> [return: bb2, unwind: bb4];
    }

    bb2: {
        drop(_1) -> bb3;
    }

    bb3: {
        return;
    }

    bb4 (cleanup): {
        drop(_1) -> [return: bb5, unwind terminate];
    }

    bb5 (cleanup): {
        resume;
    }
}

MyVec::data::{constant#0}: usize = {
    let mut _0: usize;

    bb0: {
        _0 = const 4_usize;
        return;
    }
}

fn <impl at ./buggy_vec.rs:24:1: 24:17>::new() -> MyVec<T> {
    let mut _0: MyVec<T>;
    let _1: [std::mem::MaybeUninit<std::boxed::Box<T>>; 4];
    let mut _3: [std::mem::MaybeUninit<std::boxed::Box<T>>; 4];
    scope 1 {
        debug data => _1;
        let _2: usize;
        scope 2 {
            debug len => const 0_usize;
        }
    }

    bb0: {
        _1 = MaybeUninit::<Box<T>>::uninit_array::<4>() -> bb1;
    }

    bb1: {
        _2 = const 0_usize;
        _3 = move _1;
        _0 = MyVec::<T> { data: move _3, len: _2 };
        return;
    }
}

fn <impl at ./buggy_vec.rs:24:1: 24:17>::push(_1: &mut MyVec<T>, _2: T) -> () {
    debug self => _1;
    debug item => _2;
    let mut _0: ();
    let mut _3: bool;
    let mut _4: usize;
    let mut _5: usize;
    let mut _6: &[std::mem::MaybeUninit<std::boxed::Box<T>>];
    let mut _7: &[std::mem::MaybeUninit<std::boxed::Box<T>>; 4];
    let mut _8: std::mem::MaybeUninit<std::boxed::Box<T>>;
    let mut _9: std::boxed::Box<T>;
    let mut _10: T;
    let _11: usize;
    let mut _12: usize;
    let mut _13: bool;
    let mut _14: (usize, bool);
    let mut _15: bool;

    bb0: {
        _15 = const false;
        _15 = const true;
        _4 = ((*_1).1: usize);
        _7 = &((*_1).0: [std::mem::MaybeUninit<std::boxed::Box<T>>; 4]);
        _6 = move _7 as &[std::mem::MaybeUninit<std::boxed::Box<T>>] (Pointer(Unsize));
        _5 = Len((*_6));
        _3 = Eq(move _4, move _5);
        switchInt(move _3) -> [0: bb2, otherwise: bb1];
    }

    bb1: {
        drop(_2) -> bb7;
    }

    bb2: {
        _15 = const false;
        _10 = move _2;
        _9 = Box::<T>::new(move _10) -> [return: bb3, unwind: bb10];
    }

    bb3: {
        _8 = MaybeUninit::<Box<T>>::new(move _9) -> [return: bb4, unwind: bb10];
    }

    bb4: {
        _11 = ((*_1).1: usize);
        _12 = const 4_usize;
        _13 = Lt(_11, _12);
        assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind: bb10];
    }

    bb5: {
        ((*_1).0: [std::mem::MaybeUninit<std::boxed::Box<T>>; 4])[_11] = move _8;
        _14 = CheckedAdd(((*_1).1: usize), const 1_usize);
        assert(!move (_14.1: bool), "attempt to compute `{} + {}`, which would overflow", ((*_1).1: usize), const 1_usize) -> [success: bb6, unwind: bb10];
    }

    bb6: {
        ((*_1).1: usize) = move (_14.0: usize);
        goto -> bb7;
    }

    bb7: {
        return;
    }

    bb8 (cleanup): {
        resume;
    }

    bb9 (cleanup): {
        drop(_2) -> [return: bb8, unwind terminate];
    }

    bb10 (cleanup): {
        switchInt(_15) -> [0: bb8, otherwise: bb9];
    }
}

fn <impl at ./buggy_vec.rs:24:1: 24:17>::drain_to_double_free(_1: &mut MyVec<T>, _2: &mut impl Extend<Box<T>>) -> () {
    debug self => _1;
    debug dest => _2;
    let mut _0: ();
    let mut _3: std::ops::Range<usize>;
    let mut _4: std::ops::Range<usize>;
    let mut _5: usize;
    let mut _6: std::ops::Range<usize>;
    let mut _7: std::option::Option<usize>;
    let mut _8: &mut std::ops::Range<usize>;
    let mut _9: isize;
    let _12: ();
    let mut _13: *const std::boxed::Box<T>;
    let mut _14: &std::mem::MaybeUninit<std::boxed::Box<T>>;
    let mut _15: usize;
    let mut _16: bool;
    let mut _17: *mut std::boxed::Box<T>;
    let mut _18: &mut std::mem::MaybeUninit<std::boxed::Box<T>>;
    let _19: ();
    let mut _20: std::boxed::Box<T>;
    let mut _21: std::mem::MaybeUninit<std::boxed::Box<T>>;
    scope 1 {
        debug iter => _6;
        let _10: usize;
        scope 2 {
            debug i => _10;
            let mut _11: std::mem::MaybeUninit<std::boxed::Box<T>>;
            scope 3 {
                debug item => _11;
                scope 4 {
                }
                scope 5 {
                }
            }
        }
    }

    bb0: {
        _5 = ((*_1).1: usize);
        _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _5 };
        _3 = <std::ops::Range<usize> as IntoIterator>::into_iter(move _4) -> bb1;
    }

    bb1: {
        _6 = move _3;
        goto -> bb2;
    }

    bb2: {
        _8 = &mut _6;
        _7 = <std::ops::Range<usize> as Iterator>::next(_8) -> bb3;
    }

    bb3: {
        _9 = discriminant(_7);
        switchInt(move _9) -> [0: bb6, 1: bb4, otherwise: bb5];
    }

    bb4: {
        _10 = ((_7 as Some).0: usize);
        _11 = MaybeUninit::<Box<T>>::uninit() -> bb7;
    }

    bb5: {
        unreachable;
    }

    bb6: {
        ((*_1).1: usize) = const 0_usize;
        return;
    }

    bb7: {
        _15 = const 4_usize;
        _16 = Lt(_10, _15);
        assert(move _16, "index out of bounds: the length is {} but the index is {}", move _15, _10) -> bb8;
    }

    bb8: {
        _14 = &((*_1).0: [std::mem::MaybeUninit<std::boxed::Box<T>>; 4])[_10];
        _13 = MaybeUninit::<Box<T>>::as_ptr(move _14) -> bb9;
    }

    bb9: {
        _18 = &mut _11;
        _17 = MaybeUninit::<Box<T>>::as_mut_ptr(move _18) -> bb10;
    }

    bb10: {
        _12 = std::intrinsics::copy::<Box<T>>(move _13, move _17, const 1_usize) -> bb11;
    }

    bb11: {
        _21 = move _11;
        _20 = MaybeUninit::<Box<T>>::assume_init(move _21) -> bb12;
    }

    bb12: {
        _19 = <impl Extend<Box<T>> as Extend<Box<T>>>::extend_one(_2, move _20) -> bb2;
    }
}

fn <impl at ./buggy_vec.rs:24:1: 24:17>::drain_to_drop_uninit(_1: &mut MyVec<T>, _2: &mut impl Extend<Box<T>>) -> () {
    debug self => _1;
    debug dest => _2;
    let mut _0: ();
    let mut _3: std::ops::Range<usize>;
    let mut _4: std::ops::Range<usize>;
    let mut _5: usize;
    let mut _6: std::ops::Range<usize>;
    let mut _7: std::option::Option<usize>;
    let mut _8: &mut std::ops::Range<usize>;
    let mut _9: isize;
    let _12: ();
    let mut _13: &mut std::boxed::Box<T>;
    let mut _14: &mut std::boxed::Box<T>;
    let mut _15: &mut std::mem::MaybeUninit<std::boxed::Box<T>>;
    let mut _16: usize;
    let mut _17: bool;
    let mut _18: &mut std::boxed::Box<T>;
    let mut _19: &mut std::boxed::Box<T>;
    let mut _20: &mut std::mem::MaybeUninit<std::boxed::Box<T>>;
    let _21: ();
    let mut _22: std::boxed::Box<T>;
    let mut _23: std::mem::MaybeUninit<std::boxed::Box<T>>;
    scope 1 {
        debug iter => _6;
        let _10: usize;
        scope 2 {
            debug i => _10;
            let mut _11: std::mem::MaybeUninit<std::boxed::Box<T>>;
            scope 3 {
                debug item => _11;
                scope 4 {
                }
                scope 5 {
                }
            }
        }
    }

    bb0: {
        _5 = ((*_1).1: usize);
        _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _5 };
        _3 = <std::ops::Range<usize> as IntoIterator>::into_iter(move _4) -> bb1;
    }

    bb1: {
        _6 = move _3;
        goto -> bb2;
    }

    bb2: {
        _8 = &mut _6;
        _7 = <std::ops::Range<usize> as Iterator>::next(_8) -> bb3;
    }

    bb3: {
        _9 = discriminant(_7);
        switchInt(move _9) -> [0: bb6, 1: bb4, otherwise: bb5];
    }

    bb4: {
        _10 = ((_7 as Some).0: usize);
        _11 = MaybeUninit::<Box<T>>::uninit() -> bb7;
    }

    bb5: {
        unreachable;
    }

    bb6: {
        ((*_1).1: usize) = const 0_usize;
        return;
    }

    bb7: {
        _16 = const 4_usize;
        _17 = Lt(_10, _16);
        assert(move _17, "index out of bounds: the length is {} but the index is {}", move _16, _10) -> bb8;
    }

    bb8: {
        _15 = &mut ((*_1).0: [std::mem::MaybeUninit<std::boxed::Box<T>>; 4])[_10];
        _14 = MaybeUninit::<Box<T>>::assume_init_mut(move _15) -> bb9;
    }

    bb9: {
        _13 = _14;
        _20 = &mut _11;
        _19 = MaybeUninit::<Box<T>>::assume_init_mut(move _20) -> bb10;
    }

    bb10: {
        _18 = _19;
        _12 = std::mem::swap::<Box<T>>(move _13, move _18) -> bb11;
    }

    bb11: {
        _23 = move _11;
        _22 = MaybeUninit::<Box<T>>::assume_init(move _23) -> bb12;
    }

    bb12: {
        _21 = <impl Extend<Box<T>> as Extend<Box<T>>>::extend_one(_2, move _22) -> bb2;
    }
}

fn <impl at ./buggy_vec.rs:65:1: 65:26>::drop(_1: &mut MyVec<T>) -> () {
    debug self => _1;
    let mut _0: ();
    let mut _2: std::ops::Range<usize>;
    let mut _3: std::ops::Range<usize>;
    let mut _4: usize;
    let mut _5: std::ops::Range<usize>;
    let _6: ();
    let mut _7: std::option::Option<usize>;
    let mut _8: &mut std::ops::Range<usize>;
    let mut _9: isize;
    let _11: ();
    let mut _12: std::fmt::Arguments<'_>;
    let mut _13: &[&str];
    let mut _14: &[core::fmt::rt::Argument<'_>];
    let _15: &[core::fmt::rt::Argument<'_>; 1];
    let _16: [core::fmt::rt::Argument<'_>; 1];
    let mut _17: core::fmt::rt::Argument<'_>;
    let _18: &usize;
    let mut _19: &mut std::mem::MaybeUninit<std::boxed::Box<T>>;
    let _20: usize;
    let mut _21: usize;
    let mut _22: bool;
    scope 1 {
        debug iter => _5;
        let _10: usize;
        scope 2 {
            debug i => _10;
            let mut _23: &[&str; 2];
            scope 3 {
            }
        }
    }

    bb0: {
        _4 = ((*_1).1: usize);
        _3 = std::ops::Range::<usize> { start: const 0_usize, end: move _4 };
        _2 = <std::ops::Range<usize> as IntoIterator>::into_iter(move _3) -> bb1;
    }

    bb1: {
        _5 = move _2;
        goto -> bb2;
    }

    bb2: {
        _8 = &mut _5;
        _7 = <std::ops::Range<usize> as Iterator>::next(_8) -> bb3;
    }

    bb3: {
        _9 = discriminant(_7);
        switchInt(move _9) -> [0: bb6, 1: bb4, otherwise: bb5];
    }

    bb4: {
        _10 = ((_7 as Some).0: usize);
        _23 = const _;
        _13 = _23 as &[&str] (Pointer(Unsize));
        _18 = &_10;
        _17 = core::fmt::rt::Argument::<'_>::new_debug::<usize>(_18) -> bb7;
    }

    bb5: {
        unreachable;
    }

    bb6: {
        return;
    }

    bb7: {
        _16 = [move _17];
        _15 = &_16;
        _14 = _15 as &[core::fmt::rt::Argument<'_>] (Pointer(Unsize));
        _12 = Arguments::<'_>::new_v1(move _13, move _14) -> bb8;
    }

    bb8: {
        _11 = _print(move _12) -> bb9;
    }

    bb9: {
        _20 = _10;
        _21 = const 4_usize;
        _22 = Lt(_20, _21);
        assert(move _22, "index out of bounds: the length is {} but the index is {}", move _21, _20) -> bb10;
    }

    bb10: {
        _19 = &mut ((*_1).0: [std::mem::MaybeUninit<std::boxed::Box<T>>; 4])[_20];
        _6 = MaybeUninit::<Box<T>>::assume_init_drop(move _19) -> bb2;
    }
}

promoted[0] in <impl at ./buggy_vec.rs:65:1: 65:26>::drop: &[&str; 2] = {
    let mut _0: &[&str; 2];
    let mut _1: [&str; 2];

    bb0: {
        _1 = [const "Dropping item at ", const "\n"];
        _0 = &_1;
        return;
    }
}

fn <impl at ./buggy_vec.rs:74:10: 74:15>::fmt(_1: &DropReporter, _2: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
    debug self => _1;
    debug f => _2;
    let mut _0: std::result::Result<(), std::fmt::Error>;
    let _3: &str;
    let mut _4: &dyn std::fmt::Debug;
    let _5: &&&str;
    let _6: &&str;

    bb0: {
        _3 = const "DropReporter";
        _6 = &((*_1).0: &str);
        _5 = &_6;
        _4 = _5 as &dyn std::fmt::Debug (Pointer(Unsize));
        _0 = Formatter::<'_>::debug_tuple_field1_finish(_2, _3, move _4) -> bb1;
    }

    bb1: {
        return;
    }
}

fn <impl at ./buggy_vec.rs:77:1: 77:27>::drop(_1: &mut DropReporter) -> () {
    debug self => _1;
    let mut _0: ();
    let _2: ();
    let mut _3: std::fmt::Arguments<'_>;
    let mut _4: &[&str];
    let mut _5: &[core::fmt::rt::Argument<'_>];
    let _6: &[core::fmt::rt::Argument<'_>; 1];
    let _7: [core::fmt::rt::Argument<'_>; 1];
    let mut _8: core::fmt::rt::Argument<'_>;
    let _9: &&str;
    let mut _10: &[&str; 2];

    bb0: {
        _10 = const _;
        _4 = _10 as &[&str] (Pointer(Unsize));
        _9 = &((*_1).0: &str);
        _8 = core::fmt::rt::Argument::<'_>::new_debug::<&str>(_9) -> bb1;
    }

    bb1: {
        _7 = [move _8];
        _6 = &_7;
        _5 = _6 as &[core::fmt::rt::Argument<'_>] (Pointer(Unsize));
        _3 = Arguments::<'_>::new_v1(move _4, move _5) -> bb2;
    }

    bb2: {
        _2 = _print(move _3) -> bb3;
    }

    bb3: {
        return;
    }
}

promoted[0] in <impl at ./buggy_vec.rs:77:1: 77:27>::drop: &[&str; 2] = {
    let mut _0: &[&str; 2];
    let mut _1: [&str; 2];

    bb0: {
        _1 = [const "Dropping ", const "\n"];
        _0 = &_1;
        return;
    }
}

fn <impl at ./buggy_vec.rs:83:10: 83:17>::default() -> BadExtend<T> {
    let mut _0: BadExtend<T>;
    let mut _1: std::marker::PhantomData<T>;

    bb0: {
        _1 = <PhantomData<T> as Default>::default() -> bb1;
    }

    bb1: {
        return;
    }
}

fn <impl at ./buggy_vec.rs:86:1: 86:35>::extend(_1: &mut BadExtend<X>, _2: T) -> () {
    debug self => _1;
    debug iter => _2;
    let mut _0: ();
    let mut _3: bool;
    let mut _4: &std::option::Option<X>;
    let _5: std::option::Option<X>;
    let mut _6: &mut <T as std::iter::IntoIterator>::IntoIter;
    let mut _7: <T as std::iter::IntoIterator>::IntoIter;
    let _8: !;

    bb0: {
        _7 = <T as IntoIterator>::into_iter(move _2) -> bb1;
    }

    bb1: {
        _6 = &mut _7;
        _5 = <<T as IntoIterator>::IntoIter as Iterator>::next(move _6) -> [return: bb2, unwind: bb9];
    }

    bb2: {
        _4 = &_5;
        _3 = Option::<X>::is_some(move _4) -> [return: bb3, unwind: bb8];
    }

    bb3: {
        drop(_5) -> [return: bb4, unwind: bb9];
    }

    bb4: {
        drop(_7) -> bb5;
    }

    bb5: {
        switchInt(move _3) -> [0: bb7, otherwise: bb6];
    }

    bb6: {
        _8 = begin_panic::<&str>(const "BadExtend::extend called");
    }

    bb7: {
        return;
    }

    bb8 (cleanup): {
        drop(_5) -> [return: bb9, unwind terminate];
    }

    bb9 (cleanup): {
        drop(_7) -> [return: bb10, unwind terminate];
    }

    bb10 (cleanup): {
        resume;
    }
}

fn DropReporter(_1: &str) -> DropReporter {
    let mut _0: DropReporter;

    bb0: {
        _0 = DropReporter(move _1);
        return;
    }
}

// MIR FOR CTFE
fn DropReporter(_1: &str) -> DropReporter {
    let mut _0: DropReporter;

    bb0: {
        _0 = DropReporter(move _1);
        return;
    }
}

fn BadExtend(_1: PhantomData<T>) -> BadExtend<T> {
    let mut _0: BadExtend<T>;

    bb0: {
        _0 = BadExtend::<T>(move _1);
        return;
    }
}

// MIR FOR CTFE
fn BadExtend(_1: PhantomData<T>) -> BadExtend<T> {
    let mut _0: BadExtend<T>;

    bb0: {
        _0 = BadExtend::<T>(move _1);
        return;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment