Skip to content

MIR dynamic drop flags cause spurious extra drops with optimizations on. #38437

Closed
@alexcrichton

Description

@alexcrichton

This program generates an assertion failure on nightly Rust:

use std::sync::*;
use std::sync::atomic::*;
use std::sync::atomic::Ordering::*;

fn main() {
    struct Inner(usize);

    // This assertion in theory never trips, there's not mutability in
    // `Arc<Inner>`! Yet this fails.
    impl Drop for Inner {
        fn drop(&mut self) {
            assert_eq!(self.0, 0);
        }
    }
    let b = Arc::new(Inner(0));

    drop(WriteQueue::new(b).poll());
}

pub struct WriteQueue<W>  {
    pending: Option<Sender>,
    state: State<W>,
}

enum State<W> {
    Writing(W, Sender),
    BetweenWrites(W),
    Empty,
}

impl<W> WriteQueue<W> {
    pub fn new(writer: W) -> WriteQueue<W> {
        let (complete, _) = channel();
        WriteQueue {
            pending: Some(complete),
            state: State::BetweenWrites(writer),
        }
    }
}

enum IntermediateState<W> {
    _WriteDone(W),
    StartWrite(Sender),
    _Resolve,
}

impl<W> WriteQueue<W> {
    fn poll(&mut self) -> Result<(), ()> {
        loop {
            let next: IntermediateState<W> = match self.state {
                State::Writing(..) => {
                    return Err(())
                }
                State::BetweenWrites(ref mut _writer) => {
                    let front = self.pending.take();
                    match front {
                        Some(complete) => {
                            IntermediateState::StartWrite(complete)
                        }
                        None => diverge(),
                    }
                }
                State::Empty => diverge(),
            };

            match next {
                IntermediateState::_WriteDone(_w) => diverge(),
                IntermediateState::StartWrite(c) => {
                    let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(w) => {
                            State::Writing(w, c)
                        }
                        _ => diverge(),
                    };
                    self.state = new_state;
                }
                IntermediateState::_Resolve => {
                    match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(_w) => {
                            return Ok(())
                        }
                        _ => diverge(),
                    }
                }
            }
        }
    }
}

fn diverge() -> ! {
    panic!()
}

pub struct Receiver {
    inner: Arc<Inner2>,
}

pub struct Sender {
    _inner: Arc<Inner2>,
}

struct Inner2 {
    complete: AtomicUsize,
}

pub fn channel() -> (Sender, Receiver) {
    let inner = Arc::new(Inner2 {
        complete: AtomicUsize::new(0),
    });
    let receiver = Receiver {
        inner: inner.clone(),
    };
    let sender = Sender {
        _inner: inner,
    };
    (sender, receiver)
}

impl Drop for Receiver {
    fn drop(&mut self) {
        self.inner.complete.store(1, SeqCst);
    }
}

The assertion in the destructor of the main function should never trip, but something's tripping it! This is an adapted test case from rust-lang/futures-rs#296, and although the original issue reproduces on stable/beta/nightly this test case only reproduces on nightly:

$ rustc +nightly -vV
rustc 1.15.0-nightly (8f02c429a 2016-12-15)
binary: rustc
commit-hash: 8f02c429ad3e2ad687a222d1daae2e04bb9bb876
commit-date: 2016-12-15
host: x86_64-unknown-linux-gnu
release: 1.15.0-nightly
LLVM version: 3.9
$ rustc +nightly ./crash.rs -O
$ ./crash
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `1`, right: `0`)', ./crash.rs:12
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Metadata

Metadata

Assignees

Labels

A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.htmlP-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions