<commit_before>// Copyright 2016 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.

// This test case tests the incremental compilation hash (ICH) implementation
// for struct constructor expressions.

// The general pattern followed here is: Change one thing between rev1 and rev2
// and make sure that the hash has changed, then change nothing between rev2 and
// rev3 and make sure that the hash has not changed.

// must-compile-successfully
// revisions: cfail1 cfail2 cfail3
// compile-flags: -Z query-dep-graph

#![allow(warnings)]
#![feature(rustc_attrs)]
#![crate_type="rlib"]

struct RegularStruct {
    x: i32,
    y: i64,
    z: i16,
}

// Change field value (regular struct) -----------------------------------------
#[cfg(cfail1)]
fn change_field_value_regular_struct() -> RegularStruct {
    RegularStruct {
        x: 0,
        y: 1,
        z: 2,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_value_regular_struct() -> RegularStruct {
    RegularStruct {
        x: 0,
        y: 2,
        z: 2,
    }
}

// Change field order (regular struct) -----------------------------------------
#[cfg(cfail1)]
fn change_field_order_regular_struct() -> RegularStruct {
    RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_order_regular_struct() -> RegularStruct {
    RegularStruct {
        y: 4,
        x: 3,
        z: 5,
    }
}

// Add field (regular struct) --------------------------------------------------
#[cfg(cfail1)]
fn add_field_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        .. struct1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn add_field_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        y: 8,
        .. struct1
    }
}

// Change field label (regular struct) -----------------------------------------
#[cfg(cfail1)]
fn change_field_label_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        y: 9,
        .. struct1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_label_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        z: 9,
        .. struct1
    }
}

struct RegularStruct2 {
    x: i8,
    y: i8,
    z: i8,
}

