<commit_before>extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate serde_json;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate task_hookrs;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagtodo;

use std::process::exit;
use std::process::{Command, Stdio};
use std::io::stdin;
use std::io::BufRead;

use task_hookrs::import::{import_task, import_tasks};

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagtodo::task::IntoTask;
use libimagtodo::task::Task;
use libimagerror::trace::trace_error;

mod ui;

use ui::build_ui;
fn main() {
    let rt = generate_runtime_setup("imag-todo",
                                    &version!()[..],
                                    "Interface with taskwarrior",
                                    build_ui);

    match rt.cli().subcommand_name() {
        Some("tw-hook") => tw_hook(&rt),
        Some("list") => list(&rt),
        _ => unreachable!(),
    } // end match scmd
} // end main

fn tw_hook(rt: &Runtime) {
    let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap();
    if subcmd.is_present("add") {
        let stdin     = stdin();
        let mut stdin = stdin.lock();

        match Task::import(rt.store(), stdin) {
            Ok((_, uuid)) => info!("Task {} stored in imag", uuid),
            Err(e) => {
                trace_error(&e);
                exit(1);
            }
        }
    } else if subcmd.is_present("delete") {
        // The used hook is "on-modify". This hook gives two json-objects
        // per usage und wants one (the second one) back.
        let mut counter   = 0;
        let stdin         = stdin();
        let stdin         = stdin.lock();

        match import_tasks(stdin) {
            Ok(ttasks) => for ttask in ttasks {
                if counter % 2 == 1 {
                    // Only every second task is needed, the first one is the
                    // task before the change, and the second one after
                    // the change. The (maybe modified) second one is
                    // expected by taskwarrior.
                    match serde_json::ser::to_string(&ttask) {
                        Ok(val) => println!("{}", val),
                        Err(e) => {
                            trace_error(&e);
                            exit(1);
                        }
                    }

                    match ttask.status() {
                        &task_hookrs::status::TaskStatus::Deleted => {
                            match Task::delete_by_uuid(rt.store(), *ttask.uuid()) {
                                Ok(_) => println!("Deleted task {}", *ttask.uuid()),
                                Err(e) => {
                                    trace_error(&e);
                                    exit(1);
                                }
                            }
                        }
                        _ => {
                        }
                    } // end match ttask.status()
                } // end if c % 2
                counter += 1;
            },
            Err(e) => {
                trace_error(&e);
                exit(1);
            },
        }
    } else {
        // Should not be possible, as one argument is required via
        // ArgGroup
        unreachable!();
    }
}

fn list(rt: &Runtime) {
    let subcmd   = rt.cli().subcommand_matches("list").unwrap();
    let mut args = Vec::new();
    let verbose  = subcmd.is_present("verbose");
    let iter     = match Task::all(rt.store()) {
        Ok(iter) => iter,
        Err(e)   => {
            trace_error(&e);
            exit(1);
        },
    };

    for task in iter {
        match task {
            Ok(val) => {
                let uuid = match val.get_header().read("todo.uuid") {
                    Ok(Some(u)) => u,
                    Ok(None)    => continue,
                    Err(e)      => {
                        trace_error(&e);
                        continue;
                    }
                };

                if verbose {
                    args.clear();
                    args.push(format!("uuid:{} information", uuid));

                    let tw_process = Command::new("task")
                        .stdin(Stdio::null())
                        .args(&args)
                        .spawn()
                        .unwrap_or_else(|e| {
                            trace_error(&e);
                            panic!("failed");
                        });
                    let output = tw_process
                        .wait_with_output()
                        .unwrap_or_else(|e| panic!("failed to unwrap output: {}", e));
                    let outstring = String::from_utf8(output.stdout)
                        .unwrap_or_else(|e| panic!("failed to execute: {}", e));

                    println!("{}", outstring);
                } else {
                    println!("{}", match uuid {
                        toml::Value::String(s) => s,
                        _ => {
                            error!("Unexpected type for todo.uuid: {}", uuid);
                            continue;
                        },
                    });
                }
            }
            Err(e) => {
                trace_error(&e);
                continue;
            }
        } // end match task
    } // end for
}

<commit_msg>Use Iterator::enumerate() instead of mut variable<commit_after>extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate serde_json;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate task_hookrs;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagtodo;

use std::process::exit;
use std::process::{Command, Stdio};
use std::io::stdin;
use std::io::BufRead;

use task_hookrs::import::{import_task, import_tasks};

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagtodo::task::IntoTask;
use libimagtodo::task::Task;
use libimagerror::trace::trace_error;

mod ui;

use ui::build_ui;
fn main() {
    let rt = generate_runtime_setup("imag-todo",
                                    &version!()[..],
                                    "Interface with taskwarrior",
                                    build_ui);

    match rt.cli().subcommand_name() {
        Some("tw-hook") => tw_hook(&rt),
        Some("list") => list(&rt),
        _ => unreachable!(),
    } // end match scmd
} // end main

fn tw_hook(rt: &Runtime) {
    let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap();
    if subcmd.is_present("add") {
        let stdin     = stdin();
        let mut stdin = stdin.lock();

        match Task::import(rt.store(), stdin) {
            Ok((_, uuid)) => info!("Task {} stored in imag", uuid),
            Err(e) => {
                trace_error(&e);
                exit(1);
            }
        }
    } else if subcmd.is_present("delete") {
        // The used hook is "on-modify". This hook gives two json-objects
        // per usage und wants one (the second one) back.
        let stdin         = stdin();
        let stdin         = stdin.lock();

        match import_tasks(stdin) {
            Ok(ttasks) => for (counter, ttask) in ttasks.enumerate() {
                if counter % 2 == 1 {
                    // Only every second task is needed, the first one is the
                    // task before the change, and the second one after
                    // the change. The (maybe modified) second one is
                    // expected by taskwarrior.
                    match serde_json::ser::to_string(&ttask) {
                        Ok(val) => println!("{}", val),
                        Err(e) => {
                            trace_error(&e);
                            exit(1);
                        }
                    }

                    match ttask.status() {
                        &task_hookrs::status::TaskStatus::Deleted => {
                            match Task::delete_by_uuid(rt.store(), *ttask.uuid()) {
                                Ok(_) => println!("Deleted task {}", *ttask.uuid()),
                                Err(e) => {
                                    trace_error(&e);
                                    exit(1);
                                }
                            }
                        }
                        _ => {
                        }
                    } // end match ttask.status()
                } // end if c % 2
            },
            Err(e) => {
                trace_error(&e);
                exit(1);
            },
        }
    } else {
        // Should not be possible, as one argument is required via
        // ArgGroup
        unreachable!();
    }
}

fn list(rt: &Runtime) {
    let subcmd   = rt.cli().subcommand_matches("list").unwrap();
    let mut args = Vec::new();
    let verbose  = subcmd.is_present("verbose");
    let iter     = match Task::all(rt.store()) {
        Ok(iter) => iter,
        Err(e)   => {
            trace_error(&e);
            exit(1);
        },
    };

    for task in iter {
        match task {
            Ok(val) => {
                let uuid = match val.get_header().read("todo.uuid") {
                    Ok(Some(u)) => u,
                    Ok(None)    => continue,
                    Err(e)      => {
                        trace_error(&e);
                        continue;
                    }
                };

                if verbose {
                    args.clear();
                    args.push(format!("uuid:{} information", uuid));

                    let tw_process = Command::new("task")
                        .stdin(Stdio::null())
                        .args(&args)
                        .spawn()
                        .unwrap_or_else(|e| {
                            trace_error(&e);
                            panic!("failed");
                        });
                    let output = tw_process
                        .wait_with_output()
                        .unwrap_or_else(|e| panic!("failed to unwrap output: {}", e));
                    let outstring = String::from_utf8(output.stdout)
                        .unwrap_or_else(|e| panic!("failed to execute: {}", e));

                    println!("{}", outstring);
                } else {
                    println!("{}", match uuid {
                        toml::Value::String(s) => s,
                        _ => {
                            error!("Unexpected type for todo.uuid: {}", uuid);
                            continue;
                        },
                    });
                }
            }
            Err(e) => {
                trace_error(&e);
                continue;
            }
        } // end match task
    } // end for
}

<|endoftext|>


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

use crate::{
    AdvertisedData, DataClientPayload, DataClientResponse, DiemDataClient, Error,
    GlobalDataSummary, OptimalChunkSizes, ResponseError,
};
use async_trait::async_trait;
use diem_config::network_id::PeerNetworkId;
use diem_infallible::RwLock;
use diem_time_service::{TimeService, TimeServiceTrait};
use futures::StreamExt;
use network::{application::interface::NetworkInterface, protocols::rpc::error::RpcError};
use rand::seq::SliceRandom;
use std::{
    collections::HashMap,
    convert::TryInto,
    sync::{
        atomic::{AtomicU64, Ordering},
        Arc,
    },
    time::Duration,
};
use storage_service_client::StorageServiceClient;
use storage_service_types::{
    AccountStatesChunkWithProofRequest, EpochEndingLedgerInfoRequest, StorageServerSummary,
    StorageServiceRequest, StorageServiceResponse, TransactionOutputsWithProofRequest,
    TransactionsWithProofRequest,
};

// TODO(philiphayes): does this belong in a different crate? I feel like we're
// accumulating a lot of tiny crates though...

// TODO(philiphayes): configuration / pass as argument?
const DEFAULT_TIMEOUT: Duration = Duration::from_millis(10_000);
const DATA_SUMMARY_POLL_INTERVAL: Duration = Duration::from_millis(100);

/// A [`DiemDataClient`] that fulfills requests from remote peers' Storage Service
/// over DiemNet.
///
/// The `DiemNetDataClient`:
///
/// 1. Sends requests to connected DiemNet peers.
/// 2. Does basic type conversions and error handling on the responses.
/// 3. Routes requests to peers that advertise availability for that data.
/// 4. Maintains peer scores based on each peer's observed quality of service
///    and upper client reports of invalid or malicious data.
/// 5. Selects high quality peers to send each request to.
/// 6. Exposes a condensed data summary of our peers' data advertisements.
///
/// The client currently assumes 1-request => 1-response. Streaming responses
/// are handled at an upper layer.
///
/// The client is expected to be cloneable and usable from many concurrent tasks
/// and/or threads.
#[derive(Clone, Debug)]
pub struct DiemNetDataClient {
    network_client: StorageServiceClient,
    global_summary: Arc<RwLock<GlobalDataSummary>>,
    next_response_id: Arc<AtomicU64>,
}

impl DiemNetDataClient {
    pub fn new(
        time_service: TimeService,
        network_client: StorageServiceClient,
    ) -> (Self, DataSummaryPoller) {
        let global_summary = Arc::new(RwLock::new(GlobalDataSummary::empty()));
        let client = Self {
            network_client,
            global_summary: global_summary.clone(),
            next_response_id: Arc::new(AtomicU64::new(0)),
        };
        let poller = DataSummaryPoller::new(
            time_service,
            client.clone(),
            global_summary,
            DATA_SUMMARY_POLL_INTERVAL,
        );
        (client, poller)
    }

    fn next_response_id(&self) -> u64 {
        self.next_response_id.fetch_add(1, Ordering::Relaxed)
    }

    fn sample_peer(&self) -> Option<PeerNetworkId> {
        // very dumb. just get this working e2e
        let peer_infos = self.network_client.peer_metadata_storage();
        let all_connected = peer_infos
            .networks()
            .flat_map(|network_id| {
                peer_infos
                    .read_filtered(network_id, |(_, peer_info)| peer_info.is_connected())
                    .into_keys()
            })
            .collect::<Vec<_>>();
        all_connected.choose(&mut rand::thread_rng()).copied()
    }

    // TODO(philiphayes): this should be generic in DiemDataClient
    pub async fn send_request(
        &self,
        // TODO(philiphayes): should be a separate DataClient type
        request: StorageServiceRequest,
    ) -> Result<DataClientResponse, Error> {
        let peer = self
            .sample_peer()
            .ok_or_else(|| Error::DataIsUnavailable("no connected diemnet peers".to_owned()))?;
        self.send_request_to_peer(peer, request).await
    }

    async fn send_request_to_peer(
        &self,
        peer: PeerNetworkId,
        request: StorageServiceRequest,
    ) -> Result<DataClientResponse, Error> {
        let response = self.send_request_to_peer_inner(peer, request).await?;
        Ok(DataClientResponse {
            response_id: self.next_response_id(),
            response_payload: response.try_into()?,
        })
    }

    async fn send_request_to_peer_inner(
        &self,
        peer: PeerNetworkId,
        request: StorageServiceRequest,
    ) -> Result<StorageServiceResponse, Error> {
        let result = self
            .network_client
            .send_request(peer, request, DEFAULT_TIMEOUT)
            .await;
        match result {
            Ok(response) => Ok(response),
            Err(storage_service_client::Error::RpcError(err)) => match err {
                RpcError::NotConnected(_) => Err(Error::DataIsUnavailable(err.to_string())),
                RpcError::TimedOut => Err(Error::TimeoutWaitingForResponse(err.to_string())),
                _ => Err(Error::UnexpectedErrorEncountered(err.to_string())),
            },
            Err(storage_service_client::Error::StorageServiceError(err)) => {
                Err(Error::UnexpectedErrorEncountered(err.to_string()))
            }
        }
        // TODO(philiphayes): update peer scores on error
    }

    async fn send_data_summary_request(
        &self,
        peer: PeerNetworkId,
    ) -> Result<StorageServerSummary, Error> {
        let response = self
            .send_request_to_peer_inner(peer, StorageServiceRequest::GetStorageServerSummary)
            .await?;
        match response {
            StorageServiceResponse::StorageServerSummary(summary) => Ok(summary),
            _ => Err(Error::InvalidResponse("unexpected response".to_owned())),
        }
    }
}

/// (start..=end).len()
fn range_len(start: u64, end: u64) -> Result<u64, Error> {
    // len = end - start + 1
    let len = end.checked_sub(start).ok_or_else(|| {
        Error::InvalidRequest(format!("end ({}) must be >= start ({})", end, start))
    })?;
    let len = len
        .checked_add(1)
        .ok_or_else(|| Error::InvalidRequest(format!("end ({}) must not be u64::MAX", end)))?;
    Ok(len)
}

#[async_trait]
impl DiemDataClient for DiemNetDataClient {
    fn get_global_data_summary(&self) -> Result<DataClientResponse, Error> {
        // TODO(philiphayes): feels awkward to create a DataClientResponse here
        // rather than just return the type directly. what does the response id
        // mean here?
        Ok(DataClientResponse {
            response_id: self.next_response_id(),
            response_payload: DataClientPayload::GlobalDataSummary(
                self.global_summary.read().clone(),
            ),
        })
    }

    async fn get_account_states_with_proof(
        &self,
        version: u64,
        start_index: u64,
        end_index: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetAccountStatesChunkWithProof(
            AccountStatesChunkWithProofRequest {
                version,
                start_account_index: start_index,
                expected_num_account_states: range_len(start_index, end_index)?,
            },
        ))
        .await
    }

    async fn get_epoch_ending_ledger_infos(
        &self,
        start_epoch: u64,
        expected_end_epoch: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetEpochEndingLedgerInfos(
            EpochEndingLedgerInfoRequest {
                start_epoch,
                expected_end_epoch,
            },
        ))
        .await
    }

    async fn get_number_of_account_states(
        &self,
        version: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetNumberOfAccountsAtVersion(version))
            .await
    }

    async fn get_transaction_outputs_with_proof(
        &self,
        proof_version: u64,
        start_version: u64,
        end_version: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetTransactionOutputsWithProof(
            TransactionOutputsWithProofRequest {
                proof_version,
                start_version,
                expected_num_outputs: range_len(start_version, end_version)?,
            },
        ))
        .await
    }

    async fn get_transactions_with_proof(
        &self,
        proof_version: u64,
        start_version: u64,
        end_version: u64,
        include_events: bool,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetTransactionsWithProof(
            TransactionsWithProofRequest {
                proof_version,
                start_version,
                expected_num_transactions: range_len(start_version, end_version)?,
                include_events,
            },
        ))
        .await
    }

    async fn notify_bad_response(
        &self,
        _response_id: u64,
        _response_error: ResponseError,
    ) -> Result<(), Error> {
        todo!()
    }
}

pub struct DataSummaryPoller {
    time_service: TimeService,
    data_client: DiemNetDataClient,
    global_summary: Arc<RwLock<GlobalDataSummary>>,
    peer_summaries: PeerDataSummaries,
    poll_interval: Duration,
}

impl DataSummaryPoller {
    fn new(
        time_service: TimeService,
        data_client: DiemNetDataClient,
        global_summary: Arc<RwLock<GlobalDataSummary>>,
        poll_interval: Duration,
    ) -> Self {
        Self {
            time_service,
            data_client,
            global_summary,
            peer_summaries: PeerDataSummaries::new(),
            poll_interval,
        }
    }

    pub async fn start(mut self) {
        let ticker = self.time_service.interval(self.poll_interval);
        futures::pin_mut!(ticker);

        // TODO(philiphayes): rather than polling one at a time, maybe do
        // round-robin with a few concurrent polls.
        loop {
            // wait for next round to poll
            ticker.next().await;

            // just sample a random peer for now. do something smarter here in
            // the future.
            let peer = match self.data_client.sample_peer() {
                Some(peer) => peer,
                None => continue,
            };

            let summary = match self.data_client.send_data_summary_request(peer).await {
                Ok(summary) => summary,
                Err(_err) => {
                    // TODO(philiphayes): log
                    continue;
                }
            };

            // compute the new aggregate and update the global summary cache
            self.peer_summaries.update(peer, summary);
            let new_global_summary = self.peer_summaries.aggregate();

            *self.global_summary.write() = new_global_summary;
        }
    }
}

/// Contains all of the unbanned peers' most recent [`StorageServerSummary`] data
/// advertisements. Is aggregateable into a [`GlobalDataSummary`].
struct PeerDataSummaries {
    peer_summaries: HashMap<PeerNetworkId, StorageServerSummary>,
}

impl PeerDataSummaries {
    fn new() -> Self {
        Self {
            peer_summaries: HashMap::new(),
        }
    }

    fn update(&mut self, peer: PeerNetworkId, summary: StorageServerSummary) {
        self.peer_summaries.insert(peer, summary);
    }

    fn aggregate(&self) -> GlobalDataSummary {
        let mut aggregate_data = AdvertisedData::empty();

        let mut max_epoch_chunk_sizes = vec![];
        let mut max_transaction_chunk_sizes = vec![];
        let mut max_transaction_output_chunk_sizes = vec![];
        let mut max_account_states_chunk_sizes = vec![];

        // collect each peer's protocol and data advertisements
        for summary in self.peer_summaries.values() {
            // collect aggregate data advertisements
            aggregate_data
                .account_states
                .push(summary.data_summary.account_states);
            aggregate_data
                .epoch_ending_ledger_infos
                .push(summary.data_summary.epoch_ending_ledger_infos);
            aggregate_data
                .synced_ledger_infos
                .push(summary.data_summary.synced_ledger_info.clone());
            aggregate_data
                .transactions
                .push(summary.data_summary.transactions);
            aggregate_data
                .transaction_outputs
                .push(summary.data_summary.transaction_outputs);

            // collect preferred max chunk sizes
            max_epoch_chunk_sizes.push(summary.protocol_metadata.max_epoch_chunk_size);
            max_transaction_chunk_sizes.push(summary.protocol_metadata.max_transaction_chunk_size);
            max_transaction_output_chunk_sizes
                .push(summary.protocol_metadata.max_transaction_output_chunk_size);
            max_account_states_chunk_sizes
                .push(summary.protocol_metadata.max_account_states_chunk_size);
        }

        // take the median for each max chunk size parameter.
        // this works well when we have an honest majority that mostly agrees on
        // the same chunk sizes.
        // TODO(philiphayes): move these constants somewhere?
        let aggregate_chunk_sizes = OptimalChunkSizes {
            account_states_chunk_size: median(&mut max_account_states_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_ACCOUNT_STATES_CHUNK_SIZE),
            epoch_chunk_size: median(&mut max_epoch_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_EPOCH_CHUNK_SIZE),
            transaction_chunk_size: median(&mut max_transaction_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_TRANSACTION_CHUNK_SIZE),
            transaction_output_chunk_size: median(&mut max_transaction_output_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_TRANSACTION_OUTPUT_CHUNK_SIZE),
        };

        GlobalDataSummary {
            advertised_data: aggregate_data,
            optimal_chunk_sizes: aggregate_chunk_sizes,
        }
    }
}

fn median<T: Ord + Copy>(values: &mut [T]) -> Option<T> {
    values.sort_unstable();
    let idx = values.len() / 2;
    values.get(idx).copied()
}
<commit_msg>[data-client] DiemNetDataClient owns peer state / metadata<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    AdvertisedData, DataClientPayload, DataClientResponse, DiemDataClient, Error,
    GlobalDataSummary, OptimalChunkSizes, ResponseError,
};
use async_trait::async_trait;
use diem_config::network_id::PeerNetworkId;
use diem_infallible::RwLock;
use diem_time_service::{TimeService, TimeServiceTrait};
use futures::StreamExt;
use network::{application::interface::NetworkInterface, protocols::rpc::error::RpcError};
use rand::seq::SliceRandom;
use std::{
    collections::HashMap,
    convert::TryInto,
    sync::{
        atomic::{AtomicU64, Ordering},
        Arc,
    },
    time::Duration,
};
use storage_service_client::StorageServiceClient;
use storage_service_types::{
    AccountStatesChunkWithProofRequest, EpochEndingLedgerInfoRequest, StorageServerSummary,
    StorageServiceRequest, StorageServiceResponse, TransactionOutputsWithProofRequest,
    TransactionsWithProofRequest,
};

// TODO(philiphayes): does this belong in a different crate? I feel like we're
// accumulating a lot of tiny crates though...

// TODO(philiphayes): configuration / pass as argument?
const DEFAULT_TIMEOUT: Duration = Duration::from_millis(10_000);
const DATA_SUMMARY_POLL_INTERVAL: Duration = Duration::from_millis(100);

/// A [`DiemDataClient`] that fulfills requests from remote peers' Storage Service
/// over DiemNet.
///
/// The `DiemNetDataClient`:
///
/// 1. Sends requests to connected DiemNet peers.
/// 2. Does basic type conversions and error handling on the responses.
/// 3. Routes requests to peers that advertise availability for that data.
/// 4. Maintains peer scores based on each peer's observed quality of service
///    and upper client reports of invalid or malicious data.
/// 5. Selects high quality peers to send each request to.
/// 6. Exposes a condensed data summary of our peers' data advertisements.
///
/// The client currently assumes 1-request => 1-response. Streaming responses
/// are handled at an upper layer.
///
/// The client is expected to be cloneable and usable from many concurrent tasks
/// and/or threads.
#[derive(Clone, Debug)]
pub struct DiemNetDataClient {
    network_client: StorageServiceClient,
    peer_states: Arc<RwLock<PeerStates>>,
    global_summary_cache: Arc<RwLock<GlobalDataSummary>>,
    next_response_id: Arc<AtomicU64>,
}

impl DiemNetDataClient {
    pub fn new(
        time_service: TimeService,
        network_client: StorageServiceClient,
    ) -> (Self, DataSummaryPoller) {
        let client = Self {
            network_client,
            peer_states: Arc::new(RwLock::new(PeerStates::new())),
            global_summary_cache: Arc::new(RwLock::new(GlobalDataSummary::empty())),
            next_response_id: Arc::new(AtomicU64::new(0)),
        };
        let poller =
            DataSummaryPoller::new(time_service, client.clone(), DATA_SUMMARY_POLL_INTERVAL);
        (client, poller)
    }

    fn next_response_id(&self) -> u64 {
        self.next_response_id.fetch_add(1, Ordering::Relaxed)
    }

    fn update_summary(&self, peer: PeerNetworkId, summary: StorageServerSummary) {
        self.peer_states.write().update_summary(peer, summary)
    }

    fn update_global_summary_cache(&self) {
        let aggregate = self.peer_states.read().aggregate_summary();
        *self.global_summary_cache.write() = aggregate;
    }

    fn sample_peer(&self) -> Option<PeerNetworkId> {
        // very dumb. just get this working e2e
        let peer_infos = self.network_client.peer_metadata_storage();
        let all_connected = peer_infos
            .networks()
            .flat_map(|network_id| {
                peer_infos
                    .read_filtered(network_id, |(_, peer_info)| peer_info.is_connected())
                    .into_keys()
            })
            .collect::<Vec<_>>();
        all_connected.choose(&mut rand::thread_rng()).copied()
    }

    // TODO(philiphayes): this should be generic in DiemDataClient trait
    pub async fn send_request(
        &self,
        // TODO(philiphayes): should be a separate DataClient type?
        request: StorageServiceRequest,
    ) -> Result<DataClientResponse, Error> {
        let peer = self
            .sample_peer()
            .ok_or_else(|| Error::DataIsUnavailable("no connected diemnet peers".to_owned()))?;
        self.send_request_to_peer(peer, request).await
    }

    async fn send_request_to_peer(
        &self,
        peer: PeerNetworkId,
        request: StorageServiceRequest,
    ) -> Result<DataClientResponse, Error> {
        let response = self.send_request_to_peer_inner(peer, request).await?;
        Ok(DataClientResponse {
            response_id: self.next_response_id(),
            response_payload: response.try_into()?,
        })
    }

    async fn send_request_to_peer_inner(
        &self,
        peer: PeerNetworkId,
        request: StorageServiceRequest,
    ) -> Result<StorageServiceResponse, Error> {
        let result = self
            .network_client
            .send_request(peer, request, DEFAULT_TIMEOUT)
            .await;
        match result {
            Ok(response) => Ok(response),
            Err(storage_service_client::Error::RpcError(err)) => match err {
                RpcError::NotConnected(_) => Err(Error::DataIsUnavailable(err.to_string())),
                RpcError::TimedOut => Err(Error::TimeoutWaitingForResponse(err.to_string())),
                _ => Err(Error::UnexpectedErrorEncountered(err.to_string())),
            },
            Err(storage_service_client::Error::StorageServiceError(err)) => {
                Err(Error::UnexpectedErrorEncountered(err.to_string()))
            }
        }
        // TODO(philiphayes): update peer scores on error
    }

    async fn send_data_summary_request(
        &self,
        peer: PeerNetworkId,
    ) -> Result<StorageServerSummary, Error> {
        let response = self
            .send_request_to_peer_inner(peer, StorageServiceRequest::GetStorageServerSummary)
            .await?;
        match response {
            StorageServiceResponse::StorageServerSummary(summary) => Ok(summary),
            _ => Err(Error::InvalidResponse("unexpected response".to_owned())),
        }
    }
}

/// (start..=end).len()
fn range_len(start: u64, end: u64) -> Result<u64, Error> {
    // len = end - start + 1
    let len = end.checked_sub(start).ok_or_else(|| {
        Error::InvalidRequest(format!("end ({}) must be >= start ({})", end, start))
    })?;
    let len = len
        .checked_add(1)
        .ok_or_else(|| Error::InvalidRequest(format!("end ({}) must not be u64::MAX", end)))?;
    Ok(len)
}

#[async_trait]
impl DiemDataClient for DiemNetDataClient {
    fn get_global_data_summary(&self) -> Result<DataClientResponse, Error> {
        // TODO(philiphayes): feels awkward to create a DataClientResponse here
        // rather than just return the type directly. what does the response id
        // mean here?
        Ok(DataClientResponse {
            response_id: self.next_response_id(),
            response_payload: DataClientPayload::GlobalDataSummary(
                self.global_summary_cache.read().clone(),
            ),
        })
    }

    async fn get_account_states_with_proof(
        &self,
        version: u64,
        start_index: u64,
        end_index: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetAccountStatesChunkWithProof(
            AccountStatesChunkWithProofRequest {
                version,
                start_account_index: start_index,
                expected_num_account_states: range_len(start_index, end_index)?,
            },
        ))
        .await
    }

    async fn get_epoch_ending_ledger_infos(
        &self,
        start_epoch: u64,
        expected_end_epoch: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetEpochEndingLedgerInfos(
            EpochEndingLedgerInfoRequest {
                start_epoch,
                expected_end_epoch,
            },
        ))
        .await
    }

    async fn get_number_of_account_states(
        &self,
        version: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetNumberOfAccountsAtVersion(version))
            .await
    }

    async fn get_transaction_outputs_with_proof(
        &self,
        proof_version: u64,
        start_version: u64,
        end_version: u64,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetTransactionOutputsWithProof(
            TransactionOutputsWithProofRequest {
                proof_version,
                start_version,
                expected_num_outputs: range_len(start_version, end_version)?,
            },
        ))
        .await
    }

    async fn get_transactions_with_proof(
        &self,
        proof_version: u64,
        start_version: u64,
        end_version: u64,
        include_events: bool,
    ) -> Result<DataClientResponse, Error> {
        self.send_request(StorageServiceRequest::GetTransactionsWithProof(
            TransactionsWithProofRequest {
                proof_version,
                start_version,
                expected_num_transactions: range_len(start_version, end_version)?,
                include_events,
            },
        ))
        .await
    }

    async fn notify_bad_response(
        &self,
        _response_id: u64,
        _response_error: ResponseError,
    ) -> Result<(), Error> {
        todo!()
    }
}

pub struct DataSummaryPoller {
    time_service: TimeService,
    data_client: DiemNetDataClient,
    poll_interval: Duration,
}

impl DataSummaryPoller {
    fn new(
        time_service: TimeService,
        data_client: DiemNetDataClient,
        poll_interval: Duration,
    ) -> Self {
        Self {
            time_service,
            data_client,
            poll_interval,
        }
    }

    pub async fn start(self) {
        let ticker = self.time_service.interval(self.poll_interval);
        futures::pin_mut!(ticker);

        // TODO(philiphayes): rather than polling one at a time, maybe do
        // round-robin with a few concurrent polls.
        loop {
            // wait for next round to poll
            ticker.next().await;

            // just sample a random peer for now. do something smarter here in
            // the future.
            let peer = match self.data_client.sample_peer() {
                Some(peer) => peer,
                None => continue,
            };

            let summary = match self.data_client.send_data_summary_request(peer).await {
                Ok(summary) => summary,
                Err(_err) => {
                    // TODO(philiphayes): log
                    continue;
                }
            };

            self.data_client.update_summary(peer, summary);
            self.data_client.update_global_summary_cache();
        }
    }
}

#[derive(Debug, Default)]
struct PeerState {
    storage_summary: Option<StorageServerSummary>,
    // TODO(philiphayes): imagine storing some scoring info here.
    metadata: (),
}

/// Contains all of the unbanned peers' most recent [`StorageServerSummary`] data
/// advertisements and data-client internal metadata for scoring.
#[derive(Debug)]
struct PeerStates {
    inner: HashMap<PeerNetworkId, PeerState>,
}

impl PeerStates {
    fn new() -> Self {
        Self {
            inner: HashMap::new(),
        }
    }

    fn update_summary(&mut self, peer: PeerNetworkId, summary: StorageServerSummary) {
        self.inner.entry(peer).or_default().storage_summary = Some(summary);
    }

    fn aggregate_summary(&self) -> GlobalDataSummary {
        let mut aggregate_data = AdvertisedData::empty();

        let mut max_epoch_chunk_sizes = vec![];
        let mut max_transaction_chunk_sizes = vec![];
        let mut max_transaction_output_chunk_sizes = vec![];
        let mut max_account_states_chunk_sizes = vec![];

        let summaries = self
            .inner
            .values()
            .filter_map(|state| state.storage_summary.as_ref());

        // collect each peer's protocol and data advertisements
        for summary in summaries {
            // collect aggregate data advertisements
            aggregate_data
                .account_states
                .push(summary.data_summary.account_states);
            aggregate_data
                .epoch_ending_ledger_infos
                .push(summary.data_summary.epoch_ending_ledger_infos);
            aggregate_data
                .synced_ledger_infos
                .push(summary.data_summary.synced_ledger_info.clone());
            aggregate_data
                .transactions
                .push(summary.data_summary.transactions);
            aggregate_data
                .transaction_outputs
                .push(summary.data_summary.transaction_outputs);

            // collect preferred max chunk sizes
            max_epoch_chunk_sizes.push(summary.protocol_metadata.max_epoch_chunk_size);
            max_transaction_chunk_sizes.push(summary.protocol_metadata.max_transaction_chunk_size);
            max_transaction_output_chunk_sizes
                .push(summary.protocol_metadata.max_transaction_output_chunk_size);
            max_account_states_chunk_sizes
                .push(summary.protocol_metadata.max_account_states_chunk_size);
        }

        // take the median for each max chunk size parameter.
        // this works well when we have an honest majority that mostly agrees on
        // the same chunk sizes.
        // TODO(philiphayes): move these constants somewhere?
        let aggregate_chunk_sizes = OptimalChunkSizes {
            account_states_chunk_size: median(&mut max_account_states_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_ACCOUNT_STATES_CHUNK_SIZE),
            epoch_chunk_size: median(&mut max_epoch_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_EPOCH_CHUNK_SIZE),
            transaction_chunk_size: median(&mut max_transaction_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_TRANSACTION_CHUNK_SIZE),
            transaction_output_chunk_size: median(&mut max_transaction_output_chunk_sizes)
                .unwrap_or(storage_service_server::MAX_TRANSACTION_OUTPUT_CHUNK_SIZE),
        };

        GlobalDataSummary {
            advertised_data: aggregate_data,
            optimal_chunk_sizes: aggregate_chunk_sizes,
        }
    }
}

fn median<T: Ord + Copy>(values: &mut [T]) -> Option<T> {
    values.sort_unstable();
    let idx = values.len() / 2;
    values.get(idx).copied()
}
<|endoftext|>


<commit_before>extern crate aoc_2018;
#[macro_use]
extern crate bencher;

use std::fs::File;
use std::io::Read;

use bencher::Bencher;

use aoc_2018::get_impl;

fn get_input(day: u32) -> Vec<u8> {
    let filename = format!("inputs/{:02}.txt", day);

    let mut buf = Vec::new();
    let mut file = File::open(&filename).unwrap();

    file.read_to_end(&mut buf).unwrap();

    buf
}

fn test_part1(day: u32, bench: &mut Bencher) {
    let input = get_input(day);
    bench.iter(|| {
        let mut instance = get_impl(day);
        instance.part1(&mut input.as_slice())
    })
}

fn test_part2(day: u32, bench: &mut Bencher) {
    let input = get_input(day);
    bench.iter(|| {
        let mut instance = get_impl(day);
        instance.part2(&mut input.as_slice())
    })
}

macro_rules! day_bench {

    ( $name:ident, $day:expr ) => {
            pub mod $name {
                use super::*;
                pub fn part1(bench: & mut Bencher) {
                test_part1( $day, bench);
                }
                pub fn part2(bench: & mut Bencher) {
                test_part2( $day, bench);
                }
            }
            benchmark_group!( $name, $name::part1, $name::part2);
    };
}

day_bench!(day01, 1);
day_bench!(day02, 2);
day_bench!(day03, 3);
day_bench!(day04, 4);
day_bench!(day05, 5);
day_bench!(day06, 6);
day_bench!(day07, 7);
day_bench!(day08, 8);
day_bench!(day09, 9);
day_bench!(day10, 10);
day_bench!(day11, 11);
day_bench!(day12, 12);
day_bench!(day13, 13);
day_bench!(day14, 14);
day_bench!(day15, 15);
day_bench!(day16, 16);
day_bench!(day17, 17);
day_bench!(day18, 18);
day_bench!(day19, 19);
day_bench!(day20, 20);
day_bench!(day21, 21);

benchmark_main!(day01, day02, day03, day04, day05,
                day06, day07, day08, day09, day10,
                day11, day12, day13, day14, day15,
                day16, day17, day18, day19, day20,
                day21);
<commit_msg>Include day 22 in benchmarks.<commit_after>extern crate aoc_2018;
#[macro_use]
extern crate bencher;

use std::fs::File;
use std::io::Read;

use bencher::Bencher;

use aoc_2018::get_impl;

fn get_input(day: u32) -> Vec<u8> {
    let filename = format!("inputs/{:02}.txt", day);

    let mut buf = Vec::new();
    let mut file = File::open(&filename).unwrap();

    file.read_to_end(&mut buf).unwrap();

    buf
}

fn test_part1(day: u32, bench: &mut Bencher) {
    let input = get_input(day);
    bench.iter(|| {
        let mut instance = get_impl(day);
        instance.part1(&mut input.as_slice())
    })
}

fn test_part2(day: u32, bench: &mut Bencher) {
    let input = get_input(day);
    bench.iter(|| {
        let mut instance = get_impl(day);
        instance.part2(&mut input.as_slice())
    })
}

macro_rules! day_bench {

    ( $name:ident, $day:expr ) => {
            pub mod $name {
                use super::*;
                pub fn part1(bench: & mut Bencher) {
                test_part1( $day, bench);
                }
                pub fn part2(bench: & mut Bencher) {
                test_part2( $day, bench);
                }
            }
            benchmark_group!( $name, $name::part1, $name::part2);
    };
}

day_bench!(day01, 1);
day_bench!(day02, 2);
day_bench!(day03, 3);
day_bench!(day04, 4);
day_bench!(day05, 5);
day_bench!(day06, 6);
day_bench!(day07, 7);
day_bench!(day08, 8);
day_bench!(day09, 9);
day_bench!(day10, 10);
day_bench!(day11, 11);
day_bench!(day12, 12);
day_bench!(day13, 13);
day_bench!(day14, 14);
day_bench!(day15, 15);
day_bench!(day16, 16);
day_bench!(day17, 17);
day_bench!(day18, 18);
day_bench!(day19, 19);
day_bench!(day20, 20);
day_bench!(day21, 21);
day_bench!(day22, 22);

benchmark_main!(day01, day02, day03, day04, day05,
                day06, day07, day08, day09, day10,
                day11, day12, day13, day14, day15,
                day16, day17, day18, day19, day20,
                day21, day22);
<|endoftext|>


<commit_before>#[test]
fn compile_macros() {
    let t = trybuild::TestCases::new();

    t.pass("tests/trybuild/simple.rs");
    t.compile_fail("tests/trybuild/simple-fail.rs");

    t.pass("tests/trybuild/route-ok.rs");
    t.compile_fail("tests/trybuild/route-duplicate-method-fail.rs");
    t.compile_fail("tests/trybuild/route-unexpected-method-fail.rs");

    test_route_missing_method(&t)
}

#[rustversion::stable(1.42)]
fn test_route_missing_method(t: &trybuild::TestCases) {
    t.compile_fail("tests/trybuild/route-missing-method-fail-msrv.rs");
}

#[rustversion::not(stable(1.42))]
#[rustversion::not(nightly)]
fn test_route_missing_method(t: &trybuild::TestCases) {
    t.compile_fail("tests/trybuild/route-missing-method-fail.rs");
}

#[rustversion::nightly]
fn test_route_missing_method(_t: &trybuild::TestCases) {}
<commit_msg>Skip some tests that cause ICE on nightly (#1740)<commit_after>#[test]
fn compile_macros() {
    let t = trybuild::TestCases::new();

    t.pass("tests/trybuild/simple.rs");
    t.compile_fail("tests/trybuild/simple-fail.rs");

    t.pass("tests/trybuild/route-ok.rs");

    test_route_duplicate_unexpected_method(&t);
    test_route_missing_method(&t)
}

#[rustversion::stable(1.42)]
fn test_route_missing_method(t: &trybuild::TestCases) {
    t.compile_fail("tests/trybuild/route-missing-method-fail-msrv.rs");
}

#[rustversion::not(stable(1.42))]
#[rustversion::not(nightly)]
fn test_route_missing_method(t: &trybuild::TestCases) {
    t.compile_fail("tests/trybuild/route-missing-method-fail.rs");
}

#[rustversion::nightly]
fn test_route_missing_method(_t: &trybuild::TestCases) {}

// FIXME: Re-test them on nightly once rust-lang/rust#77993 is fixed.
#[rustversion::not(nightly)]
fn test_route_duplicate_unexpected_method(t: &trybuild::TestCases) {
    t.compile_fail("tests/trybuild/route-duplicate-method-fail.rs");
    t.compile_fail("tests/trybuild/route-unexpected-method-fail.rs");
}

#[rustversion::nightly]
fn test_route_duplicate_unexpected_method(_t: &trybuild::TestCases) {}
<|endoftext|>


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

// Detecting language items.
//
// Language items are items that represent concepts intrinsic to the language
// itself. Examples are:
//
// * Traits that specify "kinds"; e.g. "Freeze", "Send".
//
// * Traits that represent operators; e.g. "Add", "Sub", "Index".
//
// * Functions called by the compiler itself.

use driver::session::Session;
use metadata::csearch::each_lang_item;
use metadata::cstore::iter_crate_data;
use middle::ty::{BuiltinBound, BoundFreeze, BoundSend, BoundSized};
use syntax::ast;
use syntax::ast_util::local_def;
use syntax::attr::AttrMetaMethods;
use syntax::visit;
use syntax::visit::Visitor;

use std::hashmap::HashMap;
use std::iter::Enumerate;
use std::vec;

pub enum LangItem {
    FreezeTraitLangItem,               // 0
    SendTraitLangItem,                 // 1
    SizedTraitLangItem,                // 2

    DropTraitLangItem,                 // 3

    AddTraitLangItem,                  // 4
    SubTraitLangItem,                  // 5
    MulTraitLangItem,                  // 6
    DivTraitLangItem,                  // 7
    RemTraitLangItem,                  // 8
    NegTraitLangItem,                  // 9
    NotTraitLangItem,                  // 10
    BitXorTraitLangItem,               // 11
    BitAndTraitLangItem,               // 12
    BitOrTraitLangItem,                // 13
    ShlTraitLangItem,                  // 14
    ShrTraitLangItem,                  // 15
    IndexTraitLangItem,                // 16

    EqTraitLangItem,                   // 17
    OrdTraitLangItem,                  // 18

    StrEqFnLangItem,                   // 19
    UniqStrEqFnLangItem,               // 20
    FailFnLangItem,                    // 21
    FailBoundsCheckFnLangItem,         // 22
    ExchangeMallocFnLangItem,          // 23
    ClosureExchangeMallocFnLangItem,   // 24
    ExchangeFreeFnLangItem,            // 25
    MallocFnLangItem,                  // 26
    FreeFnLangItem,                    // 27
    BorrowAsImmFnLangItem,             // 28
    BorrowAsMutFnLangItem,             // 29
    ReturnToMutFnLangItem,             // 30
    CheckNotBorrowedFnLangItem,        // 31
    StrDupUniqFnLangItem,              // 32
    RecordBorrowFnLangItem,            // 33
    UnrecordBorrowFnLangItem,          // 34

    StartFnLangItem,                   // 35

    TyDescStructLangItem,              // 36
    TyVisitorTraitLangItem,            // 37
    OpaqueStructLangItem,              // 38

    EventLoopFactoryLangItem,          // 39

    TypeIdLangItem,                    // 40
}

pub struct LanguageItems {
    items: [Option<ast::DefId>, ..41]
}

impl LanguageItems {
    pub fn new() -> LanguageItems {
        LanguageItems {
            items: [ None, ..41 ]
        }
    }

    pub fn items<'a>(&'a self) -> Enumerate<vec::VecIterator<'a, Option<ast::DefId>>> {
        self.items.iter().enumerate()
    }

    pub fn item_name(index: uint) -> &'static str {
        match index {
            0  => "freeze",
            1  => "send",
            2  => "sized",

            3  => "drop",

            4  => "add",
            5  => "sub",
            6  => "mul",
            7  => "div",
            8  => "rem",
            9  => "neg",
            10 => "not",
            11 => "bitxor",
            12 => "bitand",
            13 => "bitor",
            14 => "shl",
            15 => "shr",
            16 => "index",
            17 => "eq",
            18 => "ord",

            19 => "str_eq",
            20 => "uniq_str_eq",
            21 => "fail_",
            22 => "fail_bounds_check",
            23 => "exchange_malloc",
            24 => "closure_exchange_malloc",
            25 => "exchange_free",
            26 => "malloc",
            27 => "free",
            28 => "borrow_as_imm",
            29 => "borrow_as_mut",
            30 => "return_to_mut",
            31 => "check_not_borrowed",
            32 => "strdup_uniq",
            33 => "record_borrow",
            34 => "unrecord_borrow",

            35 => "start",

            36 => "ty_desc",
            37 => "ty_visitor",
            38 => "opaque",

            39 => "event_loop_factory",

            40 => "type_id",

            _ => "???"
        }
    }

    // FIXME #4621: Method macros sure would be nice here.

    pub fn require(&self, it: LangItem) -> Result<ast::DefId, ~str> {
        match self.items[it as uint] {
            Some(id) => Ok(id),
            None => Err(format!("requires `{}` lang_item",
                             LanguageItems::item_name(it as uint)))
        }
    }

    pub fn to_builtin_kind(&self, id: ast::DefId) -> Option<BuiltinBound> {
        if Some(id) == self.freeze_trait() {
            Some(BoundFreeze)
        } else if Some(id) == self.send_trait() {
            Some(BoundSend)
        } else if Some(id) == self.sized_trait() {
            Some(BoundSized)
        } else {
            None
        }
    }

    pub fn freeze_trait(&self) -> Option<ast::DefId> {
        self.items[FreezeTraitLangItem as uint]
    }
    pub fn send_trait(&self) -> Option<ast::DefId> {
        self.items[SendTraitLangItem as uint]
    }
    pub fn sized_trait(&self) -> Option<ast::DefId> {
        self.items[SizedTraitLangItem as uint]
    }

    pub fn drop_trait(&self) -> Option<ast::DefId> {
        self.items[DropTraitLangItem as uint]
    }

    pub fn add_trait(&self) -> Option<ast::DefId> {
        self.items[AddTraitLangItem as uint]
    }
    pub fn sub_trait(&self) -> Option<ast::DefId> {
        self.items[SubTraitLangItem as uint]
    }
    pub fn mul_trait(&self) -> Option<ast::DefId> {
        self.items[MulTraitLangItem as uint]
    }
    pub fn div_trait(&self) -> Option<ast::DefId> {
        self.items[DivTraitLangItem as uint]
    }
    pub fn rem_trait(&self) -> Option<ast::DefId> {
        self.items[RemTraitLangItem as uint]
    }
    pub fn neg_trait(&self) -> Option<ast::DefId> {
        self.items[NegTraitLangItem as uint]
    }
    pub fn not_trait(&self) -> Option<ast::DefId> {
        self.items[NotTraitLangItem as uint]
    }
    pub fn bitxor_trait(&self) -> Option<ast::DefId> {
        self.items[BitXorTraitLangItem as uint]
    }
    pub fn bitand_trait(&self) -> Option<ast::DefId> {
        self.items[BitAndTraitLangItem as uint]
    }
    pub fn bitor_trait(&self) -> Option<ast::DefId> {
        self.items[BitOrTraitLangItem as uint]
    }
    pub fn shl_trait(&self) -> Option<ast::DefId> {
        self.items[ShlTraitLangItem as uint]
    }
    pub fn shr_trait(&self) -> Option<ast::DefId> {
        self.items[ShrTraitLangItem as uint]
    }
    pub fn index_trait(&self) -> Option<ast::DefId> {
        self.items[IndexTraitLangItem as uint]
    }

    pub fn eq_trait(&self) -> Option<ast::DefId> {
        self.items[EqTraitLangItem as uint]
    }
    pub fn ord_trait(&self) -> Option<ast::DefId> {
        self.items[OrdTraitLangItem as uint]
    }

    pub fn str_eq_fn(&self) -> Option<ast::DefId> {
        self.items[StrEqFnLangItem as uint]
    }
    pub fn uniq_str_eq_fn(&self) -> Option<ast::DefId> {
        self.items[UniqStrEqFnLangItem as uint]
    }
    pub fn fail_fn(&self) -> Option<ast::DefId> {
        self.items[FailFnLangItem as uint]
    }
    pub fn fail_bounds_check_fn(&self) -> Option<ast::DefId> {
        self.items[FailBoundsCheckFnLangItem as uint]
    }
    pub fn exchange_malloc_fn(&self) -> Option<ast::DefId> {
        self.items[ExchangeMallocFnLangItem as uint]
    }
    pub fn closure_exchange_malloc_fn(&self) -> Option<ast::DefId> {
        self.items[ClosureExchangeMallocFnLangItem as uint]
    }
    pub fn exchange_free_fn(&self) -> Option<ast::DefId> {
        self.items[ExchangeFreeFnLangItem as uint]
    }
    pub fn malloc_fn(&self) -> Option<ast::DefId> {
        self.items[MallocFnLangItem as uint]
    }
    pub fn free_fn(&self) -> Option<ast::DefId> {
        self.items[FreeFnLangItem as uint]
    }
    pub fn borrow_as_imm_fn(&self) -> Option<ast::DefId> {
        self.items[BorrowAsImmFnLangItem as uint]
    }
    pub fn borrow_as_mut_fn(&self) -> Option<ast::DefId> {
        self.items[BorrowAsMutFnLangItem as uint]
    }
    pub fn return_to_mut_fn(&self) -> Option<ast::DefId> {
        self.items[ReturnToMutFnLangItem as uint]
    }
    pub fn check_not_borrowed_fn(&self) -> Option<ast::DefId> {
        self.items[CheckNotBorrowedFnLangItem as uint]
    }
    pub fn strdup_uniq_fn(&self) -> Option<ast::DefId> {
        self.items[StrDupUniqFnLangItem as uint]
    }
    pub fn record_borrow_fn(&self) -> Option<ast::DefId> {
        self.items[RecordBorrowFnLangItem as uint]
    }
    pub fn unrecord_borrow_fn(&self) -> Option<ast::DefId> {
        self.items[UnrecordBorrowFnLangItem as uint]
    }
    pub fn start_fn(&self) -> Option<ast::DefId> {
        self.items[StartFnLangItem as uint]
    }
    pub fn ty_desc(&self) -> Option<ast::DefId> {
        self.items[TyDescStructLangItem as uint]
    }
    pub fn ty_visitor(&self) -> Option<ast::DefId> {
        self.items[TyVisitorTraitLangItem as uint]
    }
    pub fn opaque(&self) -> Option<ast::DefId> {
        self.items[OpaqueStructLangItem as uint]
    }
    pub fn event_loop_factory(&self) -> Option<ast::DefId> {
        self.items[EventLoopFactoryLangItem as uint]
    }
    pub fn type_id(&self) -> Option<ast::DefId> {
        self.items[TypeIdLangItem as uint]
    }
}

struct LanguageItemCollector {
    items: LanguageItems,

    session: Session,

    item_refs: HashMap<&'static str, uint>,
}

struct LanguageItemVisitor<'self> {
    this: &'self mut LanguageItemCollector,
}

impl<'self> Visitor<()> for LanguageItemVisitor<'self> {
    fn visit_item(&mut self, item: @ast::item, _: ()) {
        match extract(item.attrs) {
            Some(value) => {
                let item_index = self.this.item_refs.find_equiv(&value).map(|x| *x);

                match item_index {
                    Some(item_index) => {
                        self.this.collect_item(item_index, local_def(item.id))
                    }
                    None => {}
                }
            }
            None => {}
        }

        visit::walk_item(self, item, ());
    }
}

impl LanguageItemCollector {
    pub fn new(session: Session) -> LanguageItemCollector {
        let mut item_refs = HashMap::new();

        item_refs.insert("freeze", FreezeTraitLangItem as uint);
        item_refs.insert("send", SendTraitLangItem as uint);
        item_refs.insert("sized", SizedTraitLangItem as uint);

        item_refs.insert("drop", DropTraitLangItem as uint);

        item_refs.insert("add", AddTraitLangItem as uint);
        item_refs.insert("sub", SubTraitLangItem as uint);
        item_refs.insert("mul", MulTraitLangItem as uint);
        item_refs.insert("div", DivTraitLangItem as uint);
        item_refs.insert("rem", RemTraitLangItem as uint);
        item_refs.insert("neg", NegTraitLangItem as uint);
        item_refs.insert("not", NotTraitLangItem as uint);
        item_refs.insert("bitxor", BitXorTraitLangItem as uint);
        item_refs.insert("bitand", BitAndTraitLangItem as uint);
        item_refs.insert("bitor", BitOrTraitLangItem as uint);
        item_refs.insert("shl", ShlTraitLangItem as uint);
        item_refs.insert("shr", ShrTraitLangItem as uint);
        item_refs.insert("index", IndexTraitLangItem as uint);

        item_refs.insert("eq", EqTraitLangItem as uint);
        item_refs.insert("ord", OrdTraitLangItem as uint);

        item_refs.insert("str_eq", StrEqFnLangItem as uint);
        item_refs.insert("uniq_str_eq", UniqStrEqFnLangItem as uint);
        item_refs.insert("fail_", FailFnLangItem as uint);
        item_refs.insert("fail_bounds_check",
                         FailBoundsCheckFnLangItem as uint);
        item_refs.insert("exchange_malloc", ExchangeMallocFnLangItem as uint);
        item_refs.insert("closure_exchange_malloc", ClosureExchangeMallocFnLangItem as uint);
        item_refs.insert("exchange_free", ExchangeFreeFnLangItem as uint);
        item_refs.insert("malloc", MallocFnLangItem as uint);
        item_refs.insert("free", FreeFnLangItem as uint);
        item_refs.insert("borrow_as_imm", BorrowAsImmFnLangItem as uint);
        item_refs.insert("borrow_as_mut", BorrowAsMutFnLangItem as uint);
        item_refs.insert("return_to_mut", ReturnToMutFnLangItem as uint);
        item_refs.insert("check_not_borrowed",
                         CheckNotBorrowedFnLangItem as uint);
        item_refs.insert("strdup_uniq", StrDupUniqFnLangItem as uint);
        item_refs.insert("record_borrow", RecordBorrowFnLangItem as uint);
        item_refs.insert("unrecord_borrow", UnrecordBorrowFnLangItem as uint);
        item_refs.insert("start", StartFnLangItem as uint);
        item_refs.insert("ty_desc", TyDescStructLangItem as uint);
        item_refs.insert("ty_visitor", TyVisitorTraitLangItem as uint);
        item_refs.insert("opaque", OpaqueStructLangItem as uint);
        item_refs.insert("event_loop_factory", EventLoopFactoryLangItem as uint);
        item_refs.insert("type_id", TypeIdLangItem as uint);

        LanguageItemCollector {
            session: session,
            items: LanguageItems::new(),
            item_refs: item_refs
        }
    }

