Skip to content

break stmt confuses intialization-knowledge of borrow checker #24267

Closed
@pnkfelix

Description

@pnkfelix

This code should not compile:

demo code courtesy of niko's comment below.

struct Foo { n0: i32, n1: i32 }

fn leak_1_brk() -> Foo {
    let ret;
    loop {
        ret = Foo { n0: { break }, n1: 22 };
    }
    // (`ret` is still uninitialized here!)
    ret
}

fn main() { }

Original report:

I was making regression tests for #21486 and ran into this, though @nikomatsakis has pointed out that it is not specific to FRU.

Test case 1:

use std::sync::atomic::{Ordering, AtomicUsize, ATOMIC_USIZE_INIT};

#[derive(Debug)]
struct Noisy(u8);
impl Drop for Noisy {
    fn drop(&mut self) {
        // println!("splat #{}", self.0);
        event(self.0);
    }
}

#[allow(dead_code)]
#[derive(Debug)]
struct Foo { n0: Noisy, n1: Noisy }
impl Foo {
    fn vals(&self) -> (u8, u8) { (self.n0.0, self.n1.0) }
}

fn leak_1_brk() -> Foo {
    let _old_foo = Foo { n0: Noisy(1), n1: Noisy(2) };
    let ret;
    loop {
        ret = Foo { n0: { break }, .._old_foo };
    }
    // ret has not been initialized!!!!!
    ret
}

pub fn main() {
    reset_log();
    // Note: there's not much that's reasonable to expect here...
    let value = leak_1_brk().vals();
    assert_eq!(0x01_02, event_log());
    assert_eq!(value, (1,2));
}

static LOG: AtomicUsize = ATOMIC_USIZE_INIT;

fn reset_log() {
    LOG.store(0, Ordering::SeqCst);
}

fn event_log() -> usize {
    LOG.load(Ordering::SeqCst)
}

fn event(tag: u8) {
    let old_log = LOG.load(Ordering::SeqCst);
    let new_log = (old_log << 8) + tag as usize;
    LOG.store(new_log, Ordering::SeqCst);
}

Test case 2:

use std::sync::atomic::{Ordering, AtomicUsize, ATOMIC_USIZE_INIT};

#[derive(Debug)]
struct Noisy(u8);
impl Drop for Noisy {
    fn drop(&mut self) {
        // println!("splat #{}", self.0);
        event(self.0);
    }
}

#[allow(dead_code)]
#[derive(Debug)]
struct Foo { n0: Noisy, n1: Noisy }
impl Foo {
    fn vals(&self) -> (u8, u8) { (self.n0.0, self.n1.0) }
}

fn leak_1_brk() -> Foo {
    let _old_foo = Foo { n0: Noisy(1), n1: Noisy(2) };
    let ret;
    loop {
        ret = Foo { n0: { break }, n1: { break } };
    }
    // !!!!!
    ret
}


pub fn main() {
    reset_log();
    // Note: there's not much that's reasonable to expect here...
    assert_eq!(leak_1_brk().vals(), (1,2));
    assert_eq!(0x01_02, event_log());
}

static LOG: AtomicUsize = ATOMIC_USIZE_INIT;

fn reset_log() {
    LOG.store(0, Ordering::SeqCst);
}

fn event_log() -> usize {
    LOG.load(Ordering::SeqCst)
}

fn event(tag: u8) {
    let old_log = LOG.load(Ordering::SeqCst);
    let new_log = (old_log << 8) + tag as usize;
    LOG.store(new_log, Ordering::SeqCst);
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions