<commit_before>use std::fs;
use std::io::{self, Read};
use std::net::TcpListener;
use std::process::{Stdio, Child};
use std::thread;
use std::time::Duration;

use cargotest::support::project;

#[cfg(unix)]
fn enabled() -> bool {
    true
}

// On Windows support for these tests is only enabled through the usage of job
// objects. Support for nested job objects, however, was added in recent-ish
// versions of Windows, so this test may not always be able to succeed.
//
// As a result, we try to add ourselves to a job object here
// can succeed or not.
#[cfg(windows)]
fn enabled() -> bool {
    use winapi::um::{handleapi, jobapi, jobapi2, processthreadsapi};

    unsafe {
        // If we're not currently in a job, then we can definitely run these
        // tests.
        let me = processthreadsapi::GetCurrentProcess();
        let mut ret = 0;
        let r = jobapi::IsProcessInJob(me, 0 as *mut _, &mut ret);
        assert!(r != 0);
        if ret == winapi::shared::minwindef::FALSE {
            return true
        }

        // If we are in a job, then we can run these tests if we can be added to
        // a nested job (as we're going to create a nested job no matter what as
        // part of these tests.
        //
        // If we can't be added to a nested job, then these tests will
        // definitely fail, and there's not much we can do about that.
        let job = jobapi2::CreateJobObjectW(0 as *mut _, 0 as *const _);
        assert!(!job.is_null());
        let r = jobapi2::AssignProcessToJobObject(job, me);
        handleapi::CloseHandle(job);
        r != 0
    }
}

#[test]
fn ctrl_c_kills_everyone() {
    if !enabled() {
        return
    }

    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();

    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("src/lib.rs", "")
        .file("build.rs", &format!(r#"
            use std::net::TcpStream;
            use std::io::Read;

            fn main() {{
                let mut socket = TcpStream::connect("{}").unwrap();
                let _ = socket.read(&mut [0; 10]);
                panic!("that read should never return");
            }}
        "#, addr))
        .build();

    let mut cargo = p.cargo("build").build_command();
    cargo.stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1");
    let mut child = cargo.spawn().unwrap();

    let mut sock = listener.accept().unwrap().0;
    ctrl_c(&mut child);

    assert!(!child.wait().unwrap().success());
    match sock.read(&mut [0; 10]) {
        Ok(n) => assert_eq!(n, 0),
        Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset),
    }

    // Ok so what we just did was spawn cargo that spawned a build script, then
    // we killed cargo in hopes of it killing the build script as well. If all
    // went well the build script is now dead. On Windows, however, this is
    // enforced with job objects which means that it may actually be in the
    // *process* of being torn down at this point.
    //
    // Now on Windows we can't completely remove a file until all handles to it
    // have been closed. Including those that represent running processes. So if
    // we were to return here then there may still be an open reference to some
    // file in the build directory. What we want to actually do is wait for the
    // build script to *complete* exit. Take care of that by blowing away the
    // build directory here, and panicking if we eventually spin too long
    // without being able to.
    for i in 0..10 {
        match fs::remove_dir_all(&p.root().join("target")) {
            Ok(()) => return,
            Err(e) => println!("attempt {}: {}", i, e),
        }
        thread::sleep(Duration::from_millis(100));
    }

    panic!("couldn't remove build directory after a few tries, seems like \
            we won't be able to!");
}

#[cfg(unix)]
fn ctrl_c(child: &mut Child) {
    use libc;

    let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
    if r < 0 {
        panic!("failed to kill: {}", io::Error::last_os_error());
    }
}

#[cfg(windows)]
fn ctrl_c(child: &mut Child) {
    child.kill().unwrap();
}
<commit_msg>fix winapi import<commit_after>use std::fs;
use std::io::{self, Read};
use std::net::TcpListener;
use std::process::{Stdio, Child};
use std::thread;
use std::time::Duration;

use cargotest::support::project;

#[cfg(unix)]
fn enabled() -> bool {
    true
}

// On Windows support for these tests is only enabled through the usage of job
// objects. Support for nested job objects, however, was added in recent-ish
// versions of Windows, so this test may not always be able to succeed.
//
// As a result, we try to add ourselves to a job object here
// can succeed or not.
#[cfg(windows)]
fn enabled() -> bool {
    use winapi::um::{handleapi, jobapi, jobapi2, processthreadsapi};

    unsafe {
        // If we're not currently in a job, then we can definitely run these
        // tests.
        let me = processthreadsapi::GetCurrentProcess();
        let mut ret = 0;
        let r = jobapi::IsProcessInJob(me, 0 as *mut _, &mut ret);
        assert_ne!(r, 0);
        if ret == ::winapi::shared::minwindef::FALSE {
            return true
        }

        // If we are in a job, then we can run these tests if we can be added to
        // a nested job (as we're going to create a nested job no matter what as
        // part of these tests.
        //
        // If we can't be added to a nested job, then these tests will
        // definitely fail, and there's not much we can do about that.
        let job = jobapi2::CreateJobObjectW(0 as *mut _, 0 as *const _);
        assert!(!job.is_null());
        let r = jobapi2::AssignProcessToJobObject(job, me);
        handleapi::CloseHandle(job);
        r != 0
    }
}

#[test]
fn ctrl_c_kills_everyone() {
    if !enabled() {
        return
    }

    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();

    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("src/lib.rs", "")
        .file("build.rs", &format!(r#"
            use std::net::TcpStream;
            use std::io::Read;

            fn main() {{
                let mut socket = TcpStream::connect("{}").unwrap();
                let _ = socket.read(&mut [0; 10]);
                panic!("that read should never return");
            }}
        "#, addr))
        .build();

    let mut cargo = p.cargo("build").build_command();
    cargo.stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1");
    let mut child = cargo.spawn().unwrap();

    let mut sock = listener.accept().unwrap().0;
    ctrl_c(&mut child);

    assert!(!child.wait().unwrap().success());
    match sock.read(&mut [0; 10]) {
        Ok(n) => assert_eq!(n, 0),
        Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset),
    }

    // Ok so what we just did was spawn cargo that spawned a build script, then
    // we killed cargo in hopes of it killing the build script as well. If all
    // went well the build script is now dead. On Windows, however, this is
    // enforced with job objects which means that it may actually be in the
    // *process* of being torn down at this point.
    //
    // Now on Windows we can't completely remove a file until all handles to it
    // have been closed. Including those that represent running processes. So if
    // we were to return here then there may still be an open reference to some
    // file in the build directory. What we want to actually do is wait for the
    // build script to *complete* exit. Take care of that by blowing away the
    // build directory here, and panicking if we eventually spin too long
    // without being able to.
    for i in 0..10 {
        match fs::remove_dir_all(&p.root().join("target")) {
            Ok(()) => return,
            Err(e) => println!("attempt {}: {}", i, e),
        }
        thread::sleep(Duration::from_millis(100));
    }

    panic!("couldn't remove build directory after a few tries, seems like \
            we won't be able to!");
}

#[cfg(unix)]
fn ctrl_c(child: &mut Child) {
    use libc;

    let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
    if r < 0 {
        panic!("failed to kill: {}", io::Error::last_os_error());
    }
}

#[cfg(windows)]
fn ctrl_c(child: &mut Child) {
    child.kill().unwrap();
}
<|endoftext|>


<commit_before>use crate::util;
use core::cell::Cell;
use libtock_core::{callback, syscalls};

const DRIVER_NUMBER: usize = 0x30003;

mod command_nr {
    pub const TRANSMIT: usize = 1;
    pub const RECEIVE: usize = 2;
    pub const EMULATE: usize = 3;
    pub const CONFIGURE: usize = 4;
}

mod subscribe_nr {
    pub const TRANSMIT: usize = 1;
    pub const RECEIVE: usize = 2;
    pub const SELECT: usize = 3;
}

mod allow_nr {
    pub const TRANSMIT: usize = 1;
    pub const RECEIVE: usize = 2;
}

pub fn enable_emulation() {
    emulate(true);
}

pub fn disable_emulation() {
    emulate(false);
}

pub fn emulate(enabled: bool) -> bool {
    let result_code = syscalls::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as usize, 0);
    if result_code.is_err() {
        return false;
    }

    true
}

pub fn selected() -> bool {
    let is_selected = Cell::new(false);
    let mut is_selected_alarm = || is_selected.set(true);
    let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
        DRIVER_NUMBER,
        subscribe_nr::SELECT,
        &mut is_selected_alarm,
    );
    if subscription.is_err() {
        return false;
    }

    util::yieldk_for(|| is_selected.get());
    true
}

pub fn configure(tag_type: u8) -> bool {
    let result_code = syscalls::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as usize, 0);
    if result_code.is_err() {
        return false;
    }

    true
}

pub fn receive(buf: &mut [u8]) -> bool {
    let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf);
    if result.is_err() {
        return false;
    }

    let done = Cell::new(false);
    let mut alarm = || done.set(true);
    let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
        DRIVER_NUMBER,
        subscribe_nr::RECEIVE,
        &mut alarm,
    );
    if subscription.is_err() {
        return false;
    }

    let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0);
    if result_code.is_err() {
        return false;
    }

    util::yieldk_for(|| done.get());
    true
}