    pub fn collect_item(&mut self, item_index: uint, item_def_id: ast::DefId) {
        // Check for duplicates.
        match self.items.items[item_index] {
            Some(original_def_id) if original_def_id != item_def_id => {
                self.session.err(format!("duplicate entry for `{}`",
                                      LanguageItems::item_name(item_index)));
            }
            Some(_) | None => {
                // OK.
            }
        }

        // Matched.
        self.items.items[item_index] = Some(item_def_id);
    }

    pub fn collect_local_language_items(&mut self, crate: &ast::Crate) {
        let mut v = LanguageItemVisitor { this: self };
        visit::walk_crate(&mut v, crate, ());
    }

    pub fn collect_external_language_items(&mut self) {
        let crate_store = self.session.cstore;
        iter_crate_data(crate_store, |crate_number, _crate_metadata| {
            each_lang_item(crate_store, crate_number, |node_id, item_index| {
                let def_id = ast::DefId { crate: crate_number, node: node_id };
                self.collect_item(item_index, def_id);
                true
            });
        })
    }

    pub fn collect(&mut self, crate: &ast::Crate) {
        self.collect_local_language_items(crate);
        self.collect_external_language_items();
    }
}

pub fn extract(attrs: &[ast::Attribute]) -> Option<@str> {
    for attribute in attrs.iter() {
        match attribute.name_str_pair() {
            Some((key, value)) if "lang" == key => {
                return Some(value);
            }
            Some(..) | None => {}
        }
    }

    return None;
}

pub fn collect_language_items(crate: &ast::Crate,
                              session: Session)
                           -> LanguageItems {
    let mut collector = LanguageItemCollector::new(session);
    collector.collect(crate);
    let LanguageItemCollector { items, .. } = collector;
    session.abort_if_errors();
    items
}
<commit_msg>auto merge of #10832 : chris-morgan/rust/let's-lop-lang-item-line-count, r=alexcrichton<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Detecting language items.
//
// Language items are items that represent concepts intrinsic to the language
// itself. Examples are:
//
// * Traits that specify "kinds"; e.g. "Freeze", "Send".
//
// * Traits that represent operators; e.g. "Add", "Sub", "Index".
//
// * Functions called by the compiler itself.

use driver::session::Session;
use metadata::csearch::each_lang_item;
use metadata::cstore::iter_crate_data;
use middle::ty::{BuiltinBound, BoundFreeze, BoundSend, BoundSized};
use syntax::ast;
use syntax::ast_util::local_def;
use syntax::attr::AttrMetaMethods;
use syntax::visit;
use syntax::visit::Visitor;

use std::hashmap::HashMap;
use std::iter::Enumerate;
use std::vec;

// The actual lang items defined come at the end of this file in one handy table.
// So you probably just want to nip down to the end.
macro_rules! lets_do_this {
    (
        There are $num_lang_items:expr lang items.
        $( $num:pat, $variant:ident, $name:expr, $method:ident; )*
    ) => {

pub enum LangItem {
    $($variant),*
}

pub struct LanguageItems {
    items: [Option<ast::DefId>, ..$num_lang_items]
}

impl LanguageItems {
    pub fn new() -> LanguageItems {
        LanguageItems {
            items: [ None, ..$num_lang_items ]
        }
    }

    pub fn items<'a>(&'a self) -> Enumerate<vec::VecIterator<'a, Option<ast::DefId>>> {
        self.items.iter().enumerate()
    }

    pub fn item_name(index: uint) -> &'static str {
        match index {
            $( $num => $name, )*
            _ => "???"
        }
    }

    pub fn require(&self, it: LangItem) -> Result<ast::DefId, ~str> {
        match self.items[it as uint] {
            Some(id) => Ok(id),
            None => Err(format!("requires `{}` lang_item",
                             LanguageItems::item_name(it as uint)))
        }
    }

    pub fn to_builtin_kind(&self, id: ast::DefId) -> Option<BuiltinBound> {
        if Some(id) == self.freeze_trait() {
            Some(BoundFreeze)
        } else if Some(id) == self.send_trait() {
            Some(BoundSend)
        } else if Some(id) == self.sized_trait() {
            Some(BoundSized)
        } else {
            None
        }
    }

    $(
        pub fn $method(&self) -> Option<ast::DefId> {
            self.items[$variant as uint]
        }
    )*
}

struct LanguageItemCollector {
    items: LanguageItems,

    session: Session,

    item_refs: HashMap<&'static str, uint>,
}

struct LanguageItemVisitor<'self> {
    this: &'self mut LanguageItemCollector,
}

impl<'self> Visitor<()> for LanguageItemVisitor<'self> {
    fn visit_item(&mut self, item: @ast::item, _: ()) {
        match extract(item.attrs) {
            Some(value) => {
                let item_index = self.this.item_refs.find_equiv(&value).map(|x| *x);

                match item_index {
                    Some(item_index) => {
                        self.this.collect_item(item_index, local_def(item.id))
                    }
                    None => {}
                }
            }
            None => {}
        }

        visit::walk_item(self, item, ());
    }
}

impl LanguageItemCollector {
    pub fn new(session: Session) -> LanguageItemCollector {
        let mut item_refs = HashMap::new();

        $( item_refs.insert($name, $variant as uint); )*

        LanguageItemCollector {
            session: session,
            items: LanguageItems::new(),
            item_refs: item_refs
        }
    }

    pub fn collect_item(&mut self, item_index: uint, item_def_id: ast::DefId) {
        // Check for duplicates.
        match self.items.items[item_index] {
            Some(original_def_id) if original_def_id != item_def_id => {
                self.session.err(format!("duplicate entry for `{}`",
                                      LanguageItems::item_name(item_index)));
            }
            Some(_) | None => {
                // OK.
            }
        }

        // Matched.
        self.items.items[item_index] = Some(item_def_id);
    }

    pub fn collect_local_language_items(&mut self, crate: &ast::Crate) {
        let mut v = LanguageItemVisitor { this: self };
        visit::walk_crate(&mut v, crate, ());
    }

    pub fn collect_external_language_items(&mut self) {
        let crate_store = self.session.cstore;
        iter_crate_data(crate_store, |crate_number, _crate_metadata| {
            each_lang_item(crate_store, crate_number, |node_id, item_index| {
                let def_id = ast::DefId { crate: crate_number, node: node_id };
                self.collect_item(item_index, def_id);
                true
            });
        })
    }

    pub fn collect(&mut self, crate: &ast::Crate) {
        self.collect_local_language_items(crate);
        self.collect_external_language_items();
    }
}

pub fn extract(attrs: &[ast::Attribute]) -> Option<@str> {
    for attribute in attrs.iter() {
        match attribute.name_str_pair() {
            Some((key, value)) if "lang" == key => {
                return Some(value);
            }
            Some(..) | None => {}
        }
    }

    return None;
}

pub fn collect_language_items(crate: &ast::Crate,
                              session: Session)
                           -> LanguageItems {
    let mut collector = LanguageItemCollector::new(session);
    collector.collect(crate);
    let LanguageItemCollector { items, .. } = collector;
    session.abort_if_errors();
    items
}

// End of the macro
    }
}

lets_do_this! {
    There are 41 lang items.

//  ID, Variant name,                    Name,                      Method name;
    0,  FreezeTraitLangItem,             "freeze",                  freeze_trait;
    1,  SendTraitLangItem,               "send",                    send_trait;
    2,  SizedTraitLangItem,              "sized",                   sized_trait;

    3,  DropTraitLangItem,               "drop",                    drop_trait;

    4,  AddTraitLangItem,                "add",                     add_trait;
    5,  SubTraitLangItem,                "sub",                     sub_trait;
    6,  MulTraitLangItem,                "mul",                     mul_trait;
    7,  DivTraitLangItem,                "div",                     div_trait;
    8,  RemTraitLangItem,                "rem",                     rem_trait;
    9,  NegTraitLangItem,                "neg",                     neg_trait;
    10, NotTraitLangItem,                "not",                     not_trait;
    11, BitXorTraitLangItem,             "bitxor",                  bitxor_trait;
    12, BitAndTraitLangItem,             "bitand",                  bitand_trait;
    13, BitOrTraitLangItem,              "bitor",                   bitor_trait;
    14, ShlTraitLangItem,                "shl",                     shl_trait;
    15, ShrTraitLangItem,                "shr",                     shr_trait;
    16, IndexTraitLangItem,              "index",                   index_trait;

    17, EqTraitLangItem,                 "eq",                      eq_trait;
    18, OrdTraitLangItem,                "ord",                     ord_trait;

    19, StrEqFnLangItem,                 "str_eq",                  str_eq_fn;
    20, UniqStrEqFnLangItem,             "uniq_str_eq",             uniq_str_eq_fn;
    21, FailFnLangItem,                  "fail_",                   fail_fn;
    22, FailBoundsCheckFnLangItem,       "fail_bounds_check",       fail_bounds_check_fn;
    23, ExchangeMallocFnLangItem,        "exchange_malloc",         exchange_malloc_fn;
    24, ClosureExchangeMallocFnLangItem, "closure_exchange_malloc", closure_exchange_malloc_fn;
    25, ExchangeFreeFnLangItem,          "exchange_free",           exchange_free_fn;
    26, MallocFnLangItem,                "malloc",                  malloc_fn;
    27, FreeFnLangItem,                  "free",                    free_fn;
    28, BorrowAsImmFnLangItem,           "borrow_as_imm",           borrow_as_imm_fn;
    29, BorrowAsMutFnLangItem,           "borrow_as_mut",           borrow_as_mut_fn;
    30, ReturnToMutFnLangItem,           "return_to_mut",           return_to_mut_fn;
    31, CheckNotBorrowedFnLangItem,      "check_not_borrowed",      check_not_borrowed_fn;
    32, StrDupUniqFnLangItem,            "strdup_uniq",             strdup_uniq_fn;
    33, RecordBorrowFnLangItem,          "record_borrow",           record_borrow_fn;
    34, UnrecordBorrowFnLangItem,        "unrecord_borrow",         unrecord_borrow_fn;

    35, StartFnLangItem,                 "start",                   start_fn;

    36, TyDescStructLangItem,            "ty_desc",                 ty_desc;
    37, TyVisitorTraitLangItem,          "ty_visitor",              ty_visitor;
    38, OpaqueStructLangItem,            "opaque",                  opaque;

    39, EventLoopFactoryLangItem,        "event_loop_factory",      event_loop_factory;

    40, TypeIdLangItem,                  "type_id",                 type_id;
}
<|endoftext|>


