<commit_before>//! Operator norms of matrices

use crate::layout::MatrixLayout;
use cauchy::*;
use lapacke::Layout::ColumnMajor as cm;

pub use super::NormType;

pub trait OperatorNorm_: Scalar {
    unsafe fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real;
}

macro_rules! impl_opnorm {
    ($scalar:ty, $lange:path) => {
        impl OperatorNorm_ for $scalar {
            unsafe fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real {
                match l {
                    MatrixLayout::F { col, lda } => $lange(cm, t as u8, lda, col, a, lda),
                    MatrixLayout::C { row, lda } => {
                        $lange(cm, t.transpose() as u8, lda, row, a, lda)
                    }
                }
            }
        }
    };
} // impl_opnorm!

impl_opnorm!(f64, lapacke::dlange);
impl_opnorm!(f32, lapacke::slange);
impl_opnorm!(c64, lapacke::zlange);
impl_opnorm!(c32, lapacke::clange);
<commit_msg>Rewrite opnorm by LAPACK<commit_after>//! Operator norms of matrices

use crate::layout::MatrixLayout;
use cauchy::*;
use num_traits::Zero;

pub use super::NormType;

pub trait OperatorNorm_: Scalar {
    unsafe fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real;
}

macro_rules! impl_opnorm {
    ($scalar:ty, $lange:path) => {
        impl OperatorNorm_ for $scalar {
            unsafe fn opnorm(t: NormType, l: MatrixLayout, a: &[Self]) -> Self::Real {
                let m = l.lda();
                let n = l.len();
                let t = match l {
                    MatrixLayout::F { .. } => t,
                    MatrixLayout::C { .. } => t.transpose(),
                };
                let mut work = if matches!(t, NormType::Infinity) {
                    vec![Self::Real::zero(); m as usize]
                } else {
                    Vec::new()
                };
                $lange(t as u8, m, n, a, m, &mut work)
            }
        }
    };
} // impl_opnorm!

impl_opnorm!(f64, lapack::dlange);
impl_opnorm!(f32, lapack::slange);
impl_opnorm!(c64, lapack::zlange);
impl_opnorm!(c32, lapack::clange);
<|endoftext|>


<commit_before>use std::fmt;

use serde;
use serde_json as json;

#[derive(Deserialize, Debug)]
pub struct AuthResponse {
    pub session: SessionResponse
}

#[derive(Deserialize, Debug, Clone)]
pub struct SessionResponse {
    pub key: String,
    pub subscriber: i64,
    pub name: String
}

#[derive(Deserialize)]
pub struct NowPlayingResponseWrapper {
    pub nowplaying: NowPlayingResponse
}

#[derive(Deserialize, Debug)]
pub struct NowPlayingResponse {
    pub artist: CorrectableString,
    pub album: CorrectableString,
    #[serde(rename="albumArtist")]
    pub album_artist: CorrectableString,
    pub track: CorrectableString
}

#[derive(Deserialize)]
pub struct ScrobbleResponseWrapper {
    pub scrobbles: Scrobbles
}

#[derive(Deserialize)]
pub struct Scrobbles {
    pub scrobble: ScrobbleResponse
}

#[derive(Deserialize, Debug)]
pub struct ScrobbleResponse {
    pub artist: CorrectableString,
    pub album: CorrectableString,
    #[serde(rename="albumArtist")]
    pub album_artist: CorrectableString,
    pub track: CorrectableString
}

#[derive(Deserialize, Debug)]
pub struct CorrectableString {
    #[serde(deserialize_with="CorrectableString::deserialize_corrected_field")]
    pub corrected: bool,
    #[serde(rename="#text", default)]
    pub text: String
}

impl CorrectableString {

    fn deserialize_corrected_field<D>(de: D) -> Result<bool, D::Error>
        where D: serde::Deserializer
    {
        let deser_result: json::Value = try!(serde::Deserialize::deserialize(de));
        match deser_result {
            json::Value::String(ref s) if &*s == "1" => Ok(true),
            json::Value::String(ref s) if &*s == "0" => Ok(false),
            _ => Err(serde::de::Error::custom("Unexpected value")),
        }
    }

}

impl fmt::Display for CorrectableString {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.text)
    }

}<commit_msg>Add timestamp field to ScrobbleResponse struct (Issue #4)<commit_after>use std::fmt;

use serde;
use serde_json as json;

#[derive(Deserialize, Debug)]
pub struct AuthResponse {
    pub session: SessionResponse
}

#[derive(Deserialize, Debug, Clone)]
pub struct SessionResponse {
    pub key: String,
    pub subscriber: i64,
    pub name: String
}

#[derive(Deserialize)]
pub struct NowPlayingResponseWrapper {
    pub nowplaying: NowPlayingResponse
}

#[derive(Deserialize, Debug)]
pub struct NowPlayingResponse {
    pub artist: CorrectableString,
    pub album: CorrectableString,
    #[serde(rename="albumArtist")]
    pub album_artist: CorrectableString,
    pub track: CorrectableString
}

#[derive(Deserialize)]
pub struct ScrobbleResponseWrapper {
    pub scrobbles: Scrobbles
}

#[derive(Deserialize)]
pub struct Scrobbles {
    pub scrobble: ScrobbleResponse
}

#[derive(Deserialize, Debug)]
pub struct ScrobbleResponse {
    pub artist: CorrectableString,
    pub album: CorrectableString,
    #[serde(rename="albumArtist")]
    pub album_artist: CorrectableString,
    pub track: CorrectableString,
    pub timestamp: String
}

#[derive(Deserialize, Debug)]
pub struct CorrectableString {
    #[serde(deserialize_with="CorrectableString::deserialize_corrected_field")]
    pub corrected: bool,
    #[serde(rename="#text", default)]
    pub text: String
}

impl CorrectableString {

    fn deserialize_corrected_field<D>(de: D) -> Result<bool, D::Error>
        where D: serde::Deserializer
    {
        let deser_result: json::Value = try!(serde::Deserialize::deserialize(de));
        match deser_result {
            json::Value::String(ref s) if &*s == "1" => Ok(true),
            json::Value::String(ref s) if &*s == "0" => Ok(false),
            _ => Err(serde::de::Error::custom("Unexpected value")),
        }
    }

}

impl fmt::Display for CorrectableString {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.text)
    }

}<|endoftext|>


<commit_before>// Copyright 2015-2017 pam-auth Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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::{calloc, free, strdup, c_char, c_int, c_void, size_t};
use pam::{PamMessage, PamMessageStyle, PamResponse, PamReturnCode};

use std::ffi::CStr;
use std::mem;
use std::slice;

pub extern "C" fn converse(num_msg: c_int,
                           msg: *mut *mut PamMessage,
                           resp: *mut *mut PamResponse,
                           appdata_ptr: *mut c_void)
                           -> c_int {
    unsafe {
        // allocate space for responses
        *resp = calloc(num_msg as usize, mem::size_of::<PamResponse>() as size_t) as
                *mut PamResponse;
        if (*resp).is_null() {
            return PamReturnCode::BUF_ERR as c_int;
        }
    }

    let data: &[&str] = unsafe { slice::from_raw_parts(appdata_ptr as *const &str, 2) };

    let mut result: PamReturnCode = PamReturnCode::SUCCESS;
    for i in 0..num_msg as isize {
        unsafe {
            // get indexed values
            let m: &mut PamMessage = &mut **(msg.offset(i));
            let r: &mut PamResponse = &mut *((*resp).offset(i));
            // match on msg_style
            match PamMessageStyle::from(m.msg_style) {
                // assume username is requested
                PamMessageStyle::PROMPT_ECHO_ON => {
                    r.resp = strdup(data[0].as_ptr() as *const c_char);
                }
                // assume password is requested
                PamMessageStyle::PROMPT_ECHO_OFF => {
                    r.resp = strdup(data[1].as_ptr() as *const c_char);
                }
                // an error occured
                PamMessageStyle::ERROR_MSG => {
                    result = PamReturnCode::CONV_ERR;
                }
                // print the message to stdout
                PamMessageStyle::TEXT_INFO => {
                    println!("PAM_TEXT_INFO: {}",
                             String::from_utf8_lossy(CStr::from_ptr(m.msg).to_bytes()));
                }
            }
        }
        if result != PamReturnCode::SUCCESS {
            break;
        }
    }

    // free allocated memory if an error occured
    if result != PamReturnCode::SUCCESS {
        unsafe { free(*resp as *mut c_void) };
    }

    result as c_int
}<commit_msg>Null-terminate pam responses in the converse function<commit_after>// Copyright 2015-2017 pam-auth Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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::{calloc, free, strdup, c_char, c_int, c_void, size_t};
use pam::{PamMessage, PamMessageStyle, PamResponse, PamReturnCode};

use std::ffi::{CStr, CString};
use std::mem;
use std::slice;