pub fn transmit(buf: &mut [u8]) -> bool {
    let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT, buf);
    if result.is_err() {
        return false;
    }

    let done = Cell::new(false);
    let mut alarm = || done.set(true);
    let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
        DRIVER_NUMBER,
        subscribe_nr::TRANSMIT,
        &mut alarm,
    );
    if subscription.is_err() {
        return false;
    }

    let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, 0, 0);
    if result_code.is_err() {
        return false;
    }

    util::yieldk_for(|| done.get());
    true
}
<commit_msg>Update libtock-drivers/ NFC driver<commit_after>use crate::util;
use core::cell::Cell;
use libtock_core::{callback, syscalls};

const DRIVER_NUMBER: usize = 0x30003;

mod command_nr {
    pub const TRANSMIT: usize = 1;
    pub const RECEIVE: usize = 2;
    pub const EMULATE: usize = 3;
    pub const CONFIGURE: usize = 4;
    pub const FRAMEDELAYMAX: usize = 5;
}

mod subscribe_nr {
    pub const TRANSMIT: usize = 1;
    pub const RECEIVE: usize = 2;
    pub const SELECT: usize = 3;
}

mod allow_nr {
    pub const TRANSMIT: usize = 1;
    pub const RECEIVE: usize = 2;
}

pub struct NfcTag {}

impl NfcTag {
    pub fn enable_emulation() {
        NfcTag::emulate(true);
    }

    pub fn disable_emulation() {
        NfcTag::emulate(false);
    }

    pub fn emulate(enabled: bool) -> bool {
        let result_code =
            syscalls::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as usize, 0);
        if result_code.is_err() {
            return false;
        }

        true
    }

    /// Subscribe to the tag being SELECTED callback.
    pub fn selected() -> bool {
        let is_selected = Cell::new(false);
        let mut is_selected_alarm = || is_selected.set(true);
        let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
            DRIVER_NUMBER,
            subscribe_nr::SELECT,
            &mut is_selected_alarm,
        );
        if subscription.is_err() {
            return false;
        }

        util::yieldk_for(|| is_selected.get());
        true
    }

    /// Configure the tag type command.
    pub fn configure(tag_type: u8) -> bool {
        let result_code =
            syscalls::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as usize, 0);
        if result_code.is_err() {
            return false;
        }

        true
    }

    /// Set the maximum frame delay value to support
    /// transmission with the reader.
    pub fn set_framedelaymax(delay: u32) -> bool {
        let result_code =
            syscalls::command(DRIVER_NUMBER, command_nr::FRAMEDELAYMAX, delay as usize, 0);
        if result_code.is_err() {
            return false;
        }

        true
    }

    /// 1. Share with the driver a buffer.
    /// 2. Subscribe to having a successful receive callback.
    /// 3. Issue the request for reception.
    pub fn receive(buf: &mut [u8]) -> bool {
        let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf);
        if result.is_err() {
            return false;
        }

        let done = Cell::new(false);
        let mut alarm = || done.set(true);
        let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
            DRIVER_NUMBER,
            subscribe_nr::RECEIVE,
            &mut alarm,
        );
        if subscription.is_err() {
            return false;
        }

        let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0);
        if result_code.is_err() {
            return false;
        }

        util::yieldk_for(|| done.get());
        true
    }

    /// 1. Share with the driver a buffer containing the app's reply.
    /// 2. Subscribe to having a successful transmission callback.
    /// 3. Issue the request for transmitting.
    pub fn transmit(buf: &mut [u8], amount: usize) -> bool {
        let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT, buf);
        if result.is_err() {
            return false;
        }

        let done = Cell::new(false);
        let mut alarm = || done.set(true);
        let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
            DRIVER_NUMBER,
            subscribe_nr::TRANSMIT,
            &mut alarm,
        );
        if subscription.is_err() {
            return false;
        }

        let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0);
        if result_code.is_err() {
            return false;
        }

        util::yieldk_for(|| done.get());
        true
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use libc::{c_char, c_void, size_t, uintptr_t, free, malloc, realloc};
use managed::raw::{BoxHeaderRepr, BoxRepr};
use unstable::intrinsics::TyDesc;
use sys::size_of;

extern {
    #[rust_stack]
    fn abort();
}

fn get_box_size(body_size: uint, body_align: uint) -> uint {
    let header_size = size_of::<BoxHeaderRepr>();
    // FIXME (#2699): This alignment calculation is suspicious. Is it right?
    let total_size = align_to(header_size, body_align) + body_size;
    total_size
}

// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power
// of two.
fn align_to(size: uint, align: uint) -> uint {
    assert!(align != 0);
    (size + align - 1) & !(align - 1)
}