<commit_before>use crate::{shim, util};
use rustc_ast::ast;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LocalDefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_index::vec::IndexVec;
use rustc_middle::mir::visit::Visitor as _;
use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::steal::Steal;
use rustc_middle::ty::{InstanceDef, TyCtxt, TypeFoldable};
use rustc_span::Span;
use std::borrow::Cow;

pub mod add_call_guards;
pub mod add_moves_for_packed_drops;
pub mod add_retag;
pub mod check_consts;
pub mod check_unsafety;
pub mod cleanup_post_borrowck;
pub mod const_prop;
pub mod copy_prop;
pub mod deaggregator;
pub mod dump_mir;
pub mod elaborate_drops;
pub mod generator;
pub mod inline;
pub mod instcombine;
pub mod no_landing_pads;
pub mod promote_consts;
pub mod qualify_min_const_fn;
pub mod remove_noop_landing_pads;
pub mod required_consts;
pub mod rustc_peek;
pub mod simplify;
pub mod simplify_branches;
pub mod simplify_try;
pub mod uninhabited_enum_branching;
pub mod unreachable_prop;

pub(crate) fn provide(providers: &mut Providers<'_>) {
    self::check_unsafety::provide(providers);
    *providers = Providers {
        mir_keys,
        mir_const,
        mir_const_qualif,
        mir_validated,
        optimized_mir,
        is_mir_available,
        promoted_mir,
        ..*providers
    };
}

fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
    tcx.mir_keys(def_id.krate).contains(&def_id)
}

/// Finds the full set of `DefId`s within the current crate that have
/// MIR associated with them.
fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> &DefIdSet {
    assert_eq!(krate, LOCAL_CRATE);

    let mut set = DefIdSet::default();

    // All body-owners have MIR associated with them.
    set.extend(tcx.body_owners().map(LocalDefId::to_def_id));

    // Additionally, tuple struct/variant constructors have MIR, but
    // they don't have a BodyId, so we need to build them separately.
    struct GatherCtors<'a, 'tcx> {
        tcx: TyCtxt<'tcx>,
        set: &'a mut DefIdSet,
    }
    impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> {
        fn visit_variant_data(
            &mut self,
            v: &'tcx hir::VariantData<'tcx>,
            _: ast::Name,
            _: &'tcx hir::Generics<'tcx>,
            _: hir::HirId,
            _: Span,
        ) {
            if let hir::VariantData::Tuple(_, hir_id) = *v {
                self.set.insert(self.tcx.hir().local_def_id(hir_id));
            }
            intravisit::walk_struct_def(self, v)
        }
        type Map = intravisit::ErasedMap<'tcx>;
        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
            NestedVisitorMap::None
        }
    }
    tcx.hir()
        .krate()
        .visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor());

    tcx.arena.alloc(set)
}

/// Where a specific `mir::Body` comes from.
#[derive(Debug, Copy, Clone)]
pub struct MirSource<'tcx> {
    pub instance: InstanceDef<'tcx>,

    /// If `Some`, this is a promoted rvalue within the parent function.
    pub promoted: Option<Promoted>,
}

impl<'tcx> MirSource<'tcx> {
    pub fn item(def_id: DefId) -> Self {
        MirSource { instance: InstanceDef::Item(def_id), promoted: None }
    }

    #[inline]
    pub fn def_id(&self) -> DefId {
        self.instance.def_id()
    }
}

/// Generates a default name for the pass based on the name of the
/// type `T`.
pub fn default_name<T: ?Sized>() -> Cow<'static, str> {
    let name = ::std::any::type_name::<T>();
    if let Some(tail) = name.rfind(':') { Cow::from(&name[tail + 1..]) } else { Cow::from(name) }
}

/// A streamlined trait that you can implement to create a pass; the
/// pass will be named after the type, and it will consist of a main
/// loop that goes over each available MIR and applies `run_pass`.
pub trait MirPass<'tcx> {
    fn name(&self) -> Cow<'_, str> {
        default_name::<Self>()
    }

    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>);
}

pub fn run_passes(
    tcx: TyCtxt<'tcx>,
    body: &mut Body<'tcx>,
    instance: InstanceDef<'tcx>,
    promoted: Option<Promoted>,
    mir_phase: MirPhase,
    passes: &[&dyn MirPass<'tcx>],
) {
    let phase_index = mir_phase.phase_index();

    if body.phase >= mir_phase {
        return;
    }

    let source = MirSource { instance, promoted };
    let mut index = 0;
    let mut run_pass = |pass: &dyn MirPass<'tcx>| {
        let run_hooks = |body: &_, index, is_after| {
            dump_mir::on_mir_pass(
                tcx,
                &format_args!("{:03}-{:03}", phase_index, index),
                &pass.name(),
                source,
                body,
                is_after,
            );
        };
        run_hooks(body, index, false);
        pass.run_pass(tcx, source, body);
        run_hooks(body, index, true);

        index += 1;
    };

    for pass in passes {
        run_pass(*pass);
    }

    body.phase = mir_phase;
}

fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
    let const_kind = check_consts::ConstKind::for_item(tcx, def_id);

    // No need to const-check a non-const `fn`.
    if const_kind.is_none() {
        return Default::default();
    }

    // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
    // cannot yet be stolen), because `mir_validated()`, which steals
    // from `mir_const(), forces this query to execute before
    // performing the steal.
    let body = &tcx.mir_const(def_id).borrow();

    if body.return_ty().references_error() {
        tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
        return Default::default();
    }

    let ccx =
        check_consts::ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) };

    let mut validator = check_consts::validation::Validator::new(&ccx);
    validator.check_body();

    // We return the qualifs in the return place for every MIR body, even though it is only used
    // when deciding to promote a reference to a `const` for now.
    validator.qualifs_in_return_place()
}

fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<Body<'_>> {
    // Unsafety check uses the raw mir, so make sure it is run
    let _ = tcx.unsafety_check_result(def_id);

    let mut body = tcx.mir_built(def_id).steal();

    util::dump_mir(tcx, None, "mir_map", &0, MirSource::item(def_id), &body, |_, _| Ok(()));

    run_passes(
        tcx,
        &mut body,
        InstanceDef::Item(def_id),
        None,
        MirPhase::Const,
        &[
            // What we need to do constant evaluation.
            &simplify::SimplifyCfg::new("initial"),
            &rustc_peek::SanityCheck,
        ],
    );
    tcx.alloc_steal_mir(body)
}

fn mir_validated(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
    // Ensure that we compute the `mir_const_qualif` for constants at
    // this point, before we steal the mir-const result.
    let _ = tcx.mir_const_qualif(def_id);

    let mut body = tcx.mir_const(def_id).steal();

    let mut required_consts = Vec::new();
    let mut required_consts_visitor =
        self::required_consts::RequiredConstsVisitor::new(&mut required_consts);
    for (bb, bb_data) in traversal::reverse_postorder(&body) {
        required_consts_visitor.visit_basic_block_data(bb, bb_data);
    }
    body.required_consts = required_consts;

    let promote_pass = promote_consts::PromoteTemps::default();
    run_passes(
        tcx,
        &mut body,
        InstanceDef::Item(def_id),
        None,
        MirPhase::Validated,
        &[
            // What we need to run borrowck etc.
            &promote_pass,
            &simplify::SimplifyCfg::new("qualify-consts"),
        ],
    );

    let promoted = promote_pass.promoted_fragments.into_inner();
    (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
}

fn run_optimization_passes<'tcx>(
    tcx: TyCtxt<'tcx>,
    body: &mut Body<'tcx>,
    def_id: DefId,
    promoted: Option<Promoted>,
) {
    run_passes(
        tcx,
        body,
        InstanceDef::Item(def_id),
        promoted,
        MirPhase::Optimized,
        &[
            // Remove all things only needed by analysis
            &no_landing_pads::NoLandingPads::new(tcx),
            &simplify_branches::SimplifyBranches::new("initial"),
            &remove_noop_landing_pads::RemoveNoopLandingPads,
            &cleanup_post_borrowck::CleanupNonCodegenStatements,
            &simplify::SimplifyCfg::new("early-opt"),
            // These next passes must be executed together
            &add_call_guards::CriticalCallEdges,
            &elaborate_drops::ElaborateDrops,
            &no_landing_pads::NoLandingPads::new(tcx),
            // AddMovesForPackedDrops needs to run after drop
            // elaboration.
            &add_moves_for_packed_drops::AddMovesForPackedDrops,
            // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
            // but before optimizations begin.
            &add_retag::AddRetag,
            &simplify::SimplifyCfg::new("elaborate-drops"),
            // No lifetime analysis based on borrowing can be done from here on out.

            // Optimizations begin.
            &unreachable_prop::UnreachablePropagation,
            &uninhabited_enum_branching::UninhabitedEnumBranching,
            &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"),
            &inline::Inline,
            // Lowering generator control-flow and variables
            // has to happen before we do anything else to them.
            &generator::StateTransform,
            &instcombine::InstCombine,
            &const_prop::ConstProp,
            &simplify_branches::SimplifyBranches::new("after-const-prop"),
            &deaggregator::Deaggregator,
            &copy_prop::CopyPropagation,
            &simplify_branches::SimplifyBranches::new("after-copy-prop"),
            &remove_noop_landing_pads::RemoveNoopLandingPads,
            &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"),
            &simplify_try::SimplifyArmIdentity,
            &simplify_try::SimplifyBranchSame,
            &simplify::SimplifyCfg::new("final"),
            &simplify::SimplifyLocals,
            &add_call_guards::CriticalCallEdges,
            &dump_mir::Marker("PreCodegen"),
        ],
    );
}

fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
    if tcx.is_constructor(def_id) {
        // There's no reason to run all of the MIR passes on constructors when
        // we can just output the MIR we want directly. This also saves const
        // qualification and borrow checking the trouble of special casing
        // constructors.
        return shim::build_adt_ctor(tcx, def_id);
    }

    // (Mir-)Borrowck uses `mir_validated`, so we have to force it to
    // execute before we can steal.
    tcx.ensure().mir_borrowck(def_id);

    let (body, _) = tcx.mir_validated(def_id);
    let mut body = body.steal();
    run_optimization_passes(tcx, &mut body, def_id, None);

    debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");

    tcx.arena.alloc(body)
}

fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &IndexVec<Promoted, Body<'_>> {
    if tcx.is_constructor(def_id) {
        return tcx.intern_promoted(IndexVec::new());
    }

    tcx.ensure().mir_borrowck(def_id);
    let (_, promoted) = tcx.mir_validated(def_id);
    let mut promoted = promoted.steal();

    for (p, mut body) in promoted.iter_enumerated_mut() {
        run_optimization_passes(tcx, &mut body, def_id, Some(p));
    }

    debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR");

    tcx.intern_promoted(promoted)
}
<commit_msg>Import RequiredConstsVisitor<commit_after>use crate::{shim, util};
use required_consts::RequiredConstsVisitor;
use rustc_ast::ast;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LocalDefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_index::vec::IndexVec;
use rustc_middle::mir::visit::Visitor as _;
use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::steal::Steal;
use rustc_middle::ty::{InstanceDef, TyCtxt, TypeFoldable};
use rustc_span::Span;
use std::borrow::Cow;

pub mod add_call_guards;
pub mod add_moves_for_packed_drops;
pub mod add_retag;
pub mod check_consts;
pub mod check_unsafety;
pub mod cleanup_post_borrowck;
pub mod const_prop;
pub mod copy_prop;
pub mod deaggregator;
pub mod dump_mir;
pub mod elaborate_drops;
pub mod generator;
pub mod inline;
pub mod instcombine;
pub mod no_landing_pads;
pub mod promote_consts;
pub mod qualify_min_const_fn;
pub mod remove_noop_landing_pads;
pub mod required_consts;
pub mod rustc_peek;
pub mod simplify;
pub mod simplify_branches;
pub mod simplify_try;
pub mod uninhabited_enum_branching;
pub mod unreachable_prop;

pub(crate) fn provide(providers: &mut Providers<'_>) {
    self::check_unsafety::provide(providers);
    *providers = Providers {
        mir_keys,
        mir_const,
        mir_const_qualif,
        mir_validated,
        optimized_mir,
        is_mir_available,
        promoted_mir,
        ..*providers
    };
}

fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
    tcx.mir_keys(def_id.krate).contains(&def_id)
}

/// Finds the full set of `DefId`s within the current crate that have
/// MIR associated with them.
fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> &DefIdSet {
    assert_eq!(krate, LOCAL_CRATE);

    let mut set = DefIdSet::default();

    // All body-owners have MIR associated with them.
    set.extend(tcx.body_owners().map(LocalDefId::to_def_id));

    // Additionally, tuple struct/variant constructors have MIR, but
    // they don't have a BodyId, so we need to build them separately.
    struct GatherCtors<'a, 'tcx> {
        tcx: TyCtxt<'tcx>,
        set: &'a mut DefIdSet,
    }
    impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> {
        fn visit_variant_data(
            &mut self,
            v: &'tcx hir::VariantData<'tcx>,
            _: ast::Name,
            _: &'tcx hir::Generics<'tcx>,
            _: hir::HirId,
            _: Span,
        ) {
            if let hir::VariantData::Tuple(_, hir_id) = *v {
                self.set.insert(self.tcx.hir().local_def_id(hir_id));
            }
            intravisit::walk_struct_def(self, v)
        }
        type Map = intravisit::ErasedMap<'tcx>;
        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
            NestedVisitorMap::None
        }
    }
    tcx.hir()
        .krate()
        .visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor());

    tcx.arena.alloc(set)
}

/// Where a specific `mir::Body` comes from.
#[derive(Debug, Copy, Clone)]
pub struct MirSource<'tcx> {
    pub instance: InstanceDef<'tcx>,

    /// If `Some`, this is a promoted rvalue within the parent function.
    pub promoted: Option<Promoted>,
}

impl<'tcx> MirSource<'tcx> {
    pub fn item(def_id: DefId) -> Self {
        MirSource { instance: InstanceDef::Item(def_id), promoted: None }
    }

    #[inline]
    pub fn def_id(&self) -> DefId {
        self.instance.def_id()
    }
}

/// Generates a default name for the pass based on the name of the
/// type `T`.
pub fn default_name<T: ?Sized>() -> Cow<'static, str> {
    let name = ::std::any::type_name::<T>();
    if let Some(tail) = name.rfind(':') { Cow::from(&name[tail + 1..]) } else { Cow::from(name) }
}

/// A streamlined trait that you can implement to create a pass; the
/// pass will be named after the type, and it will consist of a main
/// loop that goes over each available MIR and applies `run_pass`.
pub trait MirPass<'tcx> {
    fn name(&self) -> Cow<'_, str> {
        default_name::<Self>()
    }

    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>);
}

pub fn run_passes(
    tcx: TyCtxt<'tcx>,
    body: &mut Body<'tcx>,
    instance: InstanceDef<'tcx>,
    promoted: Option<Promoted>,
    mir_phase: MirPhase,
    passes: &[&dyn MirPass<'tcx>],
) {
    let phase_index = mir_phase.phase_index();

    if body.phase >= mir_phase {
        return;
    }

    let source = MirSource { instance, promoted };
    let mut index = 0;
    let mut run_pass = |pass: &dyn MirPass<'tcx>| {
        let run_hooks = |body: &_, index, is_after| {
            dump_mir::on_mir_pass(
                tcx,
                &format_args!("{:03}-{:03}", phase_index, index),
                &pass.name(),
                source,
                body,
                is_after,
            );
        };
        run_hooks(body, index, false);
        pass.run_pass(tcx, source, body);
        run_hooks(body, index, true);

        index += 1;
    };

    for pass in passes {
        run_pass(*pass);
    }

    body.phase = mir_phase;
}

fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
    let const_kind = check_consts::ConstKind::for_item(tcx, def_id);

    // No need to const-check a non-const `fn`.
    if const_kind.is_none() {
        return Default::default();
    }

    // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
    // cannot yet be stolen), because `mir_validated()`, which steals
    // from `mir_const(), forces this query to execute before
    // performing the steal.
    let body = &tcx.mir_const(def_id).borrow();

    if body.return_ty().references_error() {
        tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
        return Default::default();
    }

    let ccx =
        check_consts::ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) };

    let mut validator = check_consts::validation::Validator::new(&ccx);
    validator.check_body();

    // We return the qualifs in the return place for every MIR body, even though it is only used
    // when deciding to promote a reference to a `const` for now.
    validator.qualifs_in_return_place()
}

fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<Body<'_>> {
    // Unsafety check uses the raw mir, so make sure it is run
    let _ = tcx.unsafety_check_result(def_id);

    let mut body = tcx.mir_built(def_id).steal();

    util::dump_mir(tcx, None, "mir_map", &0, MirSource::item(def_id), &body, |_, _| Ok(()));

    run_passes(
        tcx,
        &mut body,
        InstanceDef::Item(def_id),
        None,
        MirPhase::Const,
        &[
            // What we need to do constant evaluation.
            &simplify::SimplifyCfg::new("initial"),
            &rustc_peek::SanityCheck,
        ],
    );
    tcx.alloc_steal_mir(body)
}

fn mir_validated(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
    // Ensure that we compute the `mir_const_qualif` for constants at
    // this point, before we steal the mir-const result.
    let _ = tcx.mir_const_qualif(def_id);

    let mut body = tcx.mir_const(def_id).steal();

    let mut required_consts = Vec::new();
    let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
    for (bb, bb_data) in traversal::reverse_postorder(&body) {
        required_consts_visitor.visit_basic_block_data(bb, bb_data);
    }
    body.required_consts = required_consts;

    let promote_pass = promote_consts::PromoteTemps::default();
    run_passes(
        tcx,
        &mut body,
        InstanceDef::Item(def_id),
        None,
        MirPhase::Validated,
        &[
            // What we need to run borrowck etc.
            &promote_pass,
            &simplify::SimplifyCfg::new("qualify-consts"),
        ],
    );

    let promoted = promote_pass.promoted_fragments.into_inner();
    (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
}

fn run_optimization_passes<'tcx>(
    tcx: TyCtxt<'tcx>,
    body: &mut Body<'tcx>,
    def_id: DefId,
    promoted: Option<Promoted>,
) {
    run_passes(
        tcx,
        body,
        InstanceDef::Item(def_id),
        promoted,
        MirPhase::Optimized,
        &[
            // Remove all things only needed by analysis
            &no_landing_pads::NoLandingPads::new(tcx),
            &simplify_branches::SimplifyBranches::new("initial"),
            &remove_noop_landing_pads::RemoveNoopLandingPads,
            &cleanup_post_borrowck::CleanupNonCodegenStatements,
            &simplify::SimplifyCfg::new("early-opt"),
            // These next passes must be executed together
            &add_call_guards::CriticalCallEdges,
            &elaborate_drops::ElaborateDrops,
            &no_landing_pads::NoLandingPads::new(tcx),
            // AddMovesForPackedDrops needs to run after drop
            // elaboration.
            &add_moves_for_packed_drops::AddMovesForPackedDrops,
            // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
            // but before optimizations begin.
            &add_retag::AddRetag,
            &simplify::SimplifyCfg::new("elaborate-drops"),
            // No lifetime analysis based on borrowing can be done from here on out.

            // Optimizations begin.
            &unreachable_prop::UnreachablePropagation,
            &uninhabited_enum_branching::UninhabitedEnumBranching,
            &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"),
            &inline::Inline,
            // Lowering generator control-flow and variables
            // has to happen before we do anything else to them.
            &generator::StateTransform,
            &instcombine::InstCombine,
            &const_prop::ConstProp,
            &simplify_branches::SimplifyBranches::new("after-const-prop"),
            &deaggregator::Deaggregator,
            &copy_prop::CopyPropagation,
            &simplify_branches::SimplifyBranches::new("after-copy-prop"),
            &remove_noop_landing_pads::RemoveNoopLandingPads,
            &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"),
            &simplify_try::SimplifyArmIdentity,
            &simplify_try::SimplifyBranchSame,
            &simplify::SimplifyCfg::new("final"),
            &simplify::SimplifyLocals,
            &add_call_guards::CriticalCallEdges,
            &dump_mir::Marker("PreCodegen"),
        ],
    );
}

fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
    if tcx.is_constructor(def_id) {
        // There's no reason to run all of the MIR passes on constructors when
        // we can just output the MIR we want directly. This also saves const
        // qualification and borrow checking the trouble of special casing
        // constructors.
        return shim::build_adt_ctor(tcx, def_id);
    }

    // (Mir-)Borrowck uses `mir_validated`, so we have to force it to
    // execute before we can steal.
    tcx.ensure().mir_borrowck(def_id);

    let (body, _) = tcx.mir_validated(def_id);
    let mut body = body.steal();
    run_optimization_passes(tcx, &mut body, def_id, None);

    debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");

    tcx.arena.alloc(body)
}

fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &IndexVec<Promoted, Body<'_>> {
    if tcx.is_constructor(def_id) {
        return tcx.intern_promoted(IndexVec::new());
    }

    tcx.ensure().mir_borrowck(def_id);
    let (_, promoted) = tcx.mir_validated(def_id);
    let mut promoted = promoted.steal();

    for (p, mut body) in promoted.iter_enumerated_mut() {
        run_optimization_passes(tcx, &mut body, def_id, Some(p));
    }

    debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR");

    tcx.intern_promoted(promoted)
}
<|endoftext|>