pub extern "C" fn converse(num_msg: c_int,
                           msg: *mut *mut PamMessage,
                           resp: *mut *mut PamResponse,
                           appdata_ptr: *mut c_void)
                           -> c_int {
    unsafe {
        // allocate space for responses
        *resp = calloc(num_msg as usize, mem::size_of::<PamResponse>() as size_t) as
                *mut PamResponse;
        if (*resp).is_null() {
            return PamReturnCode::BUF_ERR as c_int;
        }
    }

    let data: &[&str] = unsafe { slice::from_raw_parts(appdata_ptr as *const &str, 2) };

    let mut result: PamReturnCode = PamReturnCode::SUCCESS;
    for i in 0..num_msg as isize {
        unsafe {
            // get indexed values
            let m: &mut PamMessage = &mut **(msg.offset(i));
            let r: &mut PamResponse = &mut *((*resp).offset(i));
            // match on msg_style
            match PamMessageStyle::from(m.msg_style) {
                // assume username is requested
                PamMessageStyle::PROMPT_ECHO_ON => {
                    if let Ok(username) = CString::new(data[0]) {
                        r.resp = strdup(username.as_ptr() as *const c_char);
                    } else {
                        result = PamReturnCode::CONV_ERR;
                    }
                }
                // assume password is requested
                PamMessageStyle::PROMPT_ECHO_OFF => {
                    if let Ok(password) = CString::new(data[1]) {
                        r.resp = strdup(password.as_ptr() as *const c_char);
                    } else {
                        result = PamReturnCode::CONV_ERR;
                    }
                }
                // an error occured
                PamMessageStyle::ERROR_MSG => {
                    result = PamReturnCode::CONV_ERR;
                }
                // print the message to stdout
                PamMessageStyle::TEXT_INFO => {
                    println!("PAM_TEXT_INFO: {}",
                             String::from_utf8_lossy(CStr::from_ptr(m.msg).to_bytes()));
                }
            }
        }
        if result != PamReturnCode::SUCCESS {
            break;
        }
    }

    // free allocated memory if an error occured
    if result != PamReturnCode::SUCCESS {
        unsafe { free(*resp as *mut c_void) };
    }

    result as c_int
}<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters,
    epoch_manager::EpochManager,
    network::NetworkTask,
    network_interface::{ConsensusMsg, ConsensusNetworkEvents, ConsensusNetworkSender},
    network_tests::{NetworkPlayground, TwinId},
    test_utils::{
        consensus_runtime, timed_block_on, MockStateComputer, MockStorage, MockTransactionManager,
    },
    util::time_service::ClockTimeService,
};
use channel::{self, libra_channel, message_queues::QueueStyle};
use consensus_types::{
    block::Block,
    common::{Author, Payload},
};
use futures::channel::mpsc;
use libra_config::{
    config::{
        ConsensusProposerType::{self, FixedProposer, RotatingProposer},
        NodeConfig, WaypointConfig,
    },
    generator::{self, ValidatorSwarm},
};
use libra_mempool::mocks::MockSharedMempool;
use libra_types::{
    ledger_info::LedgerInfoWithSignatures,
    on_chain_config::{OnChainConfig, OnChainConfigPayload, ValidatorSet},
    validator_info::ValidatorInfo,
    waypoint::Waypoint,
};
use network::{
    peer_manager::{conn_notifs_channel, ConnectionRequestSender, PeerManagerRequestSender},
    protocols::network::{NewNetworkEvents, NewNetworkSender},
};
use std::{collections::HashMap, num::NonZeroUsize, sync::Arc};
use tokio::runtime::{Builder, Runtime};

/// Auxiliary struct that is preparing SMR for the test
struct SMRNode {
    _runtime: Runtime,
    commit_cb_receiver: mpsc::UnboundedReceiver<LedgerInfoWithSignatures>,
    storage: Arc<MockStorage>,
    _shared_mempool: MockSharedMempool,
    _state_sync: mpsc::UnboundedReceiver<Payload>,
}

impl SMRNode {
    fn start(
        playground: &mut NetworkPlayground,
        config: NodeConfig,
        smr_id: usize,
        storage: Arc<MockStorage>,
        twin_id: TwinId,
    ) -> Self {
        let (network_reqs_tx, network_reqs_rx) =
            libra_channel::new(QueueStyle::FIFO, NonZeroUsize::new(8).unwrap(), None);
        let (connection_reqs_tx, _) =
            libra_channel::new(QueueStyle::FIFO, NonZeroUsize::new(8).unwrap(), None);
        let (consensus_tx, consensus_rx) =
            libra_channel::new(QueueStyle::FIFO, NonZeroUsize::new(8).unwrap(), None);
        let (_conn_mgr_reqs_tx, conn_mgr_reqs_rx) = channel::new_test(8);
        let (_, conn_notifs_channel) = conn_notifs_channel::new();
        let network_sender = ConsensusNetworkSender::new(
            PeerManagerRequestSender::new(network_reqs_tx),
            ConnectionRequestSender::new(connection_reqs_tx),
        );
        let network_events = ConsensusNetworkEvents::new(consensus_rx, conn_notifs_channel);

        playground.add_node(twin_id, consensus_tx, network_reqs_rx, conn_mgr_reqs_rx);

        let (state_sync_client, state_sync) = mpsc::unbounded();
        let (commit_cb_sender, commit_cb_receiver) = mpsc::unbounded::<LedgerInfoWithSignatures>();
        let shared_mempool = MockSharedMempool::new(None);
        let consensus_to_mempool_sender = shared_mempool.consensus_sender.clone();
        let state_computer = Arc::new(MockStateComputer::new(
            state_sync_client,
            commit_cb_sender,
            Arc::clone(&storage),
        ));
        let txn_manager = Arc::new(MockTransactionManager::new(Some(
            consensus_to_mempool_sender,
        )));
        let (mut reconfig_sender, reconfig_events) =
            libra_channel::new(QueueStyle::LIFO, NonZeroUsize::new(1).unwrap(), None);
        let mut configs = HashMap::new();
        configs.insert(
            ValidatorSet::CONFIG_ID,
            lcs::to_bytes(storage.get_validator_set()).unwrap(),
        );
        let payload = OnChainConfigPayload::new(1, Arc::new(configs));
        reconfig_sender.push((), payload).unwrap();

        let runtime = Builder::new()
            .thread_name(format!("node-{}", smr_id))
            .threaded_scheduler()
            .enable_all()
            .build()
            .unwrap();

        let time_service = Arc::new(ClockTimeService::new(runtime.handle().clone()));

        let (timeout_sender, timeout_receiver) =
            channel::new(1_024, &counters::PENDING_ROUND_TIMEOUTS);
        let (self_sender, self_receiver) = channel::new(1_024, &counters::PENDING_SELF_MESSAGES);

        let epoch_mgr = EpochManager::new(
            &config,
            time_service,
            self_sender,
            network_sender,
            timeout_sender,
            txn_manager,
            state_computer,
            storage.clone(),
        );
        let (network_task, network_receiver) = NetworkTask::new(network_events, self_receiver);

        runtime.spawn(network_task.start());
        runtime.spawn(epoch_mgr.start(timeout_receiver, network_receiver, reconfig_events));
        Self {
            _runtime: runtime,
            commit_cb_receiver,
            storage,
            _shared_mempool: shared_mempool,
            _state_sync: state_sync,
        }
    }

    /// Starts a given number of nodes and their twins
    #[cfg(any(test, feature = "fuzzing"))]
    fn start_num_nodes_with_twins(
        num_nodes: usize,
        num_twins: usize,
        playground: &mut NetworkPlayground,
        proposer_type: ConsensusProposerType,
    ) -> (Vec<Self>, Vec<Author>) {
        assert!(num_nodes >= num_twins);
        let ValidatorSwarm { mut nodes } = generator::validator_swarm_for_testing(num_nodes);

        let validator_set = ValidatorSet::new(
            nodes
                .iter()
                .map(|config| {
                    let sr_test_config = config.consensus.safety_rules.test.as_ref().unwrap();
                    ValidatorInfo::new_with_test_network_keys(
                        sr_test_config.author,
                        sr_test_config.consensus_key.as_ref().unwrap().public_key(),
                        1,
                    )
                })
                .collect(),
        );

        // We don't add twins to ValidatorSet above because a node with
        // twins should be treated the same at the consensus level
        for i in 0..num_twins {
            let twin = nodes[i].clone();
            nodes.push(twin);
        }

        let mut smr_nodes = vec![];
        let mut node_authors = vec![];

        for (smr_id, mut config) in nodes.into_iter().enumerate() {
            let (_, storage) = MockStorage::start_for_testing(validator_set.clone());

            let waypoint = Waypoint::new_epoch_boundary(&storage.get_ledger_info())
                .expect("Unable to produce waypoint with the provided LedgerInfo");
            config
                .consensus
                .safety_rules
                .test
                .as_mut()
                .unwrap()
                .waypoint = Some(waypoint);
            config.base.waypoint = WaypointConfig::FromConfig(waypoint);
            config.consensus.proposer_type = proposer_type.clone();
            config.consensus.safety_rules.verify_vote_proposal_signature = false;

            let author = config.validator_network.as_ref().unwrap().peer_id();

            let twin_id = TwinId { id: smr_id, author };

            smr_nodes.push(Self::start(playground, config, smr_id, storage, twin_id));
            node_authors.push(author);
        }
        (smr_nodes, node_authors)
    }
}

#[test]
/// This test checks that the first proposal has its parent and
/// QC pointing to the genesis block.
///
/// Setup:
///
/// 4 honest nodes, and 0 twins
///
/// Run the test:
/// cargo xtest -p consensus basic_start_test -- --nocapture
#[test]
fn basic_start_test() {
    let mut runtime = consensus_runtime();
    let mut playground = NetworkPlayground::new(runtime.handle().clone());
    let num_nodes = 4;
    let num_twins = 0;
    let (nodes, _) = SMRNode::start_num_nodes_with_twins(
        num_nodes,
        num_twins,
        &mut playground,
        RotatingProposer,
    );
    let genesis = Block::make_genesis_block_from_ledger_info(&nodes[0].storage.get_ledger_info());
    timed_block_on(&mut runtime, async {
        let msg = playground
            .wait_for_messages(1, NetworkPlayground::proposals_only)
            .await;
        let first_proposal = match &msg[0].1 {
            ConsensusMsg::ProposalMsg(proposal) => proposal,
            _ => panic!("Unexpected message found"),
        };
        assert_eq!(first_proposal.proposal().parent_id(), genesis.id());
        assert_eq!(
            first_proposal
                .proposal()
                .quorum_cert()
                .certified_block()
                .id(),
            genesis.id()
        );
    });
}

/// This test checks that the split_network function works
/// as expected, that is: nodes in a partition with less nodes
/// than required for quorum do not commit anything.
///
/// Setup:
///
/// 4 honest nodes (n0, n1, n2, n3), and 0 twins.
/// Create two partitions p1=[n1], and p2=[n0, n2, n3] with
/// a proposer in p2.
///
/// Test:
///
/// Run consensus for enough rounds to potentially form a commit.
/// Check that n1 has no commits, and n0 has commits.
///
/// Run the test:
/// cargo xtest -p consensus drop_config_test -- --nocapture
#[test]
#[ignore]
/// See https://github.com/libra/libra/issues/4899
fn drop_config_test() {
    let mut runtime = consensus_runtime();
    let mut playground = NetworkPlayground::new(runtime.handle().clone());
    let num_nodes = 4;
    let num_twins = 0;
    let (mut nodes, node_authors) =
        SMRNode::start_num_nodes_with_twins(num_nodes, num_twins, &mut playground, FixedProposer);

    // 4 honest nodes
    let n0_twin_id = *playground.get_twin_ids(node_authors[0]).get(0).unwrap();
    let n1_twin_id = *playground.get_twin_ids(node_authors[1]).get(0).unwrap();
    let n2_twin_id = *playground.get_twin_ids(node_authors[2]).get(0).unwrap();
    let n3_twin_id = *playground.get_twin_ids(node_authors[3]).get(0).unwrap();
    assert!(playground.split_network(vec![n1_twin_id], vec![n0_twin_id, n2_twin_id, n3_twin_id]));

    timed_block_on(&mut runtime, async {
        playground
            .wait_for_messages(1, NetworkPlayground::proposals_only)
            .await;

        // Pull enough votes to get a few commits
        // The proposer's votes are implicit and do not go in the queue.
        playground
            .wait_for_messages(30, NetworkPlayground::votes_only)
            .await;

        // Check that the commit log for n0 is not empty
        let mut commit_seen = false;
        nodes[0].commit_cb_receiver.close();
        if let Ok(Some(_node_commit)) = nodes[0].commit_cb_receiver.try_next() {
            commit_seen = true;
        }
        assert!(commit_seen);

        // Check that the commit log for n1 is empty
        commit_seen = false;
        nodes[1].commit_cb_receiver.close();
        if let Ok(Some(_node_commit)) = nodes[1].commit_cb_receiver.try_next() {
            commit_seen = true;
        }
        assert!(!commit_seen);
    });
}

#[test]
/// This test checks that the vote of a node and its twin
/// should be counted as duplicate vote (because they have
/// the same public keys)
///
/// Setup:
///
/// 4 honest nodes (n0, n1, n2, n3), and 1 twin (twin0)
/// Create 2 partitions, p1=[n1, n3], p2=[n0, twin0, n2]
///
/// Test:
///
/// Extract enough votes to potentially form commits. Check
/// that no node commits any block. This is because we need
/// 3 nodes to form a quorum and no partition has enough votes
/// (note there are 3 nodes in p2, but one of them is a twin,
/// and its vote will be counted as duplicate of n0).
///
/// Run the test:
/// cargo xtest -p consensus twins_vote_dedup_test -- --nocapture
#[cfg(test)]
fn twins_vote_dedup_test() {
    let mut runtime = consensus_runtime();
    let mut playground = NetworkPlayground::new(runtime.handle().clone());
    let num_nodes = 4;
    let num_twins = 1;
    let (mut nodes, node_authors) = SMRNode::start_num_nodes_with_twins(
        num_nodes,
        num_twins,
        &mut playground,
        RotatingProposer,
    );

    // 4 honest nodes
    let n0_twin_id = *playground.get_twin_ids(node_authors[0]).get(0).unwrap();
    // twin of n0 has same author as node_authors[0]
    let twin0_twin_id = *playground.get_twin_ids(node_authors[0]).get(1).unwrap();
    let n1_twin_id = *playground.get_twin_ids(node_authors[1]).get(0).unwrap();
    let n2_twin_id = *playground.get_twin_ids(node_authors[2]).get(0).unwrap();
    let n3_twin_id = *playground.get_twin_ids(node_authors[3]).get(0).unwrap();

    assert!(playground.split_network(
        vec![n1_twin_id, n3_twin_id],
        vec![twin0_twin_id, n0_twin_id, n2_twin_id],
    ));

    timed_block_on(&mut runtime, async {
        playground
            .wait_for_messages(1, NetworkPlayground::proposals_only)
            .await;

        // Pull enough votes to get a few commits.
        // The proposer's votes are implicit and do not go in the queue.
        playground
            .wait_for_messages(30, NetworkPlayground::votes_only)
            .await;

        // No node should be able to commit because of the way partitions
        // have been created
        let mut commit_seen = false;
        for node in &mut nodes {
            node.commit_cb_receiver.close();
            if let Ok(Some(_node_commit)) = node.commit_cb_receiver.try_next() {
                commit_seen = true;
            }
        }
        assert!(!commit_seen);
    });
}
<commit_msg>[consensus] Support RoundProposers in 'start_num_nodes_with_twins'<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters,
    epoch_manager::EpochManager,
    network::NetworkTask,
    network_interface::{ConsensusMsg, ConsensusNetworkEvents, ConsensusNetworkSender},
    network_tests::{NetworkPlayground, TwinId},
    test_utils::{
        consensus_runtime, timed_block_on, MockStateComputer, MockStorage, MockTransactionManager,
    },
    util::time_service::ClockTimeService,
};
use channel::{self, libra_channel, message_queues::QueueStyle};
use consensus_types::{
    block::Block,
    common::{Author, Payload, Round},
};
use futures::channel::mpsc;
use libra_config::{
    config::{
        ConsensusProposerType::{self, FixedProposer, RotatingProposer, RoundProposer},
        NodeConfig, WaypointConfig,
    },
    generator::{self, ValidatorSwarm},
};
use libra_mempool::mocks::MockSharedMempool;
use libra_types::{
    ledger_info::LedgerInfoWithSignatures,
    on_chain_config::{OnChainConfig, OnChainConfigPayload, ValidatorSet},
    validator_info::ValidatorInfo,
    waypoint::Waypoint,
};
use network::{
    peer_manager::{conn_notifs_channel, ConnectionRequestSender, PeerManagerRequestSender},
    protocols::network::{NewNetworkEvents, NewNetworkSender},
};
use std::{collections::HashMap, num::NonZeroUsize, sync::Arc};
use tokio::runtime::{Builder, Runtime};

/// Auxiliary struct that is preparing SMR for the test
struct SMRNode {
    _runtime: Runtime,
    commit_cb_receiver: mpsc::UnboundedReceiver<LedgerInfoWithSignatures>,
    storage: Arc<MockStorage>,
    _shared_mempool: MockSharedMempool,
    _state_sync: mpsc::UnboundedReceiver<Payload>,
}

impl SMRNode {
    fn start(
        playground: &mut NetworkPlayground,
        config: NodeConfig,
        smr_id: usize,
        storage: Arc<MockStorage>,
        twin_id: TwinId,
    ) -> Self {
        let (network_reqs_tx, network_reqs_rx) =
            libra_channel::new(QueueStyle::FIFO, NonZeroUsize::new(8).unwrap(), None);
        let (connection_reqs_tx, _) =
            libra_channel::new(QueueStyle::FIFO, NonZeroUsize::new(8).unwrap(), None);
        let (consensus_tx, consensus_rx) =
            libra_channel::new(QueueStyle::FIFO, NonZeroUsize::new(8).unwrap(), None);
        let (_conn_mgr_reqs_tx, conn_mgr_reqs_rx) = channel::new_test(8);
        let (_, conn_notifs_channel) = conn_notifs_channel::new();
        let network_sender = ConsensusNetworkSender::new(
            PeerManagerRequestSender::new(network_reqs_tx),
            ConnectionRequestSender::new(connection_reqs_tx),
        );
        let network_events = ConsensusNetworkEvents::new(consensus_rx, conn_notifs_channel);

        playground.add_node(twin_id, consensus_tx, network_reqs_rx, conn_mgr_reqs_rx);

        let (state_sync_client, state_sync) = mpsc::unbounded();
        let (commit_cb_sender, commit_cb_receiver) = mpsc::unbounded::<LedgerInfoWithSignatures>();
        let shared_mempool = MockSharedMempool::new(None);
        let consensus_to_mempool_sender = shared_mempool.consensus_sender.clone();
        let state_computer = Arc::new(MockStateComputer::new(
            state_sync_client,
            commit_cb_sender,
            Arc::clone(&storage),
        ));
        let txn_manager = Arc::new(MockTransactionManager::new(Some(
            consensus_to_mempool_sender,
        )));
        let (mut reconfig_sender, reconfig_events) =
            libra_channel::new(QueueStyle::LIFO, NonZeroUsize::new(1).unwrap(), None);
        let mut configs = HashMap::new();
        configs.insert(
            ValidatorSet::CONFIG_ID,
            lcs::to_bytes(storage.get_validator_set()).unwrap(),
        );
        let payload = OnChainConfigPayload::new(1, Arc::new(configs));
        reconfig_sender.push((), payload).unwrap();

        let runtime = Builder::new()
            .thread_name(format!("node-{}", smr_id))
            .threaded_scheduler()
            .enable_all()
            .build()
            .unwrap();

        let time_service = Arc::new(ClockTimeService::new(runtime.handle().clone()));

        let (timeout_sender, timeout_receiver) =
            channel::new(1_024, &counters::PENDING_ROUND_TIMEOUTS);
        let (self_sender, self_receiver) = channel::new(1_024, &counters::PENDING_SELF_MESSAGES);

        let epoch_mgr = EpochManager::new(
            &config,
            time_service,
            self_sender,
            network_sender,
            timeout_sender,
            txn_manager,
            state_computer,
            storage.clone(),
        );
        let (network_task, network_receiver) = NetworkTask::new(network_events, self_receiver);

        runtime.spawn(network_task.start());
        runtime.spawn(epoch_mgr.start(timeout_receiver, network_receiver, reconfig_events));
        Self {
            _runtime: runtime,
            commit_cb_receiver,
            storage,
            _shared_mempool: shared_mempool,
            _state_sync: state_sync,
        }
    }