/// A wrapper around libc::malloc, aborting on out-of-memory
#[inline]
pub unsafe fn malloc_raw(size: uint) -> *c_void {
    let p = malloc(size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

/// A wrapper around libc::realloc, aborting on out-of-memory
#[inline]
pub unsafe fn realloc_raw(ptr: *mut c_void, size: uint) -> *mut c_void {
    let p = realloc(ptr, size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[cfg(stage0, not(test))]
#[lang="exchange_malloc"]
#[inline]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    let p = malloc_raw(total_size as uint);

    let box: *mut BoxRepr = p as *mut BoxRepr;
    (*box).header.ref_count = -1;
    (*box).header.type_desc = td;

    box as *c_char
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[cfg(not(stage0), not(test))]
#[lang="exchange_malloc"]
#[inline]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    malloc_raw(total_size as uint) as *c_char
}

// FIXME: #7496
#[cfg(not(test))]
#[lang="closure_exchange_malloc"]
#[inline]
pub unsafe fn closure_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    let p = malloc_raw(total_size as uint);

    let box: *mut BoxRepr = p as *mut BoxRepr;
    (*box).header.type_desc = td;

    box as *c_char
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler.
#[cfg(not(test))]
#[lang="exchange_free"]
#[inline]
pub unsafe fn exchange_free(ptr: *c_char) {
    free(ptr as *c_void);
}
<commit_msg>global_heap: inline get_box_size and align_to<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use libc::{c_char, c_void, size_t, uintptr_t, free, malloc, realloc};
use managed::raw::{BoxHeaderRepr, BoxRepr};
use unstable::intrinsics::TyDesc;
use sys::size_of;

extern {
    #[rust_stack]
    fn abort();
}

#[inline]
fn get_box_size(body_size: uint, body_align: uint) -> uint {
    let header_size = size_of::<BoxHeaderRepr>();
    // FIXME (#2699): This alignment calculation is suspicious. Is it right?
    let total_size = align_to(header_size, body_align) + body_size;
    total_size
}

// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power
// of two.
#[inline]
fn align_to(size: uint, align: uint) -> uint {
    assert!(align != 0);
    (size + align - 1) & !(align - 1)
}

/// A wrapper around libc::malloc, aborting on out-of-memory
#[inline]
pub unsafe fn malloc_raw(size: uint) -> *c_void {
    let p = malloc(size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

/// A wrapper around libc::realloc, aborting on out-of-memory
#[inline]
pub unsafe fn realloc_raw(ptr: *mut c_void, size: uint) -> *mut c_void {
    let p = realloc(ptr, size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[cfg(stage0, not(test))]
#[lang="exchange_malloc"]
#[inline]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    let p = malloc_raw(total_size as uint);

    let box: *mut BoxRepr = p as *mut BoxRepr;
    (*box).header.ref_count = -1;
    (*box).header.type_desc = td;

    box as *c_char
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[cfg(not(stage0), not(test))]
#[lang="exchange_malloc"]
#[inline]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    malloc_raw(total_size as uint) as *c_char
}

// FIXME: #7496
#[cfg(not(test))]
#[lang="closure_exchange_malloc"]
#[inline]
pub unsafe fn closure_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    let p = malloc_raw(total_size as uint);

    let box: *mut BoxRepr = p as *mut BoxRepr;
    (*box).header.type_desc = td;

    box as *c_char
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler.
#[cfg(not(test))]
#[lang="exchange_free"]
#[inline]
pub unsafe fn exchange_free(ptr: *c_char) {
    free(ptr as *c_void);
}
<|endoftext|>


<commit_before>
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;

pub use super::super::types::*;
pub use super::super::op::*;
pub use super::*;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlockId(u32);

impl fmt::Display for BlockId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", OpId(self.0))
    }
}

#[derive(Debug)]
pub enum ControlFlowChain {
    Atom(BlockId),
    Block(Vec<ControlFlowChain>),
    If(BlockId, Box<ControlFlowChain>, SelectionControl, Option<BranchWeights>),
    IfElse(BlockId,
           Box<ControlFlowChain>,
           Box<ControlFlowChain>,
           SelectionControl,
           Option<BranchWeights>),
    Loop(BlockId, Box<ControlFlowChain>, LoopControl, Option<BranchWeights>),
    Break,
}

impl fmt::Display for ControlFlowChain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.fmt_indent(f, 0)
    }
}

impl ControlFlowChain {
    /// Get block contents and optimize the structure
    fn block(mut chains: Vec<ControlFlowChain>) -> ControlFlowChain {
        match chains.len() {
            0 => {
                // Empty block will be internal parse state
                // Just return and it will be dealt with later
                ControlFlowChain::Block(chains)
            }
            1 => {
                // If we only have on chain then return that instead
                chains.pop().unwrap()
            }
            _ => {
                // Else merge everything to be simpler
                let mut merged = Vec::with_capacity(chains.len());
                for chain in chains.into_iter() {
                    match chain {
                        ControlFlowChain::Block(mut more) => {
                            // Other blocks get their elements pulled into us
                            // to remove nexted Blocks
                            merged.append(&mut more);
                        }
                        _ => {
                            // Other nodes just get added
                            merged.push(chain);
                        }
                    }
                }
                ControlFlowChain::Block(merged)
            }
        }
    }

    /// Emit If or IfElse depending on the branch
    fn conditional(id: BlockId,
                   true_chain: Box<ControlFlowChain>,
                   false_chain: Box<ControlFlowChain>,
                   selection_control: SelectionControl,
                   weights: Option<BranchWeights>)
                   -> ControlFlowChain {
        match *false_chain {
            ControlFlowChain::Block(ref block) if block.len() == 0 => {
                ControlFlowChain::If(id, true_chain, selection_control, weights)
            }
            _ => ControlFlowChain::IfElse(id, true_chain, false_chain, selection_control, weights),
        }
    }

    fn fmt_indent(&self, f: &mut fmt::Formatter, indent: u32) -> fmt::Result {
        let write_indent = |f: &mut fmt::Formatter| -> fmt::Result {
            for _ in 0..indent {
                try!(f.write_str("    "))
            }
            Ok(())
        };
        match *self {
            ControlFlowChain::Atom(ref id) => {
                try!(write_indent(f));
                writeln!(f, "{}", id)
            }
            ControlFlowChain::Block(ref block) => {
                for cfc in block {
                    try!(cfc.fmt_indent(f, indent))
                }
                Ok(())
            }
            ControlFlowChain::If(ref id, ref left, ref hint, ref weights) => {
                try!(write_indent(f));
                try!(write!(f, "if {} ", id));
                if SelectionControl::default() != *hint {
                    try!(write!(f, " [{}]", hint));
                }
                if let Some(ref weights) = *weights {
                    try!(write!(f, " [{}]", weights));
                }
                try!(writeln!(f, "{{"));
                try!(left.fmt_indent(f, indent + 1));
                try!(write_indent(f));
                writeln!(f, "}}")
            }
            ControlFlowChain::IfElse(ref id, ref left, ref right, ref hint, ref weights) => {
                try!(write_indent(f));
                try!(write!(f, "if {}", id));
                if SelectionControl::default() != *hint {
                    try!(write!(f, " [{}]", hint));
                }
                if let Some(ref weights) = *weights {
                    try!(write!(f, " [{}]", weights));
                }
                try!(writeln!(f, " {{"));
                try!(left.fmt_indent(f, indent + 1));
                try!(write_indent(f));
                try!(writeln!(f, "}} else {{"));
                try!(right.fmt_indent(f, indent + 1));
                try!(write_indent(f));
                writeln!(f, "}}")
            }
            ControlFlowChain::Loop(ref id, ref inner, ref hint, ref weights) => {
                try!(write_indent(f));
                try!(write!(f, "loop {}", id));
                if LoopControl::default() != *hint {
                    try!(write!(f, " [{}]", hint));
                }
                if let Some(ref weights) = *weights {
                    try!(write!(f, " [{}]", weights));
                }
                try!(writeln!(f, " {{"));
                try!(inner.fmt_indent(f, indent + 1));
                try!(write_indent(f));
                writeln!(f, "}}")
            }
            ControlFlowChain::Break => {
                try!(write_indent(f));
                writeln!(f, "break;")
            }
        }
    }
}

#[derive(Debug)]
pub enum ControlType {
    If,
    IfElse,
    Loop,
}

#[derive(Debug)]
pub enum ControlFlowError {
    DuplicateBlockId(BlockId),
    NoBlocks,
    UnknownBlockId(BlockId),
    CouldNotPredictConverge(BlockId),
    InvalidConvergePrediction(ControlType, BlockId),
    Failed,
}

pub type ControlFlowResult<T> = Result<T, ControlFlowError>;

type BlockMap<'a> = HashMap<BlockId, &'a BasicBlock>;

pub fn find_control_flow(decl: FunctionDefinition) -> ControlFlowResult<ControlFlowChain> {

    let mut start_opt = None;

    let block_map: BlockMap = {
        let mut map = HashMap::new();
        for block in &decl.blocks {
            let id = BlockId(block.label.result_id.0);
            if let None = start_opt {
                start_opt = Some(id);
            }
            if let Some(_) = map.insert(id, block) {
                return Err(ControlFlowError::DuplicateBlockId(id));
            }
        }
        map
    };

    let start = match start_opt {
        Some(id) => id,
        None => return Err(ControlFlowError::NoBlocks),
    };

    let (chain, next) = try!(search_block(start, &FlowList::new(), &block_map));

    match next {
        Continue::Return => Ok(chain),
        _ => Err(ControlFlowError::Failed),
    }
}

/// State to allow backtracking from end points to continue parsing from a parent block
#[derive(Debug, PartialEq)]
enum Continue {
    Next(BlockId),
    Return,
}

/// Marks how we're meant to converge from the children of a block
#[derive(Clone, Debug, PartialEq)]
enum Converge {
    If(BlockId, SelectionControl),
    Loop(BlockId, BlockId, LoopControl),
}

/// Links back to the current flow control we're inside
struct FlowNode {
    converge: Converge,
    ptr: Option<Rc<FlowNode>>,
}

impl FlowNode {
    fn merges(&self, id: &BlockId) -> bool {
        match self.converge {
            Converge::If(join, _) |
            Converge::Loop(join, _, _) => {
                if join == *id {
                    return true;
                }
            }
        }
        match self.ptr {
            Some(ref node) => node.merges(id),
            None => false,
        }
    }

    fn innermost_loop(&self) -> Option<(BlockId, BlockId)> {
        if let Converge::Loop(ref head, ref merge, _) = self.converge {
            return Some((*head, *merge));
        }
        match self.ptr {
            Some(ref node) => node.innermost_loop(),
            None => None,
        }
    }
}

/// Manages the FlowNode list
struct FlowList {
    head: Option<Rc<FlowNode>>,
}

impl FlowList {
    /// Start a new empty flow list
    fn new() -> FlowList {
        FlowList { head: None }
    }

    /// Add a node to the flow list
    fn extend(&self, converge: Converge) -> FlowList {
        FlowList {
            head: Some(Rc::new(FlowNode {
                converge: converge,
                ptr: self.head.clone(),
            })),
        }
    }

    /// Check if a block is in the flow list
    fn merges(&self, id: &BlockId) -> bool {
        match self.head {
            Some(ref node) => node.merges(id),
            None => false,
        }
    }

    /// Find the merge block for the innermost loop
    fn innermost_loop(&self) -> Option<(BlockId, BlockId)> {
        match self.head {
            Some(ref node) => node.innermost_loop(),
            None => None,
        }
    }
}

fn search_block(id: BlockId,
                flow_list: &FlowList,
                block_map: &BlockMap)
                -> ControlFlowResult<(ControlFlowChain, Continue)> {

    if flow_list.merges(&id) {
        return Ok((ControlFlowChain::block(vec![]), Continue::Next(id)));
    }

    let block = match block_map.get(&id) {
        Some(block) => block,
        None => return Err(ControlFlowError::UnknownBlockId(id)),
    };

    match block.branch {
        GroupBranch::OpBranch(ref op) => {
            let current = ControlFlowChain::Atom(id);

            let next_id = BlockId(op.target_label.0);

            match block.merge {
                // Loop construct
                Some(GroupMerge::OpLoopMerge(ref loop_merge)) => {
                    let head = id;
                    let after = BlockId(loop_merge.merge_block.0);
                    let hints = loop_merge.loop_control.clone();
                    let converge = Converge::Loop(head, after, hints.clone());

                    // Set up inner flow list
                    let inner_fl = flow_list.extend(converge.clone());

                    // Recursive parse branches
                    let (inner_chain, inner_next) =
                        try!(search_block(next_id, &inner_fl, block_map));

                    if inner_next == Continue::Next(head) {
                        let inner_box = Box::new(inner_chain);

                        let ctc = ControlFlowChain::Loop(id, inner_box, hints, None);

                        let (after_chain, after_next) =
                            try!(search_block(after, flow_list, block_map));

                        let chain = ControlFlowChain::block(vec![ctc, after_chain]);

                        Ok((chain, after_next))
                    } else {
                        Err(ControlFlowError::InvalidConvergePrediction(ControlType::Loop, id))
                    }
                }
                None => {
                    let (next_chain, next_next) = try!(search_block(next_id, flow_list, block_map));

                    let chain = ControlFlowChain::block(vec![current, next_chain]);

                    Ok((chain, next_next))
                }
                _ => unimplemented!(),
            }
        }
        GroupBranch::OpBranchConditional(ref op) => {

            let true_block = BlockId(op.true_label.0);
            let false_block = BlockId(op.false_label.0);

            match block.merge {
                // Selection construct
                Some(GroupMerge::OpSelectionMerge(ref selection)) => {
                    let merge = BlockId(selection.merge_block.0);
                    let hints = selection.selection_control.clone();
                    let converge = Converge::If(merge, hints.clone());

                    // Set up inner flow list
                    let inner_fl = flow_list.extend(converge.clone());

                    // Recursive parse branches
                    let (true_chain, true_next) =
                        try!(search_block(true_block, &inner_fl, block_map));
                    let (false_chain, false_next) =
                        try!(search_block(false_block, &inner_fl, block_map));

                    if true_next == false_next && true_next == Continue::Next(merge) {

                        let true_box = Box::new(true_chain);
                        let false_box = Box::new(false_chain);
                        let weights = op.weights.clone();
                        let ctc =
                            ControlFlowChain::conditional(id, true_box, false_box, hints, weights);

                        let (next_chain, next_next) =
                            try!(search_block(merge, flow_list, block_map));

                        let chain = ControlFlowChain::block(vec![ctc, next_chain]);

                        Ok((chain, next_next))
                    } else {
                        Err(ControlFlowError::InvalidConvergePrediction(ControlType::IfElse, id))
                    }
                }
                // Loop construct
                Some(GroupMerge::OpLoopMerge(_)) => unimplemented!(),
                None => {
                    let (head, merge) = match flow_list.innermost_loop() {
                        Some(l) => l,
                        None => return Err(ControlFlowError::CouldNotPredictConverge(id)),
                    };
                    if false_block == merge {
                        // Break block

                        let inner = try!(search_block(true_block, &flow_list, block_map));
                        let (inner_chain, inner_next) = inner;

                        if inner_next == Continue::Next(head) {
                            let inner_box = Box::new(inner_chain);
                            let break_box = Box::new(ControlFlowChain::Break);
                            let weights = op.weights.clone();
                            let ctc = ControlFlowChain::conditional(id,
                                                                    inner_box,
                                                                    break_box,
                                                                    SelectionControl::default(),
                                                                    weights);

                            Ok((ctc, inner_next))
                        } else {
                            Err(ControlFlowError::InvalidConvergePrediction(ControlType::Loop, id))
                        }
                    } else {
                        return Err(ControlFlowError::CouldNotPredictConverge(id));
                    }
                }
            }
        }
        GroupBranch::OpReturn(_) => {
            // Return block
            Ok((ControlFlowChain::Atom(id), Continue::Return))
        }
    }
}
<commit_msg>ControlFlowType: Add Continue; Merge IfElse + If.<commit_after>
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;

pub use super::super::types::*;
pub use super::super::op::*;
pub use super::*;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlockId(u32);

impl fmt::Display for BlockId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", OpId(self.0))
    }
}