// Change constructor path (regular struct) ------------------------------------
#[cfg(cfail1)]
fn change_constructor_path_regular_struct() {
    let _ = RegularStruct {
        x: 0,
        y: 1,
        z: 2,
    };
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_constructor_path_regular_struct() {
    let _ = RegularStruct2 {
        x: 0,
        y: 1,
        z: 2,
    };
}

// Change constructor path indirectly (regular struct) -------------------------
mod change_constructor_path_indirectly_regular_struct {
    #[cfg(cfail1)]
    use super::RegularStruct as Struct;
    #[cfg(not(cfail1))]
    use super::RegularStruct2 as Struct;

    fn function() -> Struct {
        Struct {
            x: 0,
            y: 1,
            z: 2,
        }
    }
}

struct TupleStruct(i32, i64, i16);

// Change field value (tuple struct) -------------------------------------------
#[cfg(cfail1)]
fn change_field_value_tuple_struct() -> TupleStruct {
    TupleStruct(0, 1, 2)
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_value_tuple_struct() -> TupleStruct {
    TupleStruct(0, 1, 3)
}

struct TupleStruct2(u16, u16, u16);

// Change constructor path (tuple struct) --------------------------------------
#[cfg(cfail1)]
fn change_constructor_path_tuple_struct() {
    let _ = TupleStruct(0, 1, 2);
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_constructor_path_tuple_struct() {
    let _ = TupleStruct2(0, 1, 2);
}

// Change constructor path indirectly (tuple struct) ---------------------------
mod change_constructor_path_indirectly_tuple_struct {
    #[cfg(cfail1)]
    use super::TupleStruct as Struct;
    #[cfg(not(cfail1))]
    use super::TupleStruct2 as Struct;

    fn function() -> Struct {
        Struct(0, 1, 2)
    }
}
<commit_msg>ICH: Add missing annotations for struct constructor expr test case.<commit_after>// Copyright 2016 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.

// This test case tests the incremental compilation hash (ICH) implementation
// for struct constructor expressions.

// The general pattern followed here is: Change one thing between rev1 and rev2
// and make sure that the hash has changed, then change nothing between rev2 and
// rev3 and make sure that the hash has not changed.

// must-compile-successfully
// revisions: cfail1 cfail2 cfail3
// compile-flags: -Z query-dep-graph

#![allow(warnings)]
#![feature(rustc_attrs)]
#![crate_type="rlib"]

struct RegularStruct {
    x: i32,
    y: i64,
    z: i16,
}

// Change field value (regular struct) -----------------------------------------
#[cfg(cfail1)]
fn change_field_value_regular_struct() -> RegularStruct {
    RegularStruct {
        x: 0,
        y: 1,
        z: 2,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_value_regular_struct() -> RegularStruct {
    RegularStruct {
        x: 0,
        y: 2,
        z: 2,
    }
}

// Change field order (regular struct) -----------------------------------------
#[cfg(cfail1)]
fn change_field_order_regular_struct() -> RegularStruct {
    RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_order_regular_struct() -> RegularStruct {
    RegularStruct {
        y: 4,
        x: 3,
        z: 5,
    }
}

// Add field (regular struct) --------------------------------------------------
#[cfg(cfail1)]
fn add_field_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        .. struct1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn add_field_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        y: 8,
        .. struct1
    }
}

// Change field label (regular struct) -----------------------------------------
#[cfg(cfail1)]
fn change_field_label_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        y: 9,
        .. struct1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_label_regular_struct() -> RegularStruct {
    let struct1 = RegularStruct {
        x: 3,
        y: 4,
        z: 5,
    };

    RegularStruct {
        x: 7,
        z: 9,
        .. struct1
    }
}

struct RegularStruct2 {
    x: i8,
    y: i8,
    z: i8,
}

// Change constructor path (regular struct) ------------------------------------
#[cfg(cfail1)]
fn change_constructor_path_regular_struct() {
    let _ = RegularStruct {
        x: 0,
        y: 1,
        z: 2,
    };
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_constructor_path_regular_struct() {
    let _ = RegularStruct2 {
        x: 0,
        y: 1,
        z: 2,
    };
}

// Change constructor path indirectly (regular struct) -------------------------
mod change_constructor_path_indirectly_regular_struct {
    #[cfg(cfail1)]
    use super::RegularStruct as Struct;
    #[cfg(not(cfail1))]
    use super::RegularStruct2 as Struct;

    #[rustc_dirty(label="Hir", cfg="cfail2")]
    #[rustc_clean(label="Hir", cfg="cfail3")]
    #[rustc_dirty(label="HirBody", cfg="cfail2")]
    #[rustc_clean(label="HirBody", cfg="cfail3")]
    #[rustc_metadata_dirty(cfg="cfail2")]
    #[rustc_metadata_clean(cfg="cfail3")]
    fn function() -> Struct {
        Struct {
            x: 0,
            y: 1,
            z: 2,
        }
    }
}

struct TupleStruct(i32, i64, i16);

// Change field value (tuple struct) -------------------------------------------
#[cfg(cfail1)]
fn change_field_value_tuple_struct() -> TupleStruct {
    TupleStruct(0, 1, 2)
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_field_value_tuple_struct() -> TupleStruct {
    TupleStruct(0, 1, 3)
}

struct TupleStruct2(u16, u16, u16);

// Change constructor path (tuple struct) --------------------------------------
#[cfg(cfail1)]
fn change_constructor_path_tuple_struct() {
    let _ = TupleStruct(0, 1, 2);
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn change_constructor_path_tuple_struct() {
    let _ = TupleStruct2(0, 1, 2);
}

// Change constructor path indirectly (tuple struct) ---------------------------
mod change_constructor_path_indirectly_tuple_struct {
    #[cfg(cfail1)]
    use super::TupleStruct as Struct;
    #[cfg(not(cfail1))]
    use super::TupleStruct2 as Struct;

    #[rustc_dirty(label="Hir", cfg="cfail2")]
    #[rustc_clean(label="Hir", cfg="cfail3")]
    #[rustc_dirty(label="HirBody", cfg="cfail2")]
    #[rustc_clean(label="HirBody", cfg="cfail3")]
    #[rustc_metadata_dirty(cfg="cfail2")]
    #[rustc_metadata_clean(cfg="cfail3")]
    fn function() -> Struct {
        Struct(0, 1, 2)
    }
}
<|endoftext|>


<commit_before>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashMap, HashSet};
use std::cmp;

use tempdir::TempDir;

use collector::{Benchmark as CollectedBenchmark, BenchmarkState, Patch, Run, Stat};

use failure::{err_msg, Error, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

use Mode;

fn command_output(cmd: &mut Command) -> Result<process::Output, Error> {
    trace!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<(), Error> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name '*.rs' | xargs touch"]);
    command_output(&mut cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
    #[serde(default = "default_true")]
    nll: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
            nll: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    nll: bool,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base_command(&self, cwd: &Path, subcommand: &str) -> Command {
        let mut cmd = self.sysroot.command("cargo");
        cmd
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env(
                "CARGO_INCREMENTAL",
                &format!("{}", self.incremental.0 as usize),
            )
            .env("USE_PERF", &format!("{}", self.perf as usize))
            .current_dir(cwd)
            .arg(subcommand)
            .arg("--manifest-path")
            .arg(&self.manifest_path);
        cmd
    }

    fn nll(mut self, nll: bool) -> Self {
        self.nll = nll;
        self
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn get_pkgid(&self, cwd: &Path) -> String {
        let mut pkgid_cmd = self.base_command(cwd, "pkgid");
        let out = command_output(&mut pkgid_cmd)
            .unwrap_or_else(|e| {
                panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
            })
            .stdout;
        let package_id = str::from_utf8(&out).unwrap();
        package_id.trim().to_string()
    }

    fn run_base_command(&self, cwd: &Path, subcommand: &str) -> Command {
        let mut cmd = self.base_command(cwd, subcommand);
        cmd.arg("-p").arg(self.get_pkgid(cwd));
        if self.options.release {
            cmd.arg("--release");
        }
        cmd
    }

    fn run_rustc(self, cwd: &Path) -> Result<Vec<Stat>, Error> {
        let mut cmd = self.run_base_command(cwd, "rustc");
        if self.options.check {
            cmd.arg("--profile").arg("check");
        }
        cmd.args(&self.cargo_args);
        cmd.arg("--");
        if self.nll {
            cmd.arg("-Znll");
        }
        cmd.arg("-Ztime-passes");
        cmd.args(&self.rustc_args);

        debug!("run_rustc: {:?}", cmd);

        loop {
            touch_all(&cwd)?;
            let output = command_output(&mut cmd)?;
            match process_output(output) {
                Ok(stats) => return Ok(stats),
                Err(DeserializeStatError::NoOutput(output)) => {
                    warn!(
                        "failed to deserialize stats, retrying; output: {:?}",
                        output
                    );
                }
                Err(e @ DeserializeStatError::ParseError { .. }) => {
                    panic!("process_output failed: {:?}", e);
                }
            }
        }
    }

    fn run_clean(self, cwd: &Path) -> Result<(), Error> {
        let mut cmd = self.run_base_command(cwd, "clean");

        debug!("run_clean: {:?}", cmd);

        let _output = command_output(&mut cmd)?;
        return Ok(());
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self, Error> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path)
                .with_context(|_| format!("failed to open {:?}", config_path))?)
                .with_context(|_| {
                format!("failed to parse {:?}", config_path)
            })?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name,
            path,
            patches,
            config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir, Error> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd.arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        command_output(&mut cmd).with_context(|_| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn mk_cargo_process<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            nll: false,
            manifest_path: self.config
                .cargo_toml
                .clone()
                .unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args: self.config
                .cargo_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
            rustc_args: self.config
                .cargo_rustc_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(
        &self,
        sysroot: &Sysroot,
        iterations: usize,
        mode: Mode,
    ) -> Result<CollectedBenchmark, Error> {
        let iterations = cmp::min(iterations, self.config.runs);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        match mode {
            Mode::Normal => {
                if self.config.run_debug {
                    opts.push(Options {
                        check: true,
                        release: false,
                    });
                    opts.push(Options {
                        check: false,
                        release: false,
                    });
                }
                if self.config.run_optimized {
                    opts.push(Options {
                        check: false,
                        release: true,
                    });
                }
            }
            Mode::Test => {
                opts.push(Options {
                    check: true,
                    release: false,
                });
            }
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut nll_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            info!(
                "Benchmarking {} with release: {}, check: {}, iterations: {}",
                self.name, opt.release, opt.check, iterations
            );

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                .run_rustc(base_build.path())?;
            clean_stats.push(clean);
            self.mk_cargo_process(sysroot, Incremental(false), opt)
                .perf(false)
                .run_clean(base_build.path())?;

            for i in 0..iterations {
                debug!("Benchmark iteration {}/{}", i + 1, iterations);
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                    .run_rustc(tmp_dir.path())?;
                if self.config.nll {
                    let nll = self.mk_cargo_process(sysroot, Incremental(false), opt)
                        .nll(true)
                        .run_rustc(base_build.path())?;
                    nll_stats.push(nll);
                }
                let incr = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run_rustc(tmp_dir.path())?;
                let incr_clean = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run_rustc(tmp_dir.path())?;

                clean_stats.push(clean);
                incr_stats.push(incr);
                incr_clean_stats.push(incr_clean);

                for patch in &self.patches {
                    debug!("applying patch {}", patch.name);
                    patch.apply(tmp_dir.path()).map_err(|s| err_msg(s))?;
                    let out = self.mk_cargo_process(sysroot, Incremental(true), opt)
                        .run_rustc(tmp_dir.path())?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut().find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs
                .push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            if self.config.nll {
                ret.runs
                    .push(process_stats(opt, BenchmarkState::Nll, nll_stats));
            }
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalStart,
                incr_stats,
            ));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalClean,
                incr_clean_stats,
            ));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

#[derive(Fail, PartialEq, Eq, Debug)]
enum DeserializeStatError {
    #[fail(display = "could not deserialize empty output to stats, output: {:?}", _0)]
    NoOutput(process::Output),
    #[fail(display = "could not parse `{}` as a float", _0)]
    ParseError(String, #[fail(cause)] ::std::num::ParseFloatError),
}

fn process_output(output: process::Output) -> Result<Vec<Stat>, DeserializeStatError> {
    let stdout = String::from_utf8(output.stdout.clone()).expect("utf8 output");
    let mut stats = Vec::new();

    for line in stdout.lines() {
        // github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e: expr) => {
                match $e {
                    Some(s) => s,
                    None => {
                        warn!("unhandled line: {}", line);
                        continue;
                    }
                }
            };
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name, pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse()
                .map_err(|e| DeserializeStatError::ParseError(cnt.to_string(), e))?,
        });
    }

    if stats.is_empty() {
        return Err(DeserializeStatError::NoOutput(output));
    }

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats
                .entry(stat.name.clone())
                .or_insert_with(|| Vec::new())
                .push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("options: {:?}", options);
        eprintln!("state: {:?}", state);
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats
        .into_iter()
        .map(|(stat, counts)| Stat {
            name: stat,
            cnt: counts
                .into_iter()
                .fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<commit_msg>Use the correct directory for the nll build.<commit_after>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashMap, HashSet};
use std::cmp;

use tempdir::TempDir;

use collector::{Benchmark as CollectedBenchmark, BenchmarkState, Patch, Run, Stat};

use failure::{err_msg, Error, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

use Mode;

fn command_output(cmd: &mut Command) -> Result<process::Output, Error> {
    trace!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<(), Error> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name '*.rs' | xargs touch"]);
    command_output(&mut cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
    #[serde(default = "default_true")]
    nll: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
            nll: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    nll: bool,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base_command(&self, cwd: &Path, subcommand: &str) -> Command {
        let mut cmd = self.sysroot.command("cargo");
        cmd
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env(
                "CARGO_INCREMENTAL",
                &format!("{}", self.incremental.0 as usize),
            )
            .env("USE_PERF", &format!("{}", self.perf as usize))
            .current_dir(cwd)
            .arg(subcommand)
            .arg("--manifest-path")
            .arg(&self.manifest_path);
        cmd
    }

    fn nll(mut self, nll: bool) -> Self {
        self.nll = nll;
        self
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn get_pkgid(&self, cwd: &Path) -> String {
        let mut pkgid_cmd = self.base_command(cwd, "pkgid");
        let out = command_output(&mut pkgid_cmd)
            .unwrap_or_else(|e| {
                panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
            })
            .stdout;
        let package_id = str::from_utf8(&out).unwrap();
        package_id.trim().to_string()
    }

    fn run_base_command(&self, cwd: &Path, subcommand: &str) -> Command {
        let mut cmd = self.base_command(cwd, subcommand);
        cmd.arg("-p").arg(self.get_pkgid(cwd));
        if self.options.release {
            cmd.arg("--release");
        }
        cmd
    }

    fn run_rustc(self, cwd: &Path) -> Result<Vec<Stat>, Error> {
        let mut cmd = self.run_base_command(cwd, "rustc");
        if self.options.check {
            cmd.arg("--profile").arg("check");
        }
        cmd.args(&self.cargo_args);
        cmd.arg("--");
        if self.nll {
            cmd.arg("-Znll");
        }
        cmd.arg("-Ztime-passes");
        cmd.args(&self.rustc_args);

        debug!("run_rustc: {:?}", cmd);

        loop {
            touch_all(&cwd)?;
            let output = command_output(&mut cmd)?;
            match process_output(output) {
                Ok(stats) => return Ok(stats),
                Err(DeserializeStatError::NoOutput(output)) => {
                    warn!(
                        "failed to deserialize stats, retrying; output: {:?}",
                        output
                    );
                }
                Err(e @ DeserializeStatError::ParseError { .. }) => {
                    panic!("process_output failed: {:?}", e);
                }
            }
        }
    }

    fn run_clean(self, cwd: &Path) -> Result<(), Error> {
        let mut cmd = self.run_base_command(cwd, "clean");

        debug!("run_clean: {:?}", cmd);

        let _output = command_output(&mut cmd)?;
        return Ok(());
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self, Error> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path)
                .with_context(|_| format!("failed to open {:?}", config_path))?)
                .with_context(|_| {
                format!("failed to parse {:?}", config_path)
            })?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name,
            path,
            patches,
            config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir, Error> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd.arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        command_output(&mut cmd).with_context(|_| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn mk_cargo_process<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            nll: false,
            manifest_path: self.config
                .cargo_toml
                .clone()
                .unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args: self.config
                .cargo_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
            rustc_args: self.config
                .cargo_rustc_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(
        &self,
        sysroot: &Sysroot,
        iterations: usize,
        mode: Mode,
    ) -> Result<CollectedBenchmark, Error> {
        let iterations = cmp::min(iterations, self.config.runs);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        match mode {
            Mode::Normal => {
                if self.config.run_debug {
                    opts.push(Options {
                        check: true,
                        release: false,
                    });
                    opts.push(Options {
                        check: false,
                        release: false,
                    });
                }
                if self.config.run_optimized {
                    opts.push(Options {
                        check: false,
                        release: true,
                    });
                }
            }
            Mode::Test => {
                opts.push(Options {
                    check: true,
                    release: false,
                });
            }
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut nll_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            info!(
                "Benchmarking {} with release: {}, check: {}, iterations: {}",
                self.name, opt.release, opt.check, iterations
            );

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                .run_rustc(base_build.path())?;
            clean_stats.push(clean);
            self.mk_cargo_process(sysroot, Incremental(false), opt)
                .perf(false)
                .run_clean(base_build.path())?;

            for i in 0..iterations {
                debug!("Benchmark iteration {}/{}", i + 1, iterations);
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                    .run_rustc(tmp_dir.path())?;
                if self.config.nll {
                    let nll = self.mk_cargo_process(sysroot, Incremental(false), opt)
                        .nll(true)
                        .run_rustc(tmp_dir.path())?;
                    nll_stats.push(nll);
                }
                let incr = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run_rustc(tmp_dir.path())?;
                let incr_clean = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run_rustc(tmp_dir.path())?;

                clean_stats.push(clean);
                incr_stats.push(incr);
                incr_clean_stats.push(incr_clean);

                for patch in &self.patches {
                    debug!("applying patch {}", patch.name);
                    patch.apply(tmp_dir.path()).map_err(|s| err_msg(s))?;
                    let out = self.mk_cargo_process(sysroot, Incremental(true), opt)
                        .run_rustc(tmp_dir.path())?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut().find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs
                .push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            if self.config.nll {
                ret.runs
                    .push(process_stats(opt, BenchmarkState::Nll, nll_stats));
            }
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalStart,
                incr_stats,
            ));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalClean,
                incr_clean_stats,
            ));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

#[derive(Fail, PartialEq, Eq, Debug)]
enum DeserializeStatError {
    #[fail(display = "could not deserialize empty output to stats, output: {:?}", _0)]
    NoOutput(process::Output),
    #[fail(display = "could not parse `{}` as a float", _0)]
    ParseError(String, #[fail(cause)] ::std::num::ParseFloatError),
}

fn process_output(output: process::Output) -> Result<Vec<Stat>, DeserializeStatError> {
    let stdout = String::from_utf8(output.stdout.clone()).expect("utf8 output");
    let mut stats = Vec::new();

    for line in stdout.lines() {
        // github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e: expr) => {
                match $e {
                    Some(s) => s,
                    None => {
                        warn!("unhandled line: {}", line);
                        continue;
                    }
                }
            };
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name, pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse()
                .map_err(|e| DeserializeStatError::ParseError(cnt.to_string(), e))?,
        });
    }

    if stats.is_empty() {
        return Err(DeserializeStatError::NoOutput(output));
    }

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats
                .entry(stat.name.clone())
                .or_insert_with(|| Vec::new())
                .push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("options: {:?}", options);
        eprintln!("state: {:?}", state);
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats
        .into_iter()
        .map(|(stat, counts)| Stat {
            name: stat,
            cnt: counts
                .into_iter()
                .fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<|endoftext|>


<commit_before>use hir::{EnumVariant, Module, ModuleDef, Name};
use ra_db::FileId;
use ra_fmt::leading_indent;
use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
use rustc_hash::FxHashSet;
use syntax::{
    algo::find_node_at_offset,
    ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
    SourceFile, SyntaxNode, TextRange, TextSize,
};

use crate::{
    assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
    AssistKind, Assists,
};

// Assist: extract_struct_from_enum_variant
//
// Extracts a struct from enum variant.
//
// ```
// enum A { <|>One(u32, u32) }
// ```
// ->
// ```
// struct One(pub u32, pub u32);
//
// enum A { One(One) }
// ```
pub(crate) fn extract_struct_from_enum_variant(
    acc: &mut Assists,
    ctx: &AssistContext,
) -> Option<()> {
    let variant = ctx.find_node_at_offset::<ast::Variant>()?;
    let field_list = match variant.kind() {
        ast::StructKind::Tuple(field_list) => field_list,
        _ => return None,
    };
    let variant_name = variant.name()?.to_string();
    let variant_hir = ctx.sema.to_def(&variant)?;
    if existing_struct_def(ctx.db(), &variant_name, &variant_hir) {
        return None;
    }
    let enum_ast = variant.parent_enum();
    let visibility = enum_ast.visibility();
    let enum_hir = ctx.sema.to_def(&enum_ast)?;
    let variant_hir_name = variant_hir.name(ctx.db());
    let enum_module_def = ModuleDef::from(enum_hir);
    let current_module = enum_hir.module(ctx.db());
    let target = variant.syntax().text_range();
    acc.add(
        AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
        "Extract struct from enum variant",
        target,
        |builder| {
            let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
            let res = definition.find_usages(&ctx.sema, None);
            let start_offset = variant.parent_enum().syntax().text_range().start();
            let mut visited_modules_set = FxHashSet::default();
            visited_modules_set.insert(current_module);
            for reference in res {
                let source_file = ctx.sema.parse(reference.file_range.file_id);
                update_reference(
                    ctx,
                    builder,
                    reference,
                    &source_file,
                    &enum_module_def,
                    &variant_hir_name,
                    &mut visited_modules_set,
                );
            }
            extract_struct_def(
                builder,
                enum_ast.syntax(),
                &variant_name,
                &field_list.to_string(),
                start_offset,
                ctx.frange.file_id,
                &visibility,
            );
            let list_range = field_list.syntax().text_range();
            update_variant(builder, &variant_name, ctx.frange.file_id, list_range);
        },
    )
}

fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
    variant
        .parent_enum(db)
        .module(db)
        .scope(db, None)
        .into_iter()
        .any(|(name, _)| name.to_string() == variant_name.to_string())
}

fn insert_import(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    path: &ast::PathExpr,
    module: &Module,
    enum_module_def: &ModuleDef,
    variant_hir_name: &Name,
) -> Option<()> {
    let db = ctx.db();
    let mod_path = module.find_use_path(db, enum_module_def.clone());
    if let Some(mut mod_path) = mod_path {
        mod_path.segments.pop();
        mod_path.segments.push(variant_hir_name.clone());
        insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
    }
    Some(())
}

fn extract_struct_def(
    builder: &mut AssistBuilder,
    enum_ast: &SyntaxNode,
    variant_name: &str,
    variant_list: &str,
    start_offset: TextSize,
    file_id: FileId,
    visibility: &Option<ast::Visibility>,
) -> Option<()> {
    let visibility_string = if let Some(visibility) = visibility {
        format!("{} ", visibility.to_string())
    } else {
        "".to_string()
    };
    let indent = if let Some(indent) = leading_indent(enum_ast) {
        indent.to_string()
    } else {
        "".to_string()
    };
    let struct_def = format!(
        r#"{}struct {}{};

{}"#,
        visibility_string,
        variant_name,
        list_with_visibility(variant_list),
        indent
    );
    builder.edit_file(file_id);
    builder.insert(start_offset, struct_def);
    Some(())
}

fn update_variant(
    builder: &mut AssistBuilder,
    variant_name: &str,
    file_id: FileId,
    list_range: TextRange,
) -> Option<()> {
    let inside_variant_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    builder.edit_file(file_id);
    builder.replace(inside_variant_range, variant_name);
    Some(())
}

fn update_reference(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    reference: Reference,
    source_file: &SourceFile,
    enum_module_def: &ModuleDef,
    variant_hir_name: &Name,
    visited_modules_set: &mut FxHashSet<Module>,
) -> Option<()> {
    let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
        source_file.syntax(),
        reference.file_range.range.start(),
    )?;
    let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
    let list = call.arg_list()?;
    let segment = path_expr.path()?.segment()?;
    let module = ctx.sema.scope(&path_expr.syntax()).module()?;
    let list_range = list.syntax().text_range();
    let inside_list_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    builder.edit_file(reference.file_range.file_id);
    if !visited_modules_set.contains(&module) {
        if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
            .is_some()
        {
            visited_modules_set.insert(module);
        }
    }
    builder.replace(inside_list_range, format!("{}{}", segment, list));
    Some(())
}

fn list_with_visibility(list: &str) -> String {
    list.split(',')
        .map(|part| {
            let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
            let mut mod_part = part.trim().to_string();
            mod_part.insert_str(index, "pub ");
            mod_part
        })
        .collect::<Vec<String>>()
        .join(", ")
}

#[cfg(test)]
mod tests {

    use crate::{
        tests::{check_assist, check_assist_not_applicable},
        utils::FamousDefs,
    };

    use super::*;

    #[test]
    fn test_extract_struct_several_fields() {
        check_assist(
            extract_struct_from_enum_variant,
            "enum A { <|>One(u32, u32) }",
            r#"struct One(pub u32, pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_one_field() {
        check_assist(
            extract_struct_from_enum_variant,
            "enum A { <|>One(u32) }",
            r#"struct One(pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_pub_visibility() {
        check_assist(
            extract_struct_from_enum_variant,
            "pub enum A { <|>One(u32, u32) }",
            r#"pub struct One(pub u32, pub u32);

pub enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_with_complex_imports() {
        check_assist(
            extract_struct_from_enum_variant,
            r#"mod my_mod {
    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(1, 1);
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(1, 1);
        }

        pub enum MyEnum {
            <|>MyField(u8, u8),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
}"#,
            r#"use my_mod::my_other_mod::MyField;

mod my_mod {
    use my_other_mod::MyField;

    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(MyField(1, 1));
        }

        pub struct MyField(pub u8, pub u8);

        pub enum MyEnum {
            MyField(MyField),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
}"#,
        );
    }

    fn check_not_applicable(ra_fixture: &str) {
        let fixture =
            format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
        check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
    }

    #[test]
    fn test_extract_enum_not_applicable_for_element_with_no_fields() {
        check_not_applicable("enum A { <|>One }");
    }

    #[test]
    fn test_extract_enum_not_applicable_if_struct_exists() {
        check_not_applicable(
            r#"struct One;
        enum A { <|>One(u8) }"#,
        );
    }
}
<commit_msg>Minor<commit_after>use hir::{EnumVariant, Module, ModuleDef, Name};
use ra_db::FileId;
use ra_fmt::leading_indent;
use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
use rustc_hash::FxHashSet;
use syntax::{
    algo::find_node_at_offset,
    ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
    SourceFile, TextRange, TextSize,
};

use crate::{
    assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
    AssistKind, Assists,
};

// Assist: extract_struct_from_enum_variant
//
// Extracts a struct from enum variant.
//
// ```
// enum A { <|>One(u32, u32) }
// ```
// ->
// ```
// struct One(pub u32, pub u32);
//
// enum A { One(One) }
// ```
pub(crate) fn extract_struct_from_enum_variant(
    acc: &mut Assists,
    ctx: &AssistContext,
) -> Option<()> {
    let variant = ctx.find_node_at_offset::<ast::Variant>()?;
    let field_list = match variant.kind() {
        ast::StructKind::Tuple(field_list) => field_list,
        _ => return None,
    };
    let variant_name = variant.name()?.to_string();
    let variant_hir = ctx.sema.to_def(&variant)?;
    if existing_struct_def(ctx.db(), &variant_name, &variant_hir) {
        return None;
    }
    let enum_ast = variant.parent_enum();
    let visibility = enum_ast.visibility();
    let enum_hir = ctx.sema.to_def(&enum_ast)?;
    let variant_hir_name = variant_hir.name(ctx.db());
    let enum_module_def = ModuleDef::from(enum_hir);
    let current_module = enum_hir.module(ctx.db());
    let target = variant.syntax().text_range();
    acc.add(
        AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
        "Extract struct from enum variant",
        target,
        |builder| {
            let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
            let res = definition.find_usages(&ctx.sema, None);
            let start_offset = variant.parent_enum().syntax().text_range().start();
            let mut visited_modules_set = FxHashSet::default();
            visited_modules_set.insert(current_module);
            for reference in res {
                let source_file = ctx.sema.parse(reference.file_range.file_id);
                update_reference(
                    ctx,
                    builder,
                    reference,
                    &source_file,
                    &enum_module_def,
                    &variant_hir_name,
                    &mut visited_modules_set,
                );
            }
            extract_struct_def(
                builder,
                &enum_ast,
                &variant_name,
                &field_list.to_string(),
                start_offset,
                ctx.frange.file_id,
                &visibility,
            );
            let list_range = field_list.syntax().text_range();
            update_variant(builder, &variant_name, ctx.frange.file_id, list_range);
        },
    )
}

fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
    variant
        .parent_enum(db)
        .module(db)
        .scope(db, None)
        .into_iter()
        .any(|(name, _)| name.to_string() == variant_name.to_string())
}

fn insert_import(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    path: &ast::PathExpr,
    module: &Module,
    enum_module_def: &ModuleDef,
    variant_hir_name: &Name,
) -> Option<()> {
    let db = ctx.db();
    let mod_path = module.find_use_path(db, enum_module_def.clone());
    if let Some(mut mod_path) = mod_path {
        mod_path.segments.pop();
        mod_path.segments.push(variant_hir_name.clone());
        insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
    }
    Some(())
}

// FIXME: this should use strongly-typed `make`, rather than string manipulation1
fn extract_struct_def(
    builder: &mut AssistBuilder,
    enum_: &ast::Enum,
    variant_name: &str,
    variant_list: &str,
    start_offset: TextSize,
    file_id: FileId,
    visibility: &Option<ast::Visibility>,
) -> Option<()> {
    let visibility_string = if let Some(visibility) = visibility {
        format!("{} ", visibility.to_string())
    } else {
        "".to_string()
    };
    let indent = if let Some(indent) = leading_indent(enum_.syntax()) {
        indent.to_string()
    } else {
        "".to_string()
    };
    let struct_def = format!(
        r#"{}struct {}{};

{}"#,
        visibility_string,
        variant_name,
        list_with_visibility(variant_list),
        indent
    );
    builder.edit_file(file_id);
    builder.insert(start_offset, struct_def);
    Some(())
}

fn update_variant(
    builder: &mut AssistBuilder,
    variant_name: &str,
    file_id: FileId,
    list_range: TextRange,
) -> Option<()> {
    let inside_variant_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    builder.edit_file(file_id);
    builder.replace(inside_variant_range, variant_name);
    Some(())
}

fn update_reference(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    reference: Reference,
    source_file: &SourceFile,
    enum_module_def: &ModuleDef,
    variant_hir_name: &Name,
    visited_modules_set: &mut FxHashSet<Module>,
) -> Option<()> {
    let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
        source_file.syntax(),
        reference.file_range.range.start(),
    )?;
    let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
    let list = call.arg_list()?;
    let segment = path_expr.path()?.segment()?;
    let module = ctx.sema.scope(&path_expr.syntax()).module()?;
    let list_range = list.syntax().text_range();
    let inside_list_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    builder.edit_file(reference.file_range.file_id);
    if !visited_modules_set.contains(&module) {
        if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
            .is_some()
        {
            visited_modules_set.insert(module);
        }
    }
    builder.replace(inside_list_range, format!("{}{}", segment, list));
    Some(())
}

fn list_with_visibility(list: &str) -> String {
    list.split(',')
        .map(|part| {
            let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
            let mut mod_part = part.trim().to_string();
            mod_part.insert_str(index, "pub ");
            mod_part
        })
        .collect::<Vec<String>>()
        .join(", ")
}

#[cfg(test)]
mod tests {

    use crate::{
        tests::{check_assist, check_assist_not_applicable},
        utils::FamousDefs,
    };

    use super::*;

    #[test]
    fn test_extract_struct_several_fields() {
        check_assist(
            extract_struct_from_enum_variant,
            "enum A { <|>One(u32, u32) }",
            r#"struct One(pub u32, pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_one_field() {
        check_assist(
            extract_struct_from_enum_variant,
            "enum A { <|>One(u32) }",
            r#"struct One(pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_pub_visibility() {
        check_assist(
            extract_struct_from_enum_variant,
            "pub enum A { <|>One(u32, u32) }",
            r#"pub struct One(pub u32, pub u32);

pub enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_with_complex_imports() {
        check_assist(
            extract_struct_from_enum_variant,
            r#"mod my_mod {
    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(1, 1);
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(1, 1);
        }

        pub enum MyEnum {
            <|>MyField(u8, u8),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
}"#,
            r#"use my_mod::my_other_mod::MyField;

mod my_mod {
    use my_other_mod::MyField;

    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(MyField(1, 1));
        }

        pub struct MyField(pub u8, pub u8);

        pub enum MyEnum {
            MyField(MyField),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
}"#,
        );
    }

    fn check_not_applicable(ra_fixture: &str) {
        let fixture =
            format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
        check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
    }

    #[test]
    fn test_extract_enum_not_applicable_for_element_with_no_fields() {
        check_not_applicable("enum A { <|>One }");
    }

    #[test]
    fn test_extract_enum_not_applicable_if_struct_exists() {
        check_not_applicable(
            r#"struct One;
        enum A { <|>One(u8) }"#,
        );
    }
}
<|endoftext|>


<commit_before>//! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.

use rustc::mir::*;
use rustc::mir::interpret::ConstValue;
use rustc::ty::{self, Ty};
use rustc_data_structures::bit_set::BitSet;
use syntax_pos::DUMMY_SP;

use super::Item as ConstCx;
use super::validation::Mode;

#[derive(Clone, Copy)]
pub struct QualifSet(u8);

impl QualifSet {
    fn contains<Q: ?Sized + Qualif>(self) -> bool {
        self.0 & (1 << Q::IDX) != 0
    }
}

/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
/// code for promotion or prevent it from evaluating at compile time. So `return true` means
/// "I found something bad, no reason to go on searching". `false` is only returned if we
/// definitely cannot find anything bad anywhere.
///
/// The default implementations proceed structurally.
pub trait Qualif {
    const IDX: usize;

    /// Whether this `Qualif` is cleared when a local is moved from.
    const IS_CLEARED_ON_MOVE: bool = false;

    /// Return the qualification that is (conservatively) correct for any value
    /// of the type.
    fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;

    fn in_static(cx: &ConstCx<'_, 'tcx>, statik: &Static<'tcx>) -> bool {
        Self::in_any_value_of_ty(cx, statik.ty)
    }

    fn in_projection_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        if let [proj_base @ .., elem] = place.projection {
            let base_qualif = Self::in_place(cx, per_local, PlaceRef {
                base: place.base,
                projection: proj_base,
            });
            let qualif = base_qualif && Self::in_any_value_of_ty(
                cx,
                Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
                    .projection_ty(cx.tcx, elem)
                    .ty,
            );
            match elem {
                ProjectionElem::Deref |
                ProjectionElem::Subslice { .. } |
                ProjectionElem::Field(..) |
                ProjectionElem::ConstantIndex { .. } |
                ProjectionElem::Downcast(..) => qualif,

                ProjectionElem::Index(local) => qualif || per_local.contains(*local),
            }
        } else {
            bug!("This should be called if projection is not empty");
        }
    }

    fn in_projection(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        Self::in_projection_structurally(cx, per_local, place)
    }

    fn in_place(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        match place {
            PlaceRef {
                base: PlaceBase::Local(local),
                projection: [],
            } => per_local.contains(*local),
            PlaceRef {
                base: PlaceBase::Static(box Static {
                    kind: StaticKind::Promoted(..),
                    ..
                }),
                projection: [],
            } => bug!("qualifying already promoted MIR"),
            PlaceRef {
                base: PlaceBase::Static(static_),
                projection: [],
            } => {
                Self::in_static(cx, static_)
            },
            PlaceRef {
                base: _,
                projection: [.., _],
            } => Self::in_projection(cx, per_local, place),
        }
    }

    fn in_operand(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        operand: &Operand<'tcx>,
    ) -> bool {
        match *operand {
            Operand::Copy(ref place) |
            Operand::Move(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Operand::Constant(ref constant) => {
                if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
                    // Don't peek inside trait associated constants.
                    if cx.tcx.trait_of_item(def_id).is_some() {
                        Self::in_any_value_of_ty(cx, constant.literal.ty)
                    } else {
                        let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);

                        let qualif = QualifSet(bits).contains::<Self>();

                        // Just in case the type is more specific than
                        // the definition, e.g., impl associated const
                        // with type parameters, take it into account.
                        qualif && Self::in_any_value_of_ty(cx, constant.literal.ty)
                    }
                } else {
                    false
                }
            }
        }
    }

    fn in_rvalue_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        match *rvalue {
            Rvalue::NullaryOp(..) => false,

            Rvalue::Discriminant(ref place) |
            Rvalue::Len(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Rvalue::Use(ref operand) |
            Rvalue::Repeat(ref operand, _) |
            Rvalue::UnaryOp(_, ref operand) |
            Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand),

            Rvalue::BinaryOp(_, ref lhs, ref rhs) |
            Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
                Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs)
            }

            Rvalue::Ref(_, _, ref place) => {
                // Special-case reborrows to be more like a copy of the reference.
                if let box [proj_base @ .., elem] = &place.projection {
                    if ProjectionElem::Deref == *elem {
                        let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
                        if let ty::Ref(..) = base_ty.sty {
                            return Self::in_place(cx, per_local, PlaceRef {
                                base: &place.base,
                                projection: proj_base,
                            });
                        }
                    }
                }

                Self::in_place(cx, per_local, place.as_ref())
            }

            Rvalue::Aggregate(_, ref operands) => {
                operands.iter().any(|o| Self::in_operand(cx, per_local, o))
            }
        }
    }

    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }

    fn in_call(
        cx: &ConstCx<'_, 'tcx>,
        _per_local: &BitSet<Local>,
        _callee: &Operand<'tcx>,
        _args: &[Operand<'tcx>],
        return_ty: Ty<'tcx>,
    ) -> bool {
        // Be conservative about the returned value of a const fn.
        Self::in_any_value_of_ty(cx, return_ty)
    }
}

/// Constant containing interior mutability (`UnsafeCell<T>`).
/// This must be ruled out to make sure that evaluating the constant at compile-time
/// and at *any point* during the run-time would produce the same result. In particular,
/// promotion of temporaries must not change program behavior; if the promoted could be
/// written to, that would be a problem.
pub struct HasMutInterior;

impl Qualif for HasMutInterior {
    const IDX: usize = 0;

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
    }

    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
        match *rvalue {
            // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
            // allowed in constants (and the `Checker` will error), and/or it
            // won't be promoted, due to `&mut ...` or interior mutability.
            Rvalue::Ref(_, kind, ref place) => {
                let ty = place.ty(cx.body, cx.tcx).ty;

                if let BorrowKind::Mut { .. } = kind {
                    // In theory, any zero-sized value could be borrowed
                    // mutably without consequences.
                    match ty.sty {
                        // Inside a `static mut`, &mut [...] is also allowed.
                        ty::Array(..) | ty::Slice(_) if cx.mode == Mode::StaticMut => {},

                        // FIXME(ecstaticmorse): uncomment the following match arm to stop marking
                        // `&mut []` as `HasMutInterior`.
                        /*
                        ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
                            => {},
                        */

                        _ => return true,
                    }
                }
            }

            Rvalue::Aggregate(ref kind, _) => {
                if let AggregateKind::Adt(def, ..) = **kind {
                    if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
                        let ty = rvalue.ty(cx.body, cx.tcx);
                        assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
                        return true;
                    }
                }
            }

            _ => {}
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}

/// Constant containing an ADT that implements `Drop`.
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
/// as that might not be a `const fn`, and (b) because implicit promotion would
/// remove side-effects that occur as part of dropping that value.
pub struct NeedsDrop;

impl Qualif for NeedsDrop {
    const IDX: usize = 1;
    const IS_CLEARED_ON_MOVE: bool = true;

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        ty.needs_drop(cx.tcx, cx.param_env)
    }

    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
        if let Rvalue::Aggregate(ref kind, _) = *rvalue {
            if let AggregateKind::Adt(def, ..) = **kind {
                if def.has_dtor(cx.tcx) {
                    return true;
                }
            }
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}
<commit_msg>Revert "Use conservative, type-based qualifcation for statics"<commit_after>//! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.

use rustc::mir::*;
use rustc::mir::interpret::ConstValue;
use rustc::ty::{self, Ty};
use rustc_data_structures::bit_set::BitSet;
use syntax_pos::DUMMY_SP;

use super::Item as ConstCx;
use super::validation::Mode;

#[derive(Clone, Copy)]
pub struct QualifSet(u8);

impl QualifSet {
    fn contains<Q: ?Sized + Qualif>(self) -> bool {
        self.0 & (1 << Q::IDX) != 0
    }
}

/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
/// code for promotion or prevent it from evaluating at compile time. So `return true` means
/// "I found something bad, no reason to go on searching". `false` is only returned if we
/// definitely cannot find anything bad anywhere.
///
/// The default implementations proceed structurally.
pub trait Qualif {
    const IDX: usize;

    /// Whether this `Qualif` is cleared when a local is moved from.
    const IS_CLEARED_ON_MOVE: bool = false;

    /// Return the qualification that is (conservatively) correct for any value
    /// of the type.
    fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;

    fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
        // FIXME(eddyb) should we do anything here for value properties?
        false
    }

    fn in_projection_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        if let [proj_base @ .., elem] = place.projection {
            let base_qualif = Self::in_place(cx, per_local, PlaceRef {
                base: place.base,
                projection: proj_base,
            });
            let qualif = base_qualif && Self::in_any_value_of_ty(
                cx,
                Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
                    .projection_ty(cx.tcx, elem)
                    .ty,
            );
            match elem {
                ProjectionElem::Deref |
                ProjectionElem::Subslice { .. } |
                ProjectionElem::Field(..) |
                ProjectionElem::ConstantIndex { .. } |
                ProjectionElem::Downcast(..) => qualif,

                ProjectionElem::Index(local) => qualif || per_local.contains(*local),
            }
        } else {
            bug!("This should be called if projection is not empty");
        }
    }

    fn in_projection(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        Self::in_projection_structurally(cx, per_local, place)
    }

    fn in_place(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        match place {
            PlaceRef {
                base: PlaceBase::Local(local),
                projection: [],
            } => per_local.contains(*local),
            PlaceRef {
                base: PlaceBase::Static(box Static {
                    kind: StaticKind::Promoted(..),
                    ..
                }),
                projection: [],
            } => bug!("qualifying already promoted MIR"),
            PlaceRef {
                base: PlaceBase::Static(static_),
                projection: [],
            } => {
                Self::in_static(cx, static_)
            },
            PlaceRef {
                base: _,
                projection: [.., _],
            } => Self::in_projection(cx, per_local, place),
        }
    }

    fn in_operand(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        operand: &Operand<'tcx>,
    ) -> bool {
        match *operand {
            Operand::Copy(ref place) |
            Operand::Move(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Operand::Constant(ref constant) => {
                if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
                    // Don't peek inside trait associated constants.
                    if cx.tcx.trait_of_item(def_id).is_some() {
                        Self::in_any_value_of_ty(cx, constant.literal.ty)
                    } else {
                        let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);

                        let qualif = QualifSet(bits).contains::<Self>();

                        // Just in case the type is more specific than
                        // the definition, e.g., impl associated const
                        // with type parameters, take it into account.
                        qualif && Self::in_any_value_of_ty(cx, constant.literal.ty)
                    }
                } else {
                    false
                }
            }
        }
    }

    fn in_rvalue_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &BitSet<Local>,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        match *rvalue {
            Rvalue::NullaryOp(..) => false,

            Rvalue::Discriminant(ref place) |
            Rvalue::Len(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Rvalue::Use(ref operand) |
            Rvalue::Repeat(ref operand, _) |
            Rvalue::UnaryOp(_, ref operand) |
            Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand),

            Rvalue::BinaryOp(_, ref lhs, ref rhs) |
            Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
                Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs)
            }

            Rvalue::Ref(_, _, ref place) => {
                // Special-case reborrows to be more like a copy of the reference.
                if let box [proj_base @ .., elem] = &place.projection {
                    if ProjectionElem::Deref == *elem {
                        let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
                        if let ty::Ref(..) = base_ty.sty {
                            return Self::in_place(cx, per_local, PlaceRef {
                                base: &place.base,
                                projection: proj_base,
                            });
                        }
                    }
                }

                Self::in_place(cx, per_local, place.as_ref())
            }

            Rvalue::Aggregate(_, ref operands) => {
                operands.iter().any(|o| Self::in_operand(cx, per_local, o))
            }
        }
    }

    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }

    fn in_call(
        cx: &ConstCx<'_, 'tcx>,
        _per_local: &BitSet<Local>,
        _callee: &Operand<'tcx>,
        _args: &[Operand<'tcx>],
        return_ty: Ty<'tcx>,
    ) -> bool {
        // Be conservative about the returned value of a const fn.
        Self::in_any_value_of_ty(cx, return_ty)
    }
}

/// Constant containing interior mutability (`UnsafeCell<T>`).
/// This must be ruled out to make sure that evaluating the constant at compile-time
/// and at *any point* during the run-time would produce the same result. In particular,
/// promotion of temporaries must not change program behavior; if the promoted could be
/// written to, that would be a problem.
pub struct HasMutInterior;

impl Qualif for HasMutInterior {
    const IDX: usize = 0;

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
    }

    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
        match *rvalue {
            // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
            // allowed in constants (and the `Checker` will error), and/or it
            // won't be promoted, due to `&mut ...` or interior mutability.
            Rvalue::Ref(_, kind, ref place) => {
                let ty = place.ty(cx.body, cx.tcx).ty;

                if let BorrowKind::Mut { .. } = kind {
                    // In theory, any zero-sized value could be borrowed
                    // mutably without consequences.
                    match ty.sty {
                        // Inside a `static mut`, &mut [...] is also allowed.
                        ty::Array(..) | ty::Slice(_) if cx.mode == Mode::StaticMut => {},

                        // FIXME(ecstaticmorse): uncomment the following match arm to stop marking
                        // `&mut []` as `HasMutInterior`.
                        /*
                        ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
                            => {},
                        */

                        _ => return true,
                    }
                }
            }

            Rvalue::Aggregate(ref kind, _) => {
                if let AggregateKind::Adt(def, ..) = **kind {
                    if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
                        let ty = rvalue.ty(cx.body, cx.tcx);
                        assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
                        return true;
                    }
                }
            }

            _ => {}
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}

/// Constant containing an ADT that implements `Drop`.
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
/// as that might not be a `const fn`, and (b) because implicit promotion would
/// remove side-effects that occur as part of dropping that value.
pub struct NeedsDrop;

impl Qualif for NeedsDrop {
    const IDX: usize = 1;
    const IS_CLEARED_ON_MOVE: bool = true;

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        ty.needs_drop(cx.tcx, cx.param_env)
    }

    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
        if let Rvalue::Aggregate(ref kind, _) = *rvalue {
            if let AggregateKind::Adt(def, ..) = **kind {
                if def.has_dtor(cx.tcx) {
                    return true;
                }
            }
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}
<|endoftext|>


<commit_before>use libc::c_void;

use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use driver::cmd::Args;
use error::diag::Diagnostic;

use ast::*;
use ast::map::Map;
use interner::*;
use jit::fct::JitFct;
use jit::map::CodeMap;
use jit::stub::Stub;

use mem::Ptr;
use mir::Mir;

use sym::*;
use sym::Sym::*;
use ty::BuiltinType;

pub struct Context<'a, 'ast> where 'ast: 'a {
    pub args: &'a Args,
    pub interner: &'a Interner,
    pub map: &'a Map<'ast>,
    pub ast: &'ast Ast,
    pub diag: RefCell<Diagnostic>,
    pub sym: RefCell<SymTable>,

    // points from AST function definition node id to FctContextId
    pub fct_defs: RefCell<HashMap<NodeId, FctContextId>>,

    // stores all function definitions
    pub fcts: RefCell<Vec<Arc<Mutex<FctContext<'ast>>>>>,

    // stores all compiled functions
    pub code_map: RefCell<CodeMap>,
}

impl<'a, 'ast> Context<'a, 'ast> {
    pub fn new(args: &'a Args, interner: &'a Interner,
           map: &'a Map<'ast>, ast: &'ast Ast) -> Context<'a, 'ast> {
        Context {
            args: args,
            interner: interner,
            map: map,
            ast: ast,
            diag: RefCell::new(Diagnostic::new()),
            sym: RefCell::new(SymTable::new()),
            fct_defs: RefCell::new(HashMap::new()),
            fcts: RefCell::new(Vec::new()),
            code_map: RefCell::new(CodeMap::new()),
        }
    }

    pub fn add_function(&self, mut fct: FctContext<'ast>) -> Result<FctContextId, Sym> {
        let name = fct.name;
        let fctid = FctContextId(self.fcts.borrow().len());

        fct.id = fctid;

        if let Some(ast) = fct.ast {
            assert!(self.fct_defs.borrow_mut().insert(ast.id, fctid).is_none());
        }

        self.fcts.borrow_mut().push(Arc::new(Mutex::new(fct)));

        let mut sym = self.sym.borrow_mut();

        match sym.get(name) {
            Some(sym) => Err(sym),
            None => {
                assert!(sym.insert(name, SymFunction(fctid)).is_none());

                Ok(fctid)
            }
        }
    }

    pub fn fct_by_id<F, R>(&self, id: FctContextId, f: F) -> R where F: FnOnce(&FctContext<'ast>) -> R {
        let fct = {
            let fcts = self.fcts.borrow();
            fcts[id.0].clone()
        };

        let fctxt = fct.lock().unwrap();

        f(&fctxt)
    }

    pub fn fct_by_id_mut<F, R>(&self, id: FctContextId, f: F) -> R where F: FnOnce(&mut FctContext<'ast>) -> R {
        let fct = {
            let fcts = self.fcts.borrow();
            fcts[id.0].clone()
        };

        let mut fctxt = fct.lock().unwrap();

        f(&mut fctxt)
    }

    pub fn fct_mut<F, R>(&self, id: NodeId, f: F) -> R where F: FnOnce(&mut FctContext<'ast>) -> R {
        let map = self.fct_defs.borrow();
        let fct_id = *map.get(&id).unwrap();

        self.fct_by_id_mut(fct_id, f)
    }

    pub fn fct<F, R>(&self, id: NodeId, f: F) -> R where
                     F: FnOnce(&FctContext<'ast>) -> R {
        let map = self.fct_defs.borrow();
        let fct_id = *map.get(&id).unwrap();

        self.fct_by_id(fct_id, f)
    }

    pub fn var_mut<F, R>(&self, fctid: NodeId, id: NodeId, f: F) -> R where
                         F: FnOnce(&mut VarContext, VarContextId) -> R {
        self.fct_mut(fctid, |fct| {
            let varid = *fct.defs.get(&id).unwrap();
            f(&mut fct.vars[varid.0], varid)
        })
    }

    pub fn var<F, R>(&self, fctid: NodeId, id: NodeId, f: F) -> R where
                     F: FnOnce(&VarContext, VarContextId) -> R {
         self.fct(fctid, |fct| {
             let varid = *fct.defs.get(&id).unwrap();
             f(&fct.vars[varid.0], varid)
         })
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct FctContextId(pub usize);

#[derive(Debug)]
pub struct FctContext<'ast> {
    pub id: FctContextId,

    pub name: Name,

    pub params_types: Vec<BuiltinType>,

    pub return_type: BuiltinType,

    pub ast: Option<&'ast Function>,

    // maps function call to FctContextId
    pub calls: HashMap<NodeId, FctContextId>,

    // points to the definition of variable from its usage
    pub defs: HashMap<NodeId, VarContextId>,

    pub ir: Option<Mir>,

    pub vars: Vec<VarContext>,

    // true if function is always exited via return statement
    // false if function execution could reach the closing } of this function
    pub always_returns: bool,

    // ptr to machine code if already compiled
    pub code: FctCode,

    // compiler stub
    pub stub: Option<Stub>
}

impl<'ast> FctContext<'ast> {
    pub fn var_by_node_id(&self, id: NodeId) -> &VarContext {
        let varid = *self.defs.get(&id).unwrap();

        &self.vars[varid.0]
    }

    pub fn var_by_node_id_mut(&mut self, id: NodeId) -> &mut VarContext {
        let varid = *self.defs.get(&id).unwrap();

        &mut self.vars[varid.0]
    }
}

#[derive(Debug)]
pub enum FctCode {
    Uncompiled, Builtin(Ptr), Fct(JitFct)
}

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

#[derive(Debug)]
pub struct VarContext {
    pub id: VarContextId,

    pub name: Name,

    pub data_type: BuiltinType,

    pub node_id: NodeId,

    pub offset: i32,
}
<commit_msg>ctxt: removed var function<commit_after>use libc::c_void;

use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use driver::cmd::Args;
use error::diag::Diagnostic;

use ast::*;
use ast::map::Map;
use interner::*;
use jit::fct::JitFct;
use jit::map::CodeMap;
use jit::stub::Stub;

use mem::Ptr;
use mir::Mir;

use sym::*;
use sym::Sym::*;
use ty::BuiltinType;

pub struct Context<'a, 'ast> where 'ast: 'a {
    pub args: &'a Args,
    pub interner: &'a Interner,
    pub map: &'a Map<'ast>,
    pub ast: &'ast Ast,
    pub diag: RefCell<Diagnostic>,
    pub sym: RefCell<SymTable>,

    // points from AST function definition node id to FctContextId
    pub fct_defs: RefCell<HashMap<NodeId, FctContextId>>,

    // stores all function definitions
    pub fcts: RefCell<Vec<Arc<Mutex<FctContext<'ast>>>>>,

    // stores all compiled functions
    pub code_map: RefCell<CodeMap>,
}

impl<'a, 'ast> Context<'a, 'ast> {
    pub fn new(args: &'a Args, interner: &'a Interner,
           map: &'a Map<'ast>, ast: &'ast Ast) -> Context<'a, 'ast> {
        Context {
            args: args,
            interner: interner,
            map: map,
            ast: ast,
            diag: RefCell::new(Diagnostic::new()),
            sym: RefCell::new(SymTable::new()),
            fct_defs: RefCell::new(HashMap::new()),
            fcts: RefCell::new(Vec::new()),
            code_map: RefCell::new(CodeMap::new()),
        }
    }

    pub fn add_function(&self, mut fct: FctContext<'ast>) -> Result<FctContextId, Sym> {
        let name = fct.name;
        let fctid = FctContextId(self.fcts.borrow().len());

        fct.id = fctid;

        if let Some(ast) = fct.ast {
            assert!(self.fct_defs.borrow_mut().insert(ast.id, fctid).is_none());
        }

        self.fcts.borrow_mut().push(Arc::new(Mutex::new(fct)));

        let mut sym = self.sym.borrow_mut();

        match sym.get(name) {
            Some(sym) => Err(sym),
            None => {
                assert!(sym.insert(name, SymFunction(fctid)).is_none());

                Ok(fctid)
            }
        }
    }

    pub fn fct_by_id<F, R>(&self, id: FctContextId, f: F) -> R where F: FnOnce(&FctContext<'ast>) -> R {
        let fct = {
            let fcts = self.fcts.borrow();
            fcts[id.0].clone()
        };

        let fctxt = fct.lock().unwrap();

        f(&fctxt)
    }

    pub fn fct_by_id_mut<F, R>(&self, id: FctContextId, f: F) -> R where F: FnOnce(&mut FctContext<'ast>) -> R {
        let fct = {
            let fcts = self.fcts.borrow();
            fcts[id.0].clone()
        };

        let mut fctxt = fct.lock().unwrap();

        f(&mut fctxt)
    }

    pub fn fct_mut<F, R>(&self, id: NodeId, f: F) -> R where F: FnOnce(&mut FctContext<'ast>) -> R {
        let map = self.fct_defs.borrow();
        let fct_id = *map.get(&id).unwrap();

        self.fct_by_id_mut(fct_id, f)
    }

    pub fn fct<F, R>(&self, id: NodeId, f: F) -> R where
                     F: FnOnce(&FctContext<'ast>) -> R {
        let map = self.fct_defs.borrow();
        let fct_id = *map.get(&id).unwrap();

        self.fct_by_id(fct_id, f)
    }

    pub fn var_mut<F, R>(&self, fctid: NodeId, id: NodeId, f: F) -> R where
                         F: FnOnce(&mut VarContext, VarContextId) -> R {
        self.fct_mut(fctid, |fct| {
            let varid = *fct.defs.get(&id).unwrap();
            f(&mut fct.vars[varid.0], varid)
        })
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct FctContextId(pub usize);

#[derive(Debug)]
pub struct FctContext<'ast> {
    pub id: FctContextId,

    pub name: Name,

    pub params_types: Vec<BuiltinType>,

    pub return_type: BuiltinType,

    pub ast: Option<&'ast Function>,

    // maps function call to FctContextId
    pub calls: HashMap<NodeId, FctContextId>,

    // points to the definition of variable from its usage
    pub defs: HashMap<NodeId, VarContextId>,

    pub ir: Option<Mir>,

    pub vars: Vec<VarContext>,

    // true if function is always exited via return statement
    // false if function execution could reach the closing } of this function
    pub always_returns: bool,

    // ptr to machine code if already compiled
    pub code: FctCode,

    // compiler stub
    pub stub: Option<Stub>
}

impl<'ast> FctContext<'ast> {
    pub fn var_by_node_id(&self, id: NodeId) -> &VarContext {
        let varid = *self.defs.get(&id).unwrap();

        &self.vars[varid.0]
    }

    pub fn var_by_node_id_mut(&mut self, id: NodeId) -> &mut VarContext {
        let varid = *self.defs.get(&id).unwrap();

        &mut self.vars[varid.0]
    }
}

#[derive(Debug)]
pub enum FctCode {
    Uncompiled, Builtin(Ptr), Fct(JitFct)
}

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

#[derive(Debug)]
pub struct VarContext {
    pub id: VarContextId,

    pub name: Name,

    pub data_type: BuiltinType,

    pub node_id: NodeId,

    pub offset: i32,
}
<|endoftext|>