    /// Starts a given number of nodes and their twins
    #[cfg(any(test, feature = "fuzzing"))]
    fn start_num_nodes_with_twins(
        num_nodes: usize,
        num_twins: usize,
        playground: &mut NetworkPlayground,
        proposer_type: ConsensusProposerType,
        round_proposers_idx: Option<HashMap<Round,usize>>
    ) -> (Vec<Self>, Vec<Author>) {
        assert!(num_nodes >= num_twins);
        let ValidatorSwarm { mut nodes } = generator::validator_swarm_for_testing(num_nodes);

        let validator_set = ValidatorSet::new(
            nodes
                .iter()
                .map(|config| {
                    let sr_test_config = config.consensus.safety_rules.test.as_ref().unwrap();
                    ValidatorInfo::new_with_test_network_keys(
                        sr_test_config.author,
                        sr_test_config.consensus_key.as_ref().unwrap().public_key(),
                        1,
                    )
                })
                .collect(),
        );

        let proposer_type = match proposer_type {
            RoundProposer(_) => {
                let mut round_proposers: HashMap<Round,Author> = HashMap::new();
                if round_proposers_idx.is_some() {
                    for (round,idx) in round_proposers_idx.unwrap().iter() {
                        round_proposers.insert(*round,nodes[*idx].validator_network.as_ref().unwrap().peer_id());
                    }
                }
                RoundProposer(round_proposers)
            },
            _ => proposer_type
        };

        // We don't add twins to ValidatorSet or round_proposers above
        // because a node with twins should be treated the same at the
        // consensus level
        for i in 0..num_twins {
            let twin = nodes[i].clone();
            nodes.push(twin);
        }

        let mut smr_nodes = vec![];
        let mut node_authors = vec![];

        for (smr_id, mut config) in nodes.into_iter().enumerate() {
            let (_, storage) = MockStorage::start_for_testing(validator_set.clone());

            let waypoint = Waypoint::new_epoch_boundary(&storage.get_ledger_info())
                .expect("Unable to produce waypoint with the provided LedgerInfo");
            config
                .consensus
                .safety_rules
                .test
                .as_mut()
                .unwrap()
                .waypoint = Some(waypoint);
            config.base.waypoint = WaypointConfig::FromConfig(waypoint);
            config.consensus.proposer_type = proposer_type.clone();
            config.consensus.safety_rules.verify_vote_proposal_signature = false;

            let author = config.validator_network.as_ref().unwrap().peer_id();

            let twin_id = TwinId { id: smr_id, author };

            smr_nodes.push(Self::start(playground, config, smr_id, storage, twin_id));
            node_authors.push(author);
        }
        (smr_nodes, node_authors)
    }
}

#[test]
/// This test checks that the first proposal has its parent and
/// QC pointing to the genesis block.
///
/// Setup:
///
/// 4 honest nodes, and 0 twins
///
/// Run the test:
/// cargo xtest -p consensus basic_start_test -- --nocapture
#[test]
fn basic_start_test() {
    let mut runtime = consensus_runtime();
    let mut playground = NetworkPlayground::new(runtime.handle().clone());
    let num_nodes = 4;
    let num_twins = 0;
    let (nodes, _) = SMRNode::start_num_nodes_with_twins(
        num_nodes,
        num_twins,
        &mut playground,
        RotatingProposer,
        None
    );
    let genesis = Block::make_genesis_block_from_ledger_info(&nodes[0].storage.get_ledger_info());
    timed_block_on(&mut runtime, async {
        let msg = playground
            .wait_for_messages(1, NetworkPlayground::proposals_only)
            .await;
        let first_proposal = match &msg[0].1 {
            ConsensusMsg::ProposalMsg(proposal) => proposal,
            _ => panic!("Unexpected message found"),
        };
        assert_eq!(first_proposal.proposal().parent_id(), genesis.id());
        assert_eq!(
            first_proposal
                .proposal()
                .quorum_cert()
                .certified_block()
                .id(),
            genesis.id()
        );
    });
}

/// This test checks that the split_network function works
/// as expected, that is: nodes in a partition with less nodes
/// than required for quorum do not commit anything.
///
/// Setup:
///
/// 4 honest nodes (n0, n1, n2, n3), and 0 twins.
/// Create two partitions p1=[n1], and p2=[n0, n2, n3] with
/// a proposer in p2.
///
/// Test:
///
/// Run consensus for enough rounds to potentially form a commit.
/// Check that n1 has no commits, and n0 has commits.
///
/// Run the test:
/// cargo xtest -p consensus drop_config_test -- --nocapture
#[test]
#[ignore]
/// See https://github.com/libra/libra/issues/4899
fn drop_config_test() {
    let mut runtime = consensus_runtime();
    let mut playground = NetworkPlayground::new(runtime.handle().clone());
    let num_nodes = 4;
    let num_twins = 0;
    let (mut nodes, node_authors) =
        SMRNode::start_num_nodes_with_twins(num_nodes, num_twins, &mut playground, FixedProposer, None);

    // 4 honest nodes
    let n0_twin_id = *playground.get_twin_ids(node_authors[0]).get(0).unwrap();
    let n1_twin_id = *playground.get_twin_ids(node_authors[1]).get(0).unwrap();
    let n2_twin_id = *playground.get_twin_ids(node_authors[2]).get(0).unwrap();
    let n3_twin_id = *playground.get_twin_ids(node_authors[3]).get(0).unwrap();
    assert!(playground.split_network(vec![n1_twin_id], vec![n0_twin_id, n2_twin_id, n3_twin_id]));

    timed_block_on(&mut runtime, async {
        playground
            .wait_for_messages(1, NetworkPlayground::proposals_only)
            .await;

        // Pull enough votes to get a few commits
        // The proposer's votes are implicit and do not go in the queue.
        playground
            .wait_for_messages(30, NetworkPlayground::votes_only)
            .await;

        // Check that the commit log for n0 is not empty
        let mut commit_seen = false;
        nodes[0].commit_cb_receiver.close();
        if let Ok(Some(_node_commit)) = nodes[0].commit_cb_receiver.try_next() {
            commit_seen = true;
        }
        assert!(commit_seen);

        // Check that the commit log for n1 is empty
        commit_seen = false;
        nodes[1].commit_cb_receiver.close();
        if let Ok(Some(_node_commit)) = nodes[1].commit_cb_receiver.try_next() {
            commit_seen = true;
        }
        assert!(!commit_seen);
    });
}

#[test]
/// This test checks that the vote of a node and its twin
/// should be counted as duplicate vote (because they have
/// the same public keys)
///
/// Setup:
///
/// 4 honest nodes (n0, n1, n2, n3), and 1 twin (twin0)
/// Create 2 partitions, p1=[n1, n3], p2=[n0, twin0, n2]
///
/// Test:
///
/// Extract enough votes to potentially form commits. Check
/// that no node commits any block. This is because we need
/// 3 nodes to form a quorum and no partition has enough votes
/// (note there are 3 nodes in p2, but one of them is a twin,
/// and its vote will be counted as duplicate of n0).
///
/// Run the test:
/// cargo xtest -p consensus twins_vote_dedup_test -- --nocapture
#[cfg(test)]
fn twins_vote_dedup_test() {
    let mut runtime = consensus_runtime();
    let mut playground = NetworkPlayground::new(runtime.handle().clone());
    let num_nodes = 4;
    let num_twins = 1;
    let (mut nodes, node_authors) = SMRNode::start_num_nodes_with_twins(
        num_nodes,
        num_twins,
        &mut playground,
        RotatingProposer,
        None
    );

    // 4 honest nodes
    let n0_twin_id = *playground.get_twin_ids(node_authors[0]).get(0).unwrap();
    // twin of n0 has same author as node_authors[0]
    let twin0_twin_id = *playground.get_twin_ids(node_authors[0]).get(1).unwrap();
    let n1_twin_id = *playground.get_twin_ids(node_authors[1]).get(0).unwrap();
    let n2_twin_id = *playground.get_twin_ids(node_authors[2]).get(0).unwrap();
    let n3_twin_id = *playground.get_twin_ids(node_authors[3]).get(0).unwrap();

    assert!(playground.split_network(
        vec![n1_twin_id, n3_twin_id],
        vec![twin0_twin_id, n0_twin_id, n2_twin_id],
    ));

    timed_block_on(&mut runtime, async {
        playground
            .wait_for_messages(1, NetworkPlayground::proposals_only)
            .await;

        // Pull enough votes to get a few commits.
        // The proposer's votes are implicit and do not go in the queue.
        playground
            .wait_for_messages(30, NetworkPlayground::votes_only)
            .await;

        // No node should be able to commit because of the way partitions
        // have been created
        let mut commit_seen = false;
        for node in &mut nodes {
            node.commit_cb_receiver.close();
            if let Ok(Some(_node_commit)) = node.commit_cb_receiver.try_next() {
                commit_seen = true;
            }
        }
        assert!(!commit_seen);
    });
}
<|endoftext|>


<commit_before>// Type Names for Debug Info.

use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, subst::SubstsRef, Ty, TyCtxt};

use std::fmt::Write;

// Compute the name of the type as it should be stored in debuginfo. Does not do
// any caching, i.e., calling the function twice with the same type will also do
// the work twice. The `qualified` parameter only affects the first level of the
// type name, further levels (i.e., type parameters) are always fully qualified.
pub fn compute_debuginfo_type_name<'tcx>(
    tcx: TyCtxt<'tcx>,
    t: Ty<'tcx>,
    qualified: bool,
) -> String {
    let mut result = String::with_capacity(64);
    let mut visited = FxHashSet::default();
    push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
    result
}