#[derive(Debug, PartialEq)]
pub enum ControlFlowChain {
    Atom(BlockId),
    Block(Vec<ControlFlowChain>),
    Selection(BlockId,
              Box<ControlFlowChain>,
              Box<ControlFlowChain>,
              SelectionControl,
              Option<BranchWeights>),
    Loop(BlockId, Box<ControlFlowChain>, LoopControl, Option<BranchWeights>),
    Break,
    Continue,
}

impl fmt::Display for ControlFlowChain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.fmt_indent(f, 0)
    }
}

impl ControlFlowChain {
    /// Get block contents and optimize the structure
    fn block(mut chains: Vec<ControlFlowChain>) -> ControlFlowChain {
        match chains.len() {
            0 => {
                // Empty block will be internal parse state
                // Just return and it will be dealt with later
                ControlFlowChain::Block(chains)
            }
            1 => {
                // If we only have on chain then return that instead
                chains.pop().unwrap()
            }
            _ => {
                // Else merge everything to be simpler
                let mut merged = Vec::with_capacity(chains.len());
                for chain in chains.into_iter() {
                    match chain {
                        ControlFlowChain::Block(mut more) => {
                            // Other blocks get their elements pulled into us
                            // to remove nexted Blocks
                            merged.append(&mut more);
                        }
                        _ => {
                            // Other nodes just get added
                            merged.push(chain);
                        }
                    }
                }
                ControlFlowChain::Block(merged)
            }
        }
    }

    fn fmt_indent(&self, f: &mut fmt::Formatter, indent: u32) -> fmt::Result {
        let write_indent = |f: &mut fmt::Formatter| -> fmt::Result {
            for _ in 0..indent {
                try!(f.write_str("    "))
            }
            Ok(())
        };
        match *self {
            ControlFlowChain::Atom(ref id) => {
                try!(write_indent(f));
                writeln!(f, "{}", id)
            }
            ControlFlowChain::Block(ref block) => {
                for cfc in block {
                    try!(cfc.fmt_indent(f, indent))
                }
                Ok(())
            }
            ControlFlowChain::Selection(ref id, ref left, ref right, ref hint, ref weights) => {
                try!(write_indent(f));
                try!(write!(f, "selection {}", id));
                if SelectionControl::default() != *hint {
                    try!(write!(f, " [{}]", hint));
                }
                if let Some(ref weights) = *weights {
                    try!(write!(f, " [{}]", weights));
                }
                try!(writeln!(f, " {{"));
                try!(left.fmt_indent(f, indent + 1));
                if **right != ControlFlowChain::Block(vec![]) {
                    try!(write_indent(f));
                    try!(writeln!(f, "}} else {{"));
                    try!(right.fmt_indent(f, indent + 1));
                }
                try!(write_indent(f));
                writeln!(f, "}}")
            }
            ControlFlowChain::Loop(ref id, ref inner, ref hint, ref weights) => {
                try!(write_indent(f));
                try!(write!(f, "loop {}", id));
                if LoopControl::default() != *hint {
                    try!(write!(f, " [{}]", hint));
                }
                if let Some(ref weights) = *weights {
                    try!(write!(f, " [{}]", weights));
                }
                try!(writeln!(f, " {{"));
                try!(inner.fmt_indent(f, indent + 1));
                try!(write_indent(f));
                writeln!(f, "}}")
            }
            ControlFlowChain::Break => {
                try!(write_indent(f));
                writeln!(f, "break;")
            }
            ControlFlowChain::Continue => {
                try!(write_indent(f));
                writeln!(f, "continue;")
            }
        }
    }
}

#[derive(Debug)]
pub enum ControlType {
    Selection,
    Loop,
}

#[derive(Debug)]
pub enum ControlFlowError {
    DuplicateBlockId(BlockId),
    NoBlocks,
    UnknownBlockId(BlockId),
    CouldNotPredictConverge(BlockId),
    InvalidConvergePrediction(ControlType, BlockId),
    Failed,
}

pub type ControlFlowResult<T> = Result<T, ControlFlowError>;

type BlockMap<'a> = HashMap<BlockId, &'a BasicBlock>;

pub fn find_control_flow(decl: FunctionDefinition) -> ControlFlowResult<ControlFlowChain> {

    let mut start_opt = None;

    let block_map: BlockMap = {
        let mut map = HashMap::new();
        for block in &decl.blocks {
            let id = BlockId(block.label.result_id.0);
            if let None = start_opt {
                start_opt = Some(id);
            }
            if let Some(_) = map.insert(id, block) {
                return Err(ControlFlowError::DuplicateBlockId(id));
            }
        }
        map
    };

    let start = match start_opt {
        Some(id) => id,
        None => return Err(ControlFlowError::NoBlocks),
    };

    let (chain, next) = try!(search_block(start, &FlowList::new(), &block_map));

    match next {
        Continue::Return => Ok(chain),
        _ => Err(ControlFlowError::Failed),
    }
}

/// State to allow backtracking from end points to continue parsing from a parent block
#[derive(Debug, PartialEq)]
enum Continue {
    Next(BlockId),
    Return,
}

/// Marks how we're meant to converge from the children of a block
#[derive(Clone, Debug, PartialEq)]
enum Converge {
    If(BlockId, SelectionControl),
    Loop(BlockId, BlockId, LoopControl),
}

/// Links back to the current flow control we're inside
struct FlowNode {
    converge: Converge,
    ptr: Option<Rc<FlowNode>>,
}

impl FlowNode {
    fn merges(&self, id: &BlockId) -> bool {
        match self.converge {
            Converge::If(join, _) |
            Converge::Loop(join, _, _) => {
                if join == *id {
                    return true;
                }
            }
        }
        match self.ptr {
            Some(ref node) => node.merges(id),
            None => false,
        }
    }