// Pushes the name of the type as it should be stored in debuginfo on the
// `output` String. See also compute_debuginfo_type_name().
pub fn push_debuginfo_type_name<'tcx>(
    tcx: TyCtxt<'tcx>,
    t: Ty<'tcx>,
    qualified: bool,
    output: &mut String,
    visited: &mut FxHashSet<Ty<'tcx>>,
) {
    // When targeting MSVC, emit C++ style type names for compatibility with
    // .natvis visualizers (and perhaps other existing native debuggers?)
    let cpp_like_names = tcx.sess.target.is_like_msvc;

    match *t.kind() {
        ty::Bool => output.push_str("bool"),
        ty::Char => output.push_str("char"),
        ty::Str => output.push_str("str"),
        ty::Never => output.push('!'),
        ty::Int(int_ty) => output.push_str(int_ty.name_str()),
        ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
        ty::Float(float_ty) => output.push_str(float_ty.name_str()),
        ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
        ty::Adt(def, substs) => {
            push_item_name(tcx, def.did, qualified, output);
            push_type_params(tcx, substs, output, visited);
        }
        ty::Tuple(component_types) => {
            if cpp_like_names {
                output.push_str("tuple<");
            } else {
                output.push('(');
            }

            for component_type in component_types {
                push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited);
                output.push_str(", ");
            }
            if !component_types.is_empty() {
                output.pop();
                output.pop();
            }

            if cpp_like_names {
                output.push('>');
            } else {
                output.push(')');
            }
        }
        ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
            if !cpp_like_names {
                output.push('*');
            }
            match mutbl {
                hir::Mutability::Not => output.push_str("const "),
                hir::Mutability::Mut => output.push_str("mut "),
            }

            push_debuginfo_type_name(tcx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('*');
            }
        }
        ty::Ref(_, inner_type, mutbl) => {
            if !cpp_like_names {
                output.push('&');
            }
            output.push_str(mutbl.prefix_str());

            push_debuginfo_type_name(tcx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('*');
            }
        }
        ty::Array(inner_type, len) => {
            output.push('[');
            push_debuginfo_type_name(tcx, inner_type, true, output, visited);
            output.push_str(&format!("; {}", len.eval_usize(tcx, ty::ParamEnv::reveal_all())));
            output.push(']');
        }
        ty::Slice(inner_type) => {
            if cpp_like_names {
                output.push_str("slice<");
            } else {
                output.push('[');
            }

            push_debuginfo_type_name(tcx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('>');
            } else {
                output.push(']');
            }
        }
        ty::Dynamic(ref trait_data, ..) => {
            if let Some(principal) = trait_data.principal() {
                let principal =
                    tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
                push_item_name(tcx, principal.def_id, false, output);
                push_type_params(tcx, principal.substs, output, visited);
            } else {
                output.push_str("dyn '_");
            }
        }
        ty::FnDef(..) | ty::FnPtr(_) => {
            // We've encountered a weird 'recursive type'
            // Currently, the only way to generate such a type
            // is by using 'impl trait':
            //
            // fn foo() -> impl Copy { foo }
            //
            // There's not really a sensible name we can generate,
            // since we don't include 'impl trait' types (e.g. ty::Opaque)
            // in the output
            //
            // Since we need to generate *something*, we just
            // use a dummy string that should make it clear
            // that something unusual is going on
            if !visited.insert(t) {
                output.push_str("<recursive_type>");
                return;
            }

            let sig = t.fn_sig(tcx);
            output.push_str(sig.unsafety().prefix_str());

            let abi = sig.abi();
            if abi != rustc_target::spec::abi::Abi::Rust {
                output.push_str("extern \"");
                output.push_str(abi.name());
                output.push_str("\" ");
            }

            output.push_str("fn(");

            let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig);
            if !sig.inputs().is_empty() {
                for &parameter_type in sig.inputs() {
                    push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
                    output.push_str(", ");
                }
                output.pop();
                output.pop();
            }

            if sig.c_variadic {
                if !sig.inputs().is_empty() {
                    output.push_str(", ...");
                } else {
                    output.push_str("...");
                }
            }

            output.push(')');

            if !sig.output().is_unit() {
                output.push_str(" -> ");
                push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
            }

            // We only keep the type in 'visited'
            // for the duration of the body of this method.
            // It's fine for a particular function type
            // to show up multiple times in one overall type
            // (e.g. MyType<fn() -> u8, fn() -> u8>
            //
            // We only care about avoiding recursing
            // directly back to the type we're currently
            // processing
            visited.remove(t);
        }
        ty::Closure(def_id, ..) => {
            output.push_str(&format!(
                "closure-{}",
                tcx.def_key(def_id).disambiguated_data.disambiguator
            ));
        }
        ty::Generator(def_id, ..) => {
            output.push_str(&format!(
                "generator-{}",
                tcx.def_key(def_id).disambiguated_data.disambiguator
            ));
        }
        // Type parameters from polymorphized functions.
        ty::Param(_) => {
            output.push_str(&format!("{:?}", t));
        }
        ty::Error(_)
        | ty::Infer(_)
        | ty::Placeholder(..)
        | ty::Projection(..)
        | ty::Bound(..)
        | ty::Opaque(..)
        | ty::GeneratorWitness(..) => {
            bug!(
                "debuginfo: Trying to create type name for \
                  unexpected type: {:?}",
                t
            );
        }
    }

    fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
        if qualified {
            output.push_str(&tcx.crate_name(def_id.krate).as_str());
            for path_element in tcx.def_path(def_id).data {
                write!(output, "::{}", path_element.data).unwrap();
            }
        } else {
            output.push_str(&tcx.item_name(def_id).as_str());
        }
    }

    // Pushes the type parameters in the given `InternalSubsts` to the output string.
    // This ignores region parameters, since they can't reliably be
    // reconstructed for items from non-local crates. For local crates, this
    // would be possible but with inlining and LTO we have to use the least
    // common denominator - otherwise we would run into conflicts.
    fn push_type_params<'tcx>(
        tcx: TyCtxt<'tcx>,
        substs: SubstsRef<'tcx>,
        output: &mut String,
        visited: &mut FxHashSet<Ty<'tcx>>,
    ) {
        if substs.types().next().is_none() {
            return;
        }

        output.push('<');

        for type_parameter in substs.types() {
            push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
            output.push_str(", ");
        }

        output.pop();
        output.pop();

        output.push('>');
    }
}
<commit_msg>Stop adding '*' at the end of type names for Ref and Slice when computing debug info for MSVC debuggers<commit_after>// Type Names for Debug Info.

use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, subst::SubstsRef, Ty, TyCtxt};

use std::fmt::Write;

// Compute the name of the type as it should be stored in debuginfo. Does not do
// any caching, i.e., calling the function twice with the same type will also do
// the work twice. The `qualified` parameter only affects the first level of the
// type name, further levels (i.e., type parameters) are always fully qualified.
pub fn compute_debuginfo_type_name<'tcx>(
    tcx: TyCtxt<'tcx>,
    t: Ty<'tcx>,
    qualified: bool,
) -> String {
    let mut result = String::with_capacity(64);
    let mut visited = FxHashSet::default();
    push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
    result
}