    fn innermost_loop(&self) -> Option<(BlockId, BlockId)> {
        if let Converge::Loop(ref head, ref merge, _) = self.converge {
            return Some((*head, *merge));
        }
        match self.ptr {
            Some(ref node) => node.innermost_loop(),
            None => None,
        }
    }
}

/// Manages the FlowNode list
struct FlowList {
    head: Option<Rc<FlowNode>>,
}

impl FlowList {
    /// Start a new empty flow list
    fn new() -> FlowList {
        FlowList { head: None }
    }

    /// Add a node to the flow list
    fn extend(&self, converge: Converge) -> FlowList {
        FlowList {
            head: Some(Rc::new(FlowNode {
                converge: converge,
                ptr: self.head.clone(),
            })),
        }
    }

    /// Check if a block is in the flow list
    fn merges(&self, id: &BlockId) -> bool {
        match self.head {
            Some(ref node) => node.merges(id),
            None => false,
        }
    }

    /// Find the merge block for the innermost loop
    fn innermost_loop(&self) -> Option<(BlockId, BlockId)> {
        match self.head {
            Some(ref node) => node.innermost_loop(),
            None => None,
        }
    }
}

fn search_block(id: BlockId,
                flow_list: &FlowList,
                block_map: &BlockMap)
                -> ControlFlowResult<(ControlFlowChain, Continue)> {

    if flow_list.merges(&id) {
        return Ok((ControlFlowChain::block(vec![]), Continue::Next(id)));
    }

    let block = match block_map.get(&id) {
        Some(block) => block,
        None => return Err(ControlFlowError::UnknownBlockId(id)),
    };

    match block.branch {
        GroupBranch::OpBranch(ref op) => {
            let current = ControlFlowChain::Atom(id);

            let next_id = BlockId(op.target_label.0);

            match block.merge {
                // Loop construct
                Some(GroupMerge::OpLoopMerge(ref loop_merge)) => {
                    let head = id;
                    let after = BlockId(loop_merge.merge_block.0);
                    let hints = loop_merge.loop_control.clone();
                    let converge = Converge::Loop(head, after, hints.clone());

                    // Set up inner flow list
                    let inner_fl = flow_list.extend(converge.clone());

                    // Recursive parse branches
                    let (inner_chain, inner_next) =
                        try!(search_block(next_id, &inner_fl, block_map));

                    if inner_next == Continue::Next(head) {
                        let inner_box = Box::new(inner_chain);

                        let ctc = ControlFlowChain::Loop(id, inner_box, hints, None);

                        let (after_chain, after_next) =
                            try!(search_block(after, flow_list, block_map));

                        let chain = ControlFlowChain::block(vec![ctc, after_chain]);

                        Ok((chain, after_next))
                    } else {
                        Err(ControlFlowError::InvalidConvergePrediction(ControlType::Loop, id))
                    }
                }
                None => {
                    let (next_chain, next_next) = try!(search_block(next_id, flow_list, block_map));

                    let chain = ControlFlowChain::block(vec![current, next_chain]);

                    Ok((chain, next_next))
                }
                _ => unimplemented!(),
            }
        }
        GroupBranch::OpBranchConditional(ref op) => {

            let true_block = BlockId(op.true_label.0);
            let false_block = BlockId(op.false_label.0);

            match block.merge {
                // Selection construct
                Some(GroupMerge::OpSelectionMerge(ref selection)) => {
                    let merge = BlockId(selection.merge_block.0);
                    let hints = selection.selection_control.clone();
                    let converge = Converge::If(merge, hints.clone());

                    // Set up inner flow list
                    let inner_fl = flow_list.extend(converge.clone());

                    // Recursive parse branches
                    let (true_chain, true_next) =
                        try!(search_block(true_block, &inner_fl, block_map));
                    let (false_chain, false_next) =
                        try!(search_block(false_block, &inner_fl, block_map));

                    if true_next == false_next && true_next == Continue::Next(merge) {

                        let true_b = Box::new(true_chain);
                        let false_b = Box::new(false_chain);
                        let weights = op.weights.clone();
                        let ctc = ControlFlowChain::Selection(id, true_b, false_b, hints, weights);

                        let (next_chain, next_next) =
                            try!(search_block(merge, flow_list, block_map));

                        let chain = ControlFlowChain::block(vec![ctc, next_chain]);

                        Ok((chain, next_next))
                    } else {
                        Err(ControlFlowError::InvalidConvergePrediction(ControlType::Selection, id))
                    }
                }
                // Loop construct
                Some(GroupMerge::OpLoopMerge(_)) => unimplemented!(),
                None => {
                    let (head, merge) = match flow_list.innermost_loop() {
                        Some(l) => l,
                        None => return Err(ControlFlowError::CouldNotPredictConverge(id)),
                    };
                    let is_break = false_block == merge;
                    let is_continue = false_block == head;
                    if is_break || is_continue {
                        // Break block or Continue block

                        let inner = try!(search_block(true_block, &flow_list, block_map));
                        let (inner_chain, inner_next) = inner;

                        if inner_next == Continue::Next(head) {
                            let inner_box = Box::new(inner_chain);
                            let break_box = if is_break {
                                Box::new(ControlFlowChain::Break)
                            } else {
                                Box::new(ControlFlowChain::Continue)
                            };
                            let weights = op.weights.clone();
                            let ctc = ControlFlowChain::Selection(id,
                                                                  inner_box,
                                                                  break_box,
                                                                  SelectionControl::default(),
                                                                  weights);

                            Ok((ctc, inner_next))
                        } else {
                            Err(ControlFlowError::InvalidConvergePrediction(ControlType::Loop, id))
                        }
                    } else {
                        return Err(ControlFlowError::CouldNotPredictConverge(id));
                    }
                }
            }
        }
        GroupBranch::OpReturn(_) => {
            // Return block
            Ok((ControlFlowChain::Atom(id), Continue::Return))
        }
    }
}
<|endoftext|>


<commit_before>//! Types for the *m.room.redaction* event.

use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events_macros::{Event, EventContent};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::{RedactedStateEventContent, Unsigned};

/// Redaction event.
#[derive(Clone, Debug, Event)]
pub struct RedactionEvent {
    /// Data specific to the event type.
    pub content: RedactionEventContent,

    /// The ID of the event that was redacted.
    pub redacts: EventId,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// Redaction event without a `room_id`.
#[derive(Clone, Debug, Event)]
pub struct SyncRedactionEvent {
    /// Data specific to the event type.
    pub content: RedactionEventContent,

    /// The ID of the event that was redacted.
    pub redacts: EventId,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A redaction of an event.
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.redaction", kind = Message)]
pub struct RedactionEventContent {
    /// The reason for the redaction, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
}

impl RedactionEventContent {
    /// Creates an empty `RedactionEventContent`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a `RedactionEventContent` with the given reason.
    pub fn with_reason(reason: String) -> Self {
        Self { reason: Some(reason) }
    }
}

impl RedactedStateEventContent for RedactedRedactionEventContent {}
<commit_msg>events: Remove questionable trait implementation<commit_after>//! Types for the *m.room.redaction* event.

use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events_macros::{Event, EventContent};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::Unsigned;

/// Redaction event.
#[derive(Clone, Debug, Event)]
pub struct RedactionEvent {
    /// Data specific to the event type.
    pub content: RedactionEventContent,

    /// The ID of the event that was redacted.
    pub redacts: EventId,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// Redaction event without a `room_id`.
#[derive(Clone, Debug, Event)]
pub struct SyncRedactionEvent {
    /// Data specific to the event type.
    pub content: RedactionEventContent,

    /// The ID of the event that was redacted.
    pub redacts: EventId,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A redaction of an event.
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.redaction", kind = Message)]
pub struct RedactionEventContent {
    /// The reason for the redaction, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
}

impl RedactionEventContent {
    /// Creates an empty `RedactionEventContent`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a `RedactionEventContent` with the given reason.
    pub fn with_reason(reason: String) -> Self {
        Self { reason: Some(reason) }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// does the second one subsume the first?
// xfail-test
// xfail-fast

// notes on this test case:
// On Thu, Apr 18, 2013 at 6:30 PM, John Clements <clements@brinckerhoff.org> wrote:
// the "issue-2185.rs" test was xfailed with a ref to #2263. Issue #2263 is now fixed, so I tried it again, and after adding some &self parameters, I got this error:
//
// Running /usr/local/bin/rustc:
// issue-2185.rs:24:0: 26:1 error: conflicting implementations for a trait
// issue-2185.rs:24 impl iterable<uint> for @fn(&fn(uint)) {
// issue-2185.rs:25     fn iter(&self, blk: &fn(v: uint)) { self( |i| blk(i) ) }
// issue-2185.rs:26 }
// issue-2185.rs:20:0: 22:1 note: note conflicting implementation here
// issue-2185.rs:20 impl<A> iterable<A> for @fn(&fn(A)) {
// issue-2185.rs:21     fn iter(&self, blk: &fn(A)) { self(blk); }
// issue-2185.rs:22 }
//
// … so it looks like it's just not possible to implement both the generic iterable<uint> and iterable<A> for the type iterable<uint>. Is it okay if I just remove this test?
//
// but Niko responded:
// think it's fine to remove this test, just because it's old and cruft and not hard to reproduce. *However* it should eventually be possible to implement the same interface for the same type multiple times with different type parameters, it's just that our current trait implementation has accidental limitations.

// so I'm leaving it in.

// This test had to do with an outdated version of the iterable trait.
// However, the condition it was testing seemed complex enough to
// warrant still having a test, so I inlined the old definitions.

trait iterable<A> {
    fn iter(&self, blk: &fn(A));
}

impl<A> iterable<A> for @fn(&fn(A)) {
    fn iter(&self, blk: &fn(A)) { self(blk); }
}

impl iterable<uint> for @fn(&fn(uint)) {
    fn iter(&self, blk: &fn(v: uint)) { self( |i| blk(i) ) }
}

fn filter<A,IA:iterable<A>>(self: IA, prd: @fn(A) -> bool, blk: &fn(A)) {
    do self.iter |a| {
        if prd(a) { blk(a) }
    }
}

fn foldl<A,B,IA:iterable<A>>(self: IA, b0: B, blk: &fn(B, A) -> B) -> B {
    let mut b = b0;
    do self.iter |a| {
        b = blk(b, a);
    }
    b
}

fn range(lo: uint, hi: uint, it: &fn(uint)) {
    let mut i = lo;
    while i < hi {
        it(i);
        i += 1u;
    }
}

pub fn main() {
    let range: @fn(&fn(uint)) = |a| range(0u, 1000u, a);
    let filt: @fn(&fn(v: uint)) = |a| filter(
        range,
        |&&n: uint| n % 3u != 0u && n % 5u != 0u,
        a);
    let sum = foldl(filt, 0u, |accum, &&n: uint| accum + n );

    io::println(fmt!("%u", sum));
}
<commit_msg>more commits on issue 2185<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// does the second one subsume the first?
// xfail-test
// xfail-fast

// notes on this test case:
// On Thu, Apr 18, 2013 at 6:30 PM, John Clements <clements@brinckerhoff.org> wrote:
// the "issue-2185.rs" test was xfailed with a ref to #2263. Issue #2263 is now fixed, so I tried it again, and after adding some &self parameters, I got this error:
//
// Running /usr/local/bin/rustc:
// issue-2185.rs:24:0: 26:1 error: conflicting implementations for a trait
// issue-2185.rs:24 impl iterable<uint> for @fn(&fn(uint)) {
// issue-2185.rs:25     fn iter(&self, blk: &fn(v: uint)) { self( |i| blk(i) ) }
// issue-2185.rs:26 }
// issue-2185.rs:20:0: 22:1 note: note conflicting implementation here
// issue-2185.rs:20 impl<A> iterable<A> for @fn(&fn(A)) {
// issue-2185.rs:21     fn iter(&self, blk: &fn(A)) { self(blk); }
// issue-2185.rs:22 }
//
// … so it looks like it's just not possible to implement both the generic iterable<uint> and iterable<A> for the type iterable<uint>. Is it okay if I just remove this test?
//
// but Niko responded:
// think it's fine to remove this test, just because it's old and cruft and not hard to reproduce. *However* it should eventually be possible to implement the same interface for the same type multiple times with different type parameters, it's just that our current trait implementation has accidental limitations.

// so I'm leaving it in.
// actually, it looks like this is related to bug #3429. I'll rename this bug.

// This test had to do with an outdated version of the iterable trait.
// However, the condition it was testing seemed complex enough to
// warrant still having a test, so I inlined the old definitions.

trait iterable<A> {
    fn iter(&self, blk: &fn(A));
}

impl<A> iterable<A> for @fn(&fn(A)) {
    fn iter(&self, blk: &fn(A)) { self(blk); }
}

impl iterable<uint> for @fn(&fn(uint)) {
    fn iter(&self, blk: &fn(v: uint)) { self( |i| blk(i) ) }
}

fn filter<A,IA:iterable<A>>(self: IA, prd: @fn(A) -> bool, blk: &fn(A)) {
    do self.iter |a| {
        if prd(a) { blk(a) }
    }
}

fn foldl<A,B,IA:iterable<A>>(self: IA, b0: B, blk: &fn(B, A) -> B) -> B {
    let mut b = b0;
    do self.iter |a| {
        b = blk(b, a);
    }
    b
}

fn range(lo: uint, hi: uint, it: &fn(uint)) {
    let mut i = lo;
    while i < hi {
        it(i);
        i += 1u;
    }
}

pub fn main() {
    let range: @fn(&fn(uint)) = |a| range(0u, 1000u, a);
    let filt: @fn(&fn(v: uint)) = |a| filter(
        range,
        |&&n: uint| n % 3u != 0u && n % 5u != 0u,
        a);
    let sum = foldl(filt, 0u, |accum, &&n: uint| accum + n );

    io::println(fmt!("%u", sum));
}
<|endoftext|>


<commit_before>use std;

fn main() {
  auto x = std.option.some[int](10);
}
<commit_msg>XFAIL lib-option.rs in stage0 due to crash on Darwin on tinderbox<commit_after>// XFAIL-stage0
use std;

fn main() {
  auto x = std.option.some[int](10);
}
<|endoftext|>


<commit_before>// FIXME #937
// xfail-fast
use std;
import std::task;
import std::comm;
import std::uint;

fn die() {
    fail;
}

fn iloop() {
    task::unsupervise();
    let f = die;
    task::spawn(f);
    let p = comm::port::<()>();
    let c = comm::chan(p);
    while true {
        comm::send(c, ());
    }
}

fn main() {
    for each i in uint::range(0u, 16u) {
        let f = iloop;
        task::spawn(f);
    }
}<commit_msg>Un-xfail-fast run-pass/send-iloop<commit_after>use std;
import std::task;
import std::comm;
import std::uint;

fn die() {
    fail;
}

fn iloop() {
    task::unsupervise();
    let f = die;
    task::spawn(f);
    let p = comm::port::<()>();
    let c = comm::chan(p);
    while true {
        comm::send(c, ());
    }
}

fn main() {
    for each i in uint::range(0u, 16u) {
        let f = iloop;
        task::spawn(f);
    }
}<|endoftext|>


<commit_before><commit_msg>Fix for trait soundness changes<commit_after><|endoftext|>


<commit_before>//! An analysis to determine which locals require allocas and
//! which do not.

use rustc_data_structures::bit_set::BitSet;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::traversal;
use rustc::ty;
use rustc::ty::layout::{LayoutOf, HasTyCtxt};
use super::FunctionCx;
use crate::traits::*;

pub fn non_ssa_locals<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
    fx: &FunctionCx<'a, 'tcx, Bx>
) -> BitSet<mir::Local> {
    let mir = fx.mir;
    let mut analyzer = LocalAnalyzer::new(fx);

    analyzer.visit_body(mir);

    for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
        let ty = fx.monomorphize(&ty);
        debug!("local {} has type {:?}", index, ty);
        let layout = fx.cx.layout_of(ty);
        if fx.cx.is_backend_immediate(layout) {
            // These sorts of types are immediates that we can store
            // in an Value without an alloca.
        } else if fx.cx.is_backend_scalar_pair(layout) {
            // We allow pairs and uses of any of their 2 fields.
        } else {
            // These sorts of types require an alloca. Note that
            // is_llvm_immediate() may *still* be true, particularly
            // for newtypes, but we currently force some types
            // (e.g., structs) into an alloca unconditionally, just so
            // that we don't have to deal with having two pathways
            // (gep vs extractvalue etc).
            analyzer.not_ssa(mir::Local::new(index));
        }
    }

    analyzer.non_ssa_locals
}

struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> {
    fx: &'mir FunctionCx<'a, 'tcx, Bx>,
    dominators: Dominators<mir::BasicBlock>,
    non_ssa_locals: BitSet<mir::Local>,
    // The location of the first visited direct assignment to each
    // local, or an invalid location (out of bounds `block` index).
    first_assignment: IndexVec<mir::Local, Location>
}

impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
        let invalid_location =
            mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
        let mut analyzer = LocalAnalyzer {
            fx,
            dominators: fx.mir.dominators(),
            non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
            first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls)
        };

        // Arguments get assigned to by means of the function being called
        for arg in fx.mir.args_iter() {
            analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
        }

        analyzer
    }

    fn first_assignment(&self, local: mir::Local) -> Option<Location> {
        let location = self.first_assignment[local];
        if location.block.index() < self.fx.mir.basic_blocks().len() {
            Some(location)
        } else {
            None
        }
    }

    fn not_ssa(&mut self, local: mir::Local) {
        debug!("marking {:?} as non-SSA", local);
        self.non_ssa_locals.insert(local);
    }