// Pushes the name of the type as it should be stored in debuginfo on the
// `output` String. See also compute_debuginfo_type_name().
pub fn push_debuginfo_type_name<'tcx>(
    tcx: TyCtxt<'tcx>,
    t: Ty<'tcx>,
    qualified: bool,
    output: &mut String,
    visited: &mut FxHashSet<Ty<'tcx>>,
) {
    // When targeting MSVC, emit C++ style type names for compatibility with
    // .natvis visualizers (and perhaps other existing native debuggers?)
    let cpp_like_names = tcx.sess.target.is_like_msvc;

    match *t.kind() {
        ty::Bool => output.push_str("bool"),
        ty::Char => output.push_str("char"),
        ty::Str => output.push_str("str"),
        ty::Never => output.push('!'),
        ty::Int(int_ty) => output.push_str(int_ty.name_str()),
        ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
        ty::Float(float_ty) => output.push_str(float_ty.name_str()),
        ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
        ty::Adt(def, substs) => {
            push_item_name(tcx, def.did, qualified, output);
            push_type_params(tcx, substs, output, visited);
        }
        ty::Tuple(component_types) => {
            if cpp_like_names {
                output.push_str("tuple<");
            } else {
                output.push('(');
            }

            for component_type in component_types {
                push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited);
                output.push_str(", ");
            }
            if !component_types.is_empty() {
                output.pop();
                output.pop();
            }

            if cpp_like_names {
                output.push('>');
            } else {
                output.push(')');
            }
        }
        ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
            if !cpp_like_names {
                output.push('*');
            }
            match mutbl {
                hir::Mutability::Not => output.push_str("const "),
                hir::Mutability::Mut => output.push_str("mut "),
            }

            push_debuginfo_type_name(tcx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('*');
            }
        }
        ty::Ref(_, inner_type, mutbl) => {
            if !cpp_like_names {
                output.push('&');
            }
            output.push_str(mutbl.prefix_str());

            push_debuginfo_type_name(tcx, inner_type, true, output, visited);

            if cpp_like_names {
                // Slices and `&str` are treated like C++ pointers when computing debug
                // info for MSVC debugger. However, adding '*' at the end of these types' names
                // causes the .natvis engine for WinDbg to fail to display their data, so we opt these
                // types out to aid debugging in MSVC.
                match *inner_type.kind() {
                    ty::Slice(_) | ty::Str => {}
                    _ => output.push('*'),
                }
            }
        }
        ty::Array(inner_type, len) => {
            output.push('[');
            push_debuginfo_type_name(tcx, inner_type, true, output, visited);
            output.push_str(&format!("; {}", len.eval_usize(tcx, ty::ParamEnv::reveal_all())));
            output.push(']');
        }
        ty::Slice(inner_type) => {
            if cpp_like_names {
                output.push_str("slice<");
            } else {
                output.push('[');
            }

            push_debuginfo_type_name(tcx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('>');
            } else {
                output.push(']');
            }
        }
        ty::Dynamic(ref trait_data, ..) => {
            if let Some(principal) = trait_data.principal() {
                let principal =
                    tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
                push_item_name(tcx, principal.def_id, false, output);
                push_type_params(tcx, principal.substs, output, visited);
            } else {
                output.push_str("dyn '_");
            }
        }
        ty::FnDef(..) | ty::FnPtr(_) => {
            // We've encountered a weird 'recursive type'
            // Currently, the only way to generate such a type
            // is by using 'impl trait':
            //
            // fn foo() -> impl Copy { foo }
            //
            // There's not really a sensible name we can generate,
            // since we don't include 'impl trait' types (e.g. ty::Opaque)
            // in the output
            //
            // Since we need to generate *something*, we just
            // use a dummy string that should make it clear
            // that something unusual is going on
            if !visited.insert(t) {
                output.push_str("<recursive_type>");
                return;
            }

            let sig = t.fn_sig(tcx);
            output.push_str(sig.unsafety().prefix_str());

            let abi = sig.abi();
            if abi != rustc_target::spec::abi::Abi::Rust {
                output.push_str("extern \"");
                output.push_str(abi.name());
                output.push_str("\" ");
            }

            output.push_str("fn(");

            let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig);
            if !sig.inputs().is_empty() {
                for &parameter_type in sig.inputs() {
                    push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
                    output.push_str(", ");
                }
                output.pop();
                output.pop();
            }

            if sig.c_variadic {
                if !sig.inputs().is_empty() {
                    output.push_str(", ...");
                } else {
                    output.push_str("...");
                }
            }

            output.push(')');

            if !sig.output().is_unit() {
                output.push_str(" -> ");
                push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
            }

            // We only keep the type in 'visited'
            // for the duration of the body of this method.
            // It's fine for a particular function type
            // to show up multiple times in one overall type
            // (e.g. MyType<fn() -> u8, fn() -> u8>
            //
            // We only care about avoiding recursing
            // directly back to the type we're currently
            // processing
            visited.remove(t);
        }
        ty::Closure(def_id, ..) => {
            output.push_str(&format!(
                "closure-{}",
                tcx.def_key(def_id).disambiguated_data.disambiguator
            ));
        }
        ty::Generator(def_id, ..) => {
            output.push_str(&format!(
                "generator-{}",
                tcx.def_key(def_id).disambiguated_data.disambiguator
            ));
        }
        // Type parameters from polymorphized functions.
        ty::Param(_) => {
            output.push_str(&format!("{:?}", t));
        }
        ty::Error(_)
        | ty::Infer(_)
        | ty::Placeholder(..)
        | ty::Projection(..)
        | ty::Bound(..)
        | ty::Opaque(..)
        | ty::GeneratorWitness(..) => {
            bug!(
                "debuginfo: Trying to create type name for \
                  unexpected type: {:?}",
                t
            );
        }
    }

    fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
        if qualified {
            output.push_str(&tcx.crate_name(def_id.krate).as_str());
            for path_element in tcx.def_path(def_id).data {
                write!(output, "::{}", path_element.data).unwrap();
            }
        } else {
            output.push_str(&tcx.item_name(def_id).as_str());
        }
    }

    // Pushes the type parameters in the given `InternalSubsts` to the output string.
    // This ignores region parameters, since they can't reliably be
    // reconstructed for items from non-local crates. For local crates, this
    // would be possible but with inlining and LTO we have to use the least
    // common denominator - otherwise we would run into conflicts.
    fn push_type_params<'tcx>(
        tcx: TyCtxt<'tcx>,
        substs: SubstsRef<'tcx>,
        output: &mut String,
        visited: &mut FxHashSet<Ty<'tcx>>,
    ) {
        if substs.types().next().is_none() {
            return;
        }

        output.push('<');

        for type_parameter in substs.types() {
            push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
            output.push_str(", ");
        }

        output.pop();
        output.pop();

        output.push('>');
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 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 rustc::mir;

use asm;
use builder::Builder;

use super::FunctionCx;
use super::LocalRef;
use super::OperandValue;

impl FunctionCx<'a, 'll, 'tcx> {
    pub fn codegen_statement(&mut self,
                           bx: Builder<'a, 'll, 'tcx>,
                           statement: &mir::Statement<'tcx>)
                           -> Builder<'a, 'll, 'tcx> {
        debug!("codegen_statement(statement={:?})", statement);

        self.set_debug_loc(&bx, statement.source_info);
        match statement.kind {
            mir::StatementKind::Assign(ref place, ref rvalue) => {
                if let mir::Place::Local(index) = *place {
                    match self.locals[index] {
                        LocalRef::Place(cg_dest) => {
                            self.codegen_rvalue(bx, cg_dest, rvalue)
                        }
                        LocalRef::UnsizedPlace(cg_indirect_dest) => {
                            self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
                        }
                        LocalRef::Operand(None) => {
                            let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
                            self.locals[index] = LocalRef::Operand(Some(operand));
                            bx
                        }
                        LocalRef::Operand(Some(op)) => {
                            if !op.layout.is_zst() {
                                span_bug!(statement.source_info.span,
                                          "operand {:?} already assigned",
                                          rvalue);
                            }

                            // If the type is zero-sized, it's already been set here,
                            // but we still need to make sure we codegen the operand
                            self.codegen_rvalue_operand(bx, rvalue).0
                        }
                    }
                } else {
                    let cg_dest = self.codegen_place(&bx, place);
                    self.codegen_rvalue(bx, cg_dest, rvalue)
                }
            }
            mir::StatementKind::SetDiscriminant{ref place, variant_index} => {
                self.codegen_place(&bx, place)
                    .codegen_set_discr(&bx, variant_index);
                bx
            }
            mir::StatementKind::StorageLive(local) => {
                if let LocalRef::Place(cg_place) = self.locals[local] {
                    cg_place.storage_live(&bx);
                } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
                    cg_indirect_place.storage_live(&bx);
                }
                bx
            }
            mir::StatementKind::StorageDead(local) => {
                if let LocalRef::Place(cg_place) = self.locals[local] {
                    cg_place.storage_dead(&bx);
                } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
                    cg_indirect_place.storage_dead(&bx);
                }
                bx
            }
            mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
                let outputs = outputs.iter().map(|output| {
                    self.codegen_place(&bx, output)
                }).collect();

                let input_vals = inputs.iter()
                    .try_fold(Vec::with_capacity(inputs.len()), |mut acc, (span, input)| {
                        let op = self.codegen_operand(&bx, input);
                        if let OperandValue::Immediate(_) = op.val {
                            acc.push(op.immediate());
                            Ok(acc)
                        } else {
                            Err(span)
                        }
                });

                if let Err(span) = input_vals {
                    span_err!(bx.sess(), span.to_owned(), E0669,
                             "invalid value for constraint in inline assembly");
                } else {
                    let input_vals = input_vals.unwrap();
                    let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
                    if !res {
                        span_err!(bx.sess(), statement.source_info.span, E0668,
                                  "malformed inline assembly");
                    }
                }
                bx
            }
            mir::StatementKind::FakeRead(..) |
            mir::StatementKind::EndRegion(_) |
            mir::StatementKind::Validate(..) |
            mir::StatementKind::AscribeUserType(..) |
            mir::StatementKind::Nop => bx,
        }
    }
}
<commit_msg>inline-asm: emit as many E0669 errors as we can<commit_after>// Copyright 2012-2014 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 rustc::mir;

use asm;
use builder::Builder;

use super::FunctionCx;
use super::LocalRef;
use super::OperandValue;

impl FunctionCx<'a, 'll, 'tcx> {
    pub fn codegen_statement(&mut self,
                           bx: Builder<'a, 'll, 'tcx>,
                           statement: &mir::Statement<'tcx>)
                           -> Builder<'a, 'll, 'tcx> {
        debug!("codegen_statement(statement={:?})", statement);

        self.set_debug_loc(&bx, statement.source_info);
        match statement.kind {
            mir::StatementKind::Assign(ref place, ref rvalue) => {
                if let mir::Place::Local(index) = *place {
                    match self.locals[index] {
                        LocalRef::Place(cg_dest) => {
                            self.codegen_rvalue(bx, cg_dest, rvalue)
                        }
                        LocalRef::UnsizedPlace(cg_indirect_dest) => {
                            self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
                        }
                        LocalRef::Operand(None) => {
                            let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
                            self.locals[index] = LocalRef::Operand(Some(operand));
                            bx
                        }
                        LocalRef::Operand(Some(op)) => {
                            if !op.layout.is_zst() {
                                span_bug!(statement.source_info.span,
                                          "operand {:?} already assigned",
                                          rvalue);
                            }

                            // If the type is zero-sized, it's already been set here,
                            // but we still need to make sure we codegen the operand
                            self.codegen_rvalue_operand(bx, rvalue).0
                        }
                    }
                } else {
                    let cg_dest = self.codegen_place(&bx, place);
                    self.codegen_rvalue(bx, cg_dest, rvalue)
                }
            }
            mir::StatementKind::SetDiscriminant{ref place, variant_index} => {
                self.codegen_place(&bx, place)
                    .codegen_set_discr(&bx, variant_index);
                bx
            }
            mir::StatementKind::StorageLive(local) => {
                if let LocalRef::Place(cg_place) = self.locals[local] {
                    cg_place.storage_live(&bx);
                } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
                    cg_indirect_place.storage_live(&bx);
                }
                bx
            }
            mir::StatementKind::StorageDead(local) => {
                if let LocalRef::Place(cg_place) = self.locals[local] {
                    cg_place.storage_dead(&bx);
                } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
                    cg_indirect_place.storage_dead(&bx);
                }
                bx
            }
            mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
                let outputs = outputs.iter().map(|output| {
                    self.codegen_place(&bx, output)
                }).collect();

                let input_vals = inputs.iter()
                    .fold(Vec::with_capacity(inputs.len()), |mut acc, (span, input)| {
                        let op = self.codegen_operand(&bx, input);
                        if let OperandValue::Immediate(_) = op.val {
                            acc.push(op.immediate());
                        } else {
                            span_err!(bx.sess(), span.to_owned(), E0669,
                                     "invalid value for constraint in inline assembly");
                        }
                        acc
                });

                if input_vals.len() == inputs.len() {
                    let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
                    if !res {
                        span_err!(bx.sess(), statement.source_info.span, E0668,
                                  "malformed inline assembly");
                    }
                }
                bx
            }
            mir::StatementKind::FakeRead(..) |
            mir::StatementKind::EndRegion(_) |
            mir::StatementKind::Validate(..) |
            mir::StatementKind::AscribeUserType(..) |
            mir::StatementKind::Nop => bx,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2017 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 borrow_check::borrow_set::{BorrowSet, BorrowData};