    fn assign(&mut self, local: mir::Local, location: Location) {
        if self.first_assignment(local).is_some() {
            self.not_ssa(local);
        } else {
            self.first_assignment[local] = location;
        }
    }
}

impl<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
    for LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn visit_assign(&mut self,
                    place: &mir::Place<'tcx>,
                    rvalue: &mir::Rvalue<'tcx>,
                    location: Location) {
        debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);

        if let mir::Place::Base(mir::PlaceBase::Local(index)) = *place {
            self.assign(index, location);
            if !self.fx.rvalue_creates_operand(rvalue) {
                self.not_ssa(index);
            }
        } else {
            self.visit_place(
                place,
                PlaceContext::MutatingUse(MutatingUseContext::Store),
                location
            );
        }

        self.visit_rvalue(rvalue, location);
    }

    fn visit_terminator_kind(&mut self,
                             kind: &mir::TerminatorKind<'tcx>,
                             location: Location) {
        let check = match *kind {
            mir::TerminatorKind::Call {
                func: mir::Operand::Constant(ref c),
                ref args, ..
            } => match c.ty.sty {
                ty::FnDef(did, _) => Some((did, args)),
                _ => None,
            },
            _ => None,
        };
        if let Some((def_id, args)) = check {
            if Some(def_id) == self.fx.cx.tcx().lang_items().box_free_fn() {
                // box_free(x) shares with `drop x` the property that it
                // is not guaranteed to be statically dominated by the
                // definition of x, so x must always be in an alloca.
                if let mir::Operand::Move(ref place) = args[0] {
                    self.visit_place(
                        place,
                        PlaceContext::MutatingUse(MutatingUseContext::Drop),
                        location
                    );
                }
            }
        }

        self.super_terminator_kind(kind, location);
    }

    fn visit_place(&mut self,
                   place: &mir::Place<'tcx>,
                   context: PlaceContext,
                   location: Location) {
        debug!("visit_place(place={:?}, context={:?})", place, context);
        let cx = self.fx.cx;

        if let mir::Place::Projection(ref proj) = *place {
            // Allow uses of projections that are ZSTs or from scalar fields.
            let is_consume = match context {
                PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
                PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
                _ => false
            };
            if is_consume {
                let base_ty = proj.base.ty(self.fx.mir, cx.tcx());
                let base_ty = self.fx.monomorphize(&base_ty);

                // ZSTs don't require any actual memory access.
                let elem_ty = base_ty
                    .projection_ty(cx.tcx(), &proj.elem)
                    .ty;
                let elem_ty = self.fx.monomorphize(&elem_ty);
                if cx.layout_of(elem_ty).is_zst() {
                    return;
                }

                if let mir::ProjectionElem::Field(..) = proj.elem {
                    let layout = cx.layout_of(base_ty.ty);
                    if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
                        // Recurse with the same context, instead of `Projection`,
                        // potentially stopping at non-operand projections,
                        // which would trigger `not_ssa` on locals.
                        self.visit_place(&proj.base, context, location);
                        return;
                    }
                }
            }

            // A deref projection only reads the pointer, never needs the place.
            if let mir::ProjectionElem::Deref = proj.elem {
                return self.visit_place(
                    &proj.base,
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                    location
                );
            }
        }

        self.super_place(place, context, location);
    }

    fn visit_local(&mut self,
                   &local: &mir::Local,
                   context: PlaceContext,
                   location: Location) {
        match context {
            PlaceContext::MutatingUse(MutatingUseContext::Call) => {
                self.assign(local, location);
            }

            PlaceContext::NonUse(_) |
            PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
                // Reads from uninitialized variables (e.g., in dead code, after
                // optimizations) require locals to be in (uninitialized) memory.
                // N.B., there can be uninitialized reads of a local visited after
                // an assignment to that local, if they happen on disjoint paths.
                let ssa_read = match self.first_assignment(local) {
                    Some(assignment_location) => {
                        assignment_location.dominates(location, &self.dominators)
                    }
                    None => false
                };
                if !ssa_read {
                    self.not_ssa(local);
                }
            }

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
            PlaceContext::MutatingUse(MutatingUseContext::Store) |
            PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
            PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
            PlaceContext::MutatingUse(MutatingUseContext::Projection) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
                self.not_ssa(local);
            }

            PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
                let ty = self.fx.mir.local_decls[local].ty;
                let ty = self.fx.monomorphize(&ty);

                // Only need the place if we're actually dropping it.
                if self.fx.cx.type_needs_drop(ty) {
                    self.not_ssa(local);
                }
            }
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CleanupKind {
    NotCleanup,
    Funclet,
    Internal { funclet: mir::BasicBlock }
}

impl CleanupKind {
    pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
        match self {
            CleanupKind::NotCleanup => None,
            CleanupKind::Funclet => Some(for_bb),
            CleanupKind::Internal { funclet } => Some(funclet),
        }
    }
}

pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Body<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
    fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                              mir: &mir::Body<'tcx>) {
        for (bb, data) in mir.basic_blocks().iter_enumerated() {
            match data.terminator().kind {
                TerminatorKind::Goto { .. } |
                TerminatorKind::Resume |
                TerminatorKind::Abort |
                TerminatorKind::Return |
                TerminatorKind::GeneratorDrop |
                TerminatorKind::Unreachable |
                TerminatorKind::SwitchInt { .. } |
                TerminatorKind::Yield { .. } |
                TerminatorKind::FalseEdges { .. } |
                TerminatorKind::FalseUnwind { .. } => {
                    /* nothing to do */
                }
                TerminatorKind::Call { cleanup: unwind, .. } |
                TerminatorKind::Assert { cleanup: unwind, .. } |
                TerminatorKind::DropAndReplace { unwind, .. } |
                TerminatorKind::Drop { unwind, .. } => {
                    if let Some(unwind) = unwind {
                        debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
                               bb, data, unwind);
                        result[unwind] = CleanupKind::Funclet;
                    }
                }
            }
        }
    }

    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                       mir: &mir::Body<'tcx>) {
        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());

        let mut set_successor = |funclet: mir::BasicBlock, succ| {
            match funclet_succs[funclet] {
                ref mut s @ None => {
                    debug!("set_successor: updating successor of {:?} to {:?}",
                           funclet, succ);
                    *s = Some(succ);
                },
                Some(s) => if s != succ {
                    span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
                              funclet, s, succ);
                }
            }
        };

        for (bb, data) in traversal::reverse_postorder(mir) {
            let funclet = match result[bb] {
                CleanupKind::NotCleanup => continue,
                CleanupKind::Funclet => bb,
                CleanupKind::Internal { funclet } => funclet,
            };

            debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
                   bb, data, result[bb], funclet);

            for &succ in data.terminator().successors() {
                let kind = result[succ];
                debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
                       funclet, succ, kind);
                match kind {
                    CleanupKind::NotCleanup => {
                        result[succ] = CleanupKind::Internal { funclet };
                    }
                    CleanupKind::Funclet => {
                        if funclet != succ {
                            set_successor(funclet, succ);
                        }
                    }
                    CleanupKind::Internal { funclet: succ_funclet } => {
                        if funclet != succ_funclet {
                            // `succ` has 2 different funclet going into it, so it must
                            // be a funclet by itself.

                            debug!("promoting {:?} to a funclet and updating {:?}", succ,
                                   succ_funclet);
                            result[succ] = CleanupKind::Funclet;
                            set_successor(succ_funclet, succ);
                            set_successor(funclet, succ);
                        }
                    }
                }
            }
        }
    }

    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());

    discover_masters(&mut result, mir);
    propagate(&mut result, mir);
    debug!("cleanup_kinds: result={:?}", result);
    result
}
<commit_msg>Make LocalAnalizer visitor iterate instead of recurse<commit_after>//! An analysis to determine which locals require allocas and
//! which do not.

use rustc_data_structures::bit_set::BitSet;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::traversal;
use rustc::ty;
use rustc::ty::layout::{LayoutOf, HasTyCtxt};
use super::FunctionCx;
use crate::traits::*;

pub fn non_ssa_locals<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
    fx: &FunctionCx<'a, 'tcx, Bx>
) -> BitSet<mir::Local> {
    let mir = fx.mir;
    let mut analyzer = LocalAnalyzer::new(fx);

    analyzer.visit_body(mir);

    for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
        let ty = fx.monomorphize(&ty);
        debug!("local {} has type {:?}", index, ty);
        let layout = fx.cx.layout_of(ty);
        if fx.cx.is_backend_immediate(layout) {
            // These sorts of types are immediates that we can store
            // in an Value without an alloca.
        } else if fx.cx.is_backend_scalar_pair(layout) {
            // We allow pairs and uses of any of their 2 fields.
        } else {
            // These sorts of types require an alloca. Note that
            // is_llvm_immediate() may *still* be true, particularly
            // for newtypes, but we currently force some types
            // (e.g., structs) into an alloca unconditionally, just so
            // that we don't have to deal with having two pathways
            // (gep vs extractvalue etc).
            analyzer.not_ssa(mir::Local::new(index));
        }
    }

    analyzer.non_ssa_locals
}

struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> {
    fx: &'mir FunctionCx<'a, 'tcx, Bx>,
    dominators: Dominators<mir::BasicBlock>,
    non_ssa_locals: BitSet<mir::Local>,
    // The location of the first visited direct assignment to each
    // local, or an invalid location (out of bounds `block` index).
    first_assignment: IndexVec<mir::Local, Location>
}

impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
        let invalid_location =
            mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
        let mut analyzer = LocalAnalyzer {
            fx,
            dominators: fx.mir.dominators(),
            non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
            first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls)
        };

        // Arguments get assigned to by means of the function being called
        for arg in fx.mir.args_iter() {
            analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
        }

        analyzer
    }

    fn first_assignment(&self, local: mir::Local) -> Option<Location> {
        let location = self.first_assignment[local];
        if location.block.index() < self.fx.mir.basic_blocks().len() {
            Some(location)
        } else {
            None
        }
    }

    fn not_ssa(&mut self, local: mir::Local) {
        debug!("marking {:?} as non-SSA", local);
        self.non_ssa_locals.insert(local);
    }

    fn assign(&mut self, local: mir::Local, location: Location) {
        if self.first_assignment(local).is_some() {
            self.not_ssa(local);
        } else {
            self.first_assignment[local] = location;
        }
    }
}

impl<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
    for LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn visit_assign(&mut self,
                    place: &mir::Place<'tcx>,
                    rvalue: &mir::Rvalue<'tcx>,
                    location: Location) {
        debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);

        if let mir::Place::Base(mir::PlaceBase::Local(index)) = *place {
            self.assign(index, location);
            if !self.fx.rvalue_creates_operand(rvalue) {
                self.not_ssa(index);
            }
        } else {
            self.visit_place(
                place,
                PlaceContext::MutatingUse(MutatingUseContext::Store),
                location
            );
        }

        self.visit_rvalue(rvalue, location);
    }

    fn visit_terminator_kind(&mut self,
                             kind: &mir::TerminatorKind<'tcx>,
                             location: Location) {
        let check = match *kind {
            mir::TerminatorKind::Call {
                func: mir::Operand::Constant(ref c),
                ref args, ..
            } => match c.ty.sty {
                ty::FnDef(did, _) => Some((did, args)),
                _ => None,
            },
            _ => None,
        };
        if let Some((def_id, args)) = check {
            if Some(def_id) == self.fx.cx.tcx().lang_items().box_free_fn() {
                // box_free(x) shares with `drop x` the property that it
                // is not guaranteed to be statically dominated by the
                // definition of x, so x must always be in an alloca.
                if let mir::Operand::Move(ref place) = args[0] {
                    self.visit_place(
                        place,
                        PlaceContext::MutatingUse(MutatingUseContext::Drop),
                        location
                    );
                }
            }
        }

        self.super_terminator_kind(kind, location);
    }

    fn visit_place(&mut self,
                   place: &mir::Place<'tcx>,
                   context: PlaceContext,
                   location: Location) {
        debug!("visit_place(place={:?}, context={:?})", place, context);
        let mut context = context;
        let cx = self.fx.cx;

        place.iterate(|place_base, place_projections| {
            for proj in place_projections {
                // Allow uses of projections that are ZSTs or from scalar fields.
                let is_consume = match context {
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
                    _ => false
                };
                if is_consume {
                    let base_ty = proj.base.ty(self.fx.mir, cx.tcx());
                    let base_ty = self.fx.monomorphize(&base_ty);

                    // ZSTs don't require any actual memory access.
                    let elem_ty = base_ty
                        .projection_ty(cx.tcx(), &proj.elem)
                        .ty;
                    let elem_ty = self.fx.monomorphize(&elem_ty);
                    if cx.layout_of(elem_ty).is_zst() {
                        return;
                    }

                    if let mir::ProjectionElem::Field(..) = proj.elem {
                        let layout = cx.layout_of(base_ty.ty);
                        if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
                            // Recurse with the same context, instead of `Projection`,
                            // potentially stopping at non-operand projections,
                            // which would trigger `not_ssa` on locals.
                            continue;
                        }
                    }
                }

                // A deref projection only reads the pointer, never needs the place.
                if let mir::ProjectionElem::Deref = proj.elem {
                    return self.visit_place(
                        &proj.base,
                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                        location
                    );
                }

                context = if context.is_mutating_use() {
                    PlaceContext::MutatingUse(MutatingUseContext::Projection)
                } else {
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
                };
            }

            // Default base visit behavior
            if let mir::PlaceBase::Local(local) = place_base {
                self.visit_local(local, context, location);
            }
        });
    }

    fn visit_local(&mut self,
                   &local: &mir::Local,
                   context: PlaceContext,
                   location: Location) {
        match context {
            PlaceContext::MutatingUse(MutatingUseContext::Call) => {
                self.assign(local, location);
            }

            PlaceContext::NonUse(_) |
            PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
                // Reads from uninitialized variables (e.g., in dead code, after
                // optimizations) require locals to be in (uninitialized) memory.
                // N.B., there can be uninitialized reads of a local visited after
                // an assignment to that local, if they happen on disjoint paths.
                let ssa_read = match self.first_assignment(local) {
                    Some(assignment_location) => {
                        assignment_location.dominates(location, &self.dominators)
                    }
                    None => false
                };
                if !ssa_read {
                    self.not_ssa(local);
                }
            }

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
            PlaceContext::MutatingUse(MutatingUseContext::Store) |
            PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
            PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
            PlaceContext::MutatingUse(MutatingUseContext::Projection) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
                self.not_ssa(local);
            }

            PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
                let ty = self.fx.mir.local_decls[local].ty;
                let ty = self.fx.monomorphize(&ty);

                // Only need the place if we're actually dropping it.
                if self.fx.cx.type_needs_drop(ty) {
                    self.not_ssa(local);
                }
            }
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CleanupKind {
    NotCleanup,
    Funclet,
    Internal { funclet: mir::BasicBlock }
}

impl CleanupKind {
    pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
        match self {
            CleanupKind::NotCleanup => None,
            CleanupKind::Funclet => Some(for_bb),
            CleanupKind::Internal { funclet } => Some(funclet),
        }
    }
}

pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Body<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
    fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                              mir: &mir::Body<'tcx>) {
        for (bb, data) in mir.basic_blocks().iter_enumerated() {
            match data.terminator().kind {
                TerminatorKind::Goto { .. } |
                TerminatorKind::Resume |
                TerminatorKind::Abort |
                TerminatorKind::Return |
                TerminatorKind::GeneratorDrop |
                TerminatorKind::Unreachable |
                TerminatorKind::SwitchInt { .. } |
                TerminatorKind::Yield { .. } |
                TerminatorKind::FalseEdges { .. } |
                TerminatorKind::FalseUnwind { .. } => {
                    /* nothing to do */
                }
                TerminatorKind::Call { cleanup: unwind, .. } |
                TerminatorKind::Assert { cleanup: unwind, .. } |
                TerminatorKind::DropAndReplace { unwind, .. } |
                TerminatorKind::Drop { unwind, .. } => {
                    if let Some(unwind) = unwind {
                        debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
                               bb, data, unwind);
                        result[unwind] = CleanupKind::Funclet;
                    }
                }
            }
        }
    }

    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                       mir: &mir::Body<'tcx>) {
        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());

        let mut set_successor = |funclet: mir::BasicBlock, succ| {
            match funclet_succs[funclet] {
                ref mut s @ None => {
                    debug!("set_successor: updating successor of {:?} to {:?}",
                           funclet, succ);
                    *s = Some(succ);
                },
                Some(s) => if s != succ {
                    span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
                              funclet, s, succ);
                }
            }
        };

        for (bb, data) in traversal::reverse_postorder(mir) {
            let funclet = match result[bb] {
                CleanupKind::NotCleanup => continue,
                CleanupKind::Funclet => bb,
                CleanupKind::Internal { funclet } => funclet,
            };

            debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
                   bb, data, result[bb], funclet);

            for &succ in data.terminator().successors() {
                let kind = result[succ];
                debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
                       funclet, succ, kind);
                match kind {
                    CleanupKind::NotCleanup => {
                        result[succ] = CleanupKind::Internal { funclet };
                    }
                    CleanupKind::Funclet => {
                        if funclet != succ {
                            set_successor(funclet, succ);
                        }
                    }
                    CleanupKind::Internal { funclet: succ_funclet } => {
                        if funclet != succ_funclet {
                            // `succ` has 2 different funclet going into it, so it must
                            // be a funclet by itself.

                            debug!("promoting {:?} to a funclet and updating {:?}", succ,
                                   succ_funclet);
                            result[succ] = CleanupKind::Funclet;
                            set_successor(succ_funclet, succ);
                            set_successor(funclet, succ);
                        }
                    }
                }
            }
        }
    }

    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());

    discover_masters(&mut result, mir);
    propagate(&mut result, mir);
    debug!("cleanup_kinds: result={:?}", result);
    result
}
<|endoftext|>