use borrow_check::place_ext::PlaceExt;

use rustc;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::region;
use rustc::mir::{self, Location, Place, Mir};
use rustc::ty::TyCtxt;
use rustc::ty::RegionKind;
use rustc::ty::RegionKind::ReScope;

use rustc_data_structures::bitslice::BitwiseOperator;
use rustc_data_structures::indexed_set::IdxSet;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::sync::Lrc;

use dataflow::{BitDenotation, BlockSets, InitialFlow};
pub use dataflow::indexes::BorrowIndex;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;

use std::rc::Rc;

/// `Borrows` stores the data used in the analyses that track the flow
/// of borrows.
///
/// It uniquely identifies every borrow (`Rvalue::Ref`) by a
/// `BorrowIndex`, and maps each such index to a `BorrowData`
/// describing the borrow. These indexes are used for representing the
/// borrows in compact bitvectors.
pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
    tcx: TyCtxt<'a, 'gcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    scope_tree: Lrc<region::ScopeTree>,
    root_scope: Option<region::Scope>,

    borrow_set: Rc<BorrowSet<'tcx>>,

    /// NLL region inference context with which NLL queries should be resolved
    nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
}

impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
    crate fn new(
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        mir: &'a Mir<'tcx>,
        nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
        def_id: DefId,
        body_id: Option<hir::BodyId>,
        borrow_set: &Rc<BorrowSet<'tcx>>
    ) -> Self {
        let scope_tree = tcx.region_scope_tree(def_id);
        let root_scope = body_id.map(|body_id| {
            region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
        });

        Borrows {
            tcx: tcx,
            mir: mir,
            borrow_set: borrow_set.clone(),
            scope_tree,
            root_scope,
            nonlexical_regioncx,
        }
    }

    crate fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrow_set.borrows }

    pub fn scope_tree(&self) -> &Lrc<region::ScopeTree> { &self.scope_tree }

    pub fn location(&self, idx: BorrowIndex) -> &Location {
        &self.borrow_set.borrows[idx].reserve_location
    }

    /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
    /// That means either they went out of either a nonlexical scope, if we care about those
    /// at the moment, or the location represents a lexical EndRegion
    fn kill_loans_out_of_scope_at_location(&self,
                                           sets: &mut BlockSets<BorrowIndex>,
                                           location: Location) {
        let regioncx = &self.nonlexical_regioncx;

        // NOTE: The state associated with a given `location`
        // reflects the dataflow on entry to the statement. If it
        // does not contain `borrow_region`, then then that means
        // that the statement at `location` kills the borrow.
        //
        // We are careful always to call this function *before* we
        // set up the gen-bits for the statement or
        // termanator. That way, if the effect of the statement or
        // terminator *does* introduce a new loan of the same
        // region, then setting that gen-bit will override any
        // potential kill introduced here.
        for (borrow_index, borrow_data) in self.borrow_set.borrows.iter_enumerated() {
            let borrow_region = borrow_data.region.to_region_vid();
            if !regioncx.region_contains_point(borrow_region, location) {
                sets.kill(&borrow_index);
            }
        }
    }

    fn kill_borrows_on_local(&self,
                             sets: &mut BlockSets<BorrowIndex>,
                             local: &rustc::mir::Local)
    {
        if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
            sets.kill_all(borrow_indexes);
        }
    }
}

impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
    type Idx = BorrowIndex;
    fn name() -> &'static str { "borrows" }
    fn bits_per_block(&self) -> usize {
        self.borrow_set.borrows.len() * 2
    }

    fn start_block_effect(&self, _entry_set: &mut IdxSet<BorrowIndex>) {
        // no borrows of code region_scopes have been taken prior to
        // function execution, so this method has no effect on
        // `_sets`.
    }

    fn before_statement_effect(&self,
                               sets: &mut BlockSets<BorrowIndex>,
                               location: Location) {
        debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn statement_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });
        let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
            panic!("could not find statement at location {:?}");
        });

        self.kill_loans_out_of_scope_at_location(sets, location);

        match stmt.kind {
            mir::StatementKind::EndRegion(_) => {
            }

            mir::StatementKind::Assign(ref lhs, ref rhs) => {
                // Make sure there are no remaining borrows for variables
                // that are assigned over.
                if let Place::Local(ref local) = *lhs {
                    // FIXME: Handle the case in which we're assigning over
                    // a projection (`foo.bar`).
                    self.kill_borrows_on_local(sets, local);
                }

                // NOTE: if/when the Assign case is revised to inspect
                // the assigned_place here, make sure to also
                // re-consider the current implementations of the
                // propagate_call_return method.

                if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
                    if place.is_unsafe_place(self.tcx, self.mir) { return; }
                    let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
                        panic!("could not find BorrowIndex for location {:?}", location);
                    });

                    if let RegionKind::ReEmpty = region {
                        // If the borrowed value dies before the borrow is used, the region for
                        // the borrow can be empty. Don't track the borrow in that case.
                        debug!("Borrows::statement_effect_on_borrows \
                                location: {:?} stmt: {:?} has empty region, killing {:?}",
                               location, stmt.kind, index);
                        sets.kill(&index);
                        return
                    } else {
                        debug!("Borrows::statement_effect_on_borrows location: {:?} stmt: {:?}",
                               location, stmt.kind);
                    }

                    assert!(self.borrow_set.region_map.get(region).unwrap_or_else(|| {
                        panic!("could not find BorrowIndexs for region {:?}", region);
                    }).contains(&index));
                    sets.gen(&index);

                    // Issue #46746: Two-phase borrows handles
                    // stmts of form `Tmp = &mut Borrow` ...
                    match lhs {
                        Place::Local(..) | Place::Static(..) => {} // okay
                        Place::Projection(..) => {
                            // ... can assign into projections,
                            // e.g. `box (&mut _)`. Current
                            // conservative solution: force
                            // immediate activation here.
                            sets.gen(&index);
                        }
                    }
                }
            }

            mir::StatementKind::StorageDead(local) => {
                // Make sure there are no remaining borrows for locals that
                // are gone out of scope.
                self.kill_borrows_on_local(sets, &local)
            }

            mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
                for (output, kind) in outputs.iter().zip(&asm.outputs) {
                    if !kind.is_indirect && !kind.is_rw {
                        // Make sure there are no remaining borrows for direct
                        // output variables.
                        if let Place::Local(ref local) = *output {
                            // FIXME: Handle the case in which we're assigning over
                            // a projection (`foo.bar`).
                            self.kill_borrows_on_local(sets, local);
                        }
                    }
                }
            }

            mir::StatementKind::SetDiscriminant { .. } |
            mir::StatementKind::StorageLive(..) |
            mir::StatementKind::Validate(..) |
            mir::StatementKind::UserAssertTy(..) |
            mir::StatementKind::Nop => {}

        }
    }

    fn before_terminator_effect(&self,
                                sets: &mut BlockSets<BorrowIndex>,
                                location: Location) {
        debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn terminator_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::terminator_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });

        let term = block.terminator();
        self.kill_loans_out_of_scope_at_location(sets, location);

        match term.kind {
            mir::TerminatorKind::Resume |
            mir::TerminatorKind::Return |
            mir::TerminatorKind::GeneratorDrop => {
                // When we return from the function, then all `ReScope`-style regions
                // are guaranteed to have ended.
                // Normally, there would be `EndRegion` statements that come before,
                // and hence most of these loans will already be dead -- but, in some cases
                // like unwind paths, we do not always emit `EndRegion` statements, so we
                // add some kills here as a "backup" and to avoid spurious error messages.
                for (borrow_index, borrow_data) in self.borrow_set.borrows.iter_enumerated() {
                    if let ReScope(scope) = borrow_data.region {
                        // Check that the scope is not actually a scope from a function that is
                        // a parent of our closure. Note that the CallSite scope itself is
                        // *outside* of the closure, for some weird reason.
                        if let Some(root_scope) = self.root_scope {
                            if *scope != root_scope &&
                                self.scope_tree.is_subscope_of(*scope, root_scope)
                            {
                                sets.kill(&borrow_index);
                            }
                        }
                    }
                }
            }
            mir::TerminatorKind::Abort |
            mir::TerminatorKind::SwitchInt {..} |
            mir::TerminatorKind::Drop {..} |
            mir::TerminatorKind::DropAndReplace {..} |
            mir::TerminatorKind::Call {..} |
            mir::TerminatorKind::Assert {..} |
            mir::TerminatorKind::Yield {..} |
            mir::TerminatorKind::Goto {..} |
            mir::TerminatorKind::FalseEdges {..} |
            mir::TerminatorKind::FalseUnwind {..} |
            mir::TerminatorKind::Unreachable => {}
        }
    }

    fn propagate_call_return(&self,
                             _in_out: &mut IdxSet<BorrowIndex>,
                             _call_bb: mir::BasicBlock,
                             _dest_bb: mir::BasicBlock,
                             _dest_place: &mir::Place) {
        // there are no effects on borrows from method call return...
        //
        // ... but if overwriting a place can affect flow state, then
        // latter is not true; see NOTE on Assign case in
        // statement_effect_on_borrows.
    }
}

impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn join(&self, pred1: usize, pred2: usize) -> usize {
        pred1 | pred2 // union effects of preds when computing reservations
    }
}

impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn bottom_value() -> bool {
        false // bottom = nothing is reserved or activated yet
    }
}

<commit_msg>Remove extra calls to kill_loans_out_of_scope_at_location - keep only before_statement_effect and before_terminator_effect.<commit_after>// Copyright 2012-2017 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 borrow_check::borrow_set::{BorrowSet, BorrowData};
use borrow_check::place_ext::PlaceExt;

use rustc;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::region;
use rustc::mir::{self, Location, Place, Mir};
use rustc::ty::TyCtxt;
use rustc::ty::RegionKind;
use rustc::ty::RegionKind::ReScope;

use rustc_data_structures::bitslice::BitwiseOperator;
use rustc_data_structures::indexed_set::IdxSet;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::sync::Lrc;

use dataflow::{BitDenotation, BlockSets, InitialFlow};
pub use dataflow::indexes::BorrowIndex;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;

use std::rc::Rc;

/// `Borrows` stores the data used in the analyses that track the flow
/// of borrows.
///
/// It uniquely identifies every borrow (`Rvalue::Ref`) by a
/// `BorrowIndex`, and maps each such index to a `BorrowData`
/// describing the borrow. These indexes are used for representing the
/// borrows in compact bitvectors.
pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
    tcx: TyCtxt<'a, 'gcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    scope_tree: Lrc<region::ScopeTree>,
    root_scope: Option<region::Scope>,

    borrow_set: Rc<BorrowSet<'tcx>>,

    /// NLL region inference context with which NLL queries should be resolved
    nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
}

impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
    crate fn new(
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        mir: &'a Mir<'tcx>,
        nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
        def_id: DefId,
        body_id: Option<hir::BodyId>,
        borrow_set: &Rc<BorrowSet<'tcx>>
    ) -> Self {
        let scope_tree = tcx.region_scope_tree(def_id);
        let root_scope = body_id.map(|body_id| {
            region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
        });

        Borrows {
            tcx: tcx,
            mir: mir,
            borrow_set: borrow_set.clone(),
            scope_tree,
            root_scope,
            nonlexical_regioncx,
        }
    }

    crate fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrow_set.borrows }

    pub fn scope_tree(&self) -> &Lrc<region::ScopeTree> { &self.scope_tree }

    pub fn location(&self, idx: BorrowIndex) -> &Location {
        &self.borrow_set.borrows[idx].reserve_location
    }

    /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
    /// That means either they went out of either a nonlexical scope, if we care about those
    /// at the moment, or the location represents a lexical EndRegion
    fn kill_loans_out_of_scope_at_location(&self,
                                           sets: &mut BlockSets<BorrowIndex>,
                                           location: Location) {
        let regioncx = &self.nonlexical_regioncx;

        // NOTE: The state associated with a given `location`
        // reflects the dataflow on entry to the statement. If it
        // does not contain `borrow_region`, then then that means
        // that the statement at `location` kills the borrow.
        //
        // We are careful always to call this function *before* we
        // set up the gen-bits for the statement or
        // termanator. That way, if the effect of the statement or
        // terminator *does* introduce a new loan of the same
        // region, then setting that gen-bit will override any
        // potential kill introduced here.
        for (borrow_index, borrow_data) in self.borrow_set.borrows.iter_enumerated() {
            let borrow_region = borrow_data.region.to_region_vid();
            if !regioncx.region_contains_point(borrow_region, location) {
                sets.kill(&borrow_index);
            }
        }
    }

    fn kill_borrows_on_local(&self,
                             sets: &mut BlockSets<BorrowIndex>,
                             local: &rustc::mir::Local)
    {
        if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
            sets.kill_all(borrow_indexes);
        }
    }
}

impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
    type Idx = BorrowIndex;
    fn name() -> &'static str { "borrows" }
    fn bits_per_block(&self) -> usize {
        self.borrow_set.borrows.len() * 2
    }

    fn start_block_effect(&self, _entry_set: &mut IdxSet<BorrowIndex>) {
        // no borrows of code region_scopes have been taken prior to
        // function execution, so this method has no effect on
        // `_sets`.
    }

    fn before_statement_effect(&self,
                               sets: &mut BlockSets<BorrowIndex>,
                               location: Location) {
        debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn statement_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });
        let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
            panic!("could not find statement at location {:?}");
        });

        match stmt.kind {
            mir::StatementKind::EndRegion(_) => {
            }

            mir::StatementKind::Assign(ref lhs, ref rhs) => {
                // Make sure there are no remaining borrows for variables
                // that are assigned over.
                if let Place::Local(ref local) = *lhs {
                    // FIXME: Handle the case in which we're assigning over
                    // a projection (`foo.bar`).
                    self.kill_borrows_on_local(sets, local);
                }

                // NOTE: if/when the Assign case is revised to inspect
                // the assigned_place here, make sure to also
                // re-consider the current implementations of the
                // propagate_call_return method.

                if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
                    if place.is_unsafe_place(self.tcx, self.mir) { return; }
                    let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
                        panic!("could not find BorrowIndex for location {:?}", location);
                    });

                    if let RegionKind::ReEmpty = region {
                        // If the borrowed value dies before the borrow is used, the region for
                        // the borrow can be empty. Don't track the borrow in that case.
                        debug!("Borrows::statement_effect_on_borrows \
                                location: {:?} stmt: {:?} has empty region, killing {:?}",
                               location, stmt.kind, index);
                        sets.kill(&index);
                        return
                    } else {
                        debug!("Borrows::statement_effect_on_borrows location: {:?} stmt: {:?}",
                               location, stmt.kind);
                    }

                    assert!(self.borrow_set.region_map.get(region).unwrap_or_else(|| {
                        panic!("could not find BorrowIndexs for region {:?}", region);
                    }).contains(&index));
                    sets.gen(&index);

                    // Issue #46746: Two-phase borrows handles
                    // stmts of form `Tmp = &mut Borrow` ...
                    match lhs {
                        Place::Local(..) | Place::Static(..) => {} // okay
                        Place::Projection(..) => {
                            // ... can assign into projections,
                            // e.g. `box (&mut _)`. Current
                            // conservative solution: force
                            // immediate activation here.
                            sets.gen(&index);
                        }
                    }
                }
            }

            mir::StatementKind::StorageDead(local) => {
                // Make sure there are no remaining borrows for locals that
                // are gone out of scope.
                self.kill_borrows_on_local(sets, &local)
            }

            mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
                for (output, kind) in outputs.iter().zip(&asm.outputs) {
                    if !kind.is_indirect && !kind.is_rw {
                        // Make sure there are no remaining borrows for direct
                        // output variables.
                        if let Place::Local(ref local) = *output {
                            // FIXME: Handle the case in which we're assigning over
                            // a projection (`foo.bar`).
                            self.kill_borrows_on_local(sets, local);
                        }
                    }
                }
            }

            mir::StatementKind::SetDiscriminant { .. } |
            mir::StatementKind::StorageLive(..) |
            mir::StatementKind::Validate(..) |
            mir::StatementKind::UserAssertTy(..) |
            mir::StatementKind::Nop => {}

        }
    }

    fn before_terminator_effect(&self,
                                sets: &mut BlockSets<BorrowIndex>,
                                location: Location) {
        debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn terminator_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::terminator_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });

        let term = block.terminator();
        match term.kind {
            mir::TerminatorKind::Resume |
            mir::TerminatorKind::Return |
            mir::TerminatorKind::GeneratorDrop => {
                // When we return from the function, then all `ReScope`-style regions
                // are guaranteed to have ended.
                // Normally, there would be `EndRegion` statements that come before,
                // and hence most of these loans will already be dead -- but, in some cases
                // like unwind paths, we do not always emit `EndRegion` statements, so we
                // add some kills here as a "backup" and to avoid spurious error messages.
                for (borrow_index, borrow_data) in self.borrow_set.borrows.iter_enumerated() {
                    if let ReScope(scope) = borrow_data.region {
                        // Check that the scope is not actually a scope from a function that is
                        // a parent of our closure. Note that the CallSite scope itself is
                        // *outside* of the closure, for some weird reason.
                        if let Some(root_scope) = self.root_scope {
                            if *scope != root_scope &&
                                self.scope_tree.is_subscope_of(*scope, root_scope)
                            {
                                sets.kill(&borrow_index);
                            }
                        }
                    }
                }
            }
            mir::TerminatorKind::Abort |
            mir::TerminatorKind::SwitchInt {..} |
            mir::TerminatorKind::Drop {..} |
            mir::TerminatorKind::DropAndReplace {..} |
            mir::TerminatorKind::Call {..} |
            mir::TerminatorKind::Assert {..} |
            mir::TerminatorKind::Yield {..} |
            mir::TerminatorKind::Goto {..} |
            mir::TerminatorKind::FalseEdges {..} |
            mir::TerminatorKind::FalseUnwind {..} |
            mir::TerminatorKind::Unreachable => {}
        }
    }

    fn propagate_call_return(&self,
                             _in_out: &mut IdxSet<BorrowIndex>,
                             _call_bb: mir::BasicBlock,
                             _dest_bb: mir::BasicBlock,
                             _dest_place: &mir::Place) {
        // there are no effects on borrows from method call return...
        //
        // ... but if overwriting a place can affect flow state, then
        // latter is not true; see NOTE on Assign case in
        // statement_effect_on_borrows.
    }
}

impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn join(&self, pred1: usize, pred2: usize) -> usize {
        pred1 | pred2 // union effects of preds when computing reservations
    }
}

impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn bottom_value() -> bool {
        false // bottom = nothing is reserved or activated yet
    }
}

<|endoftext|>