diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04b6fea75a78..9c54dfd33883 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -99,7 +99,7 @@ Before you can start building the compiler you need to configure the build for your system. In most cases, that will just mean using the defaults provided for Rust. -To change configuration, you must copy the file `src/bootstrap/config.toml.example` +To change configuration, you must copy the file `config.toml.example` to `config.toml` in the directory from which you will be running the build, and change the settings provided. @@ -237,10 +237,13 @@ Some common invocations of `x.py` are: ## Pull Requests Pull requests are the primary mechanism we use to change Rust. GitHub itself -has some [great documentation][pull-requests] on using the Pull Request -feature. We use the 'fork and pull' model described there. +has some [great documentation][pull-requests] on using the Pull Request feature. +We use the "fork and pull" model [described here][development-models], where +contributors push changes to their personal fork and create pull requests to +bring those changes into the source repository. -[pull-requests]: https://help.github.com/articles/using-pull-requests/ +[pull-requests]: https://help.github.com/articles/about-pull-requests/ +[development-models]: https://help.github.com/articles/about-collaborative-development-models/ Please make pull requests against the `master` branch. diff --git a/README.md b/README.md index a1f018610753..b6ff6df0aec9 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Read ["Installation"] from [The Book]. ``` > ***Note:*** Install locations can be adjusted by copying the config file - > from `./src/bootstrap/config.toml.example` to `./config.toml`, and + > from `./config.toml.example` to `./config.toml`, and > adjusting the `prefix` option under `[install]`. Various other options, such > as enabling debug information, are also supported, and are documented in > the config file. diff --git a/src/bootstrap/config.toml.example b/config.toml.example similarity index 99% rename from src/bootstrap/config.toml.example rename to config.toml.example index 19678dc77937..962be2e60850 100644 --- a/src/bootstrap/config.toml.example +++ b/config.toml.example @@ -258,6 +258,9 @@ # saying that the FileCheck executable is missing, you may want to disable this. #codegen-tests = true +# Flag indicating whether git info will be retrieved from .git automatically. +#ignore-git = false + # ============================================================================= # Options for specific targets # diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 1ce99eb893ef..65c1088f5473 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -80,7 +80,7 @@ handled naturally. Next, rustbuild offers a TOML-based configuration system with a `config.toml` file in the same location as `config.mk`. An example of this configuration can -be found at `src/bootstrap/config.toml.example`, and the configuration file +be found at `config.toml.example`, and the configuration file can also be passed as `--config path/to/config.toml` if the build system is being invoked manually (via the python script). diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index 5ef18b89841f..d02bc7972ae9 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -21,11 +21,10 @@ extern crate bootstrap; use std::env; -use bootstrap::{Flags, Config, Build}; +use bootstrap::{Config, Build}; fn main() { let args = env::args().skip(1).collect::>(); - let flags = Flags::parse(&args); - let config = Config::parse(&flags.build, flags.config.clone()); - Build::new(flags, config).build(); + let config = Config::parse(&args); + Build::new(config).build(); } diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index d7f795e40553..5f1830081adb 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -120,28 +120,19 @@ impl StepDescription { fn maybe_run(&self, builder: &Builder, path: Option<&Path>) { let build = builder.build; let hosts = if self.only_build_targets || self.only_build { - &build.config.host[..1] + build.build_triple() } else { &build.hosts }; - // Determine the actual targets participating in this rule. - // NOTE: We should keep the full projection from build triple to - // the hosts for the dist steps, now that the hosts array above is - // truncated to avoid duplication of work in that case. Therefore - // the original non-shadowed hosts array is used below. + // Determine the targets participating in this rule. let targets = if self.only_hosts { - // If --target was specified but --host wasn't specified, - // don't run any host-only tests. Also, respect any `--host` - // overrides as done for `hosts`. - if build.flags.host.len() > 0 { - &build.flags.host[..] - } else if build.flags.target.len() > 0 { + if build.config.run_host_only { &[] } else if self.only_build { - &build.config.host[..1] + build.build_triple() } else { - &build.config.host[..] + &build.hosts } } else { &build.targets @@ -288,7 +279,7 @@ impl<'a> Builder<'a> { let builder = Builder { build: build, - top_stage: build.flags.stage.unwrap_or(2), + top_stage: build.config.stage.unwrap_or(2), kind: kind, cache: Cache::new(), stack: RefCell::new(Vec::new()), @@ -307,7 +298,7 @@ impl<'a> Builder<'a> { } pub fn run(build: &Build) { - let (kind, paths) = match build.flags.cmd { + let (kind, paths) = match build.config.cmd { Subcommand::Build { ref paths } => (Kind::Build, &paths[..]), Subcommand::Doc { ref paths } => (Kind::Doc, &paths[..]), Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]), @@ -319,7 +310,7 @@ impl<'a> Builder<'a> { let builder = Builder { build: build, - top_stage: build.flags.stage.unwrap_or(2), + top_stage: build.config.stage.unwrap_or(2), kind: kind, cache: Cache::new(), stack: RefCell::new(Vec::new()), @@ -333,7 +324,7 @@ impl<'a> Builder<'a> { StepDescription::run(&Builder::get_step_descriptions(Kind::Doc), self, paths); } - /// Obtain a compiler at a given stage and for a given host. Explictly does + /// Obtain a compiler at a given stage and for a given host. Explicitly does /// not take `Compiler` since all `Compiler` instances are meant to be /// obtained through this function, since it ensures that they are valid /// (i.e., built and assembled). @@ -414,22 +405,19 @@ impl<'a> Builder<'a> { } } - pub fn rustdoc(&self, compiler: Compiler) -> PathBuf { - self.ensure(tool::Rustdoc { target_compiler: compiler }) + pub fn rustdoc(&self, host: Interned) -> PathBuf { + self.ensure(tool::Rustdoc { host }) } - pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command { + pub fn rustdoc_cmd(&self, host: Interned) -> Command { let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc")); + let compiler = self.compiler(self.top_stage, host); cmd .env("RUSTC_STAGE", compiler.stage.to_string()) - .env("RUSTC_SYSROOT", if compiler.is_snapshot(&self.build) { - INTERNER.intern_path(self.build.rustc_snapshot_libdir()) - } else { - self.sysroot(compiler) - }) - .env("RUSTC_LIBDIR", self.rustc_libdir(compiler)) + .env("RUSTC_SYSROOT", self.sysroot(compiler)) + .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build)) .env("CFG_RELEASE_CHANNEL", &self.build.config.channel) - .env("RUSTDOC_REAL", self.rustdoc(compiler)); + .env("RUSTDOC_REAL", self.rustdoc(host)); cmd } @@ -483,7 +471,7 @@ impl<'a> Builder<'a> { .env("RUSTC_RPATH", self.config.rust_rpath.to_string()) .env("RUSTDOC", self.out.join("bootstrap/debug/rustdoc")) .env("RUSTDOC_REAL", if cmd == "doc" || cmd == "test" { - self.rustdoc(compiler) + self.rustdoc(compiler.host) } else { PathBuf::from("/path/to/nowhere/rustdoc/not/required") }) @@ -501,7 +489,7 @@ impl<'a> Builder<'a> { // crates). Let's say, for example that rustc itself depends on the // bitflags crate. If an external crate then depends on the // bitflags crate as well, we need to make sure they don't - // conflict, even if they pick the same verison of bitflags. We'll + // conflict, even if they pick the same version of bitflags. We'll // want to make sure that e.g. a plugin and rustc each get their // own copy of bitflags. @@ -543,12 +531,12 @@ impl<'a> Builder<'a> { // Ignore incremental modes except for stage0, since we're // not guaranteeing correctness across builds if the compiler // is changing under your feet.` - if self.flags.incremental && compiler.stage == 0 { + if self.config.incremental && compiler.stage == 0 { let incr_dir = self.incremental_dir(compiler); cargo.env("RUSTC_INCREMENTAL", incr_dir); } - if let Some(ref on_fail) = self.flags.on_fail { + if let Some(ref on_fail) = self.config.on_fail { cargo.env("RUSTC_ON_FAIL", on_fail); } diff --git a/src/bootstrap/cc.rs b/src/bootstrap/cc.rs index 739904e4f7c5..0f25da8a238d 100644 --- a/src/bootstrap/cc.rs +++ b/src/bootstrap/cc.rs @@ -32,6 +32,7 @@ //! everything. use std::process::Command; +use std::iter; use build_helper::{cc2ar, output}; use gcc; @@ -43,47 +44,41 @@ use cache::Interned; pub fn find(build: &mut Build) { // For all targets we're going to need a C compiler for building some shims // and such as well as for being a linker for Rust code. - // - // This includes targets that aren't necessarily passed on the commandline - // (FIXME: Perhaps it shouldn't?) - for target in &build.config.target { + for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) { let mut cfg = gcc::Config::new(); cfg.cargo_metadata(false).opt_level(0).debug(false) - .target(target).host(&build.build); + .target(&target).host(&build.build); let config = build.config.target_config.get(&target); if let Some(cc) = config.and_then(|c| c.cc.as_ref()) { cfg.compiler(cc); } else { - set_compiler(&mut cfg, "gcc", *target, config, build); + set_compiler(&mut cfg, "gcc", target, config, build); } let compiler = cfg.get_compiler(); - let ar = cc2ar(compiler.path(), target); - build.verbose(&format!("CC_{} = {:?}", target, compiler.path())); + let ar = cc2ar(compiler.path(), &target); + build.verbose(&format!("CC_{} = {:?}", &target, compiler.path())); if let Some(ref ar) = ar { - build.verbose(&format!("AR_{} = {:?}", target, ar)); + build.verbose(&format!("AR_{} = {:?}", &target, ar)); } - build.cc.insert(*target, (compiler, ar)); + build.cc.insert(target, (compiler, ar)); } // For all host triples we need to find a C++ compiler as well - // - // This includes hosts that aren't necessarily passed on the commandline - // (FIXME: Perhaps it shouldn't?) - for host in &build.config.host { + for host in build.hosts.iter().cloned().chain(iter::once(build.build)) { let mut cfg = gcc::Config::new(); cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true) - .target(host).host(&build.build); - let config = build.config.target_config.get(host); + .target(&host).host(&build.build); + let config = build.config.target_config.get(&host); if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) { cfg.compiler(cxx); } else { - set_compiler(&mut cfg, "g++", *host, config, build); + set_compiler(&mut cfg, "g++", host, config, build); } let compiler = cfg.get_compiler(); build.verbose(&format!("CXX_{} = {:?}", host, compiler.path())); - build.cxx.insert(*host, compiler); + build.cxx.insert(host, compiler); } } diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index beefaeab90b1..9c1ae83d3828 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -21,6 +21,7 @@ use std::process::Command; use build_helper::output; use Build; +use config::Config; // The version number pub const CFG_RELEASE_NUM: &str = "1.21.0"; @@ -41,9 +42,9 @@ struct Info { } impl GitInfo { - pub fn new(dir: &Path) -> GitInfo { + pub fn new(config: &Config, dir: &Path) -> GitInfo { // See if this even begins to look like a git dir - if !dir.join(".git").exists() { + if config.ignore_git || !dir.join(".git").exists() { return GitInfo { inner: None } } diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index c65f5a9fb48b..1823dd4ebc04 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -164,7 +164,7 @@ impl Step for Cargotest { try_run(build, cmd.arg(&build.initial_cargo) .arg(&out_dir) .env("RUSTC", builder.rustc(compiler)) - .env("RUSTDOC", builder.rustdoc(compiler))); + .env("RUSTDOC", builder.rustdoc(compiler.host))); } } @@ -565,7 +565,7 @@ impl Step for Compiletest { // Avoid depending on rustdoc when we don't need it. if mode == "rustdoc" || mode == "run-make" { - cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler)); + cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host)); } cmd.arg("--src-base").arg(build.src.join("src/test").join(suite)); @@ -618,14 +618,8 @@ impl Step for Compiletest { if let Some(ref dir) = build.lldb_python_dir { cmd.arg("--lldb-python-dir").arg(dir); } - let llvm_config = build.llvm_config(target); - let llvm_version = output(Command::new(&llvm_config).arg("--version")); - cmd.arg("--llvm-version").arg(llvm_version); - if !build.is_rust_llvm(target) { - cmd.arg("--system-llvm"); - } - cmd.args(&build.flags.cmd.test_args()); + cmd.args(&build.config.cmd.test_args()); if build.is_verbose() { cmd.arg("--verbose"); @@ -635,17 +629,32 @@ impl Step for Compiletest { cmd.arg("--quiet"); } - // Only pass correct values for these flags for the `run-make` suite as it - // requires that a C++ compiler was configured which isn't always the case. - if suite == "run-make" { - let llvm_components = output(Command::new(&llvm_config).arg("--components")); - let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags")); - cmd.arg("--cc").arg(build.cc(target)) - .arg("--cxx").arg(build.cxx(target).unwrap()) - .arg("--cflags").arg(build.cflags(target).join(" ")) - .arg("--llvm-components").arg(llvm_components.trim()) - .arg("--llvm-cxxflags").arg(llvm_cxxflags.trim()); - } else { + if build.config.llvm_enabled { + let llvm_config = build.llvm_config(target); + let llvm_version = output(Command::new(&llvm_config).arg("--version")); + cmd.arg("--llvm-version").arg(llvm_version); + if !build.is_rust_llvm(target) { + cmd.arg("--system-llvm"); + } + + // Only pass correct values for these flags for the `run-make` suite as it + // requires that a C++ compiler was configured which isn't always the case. + if suite == "run-make" { + let llvm_components = output(Command::new(&llvm_config).arg("--components")); + let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags")); + cmd.arg("--cc").arg(build.cc(target)) + .arg("--cxx").arg(build.cxx(target).unwrap()) + .arg("--cflags").arg(build.cflags(target).join(" ")) + .arg("--llvm-components").arg(llvm_components.trim()) + .arg("--llvm-cxxflags").arg(llvm_cxxflags.trim()); + } + } + if suite == "run-make" && !build.config.llvm_enabled { + println!("Ignoring run-make test suite as they generally dont work without LLVM"); + return; + } + + if suite != "run-make" { cmd.arg("--cc").arg("") .arg("--cxx").arg("") .arg("--cflags").arg("") @@ -814,13 +823,13 @@ fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) { } println!("doc tests for: {}", markdown.display()); - let mut cmd = builder.rustdoc_cmd(compiler); + let mut cmd = builder.rustdoc_cmd(compiler.host); build.add_rust_test_threads(&mut cmd); cmd.arg("--test"); cmd.arg(markdown); cmd.env("RUSTC_BOOTSTRAP", "1"); - let test_args = build.flags.cmd.test_args().join(" "); + let test_args = build.config.cmd.test_args().join(" "); cmd.arg("--test-args").arg(test_args); if build.config.quiet_tests { @@ -1051,7 +1060,7 @@ impl Step for Crate { cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); cargo.arg("--"); - cargo.args(&build.flags.cmd.test_args()); + cargo.args(&build.config.cmd.test_args()); if build.config.quiet_tests { cargo.arg("--quiet"); @@ -1147,6 +1156,7 @@ pub struct Distcheck; impl Step for Distcheck { type Output = (); + const ONLY_BUILD: bool = true; fn should_run(run: ShouldRun) -> ShouldRun { run.path("distcheck") @@ -1160,16 +1170,6 @@ impl Step for Distcheck { fn run(self, builder: &Builder) { let build = builder.build; - if *build.build != *"x86_64-unknown-linux-gnu" { - return - } - if !build.config.host.iter().any(|s| s == "x86_64-unknown-linux-gnu") { - return - } - if !build.config.target.iter().any(|s| s == "x86_64-unknown-linux-gnu") { - return - } - println!("Distcheck"); let dir = build.out.join("tmp").join("distcheck"); let _ = fs::remove_dir_all(&dir); @@ -1236,7 +1236,7 @@ impl Step for Bootstrap { if !build.fail_fast { cmd.arg("--no-fail-fast"); } - cmd.arg("--").args(&build.flags.cmd.test_args()); + cmd.arg("--").args(&build.config.cmd.test_args()); try_run(build, &mut cmd); } diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index 308a0ab3076d..119340a0190c 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -26,7 +26,7 @@ pub fn clean(build: &Build) { rm_rf(&build.out.join("tmp")); rm_rf(&build.out.join("dist")); - for host in build.config.host.iter() { + for host in &build.hosts { let entries = match build.out.join(host).read_dir() { Ok(iter) => iter, Err(_) => continue, diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 92a42b59212b..33c3638a8947 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -32,6 +32,7 @@ use serde_json; use util::{exe, libdir, is_dylib, copy}; use {Build, Compiler, Mode}; use native; +use tool; use cache::{INTERNER, Interned}; use builder::{Step, RunConfig, ShouldRun, Builder}; @@ -198,6 +199,12 @@ impl Step for StdLink { // for reason why the sanitizers are not built in stage0. copy_apple_sanitizer_dylibs(&build.native_dir(target), "osx", &libdir); } + + builder.ensure(tool::CleanTools { + compiler: target_compiler, + target: target, + mode: Mode::Libstd, + }); } } @@ -389,6 +396,11 @@ impl Step for TestLink { target); add_to_sysroot(&builder.sysroot_libdir(target_compiler, target), &libtest_stamp(build, compiler, target)); + builder.ensure(tool::CleanTools { + compiler: target_compiler, + target: target, + mode: Mode::Libtest, + }); } } @@ -567,6 +579,11 @@ impl Step for RustcLink { target); add_to_sysroot(&builder.sysroot_libdir(target_compiler, target), &librustc_stamp(build, compiler, target)); + builder.ensure(tool::CleanTools { + compiler: target_compiler, + target: target, + mode: Mode::Librustc, + }); } } @@ -679,10 +696,10 @@ impl Step for Assemble { // link to these. (FIXME: Is that correct? It seems to be correct most // of the time but I think we do link to these for stage2/bin compilers // when not performing a full bootstrap). - if builder.build.flags.keep_stage.map_or(false, |s| target_compiler.stage <= s) { + if builder.build.config.keep_stage.map_or(false, |s| target_compiler.stage <= s) { builder.verbose("skipping compilation of compiler due to --keep-stage"); let compiler = build_compiler; - for stage in 0..min(target_compiler.stage, builder.flags.keep_stage.unwrap()) { + for stage in 0..min(target_compiler.stage, builder.config.keep_stage.unwrap()) { let target_compiler = builder.compiler(stage, target_compiler.host); let target = target_compiler.host; builder.ensure(StdLink { compiler, target_compiler, target }); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index ec57bb069e0a..aa688fc66e26 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -19,11 +19,14 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::path::PathBuf; use std::process; +use std::cmp; use num_cpus; use toml; use util::{exe, push_exe_path}; use cache::{INTERNER, Interned}; +use flags::Flags; +pub use flags::Subcommand; /// Global configuration for the entire build and/or bootstrap. /// @@ -35,7 +38,7 @@ use cache::{INTERNER, Interned}; /// Note that this structure is not decoded directly into, but rather it is /// filled out from the decoded forms of the structs below. For documentation /// each field, see the corresponding fields in -/// `src/bootstrap/config.toml.example`. +/// `config.toml.example`. #[derive(Default)] pub struct Config { pub ccache: Option, @@ -51,6 +54,17 @@ pub struct Config { pub extended: bool, pub sanitizers: bool, pub profiler: bool, + pub ignore_git: bool, + + pub run_host_only: bool, + + pub on_fail: Option, + pub stage: Option, + pub keep_stage: Option, + pub src: PathBuf, + pub jobs: Option, + pub cmd: Subcommand, + pub incremental: bool, // llvm codegen options pub llvm_enabled: bool, @@ -79,8 +93,8 @@ pub struct Config { pub rust_dist_src: bool, pub build: Interned, - pub host: Vec>, - pub target: Vec>, + pub hosts: Vec>, + pub targets: Vec>, pub local_rebuild: bool, // dist misc @@ -249,6 +263,7 @@ struct Rust { optimize_tests: Option, debuginfo_tests: Option, codegen_tests: Option, + ignore_git: Option, } /// TOML representation of how each build target is configured. @@ -265,7 +280,9 @@ struct TomlTarget { } impl Config { - pub fn parse(build: &str, file: Option) -> Config { + pub fn parse(args: &[String]) -> Config { + let flags = Flags::parse(&args); + let file = flags.config.clone(); let mut config = Config::default(); config.llvm_enabled = true; config.llvm_optimize = true; @@ -277,11 +294,22 @@ impl Config { config.docs = true; config.rust_rpath = true; config.rust_codegen_units = 1; - config.build = INTERNER.intern_str(build); config.channel = "dev".to_string(); config.codegen_tests = true; + config.ignore_git = false; config.rust_dist_src = true; + config.on_fail = flags.on_fail; + config.stage = flags.stage; + config.src = flags.src; + config.jobs = flags.jobs; + config.cmd = flags.cmd; + config.incremental = flags.incremental; + config.keep_stage = flags.keep_stage; + + // If --target was specified but --host wasn't specified, don't run any host-only tests. + config.run_host_only = flags.host.is_empty() && !flags.target.is_empty(); + let toml = file.map(|file| { let mut f = t!(File::open(&file)); let mut contents = String::new(); @@ -298,20 +326,37 @@ impl Config { let build = toml.build.clone().unwrap_or(Build::default()); set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x))); - config.host.push(config.build.clone()); + set(&mut config.build, flags.build); + if config.build.is_empty() { + // set by bootstrap.py + config.build = INTERNER.intern_str(&env::var("BUILD").unwrap()); + } + config.hosts.push(config.build.clone()); for host in build.host.iter() { let host = INTERNER.intern_str(host); - if !config.host.contains(&host) { - config.host.push(host); + if !config.hosts.contains(&host) { + config.hosts.push(host); } } - for target in config.host.iter().cloned() + for target in config.hosts.iter().cloned() .chain(build.target.iter().map(|s| INTERNER.intern_str(s))) { - if !config.target.contains(&target) { - config.target.push(target); + if !config.targets.contains(&target) { + config.targets.push(target); } } + config.hosts = if !flags.host.is_empty() { + flags.host + } else { + config.hosts + }; + config.targets = if !flags.target.is_empty() { + flags.target + } else { + config.targets + }; + + config.nodejs = build.nodejs.map(PathBuf::from); config.gdb = build.gdb.map(PathBuf::from); config.python = build.python.map(PathBuf::from); @@ -327,6 +372,7 @@ impl Config { set(&mut config.sanitizers, build.sanitizers); set(&mut config.profiler, build.profiler); set(&mut config.openssl_static, build.openssl_static); + config.verbose = cmp::max(config.verbose, flags.verbose); if let Some(ref install) = toml.install { config.prefix = install.prefix.clone().map(PathBuf::from); @@ -373,6 +419,7 @@ impl Config { set(&mut config.use_jemalloc, rust.use_jemalloc); set(&mut config.backtrace, rust.backtrace); set(&mut config.channel, rust.channel.clone()); + set(&mut config.ignore_git, rust.ignore_git); config.rustc_default_linker = rust.default_linker.clone(); config.rustc_default_ar = rust.default_ar.clone(); config.musl_root = rust.musl_root.clone().map(PathBuf::from); @@ -505,11 +552,11 @@ impl Config { match key { "CFG_BUILD" if value.len() > 0 => self.build = INTERNER.intern_str(value), "CFG_HOST" if value.len() > 0 => { - self.host.extend(value.split(" ").map(|s| INTERNER.intern_str(s))); + self.hosts.extend(value.split(" ").map(|s| INTERNER.intern_str(s))); } "CFG_TARGET" if value.len() > 0 => { - self.target.extend(value.split(" ").map(|s| INTERNER.intern_str(s))); + self.targets.extend(value.split(" ").map(|s| INTERNER.intern_str(s))); } "CFG_EXPERIMENTAL_TARGETS" if value.len() > 0 => { self.llvm_experimental_targets = Some(value.to_string()); diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index c322d75dd5b4..bfcfb5f9a37f 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -413,8 +413,7 @@ impl Step for Rustc { t!(fs::create_dir_all(image.join("bin"))); cp_r(&src.join("bin"), &image.join("bin")); - install(&builder.ensure(tool::Rustdoc { target_compiler: compiler }), - &image.join("bin"), 0o755); + install(&builder.rustdoc(compiler.host), &image.join("bin"), 0o755); // Copy runtime DLLs needed by the compiler if libdir != "bin" { @@ -546,7 +545,7 @@ impl Step for Std { // We want to package up as many target libraries as possible // for the `rust-std` package, so if this is a host target we // depend on librustc and otherwise we just depend on libtest. - if build.config.host.iter().any(|t| t == target) { + if build.hosts.iter().any(|t| t == target) { builder.ensure(compile::Rustc { compiler, target }); } else { builder.ensure(compile::Test { compiler, target }); diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 1ee578bb62b1..81a3845ecf76 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -260,7 +260,7 @@ fn invoke_rustdoc(builder: &Builder, compiler: Compiler, target: Interned, pub stage: Option, pub keep_stage: Option, - pub build: Interned, + pub build: Option>, + pub host: Vec>, pub target: Vec>, pub config: Option, @@ -68,6 +69,14 @@ pub enum Subcommand { }, } +impl Default for Subcommand { + fn default() -> Subcommand { + Subcommand::Build { + paths: vec![PathBuf::from("nowhere")], + } + } +} + impl Flags { pub fn parse(args: &[String]) -> Flags { let mut extra_help = String::new(); @@ -243,10 +252,8 @@ Arguments: // All subcommands can have an optional "Available paths" section if matches.opt_present("verbose") { - let flags = Flags::parse(&["build".to_string()]); - let mut config = Config::parse(&flags.build, cfg_file.clone()); - config.build = flags.build.clone(); - let mut build = Build::new(flags, config); + let config = Config::parse(&["build".to_string()]); + let mut build = Build::new(config); metadata::build(&mut build); let maybe_rules_help = Builder::get_help(&build, subcommand.as_str()); @@ -320,9 +327,7 @@ Arguments: stage: stage, on_fail: matches.opt_str("on-fail"), keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()), - build: INTERNER.intern_string(matches.opt_str("build").unwrap_or_else(|| { - env::var("BUILD").unwrap() - })), + build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)), host: split(matches.opt_strs("host")) .into_iter().map(|x| INTERNER.intern_string(x)).collect::>(), target: split(matches.opt_strs("target")) diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index ebfda1e619bd..89690e444d1f 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -28,7 +28,7 @@ pub fn install_docs(builder: &Builder, stage: u32, host: Interned) { } pub fn install_std(builder: &Builder, stage: u32) { - for target in builder.build.config.target.iter() { + for target in &builder.build.targets { install_sh(builder, "std", "rust-std", stage, Some(*target)); } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a8485d1d152d..1452a38f6ed2 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -136,13 +136,13 @@ extern crate toml; extern crate libc; use std::cell::Cell; -use std::cmp; use std::collections::{HashSet, HashMap}; use std::env; use std::fs::{self, File}; use std::io::Read; use std::path::{PathBuf, Path}; use std::process::Command; +use std::slice; use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime}; @@ -187,7 +187,7 @@ mod job { } pub use config::Config; -pub use flags::{Flags, Subcommand}; +use flags::Subcommand; use cache::{Interned, INTERNER}; /// A structure representing a Rust compiler. @@ -215,9 +215,6 @@ pub struct Build { // User-specified configuration via config.toml config: Config, - // User-specified configuration via CLI flags - flags: Flags, - // Derived properties from the above two configurations src: PathBuf, out: PathBuf, @@ -288,9 +285,9 @@ impl Build { /// line and the filesystem `config`. /// /// By default all build output will be placed in the current directory. - pub fn new(flags: Flags, config: Config) -> Build { + pub fn new(config: Config) -> Build { let cwd = t!(env::current_dir()); - let src = flags.src.clone(); + let src = config.src.clone(); let out = cwd.join("build"); let is_sudo = match env::var_os("SUDO_USER") { @@ -302,43 +299,21 @@ impl Build { } None => false, }; - let rust_info = channel::GitInfo::new(&src); - let cargo_info = channel::GitInfo::new(&src.join("src/tools/cargo")); - let rls_info = channel::GitInfo::new(&src.join("src/tools/rls")); - - let hosts = if !flags.host.is_empty() { - for host in flags.host.iter() { - if !config.host.contains(host) { - panic!("specified host `{}` is not in configuration", host); - } - } - flags.host.clone() - } else { - config.host.clone() - }; - let targets = if !flags.target.is_empty() { - for target in flags.target.iter() { - if !config.target.contains(target) { - panic!("specified target `{}` is not in configuration", target); - } - } - flags.target.clone() - } else { - config.target.clone() - }; + let rust_info = channel::GitInfo::new(&config, &src); + let cargo_info = channel::GitInfo::new(&config, &src.join("src/tools/cargo")); + let rls_info = channel::GitInfo::new(&config, &src.join("src/tools/rls")); Build { initial_rustc: config.initial_rustc.clone(), initial_cargo: config.initial_cargo.clone(), local_rebuild: config.local_rebuild, - fail_fast: flags.cmd.fail_fast(), - verbosity: cmp::max(flags.verbose, config.verbose), + fail_fast: config.cmd.fail_fast(), + verbosity: config.verbose, - build: config.host[0].clone(), - hosts: hosts, - targets: targets, + build: config.build, + hosts: config.hosts.clone(), + targets: config.targets.clone(), - flags: flags, config: config, src: src, out: out, @@ -357,13 +332,19 @@ impl Build { } } + pub fn build_triple(&self) -> &[Interned] { + unsafe { + slice::from_raw_parts(&self.build, 1) + } + } + /// Executes the entire build, as configured by the flags and configuration. pub fn build(&mut self) { unsafe { job::setup(self); } - if let Subcommand::Clean = self.flags.cmd { + if let Subcommand::Clean = self.config.cmd { return clean::clean(self); } @@ -608,7 +589,7 @@ impl Build { /// Returns the number of parallel jobs that have been configured for this /// build. fn jobs(&self) -> u32 { - self.flags.jobs.unwrap_or_else(|| num_cpus::get() as u32) + self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32) } /// Returns the path to the C compiler for the target specified. @@ -727,7 +708,7 @@ impl Build { fn force_use_stage1(&self, compiler: Compiler, target: Interned) -> bool { !self.config.full_bootstrap && compiler.stage >= 2 && - self.config.host.iter().any(|h| *h == target) + self.hosts.iter().any(|h| *h == target) } /// Returns the directory that OpenSSL artifacts are compiled into if diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 7063b28f19d0..436a13500f25 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -85,7 +85,7 @@ pub fn check(build: &mut Build) { } // We need cmake, but only if we're actually building LLVM or sanitizers. - let building_llvm = build.config.host.iter() + let building_llvm = build.hosts.iter() .filter_map(|host| build.config.target_config.get(host)) .any(|config| config.llvm_config.is_none()); if building_llvm || build.config.sanitizers { @@ -114,7 +114,7 @@ pub fn check(build: &mut Build) { // We're gonna build some custom C code here and there, host triples // also build some C++ shims for LLVM so we need a C++ compiler. - for target in &build.config.target { + for target in &build.targets { // On emscripten we don't actually need the C compiler to just // build the target artifacts, only for testing. For the sake // of easier bot configuration, just skip detection. @@ -128,7 +128,7 @@ pub fn check(build: &mut Build) { } } - for host in build.config.host.iter() { + for host in &build.hosts { cmd_finder.must_have(build.cxx(*host).unwrap()); // The msvc hosts don't use jemalloc, turn it off globally to @@ -144,7 +144,7 @@ pub fn check(build: &mut Build) { panic!("FileCheck executable {:?} does not exist", filecheck); } - for target in &build.config.target { + for target in &build.targets { // Can't compile for iOS unless we're on macOS if target.contains("apple-ios") && !build.build.contains("apple-darwin") { diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 862b3e2b1edb..d798e8de3dff 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -23,10 +23,10 @@ use channel::GitInfo; use cache::Interned; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -struct CleanTools { - compiler: Compiler, - target: Interned, - mode: Mode, +pub struct CleanTools { + pub compiler: Compiler, + pub target: Interned, + pub mode: Mode, } impl Step for CleanTools { @@ -82,7 +82,6 @@ impl Step for ToolBuild { let target = self.target; let tool = self.tool; - builder.ensure(CleanTools { compiler, target, mode: self.mode }); match self.mode { Mode::Libstd => builder.ensure(compile::Std { compiler, target }), Mode::Libtest => builder.ensure(compile::Test { compiler, target }), @@ -93,38 +92,48 @@ impl Step for ToolBuild { let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool)); println!("Building stage{} tool {} ({})", compiler.stage, tool, target); - let mut cargo = builder.cargo(compiler, Mode::Tool, target, "build"); - let dir = build.src.join("src/tools").join(tool); - cargo.arg("--manifest-path").arg(dir.join("Cargo.toml")); - - // We don't want to build tools dynamically as they'll be running across - // stages and such and it's just easier if they're not dynamically linked. - cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); - - if let Some(dir) = build.openssl_install_dir(target) { - cargo.env("OPENSSL_STATIC", "1"); - cargo.env("OPENSSL_DIR", dir); - cargo.env("LIBZ_SYS_STATIC", "1"); - } - - cargo.env("CFG_RELEASE_CHANNEL", &build.config.channel); - - let info = GitInfo::new(&dir); - if let Some(sha) = info.sha() { - cargo.env("CFG_COMMIT_HASH", sha); - } - if let Some(sha_short) = info.sha_short() { - cargo.env("CFG_SHORT_COMMIT_HASH", sha_short); - } - if let Some(date) = info.commit_date() { - cargo.env("CFG_COMMIT_DATE", date); - } - + let mut cargo = prepare_tool_cargo(builder, compiler, target, tool); build.run(&mut cargo); build.cargo_out(compiler, Mode::Tool, target).join(exe(tool, &compiler.host)) } } +fn prepare_tool_cargo( + builder: &Builder, + compiler: Compiler, + target: Interned, + tool: &'static str, +) -> Command { + let build = builder.build; + let mut cargo = builder.cargo(compiler, Mode::Tool, target, "build"); + let dir = build.src.join("src/tools").join(tool); + cargo.arg("--manifest-path").arg(dir.join("Cargo.toml")); + + // We don't want to build tools dynamically as they'll be running across + // stages and such and it's just easier if they're not dynamically linked. + cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); + + if let Some(dir) = build.openssl_install_dir(target) { + cargo.env("OPENSSL_STATIC", "1"); + cargo.env("OPENSSL_DIR", dir); + cargo.env("LIBZ_SYS_STATIC", "1"); + } + + cargo.env("CFG_RELEASE_CHANNEL", &build.config.channel); + + let info = GitInfo::new(&build.config, &dir); + if let Some(sha) = info.sha() { + cargo.env("CFG_COMMIT_HASH", sha); + } + if let Some(sha_short) = info.sha_short() { + cargo.env("CFG_SHORT_COMMIT_HASH", sha_short); + } + if let Some(date) = info.commit_date() { + cargo.env("CFG_COMMIT_DATE", date); + } + cargo +} + macro_rules! tool { ($($name:ident, $path:expr, $tool_name:expr, $mode:expr;)+) => { #[derive(Copy, Clone)] @@ -226,7 +235,7 @@ impl Step for RemoteTestServer { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Rustdoc { - pub target_compiler: Compiler, + pub host: Interned, } impl Step for Rustdoc { @@ -240,14 +249,20 @@ impl Step for Rustdoc { fn make_run(run: RunConfig) { run.builder.ensure(Rustdoc { - target_compiler: run.builder.compiler(run.builder.top_stage, run.host), + host: run.host, }); } fn run(self, builder: &Builder) -> PathBuf { - let target_compiler = self.target_compiler; + let build = builder.build; + let target_compiler = builder.compiler(builder.top_stage, self.host); + let target = target_compiler.host; let build_compiler = if target_compiler.stage == 0 { builder.compiler(0, builder.build.build) + } else if target_compiler.stage >= 2 { + // Past stage 2, we consider the compiler to be ABI-compatible and hence capable of + // building rustdoc itself. + builder.compiler(target_compiler.stage, builder.build.build) } else { // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage @@ -255,12 +270,18 @@ impl Step for Rustdoc { builder.compiler(target_compiler.stage - 1, builder.build.build) }; - let tool_rustdoc = builder.ensure(ToolBuild { - compiler: build_compiler, - target: target_compiler.host, - tool: "rustdoc", - mode: Mode::Librustc, - }); + builder.ensure(compile::Rustc { compiler: build_compiler, target }); + + let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage)); + println!("Building rustdoc for stage{} ({})", target_compiler.stage, target_compiler.host); + + let mut cargo = prepare_tool_cargo(builder, build_compiler, target, "rustdoc"); + build.run(&mut cargo); + // Cargo adds a number of paths to the dylib search path on windows, which results in + // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" + // rustdoc a different name. + let tool_rustdoc = build.cargo_out(build_compiler, Mode::Tool, target) + .join(exe("rustdoc-tool-binary", &target_compiler.host)); // don't create a stage0-sysroot/bin directory. if target_compiler.stage > 0 { diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md index cd6883a719c1..1049cc4284a0 100644 --- a/src/doc/rustdoc/src/SUMMARY.md +++ b/src/doc/rustdoc/src/SUMMARY.md @@ -2,7 +2,7 @@ - [What is rustdoc?](what-is-rustdoc.md) - [Command-line arguments](command-line-arguments.md) -- [In-source directives](in-source-directives.md) +- [The `#[doc]` attribute](the-doc-attribute.md) - [Documentation tests](documentation-tests.md) - [Plugins](plugins.md) - [Passes](passes.md) \ No newline at end of file diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index cd7d657e1165..4f7736d8df6b 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -1,3 +1,239 @@ # Documentation tests -Coming soon! \ No newline at end of file +`rustdoc` supports executing your documentation examples as tests. This makes sure +that your tests are up to date and working. + +The basic idea is this: + +```rust,ignore +/// # Examples +/// +/// ``` +/// let x = 5; +/// ``` +``` + +The triple backticks start and end code blocks. If this were in a file named `foo.rs`, +running `rustdoc --test foo.rs` will extract this example, and then run it as a test. + +There's some subtlety though! Read on for more details. + +## Pre-processing examples + +In the example above, you'll note something strange: there's no `main` +function! Forcing you to write `main` for every example, no matter how small, +adds friction. So `rustdoc` processes your examples slightly before +running them. Here's the full algorithm rustdoc uses to preprocess examples: + +1. Any leading `#![foo]` attributes are left intact as crate attributes. +2. Some common `allow` attributes are inserted, including + `unused_variables`, `unused_assignments`, `unused_mut`, + `unused_attributes`, and `dead_code`. Small examples often trigger + these lints. +3. If the example does not contain `extern crate`, then `extern crate + ;` is inserted (note the lack of `#[macro_use]`). +4. Finally, if the example does not contain `fn main`, the remainder of the + text is wrapped in `fn main() { your_code }`. + +For more about that caveat in rule 3, see "Documeting Macros" below. + +## Hiding portions of the example + +Sometimes, you need some setup code, or other things that would distract +from your example, but are important to make the tests work. Consider +an example block that looks like this: + +```text +/// Some documentation. +# fn foo() {} +``` + +It will render like this: + +```rust +/// Some documentation. +# fn foo() {} +``` + +Yes, that's right: you can add lines that start with `# `, and they will +be hidden from the output, but will be used when compiling your code. You +can use this to your advantage. In this case, documentation comments need +to apply to some kind of function, so if I want to show you just a +documentation comment, I need to add a little function definition below +it. At the same time, it's only there to satisfy the compiler, so hiding +it makes the example more clear. You can use this technique to explain +longer examples in detail, while still preserving the testability of your +documentation. + +For example, imagine that we wanted to document this code: + +```rust +let x = 5; +let y = 6; +println!("{}", x + y); +``` + +We might want the documentation to end up looking like this: + +> First, we set `x` to five: +> +> ```rust +> let x = 5; +> # let y = 6; +> # println!("{}", x + y); +> ``` +> +> Next, we set `y` to six: +> +> ```rust +> # let x = 5; +> let y = 6; +> # println!("{}", x + y); +> ``` +> +> Finally, we print the sum of `x` and `y`: +> +> ```rust +> # let x = 5; +> # let y = 6; +> println!("{}", x + y); +> ``` + +To keep each code block testable, we want the whole program in each block, but +we don't want the reader to see every line every time. Here's what we put in +our source code: + +```text + First, we set `x` to five: + + ```rust + let x = 5; + # let y = 6; + # println!("{}", x + y); + ``` + + Next, we set `y` to six: + + ```rust + # let x = 5; + let y = 6; + # println!("{}", x + y); + ``` + + Finally, we print the sum of `x` and `y`: + + ```rust + # let x = 5; + # let y = 6; + println!("{}", x + y); + ``` +``` + +By repeating all parts of the example, you can ensure that your example still +compiles, while only showing the parts that are relevant to that part of your +explanation. + +Another case where the use of `#` is handy is when you want to ignore +error handling. Lets say you want the following, + +```rust,ignore +/// use std::io; +/// let mut input = String::new(); +/// io::stdin().read_line(&mut input)?; +``` + +The problem is that `?` returns a `Result` and test functions +don't return anything so this will give a mismatched types error. + +```rust,ignore +/// A doc test using ? +/// +/// ``` +/// use std::io; +/// # fn foo() -> io::Result<()> { +/// let mut input = String::new(); +/// io::stdin().read_line(&mut input)?; +/// # Ok(()) +/// # } +/// ``` +# fn foo() {} +``` + +You can get around this by wrapping the code in a function. This catches +and swallows the `Result` when running tests on the docs. This +pattern appears regularly in the standard library. + +### Documenting macros + +Here’s an example of documenting a macro: + +```rust +/// Panic with a given message unless an expression evaluates to true. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate foo; +/// # fn main() { +/// panic_unless!(1 + 1 == 2, “Math is broken.”); +/// # } +/// ``` +/// +/// ```rust,should_panic +/// # #[macro_use] extern crate foo; +/// # fn main() { +/// panic_unless!(true == false, “I’m broken.”); +/// # } +/// ``` +#[macro_export] +macro_rules! panic_unless { + ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } }); +} +# fn main() {} +``` + +You’ll note three things: we need to add our own `extern crate` line, so that +we can add the `#[macro_use]` attribute. Second, we’ll need to add our own +`main()` as well (for reasons discussed above). Finally, a judicious use of +`#` to comment out those two things, so they don’t show up in the output. + +## Attributes + +There are a few annotations that are useful to help `rustdoc` do the right +thing when testing your code: + +```rust +/// ```ignore +/// fn foo() { +/// ``` +# fn foo() {} +``` + +The `ignore` directive tells Rust to ignore your code. This is almost never +what you want, as it's the most generic. Instead, consider annotating it +with `text` if it's not code, or using `#`s to get a working example that +only shows the part you care about. + +```rust +/// ```should_panic +/// assert!(false); +/// ``` +# fn foo() {} +``` + +`should_panic` tells `rustdoc` that the code should compile correctly, but +not actually pass as a test. + +```rust +/// ```no_run +/// loop { +/// println!("Hello, world"); +/// } +/// ``` +# fn foo() {} +``` + +The `no_run` attribute will compile your code, but not run it. This is +important for examples such as "Here's how to retrieve a web page," +which you would want to ensure compiles, but might be run in a test +environment that has no network access. \ No newline at end of file diff --git a/src/doc/rustdoc/src/in-source-directives.md b/src/doc/rustdoc/src/in-source-directives.md deleted file mode 100644 index 83f677fa7f4c..000000000000 --- a/src/doc/rustdoc/src/in-source-directives.md +++ /dev/null @@ -1,3 +0,0 @@ -# In-source directives - -Coming soon! \ No newline at end of file diff --git a/src/doc/rustdoc/src/the-doc-attribute.md b/src/doc/rustdoc/src/the-doc-attribute.md new file mode 100644 index 000000000000..978d7656bdd7 --- /dev/null +++ b/src/doc/rustdoc/src/the-doc-attribute.md @@ -0,0 +1,178 @@ +# The `#[doc]` attribute + +The `#[doc]` attribute lets you control various aspects of how `rustdoc` does +its job. + +The most basic function of `#[doc]` is to handle the actual documentation +text. That is, `///` is syntax sugar for `#[doc]`. This means that these two +are the same: + +```rust,ignore +/// This is a doc comment. +#[doc = " This is a doc comment."] +``` + +(Note the leading space in the attribute version.) + +In most cases, `///` is easier to use than `#[doc]`. One case where the latter is easier is +when generating documentation in macros; the `collapse-docs` pass will combine multiple +`#[doc]` attributes into a single doc comment, letting you generate code like this: + +```rust,ignore +#[doc = "This is"] +#[doc = " a "] +#[doc = "doc comment"] +``` + +Which can feel more flexible. Note that this would generate this: + +```rust,ignore +#[doc = "This is\n a \ndoc comment"] +``` + +but given that docs are rendered via Markdown, it will remove these newlines. + +The `doc` attribute has more options though! These don't involve the text of +the output, but instead, various aspects of the presentation of the output. +We've split them into two kinds below: attributes that are useful at the +crate level, and ones that are useful at the item level. + +## At the crate level + +These options control how the docs look at a macro level. + +### `html_favicon_url` + +This form of the `doc` attribute lets you control the favicon of your docs. + +```rust,ignore +#![doc(html_favicon_url = "https://example.com/favicon.ico")] +``` + +This will put `` into your docs, where +the string for the attribute goes into the `{}`. + +If you don't use this attribute, there will be no favicon. + +### `html_logo_url` + +This form of the `doc` attribute lets you control the logo in the upper +left hand side of the docs. + +```rust,ignore +#![doc(html_logo_url = "https://example.com/logo.jpg")] +``` + +This will put `logo` into +your docs, where the string for the attribute goes into the `{}`. + +If you don't use this attribute, there will be no logo. + +### `html_playground_url` + +This form of the `doc` attribute lets you control where the "run" buttons +on your documentation examples make requests to. + +```rust,ignore +#![doc(html_playground_url = "https://playground.example.com/")] +``` + +Now, when you press "run", the button will make a request to this domain. + +If you don't use this attribute, there will be no run buttons. + +### `issue_tracker_base_url` + +This form of the `doc` attribute is mostly only useful for the standard library; +When a feature is unstable, an issue number for tracking the feature must be +given. `rustdoc` uses this number, plus the base URL given here, to link to +the tracking issue. + +```rust,ignore +#![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] +``` + +### `html_no_source` + +By default, `rustdoc` will include the source code of your program, with links +to it in the docs. But if you include this: + +```rust,ignore +#![doc(html_no_source)] +``` + +it will not. + +## At the item level + +These forms of the `#[doc]` attribute are used on individual items, to control how +they are documented. + +## `#[doc(no_inline)]`/`#[doc(inline)]` + +These attributes are used on `use` statements, and control where the documentation shows +up. For example, consider this Rust code: + +```rust,ignore +pub use bar::Bar; + +/// bar docs +pub mod bar { + /// the docs for Bar + pub struct Bar; +} +``` + +The documentation will generate a "Reexports" section, and say `pub use bar::Bar;`, where +`Bar` is a link to its page. + +If we change the `use` line like this: + +```rust,ignore +#[doc(inline)] +pub use bar::Bar; +``` + +Instead, `Bar` will appear in a `Structs` section, just like `Bar` was defined at the +top level, rather than `pub use`'d. + +Let's change our original example, by making `bar` private: + +```rust,ignore +pub use bar::Bar; + +/// bar docs +mod bar { + /// the docs for Bar + pub struct Bar; +} +``` + +Here, because `bar` is not public, `Bar` wouldn't have its own page, so there's nowhere +to link to. `rustdoc` will inline these definitions, and so we end up in the same case +as the `#[doc(inline)]` above; `Bar` is in a `Structs` section, as if it were defined at +the top level. If we add the `no_inline` form of the attribute: + +```rust,ignore +#[doc(no_inline)] +pub use bar::Bar; + +/// bar docs +mod bar { + /// the docs for Bar + pub struct Bar; +} +``` + +Now we'll have a `Reexports` line, and `Bar` will not link to anywhere. + +## `#[doc(hidden)]` + +Any item annotated with `#[doc(hidden)]` will not appear in the documentation, unless +the `strip-hidden` pass is removed. + +## `#[doc(primitive)]` + +Since primitive types are defined in the compiler, there's no place to attach documentation +attributes. This attribute is used by the standard library to provide a way to generate +documentation for primitive types. diff --git a/src/doc/unstable-book/src/language-features/doc-cfg.md b/src/doc/unstable-book/src/language-features/doc-cfg.md new file mode 100644 index 000000000000..ddc538e12144 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/doc-cfg.md @@ -0,0 +1,42 @@ +# `doc_cfg` + +The tracking issue for this feature is: [#43781] + +------ + +The `doc_cfg` feature allows an API be documented as only available in some specific platforms. +This attribute has two effects: + +1. In the annotated item's documentation, there will be a message saying "This is supported on + (platform) only". + +2. The item's doc-tests will only run on the specific platform. + +This feature was introduced as part of PR [#43348] to allow the platform-specific parts of the +standard library be documented. + +```rust +#![feature(doc_cfg)] + +#[cfg(any(windows, feature = "documentation"))] +#[doc(cfg(windows))] +/// The application's icon in the notification area (a.k.a. system tray). +/// +/// # Examples +/// +/// ```no_run +/// extern crate my_awesome_ui_library; +/// use my_awesome_ui_library::current_app; +/// use my_awesome_ui_library::windows::notification; +/// +/// let icon = current_app().get::(); +/// icon.show(); +/// icon.show_message("Hello"); +/// ``` +pub struct Icon { + // ... +} +``` + +[#43781]: https://github.com/rust-lang/rust/issues/43781 +[#43348]: https://github.com/rust-lang/rust/issues/43348 \ No newline at end of file diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs index 42111301a9fe..2b3df15f7161 100644 --- a/src/liballoc/allocator.rs +++ b/src/liballoc/allocator.rs @@ -40,7 +40,7 @@ fn size_align() -> (usize, usize) { /// /// (Note however that layouts are *not* required to have positive /// size, even though many allocators require that all memory -/// requeusts have positive size. A caller to the `Alloc::alloc` +/// requests have positive size. A caller to the `Alloc::alloc` /// method must either ensure that conditions like this are met, or /// use specific allocators with looser requirements.) #[derive(Clone, Debug, PartialEq, Eq)] @@ -240,7 +240,7 @@ impl Layout { /// /// Returns `Some((k, offset))`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded witnin the concatenated record + /// start of the `next` embedded within the concatenated record /// (assuming that the record itself starts at offset 0). /// /// On arithmetic overflow, returns `None`. @@ -297,7 +297,7 @@ impl Layout { /// /// Returns `(k, offset)`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded witnin the concatenated record + /// start of the `next` embedded within the concatenated record /// (assuming that the record itself starts at offset 0). /// /// (The `offset` is always the same as `self.size()`; we use this @@ -354,15 +354,19 @@ pub enum AllocErr { } impl AllocErr { + #[inline] pub fn invalid_input(details: &'static str) -> Self { AllocErr::Unsupported { details: details } } + #[inline] pub fn is_memory_exhausted(&self) -> bool { if let AllocErr::Exhausted { .. } = *self { true } else { false } } + #[inline] pub fn is_request_unsupported(&self) -> bool { if let AllocErr::Unsupported { .. } = *self { true } else { false } } + #[inline] pub fn description(&self) -> &str { match *self { AllocErr::Exhausted { .. } => "allocator memory exhausted", @@ -544,7 +548,7 @@ pub unsafe trait Alloc { /// practice this means implementors should eschew allocating, /// especially from `self` (directly or indirectly). /// - /// Implementions of the allocation and reallocation methods + /// Implementations of the allocation and reallocation methods /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from /// panicking (or aborting) in the event of memory exhaustion; /// instead they should return an appropriate error from the diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 8cea6c482c33..0e61905131f6 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -132,7 +132,7 @@ impl InternalNode { /// An owned pointer to a node. This basically is either `Box>` or /// `Box>`. However, it contains no information as to which of the two types -/// of nodes is acutally behind the box, and, partially due to this lack of information, has no +/// of nodes is actually behind the box, and, partially due to this lack of information, has no /// destructor. struct BoxedNode { ptr: Unique> @@ -264,7 +264,7 @@ impl Root { // correct variance. /// A reference to a node. /// -/// This type has a number of paramaters that controls how it acts: +/// This type has a number of parameters that controls how it acts: /// - `BorrowType`: This can be `Immut<'a>` or `Mut<'a>` for some `'a` or `Owned`. /// When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`, /// when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 4847b21c0b3b..480fb4b9eaa2 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -10,16 +10,16 @@ //! Utilities for formatting and printing `String`s //! -//! This module contains the runtime support for the `format!` syntax extension. +//! This module contains the runtime support for the [`format!`] syntax extension. //! This macro is implemented in the compiler to emit calls to this module in //! order to format arguments at runtime into strings. //! //! # Usage //! -//! The `format!` macro is intended to be familiar to those coming from C's -//! printf/fprintf functions or Python's `str.format` function. +//! The [`format!`] macro is intended to be familiar to those coming from C's +//! `printf`/`fprintf` functions or Python's `str.format` function. //! -//! Some examples of the `format!` extension are: +//! Some examples of the [`format!`] extension are: //! //! ``` //! format!("Hello"); // => "Hello" @@ -67,7 +67,7 @@ //! ## Named parameters //! //! Rust itself does not have a Python-like equivalent of named parameters to a -//! function, but the `format!` macro is a syntax extension which allows it to +//! function, but the [`format!`] macro is a syntax extension which allows it to //! leverage named parameters. Named parameters are listed at the end of the //! argument list and have the syntax: //! @@ -75,7 +75,7 @@ //! identifier '=' expression //! ``` //! -//! For example, the following `format!` expressions all use named argument: +//! For example, the following [`format!`] expressions all use named argument: //! //! ``` //! format!("{argument}", argument = "test"); // => "test" @@ -102,30 +102,30 @@ //! //! If this syntax is used, then the number of characters to print precedes the //! actual object being formatted, and the number of characters must have the -//! type `usize`. +//! type [`usize`]. //! //! ## Formatting traits //! //! When requesting that an argument be formatted with a particular type, you //! are actually requesting that an argument ascribes to a particular trait. -//! This allows multiple actual types to be formatted via `{:x}` (like `i8` as -//! well as `isize`). The current mapping of types to traits is: +//! This allows multiple actual types to be formatted via `{:x}` (like [`i8`] as +//! well as [`isize`]). The current mapping of types to traits is: //! -//! * *nothing* ⇒ [`Display`](trait.Display.html) -//! * `?` ⇒ [`Debug`](trait.Debug.html) +//! * *nothing* ⇒ [`Display`] +//! * `?` ⇒ [`Debug`] //! * `o` ⇒ [`Octal`](trait.Octal.html) //! * `x` ⇒ [`LowerHex`](trait.LowerHex.html) //! * `X` ⇒ [`UpperHex`](trait.UpperHex.html) //! * `p` ⇒ [`Pointer`](trait.Pointer.html) -//! * `b` ⇒ [`Binary`](trait.Binary.html) +//! * `b` ⇒ [`Binary`] //! * `e` ⇒ [`LowerExp`](trait.LowerExp.html) //! * `E` ⇒ [`UpperExp`](trait.UpperExp.html) //! //! What this means is that any type of argument which implements the -//! `fmt::Binary` trait can then be formatted with `{:b}`. Implementations +//! [`fmt::Binary`][`Binary`] trait can then be formatted with `{:b}`. Implementations //! are provided for these traits for a number of primitive types by the //! standard library as well. If no format is specified (as in `{}` or `{:6}`), -//! then the format trait used is the `Display` trait. +//! then the format trait used is the [`Display`] trait. //! //! When implementing a format trait for your own type, you will have to //! implement a method of the signature: @@ -144,15 +144,15 @@ //! should emit output into the `f.buf` stream. It is up to each format trait //! implementation to correctly adhere to the requested formatting parameters. //! The values of these parameters will be listed in the fields of the -//! `Formatter` struct. In order to help with this, the `Formatter` struct also +//! [`Formatter`] struct. In order to help with this, the [`Formatter`] struct also //! provides some helper methods. //! -//! Additionally, the return value of this function is `fmt::Result` which is a -//! type alias of `Result<(), std::fmt::Error>`. Formatting implementations -//! should ensure that they propagate errors from the `Formatter` (e.g., when -//! calling `write!`) however, they should never return errors spuriously. That +//! Additionally, the return value of this function is [`fmt::Result`] which is a +//! type alias of [`Result`]`<(), `[`std::fmt::Error`]`>`. Formatting implementations +//! should ensure that they propagate errors from the [`Formatter`][`Formatter`] (e.g., when +//! calling [`write!`]) however, they should never return errors spuriously. That //! is, a formatting implementation must and may only return an error if the -//! passed-in `Formatter` returns an error. This is because, contrary to what +//! passed-in [`Formatter`] returns an error. This is because, contrary to what //! the function signature might suggest, string formatting is an infallible //! operation. This function only returns a result because writing to the //! underlying stream might fail and it must provide a way to propagate the fact @@ -209,12 +209,12 @@ //! //! These two formatting traits have distinct purposes: //! -//! - `fmt::Display` implementations assert that the type can be faithfully +//! - [`fmt::Display`][`Display`] implementations assert that the type can be faithfully //! represented as a UTF-8 string at all times. It is **not** expected that //! all types implement the `Display` trait. -//! - `fmt::Debug` implementations should be implemented for **all** public types. +//! - [`fmt::Debug`][`Debug`] implementations should be implemented for **all** public types. //! Output will typically represent the internal state as faithfully as possible. -//! The purpose of the `Debug` trait is to facilitate debugging Rust code. In +//! The purpose of the [`Debug`] trait is to facilitate debugging Rust code. In //! most cases, using `#[derive(Debug)]` is sufficient and recommended. //! //! Some examples of the output from both traits: @@ -227,7 +227,7 @@ //! //! ## Related macros //! -//! There are a number of related macros in the `format!` family. The ones that +//! There are a number of related macros in the [`format!`] family. The ones that //! are currently implemented are: //! //! ```ignore (only-for-syntax-highlight) @@ -241,11 +241,11 @@ //! //! ### `write!` //! -//! This and `writeln` are two macros which are used to emit the format string +//! This and [`writeln!`] are two macros which are used to emit the format string //! to a specified stream. This is used to prevent intermediate allocations of //! format strings and instead directly write the output. Under the hood, this -//! function is actually invoking the `write_fmt` function defined on the -//! `std::io::Write` trait. Example usage is: +//! function is actually invoking the [`write_fmt`] function defined on the +//! [`std::io::Write`] trait. Example usage is: //! //! ``` //! # #![allow(unused_must_use)] @@ -256,7 +256,7 @@ //! //! ### `print!` //! -//! This and `println` emit their output to stdout. Similarly to the `write!` +//! This and [`println!`] emit their output to stdout. Similarly to the [`write!`] //! macro, the goal of these macros is to avoid intermediate allocations when //! printing output. Example usage is: //! @@ -288,8 +288,8 @@ //! my_fmt_fn(format_args!(", or a {} too", "function")); //! ``` //! -//! The result of the `format_args!` macro is a value of type `fmt::Arguments`. -//! This structure can then be passed to the `write` and `format` functions +//! The result of the [`format_args!`] macro is a value of type [`fmt::Arguments`]. +//! This structure can then be passed to the [`write`] and [`format`] functions //! inside this module in order to process the format string. //! The goal of this macro is to even further prevent intermediate allocations //! when dealing formatting strings. @@ -357,7 +357,7 @@ //! * `-` - Currently not used //! * `#` - This flag is indicates that the "alternate" form of printing should //! be used. The alternate forms are: -//! * `#?` - pretty-print the `Debug` formatting +//! * `#?` - pretty-print the [`Debug`] formatting //! * `#x` - precedes the argument with a `0x` //! * `#X` - precedes the argument with a `0x` //! * `#b` - precedes the argument with a `0b` @@ -384,9 +384,9 @@ //! the `0` flag is specified for numerics, then the implicit fill character is //! `0`. //! -//! The value for the width can also be provided as a `usize` in the list of +//! The value for the width can also be provided as a [`usize`] in the list of //! parameters by using the dollar syntax indicating that the second argument is -//! a `usize` specifying the width, for example: +//! a [`usize`] specifying the width, for example: //! //! ``` //! // All of these print "Hello x !" @@ -474,6 +474,29 @@ //! The literal characters `{` and `}` may be included in a string by preceding //! them with the same character. For example, the `{` character is escaped with //! `{{` and the `}` character is escaped with `}}`. +//! +//! [`format!`]: ../../macro.format.html +//! [`usize`]: ../../std/primitive.usize.html +//! [`isize`]: ../../std/primitive.isize.html +//! [`i8`]: ../../std/primitive.i8.html +//! [`Display`]: trait.Display.html +//! [`Binary`]: trait.Binary.html +//! [`fmt::Result`]: type.Result.html +//! [`Result`]: ../../std/result/enum.Result.html +//! [`std::fmt::Error`]: struct.Error.html +//! [`Formatter`]: struct.Formatter.html +//! [`write!`]: ../../std/macro.write.html +//! [`Debug`]: trait.Debug.html +//! [`format!`]: ../../std/macro.format.html +//! [`writeln!`]: ../../std/macro.writeln.html +//! [`write_fmt`]: ../../std/io/trait.Write.html#method.write_fmt +//! [`std::io::Write`]: ../../std/io/trait.Write.html +//! [`println!`]: ../../std/macro.println.html +//! [`write!`]: ../../std/macro.write.html +//! [`format_args!`]: ../../std/macro.format_args.html +//! [`fmt::Arguments`]: struct.Arguments.html +//! [`write`]: fn.write.html +//! [`format`]: fn.format.html #![stable(feature = "rust1", since = "1.0.0")] @@ -498,10 +521,10 @@ pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; use string; -/// The `format` function takes an `Arguments` struct and returns the resulting +/// The `format` function takes an [`Arguments`] struct and returns the resulting /// formatted string. /// -/// The `Arguments` instance can be created with the `format_args!` macro. +/// The [`Arguments`] instance can be created with the [`format_args!`] macro. /// /// # Examples /// @@ -514,7 +537,7 @@ use string; /// assert_eq!(s, "Hello, world!"); /// ``` /// -/// Please note that using [`format!`][format!] might be preferrable. +/// Please note that using [`format!`] might be preferrable. /// Example: /// /// ``` @@ -522,7 +545,9 @@ use string; /// assert_eq!(s, "Hello, world!"); /// ``` /// -/// [format!]: ../macro.format.html +/// [`Arguments`]: struct.Arguments.html +/// [`format_args!`]: ../../std/macro.format_args.html +/// [`format!`]: ../../std/macro.format.html #[stable(feature = "rust1", since = "1.0.0")] pub fn format(args: Arguments) -> string::String { let capacity = args.estimated_capacity(); diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 1d959ac5bf6d..820f2d958d9a 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -28,6 +28,7 @@ pub mod __core { extern "Rust" { #[allocator] fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; + #[cold] fn __rust_oom(err: *const u8) -> !; fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); fn __rust_usable_size(layout: *const u8, @@ -81,6 +82,7 @@ unsafe impl Alloc for Heap { } #[inline] + #[cold] fn oom(&mut self, err: AllocErr) -> ! { unsafe { __rust_oom(&err as *const AllocErr as *const u8) diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index ca55831220da..6090fc3942a5 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use allocator::{Alloc, Layout}; -use core::ptr::{self, Unique}; -use core::mem; -use core::slice; -use heap::Heap; -use super::boxed::Box; -use core::ops::Drop; use core::cmp; +use core::mem; +use core::ops::Drop; +use core::ptr::{self, Unique}; +use core::slice; +use heap::{Alloc, Layout, Heap}; +use super::boxed::Box; /// A low-level utility for more ergonomically allocating, reallocating, and deallocating /// a buffer of memory on the heap without having to worry about all the corner cases @@ -222,6 +221,20 @@ impl RawVec { &mut self.a } + fn current_layout(&self) -> Option { + if self.cap == 0 { + None + } else { + // We have an allocated chunk of memory, so we can bypass runtime + // checks to get our current layout. + unsafe { + let align = mem::align_of::(); + let size = mem::size_of::() * self.cap; + Some(Layout::from_size_align_unchecked(size, align)) + } + } + } + /// Doubles the size of the type's backing allocation. This is common enough /// to want to do that it's easiest to just have a dedicated method. Slightly /// more efficient logic can be provided for this than the general case. @@ -280,27 +293,40 @@ impl RawVec { // 0, getting to here necessarily means the RawVec is overfull. assert!(elem_size != 0, "capacity overflow"); - let (new_cap, ptr_res) = if self.cap == 0 { - // skip to 4 because tiny Vec's are dumb; but not if that would cause overflow - let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; - let ptr_res = self.a.alloc_array::(new_cap); - (new_cap, ptr_res) - } else { - // Since we guarantee that we never allocate more than isize::MAX bytes, - // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow - let new_cap = 2 * self.cap; - let new_alloc_size = new_cap * elem_size; - alloc_guard(new_alloc_size); - let ptr_res = self.a.realloc_array(self.ptr, self.cap, new_cap); - (new_cap, ptr_res) + let (new_cap, uniq) = match self.current_layout() { + Some(cur) => { + // Since we guarantee that we never allocate more than + // isize::MAX bytes, `elem_size * self.cap <= isize::MAX` as + // a precondition, so this can't overflow. Additionally the + // alignment will never be too large as to "not be + // satisfiable", so `Layout::from_size_align` will always + // return `Some`. + // + // tl;dr; we bypass runtime checks due to dynamic assertions + // in this module, allowing us to use + // `from_size_align_unchecked`. + let new_cap = 2 * self.cap; + let new_size = new_cap * elem_size; + let new_layout = Layout::from_size_align_unchecked(new_size, cur.align()); + alloc_guard(new_size); + let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8, + cur, + new_layout); + match ptr_res { + Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), + Err(e) => self.a.oom(e), + } + } + None => { + // skip to 4 because tiny Vec's are dumb; but not if that + // would cause overflow + let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; + match self.a.alloc_array::(new_cap) { + Ok(ptr) => (new_cap, ptr), + Err(e) => self.a.oom(e), + } + } }; - - // If allocate or reallocate fail, we'll get `null` back - let uniq = match ptr_res { - Err(err) => self.a.oom(err), - Ok(uniq) => uniq, - }; - self.ptr = uniq; self.cap = new_cap; } @@ -323,21 +349,27 @@ impl RawVec { pub fn double_in_place(&mut self) -> bool { unsafe { let elem_size = mem::size_of::(); + let old_layout = match self.current_layout() { + Some(layout) => layout, + None => return false, // nothing to double + }; // since we set the capacity to usize::MAX when elem_size is // 0, getting to here necessarily means the RawVec is overfull. assert!(elem_size != 0, "capacity overflow"); - // Since we guarantee that we never allocate more than isize::MAX bytes, - // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow + // Since we guarantee that we never allocate more than isize::MAX + // bytes, `elem_size * self.cap <= isize::MAX` as a precondition, so + // this can't overflow. + // + // Similarly like with `double` above we can go straight to + // `Layout::from_size_align_unchecked` as we know this won't + // overflow and the alignment is sufficiently small. let new_cap = 2 * self.cap; - let new_alloc_size = new_cap * elem_size; - - alloc_guard(new_alloc_size); - + let new_size = new_cap * elem_size; + alloc_guard(new_size); let ptr = self.ptr() as *mut _; - let old_layout = Layout::new::().repeat(self.cap).unwrap().0; - let new_layout = Layout::new::().repeat(new_cap).unwrap().0; + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); match self.a.grow_in_place(ptr, old_layout, new_layout) { Ok(_) => { // We can't directly divide `size`. @@ -373,8 +405,6 @@ impl RawVec { /// Aborts on OOM pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { unsafe { - let elem_size = mem::size_of::(); - // NOTE: we don't early branch on ZSTs here because we want this // to actually catch "asking for more than usize::MAX" in that case. // If we make it past the first branch then we are guaranteed to @@ -388,21 +418,22 @@ impl RawVec { // Nothing we can really do about these checks :( let new_cap = used_cap.checked_add(needed_extra_cap).expect("capacity overflow"); - let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow"); - alloc_guard(new_alloc_size); - - let result = if self.cap == 0 { - self.a.alloc_array::(new_cap) - } else { - self.a.realloc_array(self.ptr, self.cap, new_cap) + let new_layout = match Layout::array::(new_cap) { + Some(layout) => layout, + None => panic!("capacity overflow"), }; - - // If allocate or reallocate fail, we'll get `null` back - let uniq = match result { - Err(err) => self.a.oom(err), - Ok(uniq) => uniq, + alloc_guard(new_layout.size()); + let res = match self.current_layout() { + Some(layout) => { + let old_ptr = self.ptr.as_ptr() as *mut u8; + self.a.realloc(old_ptr, layout, new_layout) + } + None => self.a.alloc(new_layout), + }; + let uniq = match res { + Ok(ptr) => Unique::new_unchecked(ptr as *mut T), + Err(e) => self.a.oom(e), }; - self.ptr = uniq; self.cap = new_cap; } @@ -411,17 +442,14 @@ impl RawVec { /// Calculates the buffer's new size given that it'll hold `used_cap + /// needed_extra_cap` elements. This logic is used in amortized reserve methods. /// Returns `(new_capacity, new_alloc_size)`. - fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> (usize, usize) { - let elem_size = mem::size_of::(); + fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> usize { // Nothing we can really do about these checks :( let required_cap = used_cap.checked_add(needed_extra_cap) .expect("capacity overflow"); // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`. let double_cap = self.cap * 2; // `double_cap` guarantees exponential growth. - let new_cap = cmp::max(double_cap, required_cap); - let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow"); - (new_cap, new_alloc_size) + cmp::max(double_cap, required_cap) } /// Ensures that the buffer contains at least enough space to hold @@ -489,21 +517,25 @@ impl RawVec { return; } - let (new_cap, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap); + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap); + + let new_layout = match Layout::array::(new_cap) { + Some(layout) => layout, + None => panic!("capacity overflow"), + }; // FIXME: may crash and burn on over-reserve - alloc_guard(new_alloc_size); - - let result = if self.cap == 0 { - self.a.alloc_array::(new_cap) - } else { - self.a.realloc_array(self.ptr, self.cap, new_cap) + alloc_guard(new_layout.size()); + let res = match self.current_layout() { + Some(layout) => { + let old_ptr = self.ptr.as_ptr() as *mut u8; + self.a.realloc(old_ptr, layout, new_layout) + } + None => self.a.alloc(new_layout), }; - - let uniq = match result { - Err(err) => self.a.oom(err), - Ok(uniq) => uniq, + let uniq = match res { + Ok(ptr) => Unique::new_unchecked(ptr as *mut T), + Err(e) => self.a.oom(e), }; - self.ptr = uniq; self.cap = new_cap; } @@ -536,21 +568,24 @@ impl RawVec { // Don't actually need any more capacity. If the current `cap` is 0, we can't // reallocate in place. // Wrapping in case they give a bad `used_cap` - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap || self.cap == 0 { + let old_layout = match self.current_layout() { + Some(layout) => layout, + None => return false, + }; + if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { return false; } - let (new_cap, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap); - // FIXME: may crash and burn on over-reserve - alloc_guard(new_alloc_size); + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap); // Here, `cap < used_cap + needed_extra_cap <= new_cap` // (regardless of whether `self.cap - used_cap` wrapped). // Therefore we can safely call grow_in_place. let ptr = self.ptr() as *mut _; - let old_layout = Layout::new::().repeat(self.cap).unwrap().0; let new_layout = Layout::new::().repeat(new_cap).unwrap().0; + // FIXME: may crash and burn on over-reserve + alloc_guard(new_layout.size()); match self.a.grow_in_place(ptr, old_layout, new_layout) { Ok(_) => { self.cap = new_cap; @@ -599,9 +634,24 @@ impl RawVec { } } else if self.cap != amount { unsafe { - match self.a.realloc_array(self.ptr, self.cap, amount) { + // We know here that our `amount` is greater than zero. This + // implies, via the assert above, that capacity is also greater + // than zero, which means that we've got a current layout that + // "fits" + // + // We also know that `self.cap` is greater than `amount`, and + // consequently we don't need runtime checks for creating either + // layout + let old_size = elem_size * self.cap; + let new_size = elem_size * amount; + let align = mem::align_of::(); + let old_layout = Layout::from_size_align_unchecked(old_size, align); + let new_layout = Layout::from_size_align_unchecked(new_size, align); + match self.a.realloc(self.ptr.as_ptr() as *mut u8, + old_layout, + new_layout) { + Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), Err(err) => self.a.oom(err), - Ok(uniq) => self.ptr = uniq, } } self.cap = amount; @@ -631,10 +681,11 @@ impl RawVec { /// Frees the memory owned by the RawVec *without* trying to Drop its contents. pub unsafe fn dealloc_buffer(&mut self) { let elem_size = mem::size_of::(); - if elem_size != 0 && self.cap != 0 { - let ptr = self.ptr() as *mut u8; - let layout = Layout::new::().repeat(self.cap).unwrap().0; - self.a.dealloc(ptr, layout); + if elem_size != 0 { + if let Some(layout) = self.current_layout() { + let ptr = self.ptr() as *mut u8; + self.a.dealloc(ptr, layout); + } } } } diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 322b137e99f0..3ed5d2df1aba 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -653,7 +653,7 @@ impl String { /// * `capacity` needs to be the correct value. /// /// Violating these may cause problems like corrupting the allocator's - /// internal datastructures. + /// internal data structures. /// /// The ownership of `ptr` is effectively transferred to the /// `String` which may then deallocate, reallocate or change the diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 160c0ba2ab0e..5f68e59289d7 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -374,7 +374,7 @@ impl Vec { /// * `capacity` needs to be the capacity that the pointer was allocated with. /// /// Violating these may cause problems like corrupting the allocator's - /// internal datastructures. For example it is **not** safe + /// internal data structures. For example it is **not** safe /// to build a `Vec` from a pointer to a C `char` array and a `size_t`. /// /// The ownership of `ptr` is effectively transferred to the diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 21b5557db99f..6068f1a79611 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -571,6 +571,59 @@ impl RefCell { debug_assert!(self.borrow.get() == UNUSED); unsafe { self.value.into_inner() } } + + /// Replaces the wrapped value with a new one, returning the old value, + /// without deinitializing either one. + /// + /// This function corresponds to [`std::mem::replace`](../mem/fn.replace.html). + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_replace_swap)] + /// use std::cell::RefCell; + /// let c = RefCell::new(5); + /// let u = c.replace(6); + /// assert_eq!(u, 5); + /// assert_eq!(c, RefCell::new(6)); + /// ``` + /// + /// # Panics + /// + /// This function will panic if the `RefCell` has any outstanding borrows, + /// whether or not they are full mutable borrows. + #[inline] + #[unstable(feature = "refcell_replace_swap", issue="43570")] + pub fn replace(&self, t: T) -> T { + mem::replace(&mut *self.borrow_mut(), t) + } + + /// Swaps the wrapped value of `self` with the wrapped value of `other`, + /// without deinitializing either one. + /// + /// This function corresponds to [`std::mem::swap`](../mem/fn.swap.html). + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_replace_swap)] + /// use std::cell::RefCell; + /// let c = RefCell::new(5); + /// let d = RefCell::new(6); + /// c.swap(&d); + /// assert_eq!(c, RefCell::new(6)); + /// assert_eq!(d, RefCell::new(5)); + /// ``` + /// + /// # Panics + /// + /// This function will panic if either `RefCell` has any outstanding borrows, + /// whether or not they are full mutable borrows. + #[inline] + #[unstable(feature = "refcell_replace_swap", issue="43570")] + pub fn swap(&self, other: &Self) { + mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) + } } impl RefCell { diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 193c8b9f925f..97839844087c 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1347,7 +1347,6 @@ impl<'a> Formatter<'a> { /// println!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() }); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] - #[inline] pub fn debug_struct<'b>(&'b mut self, name: &str) -> DebugStruct<'b, 'a> { builders::debug_struct_new(self, name) } @@ -1375,7 +1374,6 @@ impl<'a> Formatter<'a> { /// println!("{:?}", Foo(10, "Hello World".to_string())); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] - #[inline] pub fn debug_tuple<'b>(&'b mut self, name: &str) -> DebugTuple<'b, 'a> { builders::debug_tuple_new(self, name) } @@ -1400,7 +1398,6 @@ impl<'a> Formatter<'a> { /// println!("{:?}", Foo(vec![10, 11])); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] - #[inline] pub fn debug_list<'b>(&'b mut self) -> DebugList<'b, 'a> { builders::debug_list_new(self) } @@ -1425,7 +1422,6 @@ impl<'a> Formatter<'a> { /// println!("{:?}", Foo(vec![10, 11])); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] - #[inline] pub fn debug_set<'b>(&'b mut self) -> DebugSet<'b, 'a> { builders::debug_set_new(self) } @@ -1450,7 +1446,6 @@ impl<'a> Formatter<'a> { /// println!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] - #[inline] pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> { builders::debug_map_new(self) } diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index fdca8d00d7a7..ad776c8605ac 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1044,20 +1044,23 @@ extern "rust-intrinsic" { /// a size of `count` * `size_of::()` and an alignment of /// `min_align_of::()` /// - /// The volatile parameter is set to `true`, so it will not be optimized out. + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. pub fn volatile_copy_nonoverlapping_memory(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with /// a size of `count` * `size_of::()` and an alignment of /// `min_align_of::()` /// - /// The volatile parameter is set to `true`, so it will not be optimized out. + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero.. pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a /// size of `count` * `size_of::()` and an alignment of /// `min_align_of::()`. /// - /// The volatile parameter is set to `true`, so it will not be optimized out. + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. pub fn volatile_set_memory(dst: *mut T, val: u8, count: usize); /// Perform a volatile load from the `src` pointer. diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 79e6b11beaca..22b997a768e6 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -1035,7 +1035,7 @@ unsafe impl TrustedLen for Zip /// Now consider this twist where we add a call to `rev`. This version will /// print `('c', 1), ('b', 2), ('a', 3)`. Note that the letters are reversed, /// but the values of the counter still go in order. This is because `map()` is -/// still being called lazilly on each item, but we are popping items off the +/// still being called lazily on each item, but we are popping items off the /// back of the vector now, instead of shifting them from the front. /// /// ```rust diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index ccfeb91aff14..19098f036acd 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -345,7 +345,7 @@ pub trait Extend { /// In a similar fashion to the [`Iterator`] protocol, once a /// `DoubleEndedIterator` returns `None` from a `next_back()`, calling it again /// may or may not ever return `Some` again. `next()` and `next_back()` are -/// interchangable for this purpose. +/// interchangeable for this purpose. /// /// [`Iterator`]: trait.Iterator.html /// diff --git a/src/libcore/num/dec2flt/algorithm.rs b/src/libcore/num/dec2flt/algorithm.rs index 42bc46c0c683..ccf3950c2ba3 100644 --- a/src/libcore/num/dec2flt/algorithm.rs +++ b/src/libcore/num/dec2flt/algorithm.rs @@ -336,7 +336,7 @@ pub fn algorithm_m(f: &Big, e: i16) -> T { round_by_remainder(v, rem, q, z) } -/// Skip over most AlgorithmM iterations by checking the bit length. +/// Skip over most Algorithm M iterations by checking the bit length. fn quick_start(u: &mut Big, v: &mut Big, k: &mut i16) { // The bit length is an estimate of the base two logarithm, and log(u / v) = log(u) - log(v). // The estimate is off by at most 1, but always an under-estimate, so the error on log(u) diff --git a/src/libcore/ops/arith.rs b/src/libcore/ops/arith.rs index d898f9146cd1..62007caedd3f 100644 --- a/src/libcore/ops/arith.rs +++ b/src/libcore/ops/arith.rs @@ -10,15 +10,20 @@ /// The addition operator `+`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. For +/// example, [`std::time::SystemTime`] implements `Add`, which permits +/// operations of the form `SystemTime = SystemTime + Duration`. +/// +/// [`std::time::SystemTime`]: ../../std/time/struct.SystemTime.html +/// /// # Examples /// -/// This example creates a `Point` struct that implements the `Add` trait, and -/// then demonstrates adding two `Point`s. +/// ## `Add`able points /// /// ``` /// use std::ops::Add; /// -/// #[derive(Debug)] +/// #[derive(Debug, PartialEq)] /// struct Point { /// x: i32, /// y: i32, @@ -35,31 +40,25 @@ /// } /// } /// -/// impl PartialEq for Point { -/// fn eq(&self, other: &Self) -> bool { -/// self.x == other.x && self.y == other.y -/// } -/// } -/// -/// fn main() { -/// assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 }, -/// Point { x: 3, y: 3 }); -/// } +/// assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 }, +/// Point { x: 3, y: 3 }); /// ``` /// +/// ## Implementing `Add` with generics +/// /// Here is an example of the same `Point` struct implementing the `Add` trait /// using generics. /// /// ``` /// use std::ops::Add; /// -/// #[derive(Debug)] +/// #[derive(Debug, PartialEq)] /// struct Point { /// x: T, /// y: T, /// } /// -/// // Notice that the implementation uses the `Output` associated type +/// // Notice that the implementation uses the associated type `Output`. /// impl> Add for Point { /// type Output = Point; /// @@ -71,32 +70,18 @@ /// } /// } /// -/// impl PartialEq for Point { -/// fn eq(&self, other: &Self) -> bool { -/// self.x == other.x && self.y == other.y -/// } -/// } -/// -/// fn main() { -/// assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 }, -/// Point { x: 3, y: 3 }); -/// } +/// assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 }, +/// Point { x: 3, y: 3 }); /// ``` -/// -/// Note that `RHS = Self` by default, but this is not mandatory. For example, -/// [std::time::SystemTime] implements `Add`, which permits -/// operations of the form `SystemTime = SystemTime + Duration`. -/// -/// [std::time::SystemTime]: ../../std/time/struct.SystemTime.html #[lang = "add"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} + {RHS}`"] pub trait Add { - /// The resulting type after applying the `+` operator + /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `+` operator + /// Performs the `+` operation. #[stable(feature = "rust1", since = "1.0.0")] fn add(self, rhs: RHS) -> Self::Output; } @@ -120,15 +105,20 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// The subtraction operator `-`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. For +/// example, [`std::time::SystemTime`] implements `Sub`, which permits +/// operations of the form `SystemTime = SystemTime - Duration`. +/// +/// [`std::time::SystemTime`]: ../../std/time/struct.SystemTime.html +/// /// # Examples /// -/// This example creates a `Point` struct that implements the `Sub` trait, and -/// then demonstrates subtracting two `Point`s. +/// ## `Sub`tractable points /// /// ``` /// use std::ops::Sub; /// -/// #[derive(Debug)] +/// #[derive(Debug, PartialEq)] /// struct Point { /// x: i32, /// y: i32, @@ -145,31 +135,25 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// } /// } /// -/// impl PartialEq for Point { -/// fn eq(&self, other: &Self) -> bool { -/// self.x == other.x && self.y == other.y -/// } -/// } -/// -/// fn main() { -/// assert_eq!(Point { x: 3, y: 3 } - Point { x: 2, y: 3 }, -/// Point { x: 1, y: 0 }); -/// } +/// assert_eq!(Point { x: 3, y: 3 } - Point { x: 2, y: 3 }, +/// Point { x: 1, y: 0 }); /// ``` /// +/// ## Implementing `Sub` with generics +/// /// Here is an example of the same `Point` struct implementing the `Sub` trait /// using generics. /// /// ``` /// use std::ops::Sub; /// -/// #[derive(Debug)] +/// #[derive(Debug, PartialEq)] /// struct Point { /// x: T, /// y: T, /// } /// -/// // Notice that the implementation uses the `Output` associated type +/// // Notice that the implementation uses the associated type `Output`. /// impl> Sub for Point { /// type Output = Point; /// @@ -181,32 +165,18 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// } /// } /// -/// impl PartialEq for Point { -/// fn eq(&self, other: &Self) -> bool { -/// self.x == other.x && self.y == other.y -/// } -/// } -/// -/// fn main() { -/// assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 }, -/// Point { x: 1, y: 3 }); -/// } +/// assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 }, +/// Point { x: 1, y: 3 }); /// ``` -/// -/// Note that `RHS = Self` by default, but this is not mandatory. For example, -/// [std::time::SystemTime] implements `Sub`, which permits -/// operations of the form `SystemTime = SystemTime - Duration`. -/// -/// [std::time::SystemTime]: ../../std/time/struct.SystemTime.html #[lang = "sub"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} - {RHS}`"] pub trait Sub { - /// The resulting type after applying the `-` operator + /// The resulting type after applying the `-` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `-` operator + /// Performs the `-` operation. #[stable(feature = "rust1", since = "1.0.0")] fn sub(self, rhs: RHS) -> Self::Output; } @@ -230,17 +200,19 @@ sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// The multiplication operator `*`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. +/// /// # Examples /// -/// Implementing a `Mul`tipliable rational number struct: +/// ## `Mul`tipliable rational numbers /// /// ``` /// use std::ops::Mul; /// -/// // The uniqueness of rational numbers in lowest terms is a consequence of -/// // the fundamental theorem of arithmetic. -/// #[derive(Eq)] -/// #[derive(PartialEq, Debug)] +/// // By the fundamental theorem of arithmetic, rational numbers in lowest +/// // terms are unique. So, by keeping `Rational`s in reduced form, we can +/// // derive `Eq` and `PartialEq`. +/// #[derive(Debug, Eq, PartialEq)] /// struct Rational { /// nominator: usize, /// denominator: usize, @@ -291,45 +263,37 @@ sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// Rational::new(1, 2)); /// ``` /// -/// Note that `RHS = Self` by default, but this is not mandatory. Here is an -/// implementation which enables multiplication of vectors by scalars, as is -/// done in linear algebra. +/// ## Multiplying vectors by scalars as in linear algebra /// /// ``` /// use std::ops::Mul; /// -/// struct Scalar {value: usize}; +/// struct Scalar { value: usize } /// -/// #[derive(Debug)] -/// struct Vector {value: Vec}; +/// #[derive(Debug, PartialEq)] +/// struct Vector { value: Vec } /// -/// impl Mul for Scalar { +/// impl Mul for Vector { /// type Output = Vector; /// -/// fn mul(self, rhs: Vector) -> Vector { -/// Vector {value: rhs.value.iter().map(|v| self.value * v).collect()} +/// fn mul(self, rhs: Scalar) -> Vector { +/// Vector { value: self.value.iter().map(|v| v * rhs.value).collect() } /// } /// } /// -/// impl PartialEq for Vector { -/// fn eq(&self, other: &Self) -> bool { -/// self.value == other.value -/// } -/// } -/// -/// let scalar = Scalar{value: 3}; -/// let vector = Vector{value: vec![2, 4, 6]}; -/// assert_eq!(scalar * vector, Vector{value: vec![6, 12, 18]}); +/// let vector = Vector { value: vec![2, 4, 6] }; +/// let scalar = Scalar { value: 3 }; +/// assert_eq!(vector * scalar, Vector { value: vec![6, 12, 18] }); /// ``` #[lang = "mul"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} * {RHS}`"] pub trait Mul { - /// The resulting type after applying the `*` operator + /// The resulting type after applying the `*` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `*` operator + /// Performs the `*` operation. #[stable(feature = "rust1", since = "1.0.0")] fn mul(self, rhs: RHS) -> Self::Output; } @@ -353,17 +317,19 @@ mul_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// The division operator `/`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. +/// /// # Examples /// -/// Implementing a `Div`idable rational number struct: +/// ## `Div`idable rational numbers /// /// ``` /// use std::ops::Div; /// -/// // The uniqueness of rational numbers in lowest terms is a consequence of -/// // the fundamental theorem of arithmetic. -/// #[derive(Eq)] -/// #[derive(PartialEq, Debug)] +/// // By the fundamental theorem of arithmetic, rational numbers in lowest +/// // terms are unique. So, by keeping `Rational`s in reduced form, we can +/// // derive `Eq` and `PartialEq`. +/// #[derive(Debug, Eq, PartialEq)] /// struct Rational { /// nominator: usize, /// denominator: usize, @@ -413,52 +379,42 @@ mul_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// x /// } /// -/// fn main() { -/// assert_eq!(Rational::new(1, 2), Rational::new(2, 4)); -/// assert_eq!(Rational::new(1, 2) / Rational::new(3, 4), -/// Rational::new(2, 3)); -/// } +/// assert_eq!(Rational::new(1, 2), Rational::new(2, 4)); +/// assert_eq!(Rational::new(1, 2) / Rational::new(3, 4), +/// Rational::new(2, 3)); /// ``` /// -/// Note that `RHS = Self` by default, but this is not mandatory. Here is an -/// implementation which enables division of vectors by scalars, as is done in -/// linear algebra. +/// ## Dividing vectors by scalars as in linear algebra /// /// ``` /// use std::ops::Div; /// -/// struct Scalar {value: f32}; +/// struct Scalar { value: f32 } /// -/// #[derive(Debug)] -/// struct Vector {value: Vec}; +/// #[derive(Debug, PartialEq)] +/// struct Vector { value: Vec } /// /// impl Div for Vector { /// type Output = Vector; /// /// fn div(self, rhs: Scalar) -> Vector { -/// Vector {value: self.value.iter().map(|v| v / rhs.value).collect()} +/// Vector { value: self.value.iter().map(|v| v / rhs.value).collect() } /// } /// } /// -/// impl PartialEq for Vector { -/// fn eq(&self, other: &Self) -> bool { -/// self.value == other.value -/// } -/// } -/// -/// let scalar = Scalar{value: 2f32}; -/// let vector = Vector{value: vec![2f32, 4f32, 6f32]}; -/// assert_eq!(vector / scalar, Vector{value: vec![1f32, 2f32, 3f32]}); +/// let scalar = Scalar { value: 2f32 }; +/// let vector = Vector { value: vec![2f32, 4f32, 6f32] }; +/// assert_eq!(vector / scalar, Vector { value: vec![1f32, 2f32, 3f32] }); /// ``` #[lang = "div"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} / {RHS}`"] pub trait Div { - /// The resulting type after applying the `/` operator + /// The resulting type after applying the `/` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `/` operator + /// Performs the `/` operation. #[stable(feature = "rust1", since = "1.0.0")] fn div(self, rhs: RHS) -> Self::Output; } @@ -499,6 +455,8 @@ div_impl_float! { f32 f64 } /// The remainder operator `%`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. +/// /// # Examples /// /// This example implements `Rem` on a `SplitSlice` object. After `Rem` is @@ -526,7 +484,7 @@ div_impl_float! { f32 f64 } /// } /// /// // If we were to divide &[0, 1, 2, 3, 4, 5, 6, 7] into slices of size 3, -/// // the remainder would be &[6, 7] +/// // the remainder would be &[6, 7]. /// assert_eq!(SplitSlice { slice: &[0, 1, 2, 3, 4, 5, 6, 7] } % 3, /// SplitSlice { slice: &[6, 7] }); /// ``` @@ -534,11 +492,11 @@ div_impl_float! { f32 f64 } #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} % {RHS}`"] pub trait Rem { - /// The resulting type after applying the `%` operator + /// The resulting type after applying the `%` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output = Self; - /// The method for the `%` operator + /// Performs the `%` operation. #[stable(feature = "rust1", since = "1.0.0")] fn rem(self, rhs: RHS) -> Self::Output; } @@ -607,21 +565,21 @@ rem_impl_float! { f32 f64 } /// } /// } /// -/// // a negative positive is a negative +/// // A negative positive is a negative. /// assert_eq!(-Sign::Positive, Sign::Negative); -/// // a double negative is a positive +/// // A double negative is a positive. /// assert_eq!(-Sign::Negative, Sign::Positive); -/// // zero is its own negation +/// // Zero is its own negation. /// assert_eq!(-Sign::Zero, Sign::Zero); /// ``` #[lang = "neg"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Neg { - /// The resulting type after applying the `-` operator + /// The resulting type after applying the `-` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the unary `-` operator + /// Performs the unary `-` operation. #[stable(feature = "rust1", since = "1.0.0")] fn neg(self) -> Self::Output; } @@ -668,7 +626,7 @@ neg_impl_numeric! { isize i8 i16 i32 i64 i128 f32 f64 } /// ``` /// use std::ops::AddAssign; /// -/// #[derive(Debug)] +/// #[derive(Debug, PartialEq)] /// struct Point { /// x: i32, /// y: i32, @@ -683,12 +641,6 @@ neg_impl_numeric! { isize i8 i16 i32 i64 i128 f32 f64 } /// } /// } /// -/// impl PartialEq for Point { -/// fn eq(&self, other: &Self) -> bool { -/// self.x == other.x && self.y == other.y -/// } -/// } -/// /// let mut point = Point { x: 1, y: 0 }; /// point += Point { x: 2, y: 3 }; /// assert_eq!(point, Point { x: 3, y: 3 }); @@ -697,7 +649,7 @@ neg_impl_numeric! { isize i8 i16 i32 i64 i128 f32 f64 } #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} += {Rhs}`"] pub trait AddAssign { - /// The method for the `+=` operator + /// Performs the `+=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn add_assign(&mut self, rhs: Rhs); } @@ -725,7 +677,7 @@ add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// ``` /// use std::ops::SubAssign; /// -/// #[derive(Debug)] +/// #[derive(Debug, PartialEq)] /// struct Point { /// x: i32, /// y: i32, @@ -740,12 +692,6 @@ add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// } /// } /// -/// impl PartialEq for Point { -/// fn eq(&self, other: &Self) -> bool { -/// self.x == other.x && self.y == other.y -/// } -/// } -/// /// let mut point = Point { x: 3, y: 3 }; /// point -= Point { x: 2, y: 3 }; /// assert_eq!(point, Point {x: 1, y: 0}); @@ -754,7 +700,7 @@ add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} -= {Rhs}`"] pub trait SubAssign { - /// The method for the `-=` operator + /// Performs the `-=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn sub_assign(&mut self, rhs: Rhs); } @@ -776,31 +722,27 @@ sub_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// /// # Examples /// -/// A trivial implementation of `MulAssign`. When `Foo *= Foo` happens, it ends up -/// calling `mul_assign`, and therefore, `main` prints `Multiplying!`. -/// /// ``` /// use std::ops::MulAssign; /// -/// struct Foo; +/// #[derive(Debug, PartialEq)] +/// struct Frequency { hertz: f64 } /// -/// impl MulAssign for Foo { -/// fn mul_assign(&mut self, _rhs: Foo) { -/// println!("Multiplying!"); +/// impl MulAssign for Frequency { +/// fn mul_assign(&mut self, rhs: f64) { +/// self.hertz *= rhs; /// } /// } /// -/// # #[allow(unused_assignments)] -/// fn main() { -/// let mut foo = Foo; -/// foo *= Foo; -/// } +/// let mut frequency = Frequency { hertz: 50.0 }; +/// frequency *= 4.0; +/// assert_eq!(Frequency { hertz: 200.0 }, frequency); /// ``` #[lang = "mul_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} *= {Rhs}`"] pub trait MulAssign { - /// The method for the `*=` operator + /// Performs the `*=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn mul_assign(&mut self, rhs: Rhs); } @@ -822,31 +764,27 @@ mul_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// /// # Examples /// -/// A trivial implementation of `DivAssign`. When `Foo /= Foo` happens, it ends up -/// calling `div_assign`, and therefore, `main` prints `Dividing!`. -/// /// ``` /// use std::ops::DivAssign; /// -/// struct Foo; +/// #[derive(Debug, PartialEq)] +/// struct Frequency { hertz: f64 } /// -/// impl DivAssign for Foo { -/// fn div_assign(&mut self, _rhs: Foo) { -/// println!("Dividing!"); +/// impl DivAssign for Frequency { +/// fn div_assign(&mut self, rhs: f64) { +/// self.hertz /= rhs; /// } /// } /// -/// # #[allow(unused_assignments)] -/// fn main() { -/// let mut foo = Foo; -/// foo /= Foo; -/// } +/// let mut frequency = Frequency { hertz: 200.0 }; +/// frequency /= 4.0; +/// assert_eq!(Frequency { hertz: 50.0 }, frequency); /// ``` #[lang = "div_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} /= {Rhs}`"] pub trait DivAssign { - /// The method for the `/=` operator + /// Performs the `/=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn div_assign(&mut self, rhs: Rhs); } @@ -867,31 +805,31 @@ div_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// /// # Examples /// -/// A trivial implementation of `RemAssign`. When `Foo %= Foo` happens, it ends up -/// calling `rem_assign`, and therefore, `main` prints `Remainder-ing!`. -/// /// ``` /// use std::ops::RemAssign; /// -/// struct Foo; +/// struct CookieJar { cookies: u32 } /// -/// impl RemAssign for Foo { -/// fn rem_assign(&mut self, _rhs: Foo) { -/// println!("Remainder-ing!"); +/// impl RemAssign for CookieJar { +/// fn rem_assign(&mut self, piles: u32) { +/// self.cookies %= piles; /// } /// } /// -/// # #[allow(unused_assignments)] -/// fn main() { -/// let mut foo = Foo; -/// foo %= Foo; -/// } +/// let mut jar = CookieJar { cookies: 31 }; +/// let piles = 4; +/// +/// println!("Splitting up {} cookies into {} even piles!", jar.cookies, piles); +/// +/// jar %= piles; +/// +/// println!("{} cookies remain in the cookie jar!", jar.cookies); /// ``` #[lang = "rem_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} %= {Rhs}`"] pub trait RemAssign { - /// The method for the `%=` operator + /// Performs the `%=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn rem_assign(&mut self, rhs: Rhs); } diff --git a/src/libcore/ops/bit.rs b/src/libcore/ops/bit.rs index 8743be3557cc..0bc5e554cb34 100644 --- a/src/libcore/ops/bit.rs +++ b/src/libcore/ops/bit.rs @@ -41,11 +41,11 @@ #[lang = "not"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Not { - /// The resulting type after applying the `!` operator + /// The resulting type after applying the `!` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the unary `!` operator + /// Performs the unary `!` operation. #[stable(feature = "rust1", since = "1.0.0")] fn not(self) -> Self::Output; } @@ -68,9 +68,11 @@ not_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// The bitwise AND operator `&`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. +/// /// # Examples /// -/// In this example, the `&` operator is lifted to a trivial `Scalar` type. +/// An implementation of `BitAnd` for a wrapper around `bool`. /// /// ``` /// use std::ops::BitAnd; @@ -87,16 +89,13 @@ not_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// } /// } /// -/// fn main() { -/// assert_eq!(Scalar(true) & Scalar(true), Scalar(true)); -/// assert_eq!(Scalar(true) & Scalar(false), Scalar(false)); -/// assert_eq!(Scalar(false) & Scalar(true), Scalar(false)); -/// assert_eq!(Scalar(false) & Scalar(false), Scalar(false)); -/// } +/// assert_eq!(Scalar(true) & Scalar(true), Scalar(true)); +/// assert_eq!(Scalar(true) & Scalar(false), Scalar(false)); +/// assert_eq!(Scalar(false) & Scalar(true), Scalar(false)); +/// assert_eq!(Scalar(false) & Scalar(false), Scalar(false)); /// ``` /// -/// In this example, the `BitAnd` trait is implemented for a `BooleanVector` -/// struct. +/// An implementation of `BitAnd` for a wrapper around `Vec`. /// /// ``` /// use std::ops::BitAnd; @@ -114,22 +113,20 @@ not_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// } /// } /// -/// fn main() { -/// let bv1 = BooleanVector(vec![true, true, false, false]); -/// let bv2 = BooleanVector(vec![true, false, true, false]); -/// let expected = BooleanVector(vec![true, false, false, false]); -/// assert_eq!(bv1 & bv2, expected); -/// } +/// let bv1 = BooleanVector(vec![true, true, false, false]); +/// let bv2 = BooleanVector(vec![true, false, true, false]); +/// let expected = BooleanVector(vec![true, false, false, false]); +/// assert_eq!(bv1 & bv2, expected); /// ``` #[lang = "bitand"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} & {RHS}`"] pub trait BitAnd { - /// The resulting type after applying the `&` operator + /// The resulting type after applying the `&` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `&` operator + /// Performs the `&` operation. #[stable(feature = "rust1", since = "1.0.0")] fn bitand(self, rhs: RHS) -> Self::Output; } @@ -152,9 +149,11 @@ bitand_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// The bitwise OR operator `|`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. +/// /// # Examples /// -/// In this example, the `|` operator is lifted to a trivial `Scalar` type. +/// An implementation of `BitOr` for a wrapper around `bool`. /// /// ``` /// use std::ops::BitOr; @@ -171,16 +170,13 @@ bitand_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// } /// } /// -/// fn main() { -/// assert_eq!(Scalar(true) | Scalar(true), Scalar(true)); -/// assert_eq!(Scalar(true) | Scalar(false), Scalar(true)); -/// assert_eq!(Scalar(false) | Scalar(true), Scalar(true)); -/// assert_eq!(Scalar(false) | Scalar(false), Scalar(false)); -/// } +/// assert_eq!(Scalar(true) | Scalar(true), Scalar(true)); +/// assert_eq!(Scalar(true) | Scalar(false), Scalar(true)); +/// assert_eq!(Scalar(false) | Scalar(true), Scalar(true)); +/// assert_eq!(Scalar(false) | Scalar(false), Scalar(false)); /// ``` /// -/// In this example, the `BitOr` trait is implemented for a `BooleanVector` -/// struct. +/// An implementation of `BitOr` for a wrapper around `Vec`. /// /// ``` /// use std::ops::BitOr; @@ -198,22 +194,20 @@ bitand_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// } /// } /// -/// fn main() { -/// let bv1 = BooleanVector(vec![true, true, false, false]); -/// let bv2 = BooleanVector(vec![true, false, true, false]); -/// let expected = BooleanVector(vec![true, true, true, false]); -/// assert_eq!(bv1 | bv2, expected); -/// } +/// let bv1 = BooleanVector(vec![true, true, false, false]); +/// let bv2 = BooleanVector(vec![true, false, true, false]); +/// let expected = BooleanVector(vec![true, true, true, false]); +/// assert_eq!(bv1 | bv2, expected); /// ``` #[lang = "bitor"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} | {RHS}`"] pub trait BitOr { - /// The resulting type after applying the `|` operator + /// The resulting type after applying the `|` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `|` operator + /// Performs the `|` operation. #[stable(feature = "rust1", since = "1.0.0")] fn bitor(self, rhs: RHS) -> Self::Output; } @@ -236,9 +230,11 @@ bitor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// The bitwise XOR operator `^`. /// +/// Note that `RHS` is `Self` by default, but this is not mandatory. +/// /// # Examples /// -/// In this example, the `^` operator is lifted to a trivial `Scalar` type. +/// An implementation of `BitXor` that lifts `^` to a wrapper around `bool`. /// /// ``` /// use std::ops::BitXor; @@ -255,16 +251,13 @@ bitor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// } /// } /// -/// fn main() { -/// assert_eq!(Scalar(true) ^ Scalar(true), Scalar(false)); -/// assert_eq!(Scalar(true) ^ Scalar(false), Scalar(true)); -/// assert_eq!(Scalar(false) ^ Scalar(true), Scalar(true)); -/// assert_eq!(Scalar(false) ^ Scalar(false), Scalar(false)); -/// } +/// assert_eq!(Scalar(true) ^ Scalar(true), Scalar(false)); +/// assert_eq!(Scalar(true) ^ Scalar(false), Scalar(true)); +/// assert_eq!(Scalar(false) ^ Scalar(true), Scalar(true)); +/// assert_eq!(Scalar(false) ^ Scalar(false), Scalar(false)); /// ``` /// -/// In this example, the `BitXor` trait is implemented for a `BooleanVector` -/// struct. +/// An implementation of `BitXor` trait for a wrapper around `Vec`. /// /// ``` /// use std::ops::BitXor; @@ -285,22 +278,20 @@ bitor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// } /// } /// -/// fn main() { -/// let bv1 = BooleanVector(vec![true, true, false, false]); -/// let bv2 = BooleanVector(vec![true, false, true, false]); -/// let expected = BooleanVector(vec![false, true, true, false]); -/// assert_eq!(bv1 ^ bv2, expected); -/// } +/// let bv1 = BooleanVector(vec![true, true, false, false]); +/// let bv2 = BooleanVector(vec![true, false, true, false]); +/// let expected = BooleanVector(vec![false, true, true, false]); +/// assert_eq!(bv1 ^ bv2, expected); /// ``` #[lang = "bitxor"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} ^ {RHS}`"] pub trait BitXor { - /// The resulting type after applying the `^` operator + /// The resulting type after applying the `^` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `^` operator + /// Performs the `^` operation. #[stable(feature = "rust1", since = "1.0.0")] fn bitxor(self, rhs: RHS) -> Self::Output; } @@ -326,7 +317,7 @@ bitxor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// # Examples /// /// An implementation of `Shl` that lifts the `<<` operation on integers to a -/// `Scalar` struct. +/// wrapper around `usize`. /// /// ``` /// use std::ops::Shl; @@ -342,9 +333,8 @@ bitxor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// Scalar(lhs << rhs) /// } /// } -/// fn main() { -/// assert_eq!(Scalar(4) << Scalar(2), Scalar(16)); -/// } +/// +/// assert_eq!(Scalar(4) << Scalar(2), Scalar(16)); /// ``` /// /// An implementation of `Shl` that spins a vector leftward by a given amount. @@ -361,7 +351,7 @@ bitxor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// type Output = Self; /// /// fn shl(self, rhs: usize) -> SpinVector { -/// // rotate the vector by `rhs` places +/// // Rotate the vector by `rhs` places. /// let (a, b) = self.vec.split_at(rhs); /// let mut spun_vector: Vec = vec![]; /// spun_vector.extend_from_slice(b); @@ -370,20 +360,18 @@ bitxor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// } /// } /// -/// fn main() { -/// assert_eq!(SpinVector { vec: vec![0, 1, 2, 3, 4] } << 2, -/// SpinVector { vec: vec![2, 3, 4, 0, 1] }); -/// } +/// assert_eq!(SpinVector { vec: vec![0, 1, 2, 3, 4] } << 2, +/// SpinVector { vec: vec![2, 3, 4, 0, 1] }); /// ``` #[lang = "shl"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} << {RHS}`"] pub trait Shl { - /// The resulting type after applying the `<<` operator + /// The resulting type after applying the `<<` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `<<` operator + /// Performs the `<<` operation. #[stable(feature = "rust1", since = "1.0.0")] fn shl(self, rhs: RHS) -> Self::Output; } @@ -430,7 +418,7 @@ shl_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 isize i128 } /// # Examples /// /// An implementation of `Shr` that lifts the `>>` operation on integers to a -/// `Scalar` struct. +/// wrapper around `usize`. /// /// ``` /// use std::ops::Shr; @@ -446,9 +434,8 @@ shl_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 isize i128 } /// Scalar(lhs >> rhs) /// } /// } -/// fn main() { -/// assert_eq!(Scalar(16) >> Scalar(2), Scalar(4)); -/// } +/// +/// assert_eq!(Scalar(16) >> Scalar(2), Scalar(4)); /// ``` /// /// An implementation of `Shr` that spins a vector rightward by a given amount. @@ -465,7 +452,7 @@ shl_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 isize i128 } /// type Output = Self; /// /// fn shr(self, rhs: usize) -> SpinVector { -/// // rotate the vector by `rhs` places +/// // Rotate the vector by `rhs` places. /// let (a, b) = self.vec.split_at(self.vec.len() - rhs); /// let mut spun_vector: Vec = vec![]; /// spun_vector.extend_from_slice(b); @@ -474,20 +461,18 @@ shl_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 isize i128 } /// } /// } /// -/// fn main() { -/// assert_eq!(SpinVector { vec: vec![0, 1, 2, 3, 4] } >> 2, -/// SpinVector { vec: vec![3, 4, 0, 1, 2] }); -/// } +/// assert_eq!(SpinVector { vec: vec![0, 1, 2, 3, 4] } >> 2, +/// SpinVector { vec: vec![3, 4, 0, 1, 2] }); /// ``` #[lang = "shr"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "no implementation for `{Self} >> {RHS}`"] pub trait Shr { - /// The resulting type after applying the `>>` operator + /// The resulting type after applying the `>>` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// The method for the `>>` operator + /// Performs the `>>` operation. #[stable(feature = "rust1", since = "1.0.0")] fn shr(self, rhs: RHS) -> Self::Output; } @@ -533,7 +518,8 @@ shr_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// /// # Examples /// -/// In this example, the `&=` operator is lifted to a trivial `Scalar` type. +/// An implementation of `BitAndAssign` that lifts the `&=` operator to a +/// wrapper around `bool`. /// /// ``` /// use std::ops::BitAndAssign; @@ -548,27 +534,25 @@ shr_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// } /// } /// -/// fn main() { -/// let mut scalar = Scalar(true); -/// scalar &= Scalar(true); -/// assert_eq!(scalar, Scalar(true)); +/// let mut scalar = Scalar(true); +/// scalar &= Scalar(true); +/// assert_eq!(scalar, Scalar(true)); /// -/// let mut scalar = Scalar(true); -/// scalar &= Scalar(false); -/// assert_eq!(scalar, Scalar(false)); +/// let mut scalar = Scalar(true); +/// scalar &= Scalar(false); +/// assert_eq!(scalar, Scalar(false)); /// -/// let mut scalar = Scalar(false); -/// scalar &= Scalar(true); -/// assert_eq!(scalar, Scalar(false)); +/// let mut scalar = Scalar(false); +/// scalar &= Scalar(true); +/// assert_eq!(scalar, Scalar(false)); /// -/// let mut scalar = Scalar(false); -/// scalar &= Scalar(false); -/// assert_eq!(scalar, Scalar(false)); -/// } +/// let mut scalar = Scalar(false); +/// scalar &= Scalar(false); +/// assert_eq!(scalar, Scalar(false)); /// ``` /// -/// In this example, the `BitAndAssign` trait is implemented for a -/// `BooleanVector` struct. +/// Here, the `BitAndAssign` trait is implemented for a wrapper around +/// `Vec`. /// /// ``` /// use std::ops::BitAndAssign; @@ -577,7 +561,7 @@ shr_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// struct BooleanVector(Vec); /// /// impl BitAndAssign for BooleanVector { -/// // rhs is the "right-hand side" of the expression `a &= b` +/// // `rhs` is the "right-hand side" of the expression `a &= b`. /// fn bitand_assign(&mut self, rhs: Self) { /// assert_eq!(self.0.len(), rhs.0.len()); /// *self = BooleanVector(self.0 @@ -588,18 +572,16 @@ shr_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// } /// } /// -/// fn main() { -/// let mut bv = BooleanVector(vec![true, true, false, false]); -/// bv &= BooleanVector(vec![true, false, true, false]); -/// let expected = BooleanVector(vec![true, false, false, false]); -/// assert_eq!(bv, expected); -/// } +/// let mut bv = BooleanVector(vec![true, true, false, false]); +/// bv &= BooleanVector(vec![true, false, true, false]); +/// let expected = BooleanVector(vec![true, false, false, false]); +/// assert_eq!(bv, expected); /// ``` #[lang = "bitand_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} &= {Rhs}`"] pub trait BitAndAssign { - /// The method for the `&=` operator + /// Performs the `&=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn bitand_assign(&mut self, rhs: Rhs); } @@ -620,31 +602,31 @@ bitand_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// /// # Examples /// -/// A trivial implementation of `BitOrAssign`. When `Foo |= Foo` happens, it ends up -/// calling `bitor_assign`, and therefore, `main` prints `Bitwise Or-ing!`. -/// /// ``` /// use std::ops::BitOrAssign; /// -/// struct Foo; +/// #[derive(Debug, PartialEq)] +/// struct PersonalPreferences { +/// likes_cats: bool, +/// likes_dogs: bool, +/// } /// -/// impl BitOrAssign for Foo { -/// fn bitor_assign(&mut self, _rhs: Foo) { -/// println!("Bitwise Or-ing!"); +/// impl BitOrAssign for PersonalPreferences { +/// fn bitor_assign(&mut self, rhs: Self) { +/// self.likes_cats |= rhs.likes_cats; +/// self.likes_dogs |= rhs.likes_dogs; /// } /// } /// -/// # #[allow(unused_assignments)] -/// fn main() { -/// let mut foo = Foo; -/// foo |= Foo; -/// } +/// let mut prefs = PersonalPreferences { likes_cats: true, likes_dogs: false }; +/// prefs |= PersonalPreferences { likes_cats: false, likes_dogs: true }; +/// assert_eq!(prefs, PersonalPreferences { likes_cats: true, likes_dogs: true }); /// ``` #[lang = "bitor_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} |= {Rhs}`"] pub trait BitOrAssign { - /// The method for the `|=` operator + /// Performs the `|=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn bitor_assign(&mut self, rhs: Rhs); } @@ -665,31 +647,31 @@ bitor_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// /// # Examples /// -/// A trivial implementation of `BitXorAssign`. When `Foo ^= Foo` happens, it ends up -/// calling `bitxor_assign`, and therefore, `main` prints `Bitwise Xor-ing!`. -/// /// ``` /// use std::ops::BitXorAssign; /// -/// struct Foo; +/// #[derive(Debug, PartialEq)] +/// struct Personality { +/// has_soul: bool, +/// likes_knitting: bool, +/// } /// -/// impl BitXorAssign for Foo { -/// fn bitxor_assign(&mut self, _rhs: Foo) { -/// println!("Bitwise Xor-ing!"); +/// impl BitXorAssign for Personality { +/// fn bitxor_assign(&mut self, rhs: Self) { +/// self.has_soul ^= rhs.has_soul; +/// self.likes_knitting ^= rhs.likes_knitting; /// } /// } /// -/// # #[allow(unused_assignments)] -/// fn main() { -/// let mut foo = Foo; -/// foo ^= Foo; -/// } +/// let mut personality = Personality { has_soul: false, likes_knitting: true }; +/// personality ^= Personality { has_soul: true, likes_knitting: true }; +/// assert_eq!(personality, Personality { has_soul: true, likes_knitting: false}); /// ``` #[lang = "bitxor_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} ^= {Rhs}`"] pub trait BitXorAssign { - /// The method for the `^=` operator + /// Performs the `^=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn bitxor_assign(&mut self, rhs: Rhs); } @@ -710,31 +692,29 @@ bitxor_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// /// # Examples /// -/// A trivial implementation of `ShlAssign`. When `Foo <<= Foo` happens, it ends up -/// calling `shl_assign`, and therefore, `main` prints `Shifting left!`. +/// An implementation of `ShlAssign` for a wrapper around `usize`. /// /// ``` /// use std::ops::ShlAssign; /// -/// struct Foo; +/// #[derive(Debug, PartialEq)] +/// struct Scalar(usize); /// -/// impl ShlAssign for Foo { -/// fn shl_assign(&mut self, _rhs: Foo) { -/// println!("Shifting left!"); +/// impl ShlAssign for Scalar { +/// fn shl_assign(&mut self, rhs: usize) { +/// self.0 <<= rhs; /// } /// } /// -/// # #[allow(unused_assignments)] -/// fn main() { -/// let mut foo = Foo; -/// foo <<= Foo; -/// } +/// let mut scalar = Scalar(4); +/// scalar <<= 2; +/// assert_eq!(scalar, Scalar(16)); /// ``` #[lang = "shl_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} <<= {Rhs}`"] pub trait ShlAssign { - /// The method for the `<<=` operator + /// Performs the `<<=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn shl_assign(&mut self, rhs: Rhs); } @@ -776,31 +756,29 @@ shl_assign_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// /// # Examples /// -/// A trivial implementation of `ShrAssign`. When `Foo >>= Foo` happens, it ends up -/// calling `shr_assign`, and therefore, `main` prints `Shifting right!`. +/// An implementation of `ShrAssign` for a wrapper around `usize`. /// /// ``` /// use std::ops::ShrAssign; /// -/// struct Foo; +/// #[derive(Debug, PartialEq)] +/// struct Scalar(usize); /// -/// impl ShrAssign for Foo { -/// fn shr_assign(&mut self, _rhs: Foo) { -/// println!("Shifting right!"); +/// impl ShrAssign for Scalar { +/// fn shr_assign(&mut self, rhs: usize) { +/// self.0 >>= rhs; /// } /// } /// -/// # #[allow(unused_assignments)] -/// fn main() { -/// let mut foo = Foo; -/// foo >>= Foo; -/// } +/// let mut scalar = Scalar(16); +/// scalar >>= 2; +/// assert_eq!(scalar, Scalar(4)); /// ``` #[lang = "shr_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented = "no implementation for `{Self} >>= {Rhs}`"] pub trait ShrAssign { - /// The method for the `>>=` operator + /// Performs the `>>=` operation. #[stable(feature = "op_assign_traits", since = "1.8.0")] fn shr_assign(&mut self, rhs: Rhs); } diff --git a/src/libcore/ops/deref.rs b/src/libcore/ops/deref.rs index 18cf20ac411d..a2e7c44cb249 100644 --- a/src/libcore/ops/deref.rs +++ b/src/libcore/ops/deref.rs @@ -8,16 +8,44 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/// The `Deref` trait is used to specify the functionality of dereferencing -/// operations, like `*v`. +/// Used for immutable dereferencing operations, like `*v`. /// -/// `Deref` also enables ['`Deref` coercions'][coercions]. +/// In addition to being used for explicit dereferencing operations with the +/// (unary) `*` operator in immutable contexts, `Deref` is also used implicitly +/// by the compiler in many circumstances. This mechanism is called +/// ['`Deref` coercion'][more]. In mutable contexts, [`DerefMut`] is used. /// -/// [coercions]: ../../book/first-edition/deref-coercions.html +/// Implementing `Deref` for smart pointers makes accessing the data behind them +/// convenient, which is why they implement `Deref`. On the other hand, the +/// rules regarding `Deref` and [`DerefMut`] were designed specifically to +/// accomodate smart pointers. Because of this, **`Deref` should only be +/// implemented for smart pointers** to avoid confusion. +/// +/// For similar reasons, **this trait should never fail**. Failure during +/// dereferencing can be extremely confusing when `Deref` is invoked implicitly. +/// +/// # More on `Deref` coercion +/// +/// If `T` implements `Deref`, and `x` is a value of type `T`, then: +/// * In immutable contexts, `*x` on non-pointer types is equivalent to +/// `*Deref::deref(&x)`. +/// * Values of type `&T` are coerced to values of type `&U` +/// * `T` implicitly implements all the (immutable) methods of the type `U`. +/// +/// For more details, visit [the chapter in *The Rust Programming Language*] +/// [book] as well as the reference sections on [the dereference operator] +/// [ref-deref-op], [the `Deref` trait][ref-deref-trait], and [type coercions]. +/// +/// [book]: ../../book/second-edition/ch15-02-deref.html +/// [`DerefMut`]: trait.DerefMut.html +/// [more]: #more-on-deref-coercion +/// [ref-deref-op]: ../../reference/expressions.html#the-dereference-operator +/// [ref-deref-trait]: ../../reference/the-deref-trait.html +/// [type coercions]: ../../reference/type-coercions.html /// /// # Examples /// -/// A struct with a single field which is accessible via dereferencing the +/// A struct with a single field which is accessible by dereferencing the /// struct. /// /// ``` @@ -35,19 +63,17 @@ /// } /// } /// -/// fn main() { -/// let x = DerefExample { value: 'a' }; -/// assert_eq!('a', *x); -/// } +/// let x = DerefExample { value: 'a' }; +/// assert_eq!('a', *x); /// ``` #[lang = "deref"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Deref { - /// The resulting type after dereferencing + /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] type Target: ?Sized; - /// The method called to dereference a value + /// Dereferences the value. #[stable(feature = "rust1", since = "1.0.0")] fn deref(&self) -> &Self::Target; } @@ -66,16 +92,46 @@ impl<'a, T: ?Sized> Deref for &'a mut T { fn deref(&self) -> &T { *self } } -/// The `DerefMut` trait is used to specify the functionality of dereferencing -/// mutably like `*v = 1;` +/// Used for mutable dereferencing operations, like in `*v = 1;`. /// -/// `DerefMut` also enables ['`Deref` coercions'][coercions]. +/// In addition to being used for explicit dereferencing operations with the +/// (unary) `*` operator in mutable contexts, `DerefMut` is also used implicitly +/// by the compiler in many circumstances. This mechanism is called +/// ['`Deref` coercion'][more]. In immutable contexts, [`Deref`] is used. /// -/// [coercions]: ../../book/first-edition/deref-coercions.html +/// Implementing `DerefMut` for smart pointers makes mutating the data behind +/// them convenient, which is why they implement `DerefMut`. On the other hand, +/// the rules regarding [`Deref`] and `DerefMut` were designed specifically to +/// accomodate smart pointers. Because of this, **`DerefMut` should only be +/// implemented for smart pointers** to avoid confusion. +/// +/// For similar reasons, **this trait should never fail**. Failure during +/// dereferencing can be extremely confusing when `DerefMut` is invoked +/// implicitly. +/// +/// # More on `Deref` coercion +/// +/// If `T` implements `DerefMut`, and `x` is a value of type `T`, +/// then: +/// * In mutable contexts, `*x` on non-pointer types is equivalent to +/// `*Deref::deref(&x)`. +/// * Values of type `&mut T` are coerced to values of type `&mut U` +/// * `T` implicitly implements all the (mutable) methods of the type `U`. +/// +/// For more details, visit [the chapter in *The Rust Programming Language*] +/// [book] as well as the reference sections on [the dereference operator] +/// [ref-deref-op], [the `Deref` trait][ref-deref-trait], and [type coercions]. +/// +/// [book]: ../../book/second-edition/ch15-02-deref.html +/// [`Deref`]: trait.Deref.html +/// [more]: #more-on-deref-coercion +/// [ref-deref-op]: ../../reference/expressions.html#the-dereference-operator +/// [ref-deref-trait]: ../../reference/the-deref-trait.html +/// [type coercions]: ../../reference/type-coercions.html /// /// # Examples /// -/// A struct with a single field which is modifiable via dereferencing the +/// A struct with a single field which is modifiable by dereferencing the /// struct. /// /// ``` @@ -99,16 +155,14 @@ impl<'a, T: ?Sized> Deref for &'a mut T { /// } /// } /// -/// fn main() { -/// let mut x = DerefMutExample { value: 'a' }; -/// *x = 'b'; -/// assert_eq!('b', *x); -/// } +/// let mut x = DerefMutExample { value: 'a' }; +/// *x = 'b'; +/// assert_eq!('b', *x); /// ``` #[lang = "deref_mut"] #[stable(feature = "rust1", since = "1.0.0")] pub trait DerefMut: Deref { - /// The method called to mutably dereference a value + /// Mutably dereferences the value. #[stable(feature = "rust1", since = "1.0.0")] fn deref_mut(&mut self) -> &mut Self::Target; } diff --git a/src/libcore/ops/drop.rs b/src/libcore/ops/drop.rs index 92f3cb256c83..70ab7b2f3b7e 100644 --- a/src/libcore/ops/drop.rs +++ b/src/libcore/ops/drop.rs @@ -8,20 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/// The `Drop` trait is used to run some code when a value goes out of scope. +/// Used to run some code when a value goes out of scope. /// This is sometimes called a 'destructor'. /// -/// When a value goes out of scope, if it implements this trait, it will have -/// its `drop` method called. Then any fields the value contains will also +/// When a value goes out of scope, it will have its `drop` method called if +/// its type implements `Drop`. Then, any fields the value contains will also /// be dropped recursively. /// -/// Because of the recursive dropping, you do not need to implement this trait +/// Because of this recursive dropping, you do not need to implement this trait /// unless your type needs its own destructor logic. /// +/// Refer to [the chapter on `Drop` in *The Rust Programming Language*][book] +/// for some more elaboration. +/// +/// [book]: ../../book/second-edition/ch15-03-drop.html +/// /// # Examples /// -/// A trivial implementation of `Drop`. The `drop` method is called when `_x` -/// goes out of scope, and therefore `main` prints `Dropping!`. +/// ## Implementing `Drop` +/// +/// The `drop` method is called when `_x` goes out of scope, and therefore +/// `main` prints `Dropping!`. /// /// ``` /// struct HasDrop; @@ -37,9 +44,11 @@ /// } /// ``` /// -/// Showing the recursive nature of `Drop`. When `outer` goes out of scope, the -/// `drop` method will be called first for `Outer`, then for `Inner`. Therefore -/// `main` prints `Dropping Outer!` and then `Dropping Inner!`. +/// ## Dropping is done recursively +/// +/// When `outer` goes out of scope, the `drop` method will be called first for +/// `Outer`, then for `Inner`. Therefore, `main` prints `Dropping Outer!` and +/// then `Dropping Inner!`. /// /// ``` /// struct Inner; @@ -62,12 +71,20 @@ /// } /// ``` /// -/// Because variables are dropped in the reverse order they are declared, -/// `main` will print `Declared second!` and then `Declared first!`. +/// ## Variables are dropped in reverse order of declaration +/// +/// `_first` is declared first and `_second` is declared second, so `main` will +/// print `Declared second!` and then `Declared first!`. /// /// ``` /// struct PrintOnDrop(&'static str); /// +/// impl Drop for PrintOnDrop { +/// fn drop(&mut self) { +/// println!("{}", self.0); +/// } +/// } +/// /// fn main() { /// let _first = PrintOnDrop("Declared first!"); /// let _second = PrintOnDrop("Declared second!"); @@ -76,24 +93,25 @@ #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Drop { - /// A method called when the value goes out of scope. + /// Executes the destructor for this type. /// - /// When this method has been called, `self` has not yet been deallocated. - /// If it were, `self` would be a dangling reference. - /// - /// After this function is over, the memory of `self` will be deallocated. - /// - /// This function cannot be called explicitly. This is compiler error - /// [E0040]. However, the [`std::mem::drop`] function in the prelude can be + /// This method is called implilcitly when the value goes out of scope, + /// and cannot be called explicitly (this is compiler error [E0040]). + /// However, the [`std::mem::drop`] function in the prelude can be /// used to call the argument's `Drop` implementation. /// - /// [E0040]: ../../error-index.html#E0040 - /// [`std::mem::drop`]: ../../std/mem/fn.drop.html + /// When this method has been called, `self` has not yet been deallocated. + /// That only happens after the method is over. + /// If this wasn't the case, `self` would be a dangling reference. /// /// # Panics /// - /// Given that a `panic!` will call `drop()` as it unwinds, any `panic!` in - /// a `drop()` implementation will likely abort. + /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`] + /// in a `drop` implementation will likely abort. + /// + /// [E0040]: ../../error-index.html#E0040 + /// [`panic!`]: ../macro.panic.html + /// [`std::mem::drop`]: ../../std/mem/fn.drop.html #[stable(feature = "rust1", since = "1.0.0")] fn drop(&mut self); } diff --git a/src/libcore/ops/function.rs b/src/libcore/ops/function.rs index c5b3fbca1a6d..d10fcb86b241 100644 --- a/src/libcore/ops/function.rs +++ b/src/libcore/ops/function.rs @@ -8,26 +8,51 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/// A version of the call operator that takes an immutable receiver. +/// The version of the call operator that takes an immutable receiver. +/// +/// Instances of `Fn` can be called repeatedly without mutating state. +/// +/// *This trait (`Fn`) is not to be confused with [function pointers][] +/// (`fn`).* +/// +/// `Fn` is implemented automatically by closures which only take immutable +/// references to captured variables or don't capture anything at all, as well +/// as (safe) [function pointers][] (with some caveats, see their documentation +/// for more details). Additionally, for any type `F` that implements `Fn`, `&F` +/// implements `Fn`, too. +/// +/// Since both [`FnMut`] and [`FnOnce`] are supertraits of `Fn`, any +/// instance of `Fn` can be used as a parameter where a [`FnMut`] or [`FnOnce`] +/// is expected. +/// +/// Use `Fn` as a bound when you want to accept a parameter of function-like +/// type and need to call it repeatedly and without mutating state (e.g. when +/// calling it concurrently). If you do not need such strict requirements, use +/// [`FnMut`] or [`FnOnce`] as bounds. +/// +/// See the [chapter on closures in *The Rust Programming Language*][book] for +/// some more information on this topic. +/// +/// Also of note is the special syntax for `Fn` traits (e.g. +/// `Fn(usize, bool) -> usize`). Those interested in the technical details of +/// this can refer to [the relevant section in the *Rustonomicon*][nomicon]. +/// +/// [book]: ../../book/second-edition/ch13-01-closures.html +/// [`FnMut`]: trait.FnMut.html +/// [`FnOnce`]: trait.FnOnce.html +/// [function pointers]: ../../std/primitive.fn.html +/// [nomicon]: ../../nomicon/hrtb.html /// /// # Examples /// -/// Closures automatically implement this trait, which allows them to be -/// invoked. Note, however, that `Fn` takes an immutable reference to any -/// captured variables. To take a mutable capture, implement [`FnMut`], and to -/// consume the capture, implement [`FnOnce`]. -/// -/// [`FnMut`]: trait.FnMut.html -/// [`FnOnce`]: trait.FnOnce.html +/// ## Calling a closure /// /// ``` /// let square = |x| x * x; /// assert_eq!(square(5), 25); /// ``` /// -/// Closures can also be passed to higher-level functions through a `Fn` -/// parameter (or a `FnMut` or `FnOnce` parameter, which are supertraits of -/// `Fn`). +/// ## Using a `Fn` parameter /// /// ``` /// fn call_with_one(func: F) -> usize @@ -43,17 +68,46 @@ #[rustc_paren_sugar] #[fundamental] // so that regex can rely that `&str: !FnMut` pub trait Fn : FnMut { - /// This is called when the call operator is used. + /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call(&self, args: Args) -> Self::Output; } -/// A version of the call operator that takes a mutable receiver. +/// The version of the call operator that takes a mutable receiver. +/// +/// Instances of `FnMut` can be called repeatedly and may mutate state. +/// +/// `FnMut` is implemented automatically by closures which take mutable +/// references to captured variables, as well as all types that implement +/// [`Fn`], e.g. (safe) [function pointers][] (since `FnMut` is a supertrait of +/// [`Fn`]). Additionally, for any type `F` that implements `FnMut`, `&mut F` +/// implements `FnMut`, too. +/// +/// Since [`FnOnce`] is a supertrait of `FnMut`, any instance of `FnMut` can be +/// used where a [`FnOnce`] is expected, and since [`Fn`] is a subtrait of +/// `FnMut`, any instance of [`Fn`] can be used where `FnMut` is expected. +/// +/// Use `FnMut` as a bound when you want to accept a parameter of function-like +/// type and need to call it repeatedly, while allowing it to mutate state. +/// If you don't want the parameter to mutate state, use [`Fn`] as a +/// bound; if you don't need to call it repeatedly, use [`FnOnce`]. +/// +/// See the [chapter on closures in *The Rust Programming Language*][book] for +/// some more information on this topic. +/// +/// Also of note is the special syntax for `Fn` traits (e.g. +/// `Fn(usize, bool) -> usize`). Those interested in the technical details of +/// this can refer to [the relevant section in the *Rustonomicon*][nomicon]. +/// +/// [book]: ../../book/second-edition/ch13-01-closures.html +/// [`Fn`]: trait.Fn.html +/// [`FnOnce`]: trait.FnOnce.html +/// [function pointers]: ../../std/primitive.fn.html +/// [nomicon]: ../../nomicon/hrtb.html /// /// # Examples /// -/// Closures that mutably capture variables automatically implement this trait, -/// which allows them to be invoked. +/// ## Calling a mutably capturing closure /// /// ``` /// let mut x = 5; @@ -64,8 +118,7 @@ pub trait Fn : FnMut { /// assert_eq!(x, 25); /// ``` /// -/// Closures can also be passed to higher-level functions through a `FnMut` -/// parameter (or a `FnOnce` parameter, which is a supertrait of `FnMut`). +/// ## Using a `FnMut` parameter /// /// ``` /// fn do_twice(mut func: F) @@ -88,17 +141,45 @@ pub trait Fn : FnMut { #[rustc_paren_sugar] #[fundamental] // so that regex can rely that `&str: !FnMut` pub trait FnMut : FnOnce { - /// This is called when the call operator is used. + /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; } -/// A version of the call operator that takes a by-value receiver. +/// The version of the call operator that takes a by-value receiver. +/// +/// Instances of `FnOnce` can be called, but might not be callable multiple +/// times. Because of this, if the only thing known about a type is that it +/// implements `FnOnce`, it can only be called once. +/// +/// `FnOnce` is implemented automatically by closure that might consume captured +/// variables, as well as all types that implement [`FnMut`], e.g. (safe) +/// [function pointers][] (since `FnOnce` is a supertrait of [`FnMut`]). +/// +/// Since both [`Fn`] and [`FnMut`] are subtraits of `FnOnce`, any instance of +/// [`Fn`] or [`FnMut`] can be used where a `FnOnce` is expected. +/// +/// Use `FnOnce` as a bound when you want to accept a parameter of function-like +/// type and only need to call it once. If you need to call the parameter +/// repeatedly, use [`FnMut`] as a bound; if you also need it to not mutate +/// state, use [`Fn`]. +/// +/// See the [chapter on closures in *The Rust Programming Language*][book] for +/// some more information on this topic. +/// +/// Also of note is the special syntax for `Fn` traits (e.g. +/// `Fn(usize, bool) -> usize`). Those interested in the technical details of +/// this can refer to [the relevant section in the *Rustonomicon*][nomicon]. +/// +/// [book]: ../../book/second-edition/ch13-01-closures.html +/// [`Fn`]: trait.Fn.html +/// [`FnMut`]: trait.FnMut.html +/// [function pointers]: ../../std/primitive.fn.html +/// [nomicon]: ../../nomicon/hrtb.html /// /// # Examples /// -/// By-value closures automatically implement this trait, which allows them to -/// be invoked. +/// ## Calling a by-value closure /// /// ``` /// let x = 5; @@ -106,21 +187,20 @@ pub trait FnMut : FnOnce { /// assert_eq!(square_x(), 25); /// ``` /// -/// By-value Closures can also be passed to higher-level functions through a -/// `FnOnce` parameter. +/// ## Using a `FnOnce` parameter /// /// ``` /// fn consume_with_relish(func: F) /// where F: FnOnce() -> String /// { /// // `func` consumes its captured variables, so it cannot be run more -/// // than once +/// // than once. /// println!("Consumed: {}", func()); /// /// println!("Delicious!"); /// /// // Attempting to invoke `func()` again will throw a `use of moved -/// // value` error for `func` +/// // value` error for `func`. /// } /// /// let x = String::from("x"); @@ -138,7 +218,7 @@ pub trait FnOnce { #[stable(feature = "fn_once_output", since = "1.12.0")] type Output; - /// This is called when the call operator is used. + /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call_once(self, args: Args) -> Self::Output; } diff --git a/src/libcore/ops/index.rs b/src/libcore/ops/index.rs index b16b95677874..d65c0aba5048 100644 --- a/src/libcore/ops/index.rs +++ b/src/libcore/ops/index.rs @@ -8,13 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/// The `Index` trait is used to specify the functionality of indexing operations -/// like `container[index]` when used in an immutable context. +/// Used for indexing operations (`container[index]`) in immutable contexts. /// /// `container[index]` is actually syntactic sugar for `*container.index(index)`, /// but only when used as an immutable value. If a mutable value is requested, /// [`IndexMut`] is used instead. This allows nice things such as -/// `let value = v[index]` if `value` implements [`Copy`]. +/// `let value = v[index]` if the type of `value` implements [`Copy`]. /// /// [`IndexMut`]: ../../std/ops/trait.IndexMut.html /// [`Copy`]: ../../std/marker/trait.Copy.html @@ -64,25 +63,23 @@ #[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Index { - /// The returned type after indexing + /// The returned type after indexing. #[stable(feature = "rust1", since = "1.0.0")] type Output: ?Sized; - /// The method for the indexing (`container[index]`) operation + /// Performs the indexing (`container[index]`) operation. #[stable(feature = "rust1", since = "1.0.0")] fn index(&self, index: Idx) -> &Self::Output; } -/// The `IndexMut` trait is used to specify the functionality of indexing -/// operations like `container[index]` when used in a mutable context. +/// Used for indexing operations (`container[index]`) in mutable contexts. /// /// `container[index]` is actually syntactic sugar for /// `*container.index_mut(index)`, but only when used as a mutable value. If /// an immutable value is requested, the [`Index`] trait is used instead. This -/// allows nice things such as `v[index] = value` if `value` implements [`Copy`]. +/// allows nice things such as `v[index] = value`. /// /// [`Index`]: ../../std/ops/trait.Index.html -/// [`Copy`]: ../../std/marker/trait.Copy.html /// /// # Examples /// @@ -106,7 +103,7 @@ pub trait Index { /// /// struct Balance { /// pub left: Weight, -/// pub right:Weight, +/// pub right: Weight, /// } /// /// impl Index for Balance { @@ -131,28 +128,26 @@ pub trait Index { /// } /// } /// -/// fn main() { -/// let mut balance = Balance { -/// right: Weight::Kilogram(2.5), -/// left: Weight::Pound(1.5), -/// }; +/// let mut balance = Balance { +/// right: Weight::Kilogram(2.5), +/// left: Weight::Pound(1.5), +/// }; /// -/// // In this case balance[Side::Right] is sugar for -/// // *balance.index(Side::Right), since we are only reading -/// // balance[Side::Right], not writing it. -/// assert_eq!(balance[Side::Right],Weight::Kilogram(2.5)); +/// // In this case, `balance[Side::Right]` is sugar for +/// // `*balance.index(Side::Right)`, since we are only *reading* +/// // `balance[Side::Right]`, not writing it. +/// assert_eq!(balance[Side::Right], Weight::Kilogram(2.5)); /// -/// // However in this case balance[Side::Left] is sugar for -/// // *balance.index_mut(Side::Left), since we are writing -/// // balance[Side::Left]. -/// balance[Side::Left] = Weight::Kilogram(3.0); -/// } +/// // However, in this case `balance[Side::Left]` is sugar for +/// // `*balance.index_mut(Side::Left)`, since we are writing +/// // `balance[Side::Left]`. +/// balance[Side::Left] = Weight::Kilogram(3.0); /// ``` #[lang = "index_mut"] #[rustc_on_unimplemented = "the type `{Self}` cannot be mutably indexed by `{Idx}`"] #[stable(feature = "rust1", since = "1.0.0")] pub trait IndexMut: Index { - /// The method for the mutable indexing (`container[index]`) operation + /// Performs the mutable indexing (`container[index]`) operation. #[stable(feature = "rust1", since = "1.0.0")] fn index_mut(&mut self, index: Idx) -> &mut Self::Output; } diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index 767b22e851ee..8975b680ca7f 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -21,6 +21,12 @@ //! custom operators are required, you should look toward macros or compiler //! plugins to extend Rust's syntax. //! +//! Implementations of operator traits should be unsurprising in their +//! respective contexts, keeping in mind their usual meanings and +//! [operator precedence]. For example, when implementing [`Mul`], the operation +//! should have some resemblance to multiplication (and share expected +//! properties like associativity). +//! //! Note that the `&&` and `||` operators short-circuit, i.e. they only //! evaluate their second operand if it contributes to the result. Since this //! behavior is not enforceable by traits, `&&` and `||` are not supported as @@ -46,7 +52,7 @@ //! ```rust //! use std::ops::{Add, Sub}; //! -//! #[derive(Debug)] +//! #[derive(Debug, PartialEq)] //! struct Point { //! x: i32, //! y: i32, @@ -67,10 +73,9 @@ //! Point {x: self.x - other.x, y: self.y - other.y} //! } //! } -//! fn main() { -//! println!("{:?}", Point {x: 1, y: 0} + Point {x: 2, y: 3}); -//! println!("{:?}", Point {x: 1, y: 0} - Point {x: 2, y: 3}); -//! } +//! +//! assert_eq!(Point {x: 3, y: 3}, Point {x: 1, y: 0} + Point {x: 2, y: 3}); +//! assert_eq!(Point {x: -1, y: -3}, Point {x: 1, y: 0} - Point {x: 2, y: 3}); //! ``` //! //! See the documentation for each trait for an example implementation. @@ -143,7 +148,9 @@ //! [`FnOnce`]: trait.FnOnce.html //! [`Add`]: trait.Add.html //! [`Sub`]: trait.Sub.html +//! [`Mul`]: trait.Mul.html //! [`clone`]: ../clone/trait.Clone.html#tymethod.clone +//! [operator precedence]: ../../reference/expressions.html#operator-precedence #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/ops/place.rs b/src/libcore/ops/place.rs index 19da887cbbfb..9fb171e7b924 100644 --- a/src/libcore/ops/place.rs +++ b/src/libcore/ops/place.rs @@ -66,7 +66,7 @@ pub trait Place { /// or `Copy`, since the `make_place` method takes `self` by value. #[unstable(feature = "placement_new_protocol", issue = "27779")] pub trait Placer { - /// `Place` is the intermedate agent guarding the + /// `Place` is the intermediate agent guarding the /// uninitialized state for `Data`. type Place: InPlace; diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 33258b7a875c..463a50491a86 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -10,10 +10,10 @@ use fmt; -/// An unbounded range. Use `..` (two dots) for its shorthand. +/// An unbounded range (`..`). /// -/// Its primary use case is slicing index. It cannot serve as an iterator -/// because it doesn't have a starting point. +/// `RangeFull` is primarily used as a [slicing index], its shorthand is `..`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. /// /// # Examples /// @@ -23,8 +23,8 @@ use fmt; /// assert_eq!((..), std::ops::RangeFull); /// ``` /// -/// It does not have an `IntoIterator` implementation, so you can't use it in a -/// `for` loop directly. This won't compile: +/// It does not have an [`IntoIterator`] implementation, so you can't use it in +/// a `for` loop directly. This won't compile: /// /// ```compile_fail,E0277 /// for i in .. { @@ -32,7 +32,7 @@ use fmt; /// } /// ``` /// -/// Used as a slicing index, `RangeFull` produces the full array as a slice. +/// Used as a [slicing index], `RangeFull` produces the full array as a slice. /// /// ``` /// let arr = [0, 1, 2, 3]; @@ -41,6 +41,10 @@ use fmt; /// assert_eq!(arr[1.. ], [ 1,2,3]); /// assert_eq!(arr[1..3], [ 1,2 ]); /// ``` +/// +/// [`IntoIterator`]: ../iter/trait.Iterator.html +/// [`Iterator`]: ../iter/trait.IntoIterator.html +/// [slicing index]: ../slice/trait.SliceIndex.html #[derive(Copy, Clone, PartialEq, Eq, Hash)] #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFull; @@ -52,24 +56,23 @@ impl fmt::Debug for RangeFull { } } -/// A (half-open) range which is bounded at both ends: { x | start <= x < end }. -/// Use `start..end` (two dots) for its shorthand. +/// A (half-open) range bounded inclusively below and exclusively above +/// (`start..end`). /// -/// See the [`contains`](#method.contains) method for its characterization. +/// The `Range` `start..end` contains all values with `x >= start` and +/// `x < end`. /// /// # Examples /// /// ``` -/// fn main() { -/// assert_eq!((3..5), std::ops::Range{ start: 3, end: 5 }); -/// assert_eq!(3+4+5, (3..6).sum()); +/// assert_eq!((3..5), std::ops::Range { start: 3, end: 5 }); +/// assert_eq!(3 + 4 + 5, (3..6).sum()); /// -/// let arr = [0, 1, 2, 3]; -/// assert_eq!(arr[ .. ], [0,1,2,3]); -/// assert_eq!(arr[ ..3], [0,1,2 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3]); -/// assert_eq!(arr[1..3], [ 1,2 ]); // Range -/// } +/// let arr = [0, 1, 2, 3]; +/// assert_eq!(arr[ .. ], [0,1,2,3]); +/// assert_eq!(arr[ ..3], [0,1,2 ]); +/// assert_eq!(arr[1.. ], [ 1,2,3]); +/// assert_eq!(arr[1..3], [ 1,2 ]); // Range /// ``` #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[stable(feature = "rust1", since = "1.0.0")] @@ -91,49 +94,49 @@ impl fmt::Debug for Range { #[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")] impl> Range { + /// Returns `true` if `item` is contained in the range. + /// /// # Examples /// /// ``` /// #![feature(range_contains)] - /// fn main() { - /// assert!( ! (3..5).contains(2)); - /// assert!( (3..5).contains(3)); - /// assert!( (3..5).contains(4)); - /// assert!( ! (3..5).contains(5)); /// - /// assert!( ! (3..3).contains(3)); - /// assert!( ! (3..2).contains(3)); - /// } + /// assert!(!(3..5).contains(2)); + /// assert!( (3..5).contains(3)); + /// assert!( (3..5).contains(4)); + /// assert!(!(3..5).contains(5)); + /// + /// assert!(!(3..3).contains(3)); + /// assert!(!(3..2).contains(3)); /// ``` pub fn contains(&self, item: Idx) -> bool { (self.start <= item) && (item < self.end) } } -/// A range which is only bounded below: { x | start <= x }. -/// Use `start..` for its shorthand. +/// A range only bounded inclusively below (`start..`). /// -/// See the [`contains`](#method.contains) method for its characterization. +/// The `RangeFrom` `start..` contains all values with `x >= start`. /// -/// Note: Currently, no overflow checking is done for the iterator +/// *Note*: Currently, no overflow checking is done for the [`Iterator`] /// implementation; if you use an integer range and the integer overflows, it -/// might panic in debug mode or create an endless loop in release mode. This -/// overflow behavior might change in the future. +/// might panic in debug mode or create an endless loop in release mode. **This +/// overflow behavior might change in the future.** /// /// # Examples /// /// ``` -/// fn main() { -/// assert_eq!((2..), std::ops::RangeFrom{ start: 2 }); -/// assert_eq!(2+3+4, (2..).take(3).sum()); +/// assert_eq!((2..), std::ops::RangeFrom { start: 2 }); +/// assert_eq!(2 + 3 + 4, (2..).take(3).sum()); /// -/// let arr = [0, 1, 2, 3]; -/// assert_eq!(arr[ .. ], [0,1,2,3]); -/// assert_eq!(arr[ ..3], [0,1,2 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3]); // RangeFrom -/// assert_eq!(arr[1..3], [ 1,2 ]); -/// } +/// let arr = [0, 1, 2, 3]; +/// assert_eq!(arr[ .. ], [0,1,2,3]); +/// assert_eq!(arr[ ..3], [0,1,2 ]); +/// assert_eq!(arr[1.. ], [ 1,2,3]); // RangeFrom +/// assert_eq!(arr[1..3], [ 1,2 ]); /// ``` +/// +/// [`Iterator`]: ../iter/trait.IntoIterator.html #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFrom { @@ -151,46 +154,47 @@ impl fmt::Debug for RangeFrom { #[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")] impl> RangeFrom { + /// Returns `true` if `item` is contained in the range. + /// /// # Examples /// /// ``` /// #![feature(range_contains)] - /// fn main() { - /// assert!( ! (3..).contains(2)); - /// assert!( (3..).contains(3)); - /// assert!( (3..).contains(1_000_000_000)); - /// } + /// + /// assert!(!(3..).contains(2)); + /// assert!( (3..).contains(3)); + /// assert!( (3..).contains(1_000_000_000)); /// ``` pub fn contains(&self, item: Idx) -> bool { (self.start <= item) } } -/// A range which is only bounded above: { x | x < end }. -/// Use `..end` (two dots) for its shorthand. +/// A range only bounded exclusively above (`..end`). /// -/// See the [`contains`](#method.contains) method for its characterization. -/// -/// It cannot serve as an iterator because it doesn't have a starting point. +/// The `RangeTo` `..end` contains all values with `x < end`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. /// /// # Examples /// -/// The `..{integer}` syntax is a `RangeTo`: +/// The `..end` syntax is a `RangeTo`: /// /// ``` -/// assert_eq!((..5), std::ops::RangeTo{ end: 5 }); +/// assert_eq!((..5), std::ops::RangeTo { end: 5 }); /// ``` /// -/// It does not have an `IntoIterator` implementation, so you can't use it in a -/// `for` loop directly. This won't compile: +/// It does not have an [`IntoIterator`] implementation, so you can't use it in +/// a `for` loop directly. This won't compile: /// /// ```compile_fail,E0277 +/// // error[E0277]: the trait bound `std::ops::RangeTo<{integer}>: +/// // std::iter::Iterator` is not satisfied /// for i in ..5 { /// // ... /// } /// ``` /// -/// When used as a slicing index, `RangeTo` produces a slice of all array +/// When used as a [slicing index], `RangeTo` produces a slice of all array /// elements before the index indicated by `end`. /// /// ``` @@ -200,6 +204,10 @@ impl> RangeFrom { /// assert_eq!(arr[1.. ], [ 1,2,3]); /// assert_eq!(arr[1..3], [ 1,2 ]); /// ``` +/// +/// [`IntoIterator`]: ../iter/trait.Iterator.html +/// [`Iterator`]: ../iter/trait.IntoIterator.html +/// [slicing index]: ../slice/trait.SliceIndex.html #[derive(Copy, Clone, PartialEq, Eq, Hash)] #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeTo { @@ -217,38 +225,38 @@ impl fmt::Debug for RangeTo { #[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")] impl> RangeTo { + /// Returns `true` if `item` is contained in the range. + /// /// # Examples /// /// ``` /// #![feature(range_contains)] - /// fn main() { - /// assert!( (..5).contains(-1_000_000_000)); - /// assert!( (..5).contains(4)); - /// assert!( ! (..5).contains(5)); - /// } + /// + /// assert!( (..5).contains(-1_000_000_000)); + /// assert!( (..5).contains(4)); + /// assert!(!(..5).contains(5)); /// ``` pub fn contains(&self, item: Idx) -> bool { (item < self.end) } } -/// An inclusive range which is bounded at both ends: { x | start <= x <= end }. -/// Use `start...end` (three dots) for its shorthand. +/// An range bounded inclusively below and above (`start...end`). /// -/// See the [`contains`](#method.contains) method for its characterization. +/// The `RangeInclusive` `start...end` contains all values with `x >= start` +/// and `x <= end`. /// /// # Examples /// /// ``` /// #![feature(inclusive_range,inclusive_range_syntax)] -/// fn main() { -/// assert_eq!((3...5), std::ops::RangeInclusive{ start: 3, end: 5 }); -/// assert_eq!(3+4+5, (3...5).sum()); /// -/// let arr = [0, 1, 2, 3]; -/// assert_eq!(arr[ ...2], [0,1,2 ]); -/// assert_eq!(arr[1...2], [ 1,2 ]); // RangeInclusive -/// } +/// assert_eq!((3...5), std::ops::RangeInclusive { start: 3, end: 5 }); +/// assert_eq!(3 + 4 + 5, (3...5).sum()); +/// +/// let arr = [0, 1, 2, 3]; +/// assert_eq!(arr[ ...2], [0,1,2 ]); +/// assert_eq!(arr[1...2], [ 1,2 ]); // RangeInclusive /// ``` #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] @@ -274,61 +282,68 @@ impl fmt::Debug for RangeInclusive { #[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")] impl> RangeInclusive { + /// Returns `true` if `item` is contained in the range. + /// /// # Examples /// /// ``` /// #![feature(range_contains,inclusive_range_syntax)] - /// fn main() { - /// assert!( ! (3...5).contains(2)); - /// assert!( (3...5).contains(3)); - /// assert!( (3...5).contains(4)); - /// assert!( (3...5).contains(5)); - /// assert!( ! (3...5).contains(6)); /// - /// assert!( (3...3).contains(3)); - /// assert!( ! (3...2).contains(3)); - /// } + /// assert!(!(3...5).contains(2)); + /// assert!( (3...5).contains(3)); + /// assert!( (3...5).contains(4)); + /// assert!( (3...5).contains(5)); + /// assert!(!(3...5).contains(6)); + /// + /// assert!( (3...3).contains(3)); + /// assert!(!(3...2).contains(3)); /// ``` pub fn contains(&self, item: Idx) -> bool { self.start <= item && item <= self.end } } -/// An inclusive range which is only bounded above: { x | x <= end }. -/// Use `...end` (three dots) for its shorthand. +/// A range only bounded inclusively above (`...end`). /// -/// See the [`contains`](#method.contains) method for its characterization. -/// -/// It cannot serve as an iterator because it doesn't have a starting point. +/// The `RangeToInclusive` `...end` contains all values with `x <= end`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. /// /// # Examples /// -/// The `...{integer}` syntax is a `RangeToInclusive`: +/// The `...end` syntax is a `RangeToInclusive`: /// /// ``` /// #![feature(inclusive_range,inclusive_range_syntax)] /// assert_eq!((...5), std::ops::RangeToInclusive{ end: 5 }); /// ``` /// -/// It does not have an `IntoIterator` implementation, so you can't use it in a +/// It does not have an [`IntoIterator`] implementation, so you can't use it in a /// `for` loop directly. This won't compile: /// /// ```compile_fail,E0277 /// #![feature(inclusive_range_syntax)] +/// +/// // error[E0277]: the trait bound `std::ops::RangeToInclusive<{integer}>: +/// // std::iter::Iterator` is not satisfied /// for i in ...5 { /// // ... /// } /// ``` /// -/// When used as a slicing index, `RangeToInclusive` produces a slice of all +/// When used as a [slicing index], `RangeToInclusive` produces a slice of all /// array elements up to and including the index indicated by `end`. /// /// ``` /// #![feature(inclusive_range_syntax)] +/// /// let arr = [0, 1, 2, 3]; /// assert_eq!(arr[ ...2], [0,1,2 ]); // RangeToInclusive /// assert_eq!(arr[1...2], [ 1,2 ]); /// ``` +/// +/// [`IntoIterator`]: ../iter/trait.Iterator.html +/// [`Iterator`]: ../iter/trait.IntoIterator.html +/// [slicing index]: ../slice/trait.SliceIndex.html #[derive(Copy, Clone, PartialEq, Eq, Hash)] #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] pub struct RangeToInclusive { @@ -348,15 +363,16 @@ impl fmt::Debug for RangeToInclusive { #[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")] impl> RangeToInclusive { + /// Returns `true` if `item` is contained in the range. + /// /// # Examples /// /// ``` /// #![feature(range_contains,inclusive_range_syntax)] - /// fn main() { - /// assert!( (...5).contains(-1_000_000_000)); - /// assert!( (...5).contains(5)); - /// assert!( ! (...5).contains(6)); - /// } + /// + /// assert!( (...5).contains(-1_000_000_000)); + /// assert!( (...5).contains(5)); + /// assert!(!(...5).contains(6)); /// ``` pub fn contains(&self, item: Idx) -> bool { (item <= self.end) diff --git a/src/libcore/ops/unsize.rs b/src/libcore/ops/unsize.rs index 1914216e9f08..58da290cfb69 100644 --- a/src/libcore/ops/unsize.rs +++ b/src/libcore/ops/unsize.rs @@ -24,7 +24,7 @@ use marker::Unsize; /// Such an impl can only be written if `Foo` has only a single non-phantomdata /// field involving `T`. If the type of that field is `Bar`, an implementation /// of `CoerceUnsized> for Bar` must exist. The coercion will work by -/// by coercing the `Bar` field into `Bar` and filling in the rest of the fields +/// coercing the `Bar` field into `Bar` and filling in the rest of the fields /// from `Foo` to create a `Foo`. This will effectively drill down to a pointer /// field and coerce that. /// diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 60cf1a205306..e35777d222c0 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -384,6 +384,11 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { /// over time. That being said, the semantics will almost always end up pretty /// similar to [C11's definition of volatile][c11]. /// +/// The compiler shouldn't change the relative order or number of volatile +/// memory operations. However, volatile memory operations on zero-sized types +/// (e.g. if a zero-sized type is passed to `read_volatile`) are no-ops +/// and may be ignored. +/// /// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf /// /// # Safety @@ -427,6 +432,11 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// over time. That being said, the semantics will almost always end up pretty /// similar to [C11's definition of volatile][c11]. /// +/// The compiler shouldn't change the relative order or number of volatile +/// memory operations. However, volatile memory operations on zero-sized types +/// (e.g. if a zero-sized type is passed to `write_volatile`) are no-ops +/// and may be ignored. +/// /// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf /// /// # Safety diff --git a/src/libcore/tests/cell.rs b/src/libcore/tests/cell.rs index 8585f2f08711..cc0ef6a6f17e 100644 --- a/src/libcore/tests/cell.rs +++ b/src/libcore/tests/cell.rs @@ -287,3 +287,20 @@ fn refcell_ref_coercion() { assert_eq!(&*coerced, comp); } } + +#[test] +#[should_panic] +fn refcell_swap_borrows() { + let x = RefCell::new(0); + let _b = x.borrow(); + let y = RefCell::new(1); + x.swap(&y); +} + +#[test] +#[should_panic] +fn refcell_replace_borrows() { + let x = RefCell::new(0); + let _b = x.borrow(); + x.replace(1); +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index a85c347146b0..84a3be99c275 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -31,6 +31,7 @@ #![feature(ord_max_min)] #![feature(rand)] #![feature(raw)] +#![feature(refcell_replace_swap)] #![feature(sip_hash_13)] #![feature(slice_patterns)] #![feature(slice_rotate)] diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index 046b81e19f70..400d53ce51a0 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -566,7 +566,7 @@ assume_usize_width! { ); } -/// Conversinos where neither the min nor the max of $source can be represented by +/// Conversions where neither the min nor the max of $source can be represented by /// $target, but max/min of the target can be represented by the source. macro_rules! test_impl_try_from_signed_to_unsigned_err { ($fn_name:ident, $source:ty, $target:ty) => { diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 7412a01e11e1..c01938f5e117 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -548,7 +548,7 @@ impl<'a> LabelText<'a> { } /// Renders text as string suitable for a label in a .dot file. - /// This includes quotes or suitable delimeters. + /// This includes quotes or suitable delimiters. pub fn to_dot_string(&self) -> String { match self { &LabelStr(ref s) => format!("\"{}\"", s.escape_default()), diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 1bffffd6c9e7..6a71e67676ae 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -111,7 +111,7 @@ impl fmt::Display for TokenStream { /// `quote!(..)` accepts arbitrary tokens and expands into a `TokenStream` describing the input. /// For example, `quote!(a + b)` will produce a expression, that, when evaluated, constructs -/// constructs the `TokenStream` `[Word("a"), Op('+', Alone), Word("b")]`. +/// the `TokenStream` `[Word("a"), Op('+', Alone), Word("b")]`. /// /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 43496540c117..3e3985622426 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -87,7 +87,7 @@ pub enum NestedVisitorMap<'this, 'tcx: 'this> { /// Do not visit nested item-like things, but visit nested things /// that are inside of an item-like. /// - /// **This is the most common choice.** A very commmon pattern is + /// **This is the most common choice.** A very common pattern is /// to use `visit_all_item_likes()` as an outer loop, /// and to have the visitor that visits the contents of each item /// using this setting. diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index c9b5aaf3877c..f7ffda03f238 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -158,6 +158,11 @@ enum ParamMode { Optional } +struct LoweredNodeId { + node_id: NodeId, + hir_id: hir::HirId, +} + impl<'a> LoweringContext<'a> { fn lower_crate(mut self, c: &Crate) -> hir::Crate { /// Full-crate AST visitor that inserts into a fresh @@ -281,11 +286,14 @@ impl<'a> LoweringContext<'a> { fn lower_node_id_generic(&mut self, ast_node_id: NodeId, alloc_hir_id: F) - -> NodeId + -> LoweredNodeId where F: FnOnce(&mut Self) -> hir::HirId { if ast_node_id == DUMMY_NODE_ID { - return ast_node_id; + return LoweredNodeId { + node_id: DUMMY_NODE_ID, + hir_id: hir::DUMMY_HIR_ID, + } } let min_size = ast_node_id.as_usize() + 1; @@ -294,12 +302,22 @@ impl<'a> LoweringContext<'a> { self.node_id_to_hir_id.resize(min_size, hir::DUMMY_HIR_ID); } - if self.node_id_to_hir_id[ast_node_id] == hir::DUMMY_HIR_ID { - // Generate a new HirId - self.node_id_to_hir_id[ast_node_id] = alloc_hir_id(self); - } + let existing_hir_id = self.node_id_to_hir_id[ast_node_id]; - ast_node_id + if existing_hir_id == hir::DUMMY_HIR_ID { + // Generate a new HirId + let hir_id = alloc_hir_id(self); + self.node_id_to_hir_id[ast_node_id] = hir_id; + LoweredNodeId { + node_id: ast_node_id, + hir_id, + } + } else { + LoweredNodeId { + node_id: ast_node_id, + hir_id: existing_hir_id, + } + } } fn with_hir_id_owner(&mut self, owner: NodeId, f: F) @@ -326,7 +344,7 @@ impl<'a> LoweringContext<'a> { /// actually used in the HIR, as that would trigger an assertion in the /// HirIdValidator later on, which makes sure that all NodeIds got mapped /// properly. Calling the method twice with the same NodeId is fine though. - fn lower_node_id(&mut self, ast_node_id: NodeId) -> NodeId { + fn lower_node_id(&mut self, ast_node_id: NodeId) -> LoweredNodeId { self.lower_node_id_generic(ast_node_id, |this| { let &mut (def_index, ref mut local_id_counter) = this.current_hir_id_owner .last_mut() @@ -343,7 +361,7 @@ impl<'a> LoweringContext<'a> { fn lower_node_id_with_owner(&mut self, ast_node_id: NodeId, owner: NodeId) - -> NodeId { + -> LoweredNodeId { self.lower_node_id_generic(ast_node_id, |this| { let local_id_counter = this.item_local_id_counters .get_mut(&owner) @@ -379,7 +397,7 @@ impl<'a> LoweringContext<'a> { id } - fn next_id(&mut self) -> NodeId { + fn next_id(&mut self) -> LoweredNodeId { self.lower_node_id(self.sess.next_node_id()) } @@ -408,6 +426,7 @@ impl<'a> LoweringContext<'a> { format: codemap::CompilerDesugaring(Symbol::intern(reason)), span: Some(span), allow_internal_unstable: true, + allow_internal_unsafe: false, }, }); span.ctxt = SyntaxContext::empty().apply_mark(mark); @@ -531,7 +550,7 @@ impl<'a> LoweringContext<'a> { match destination { Some((id, label_ident)) => { let target = if let Def::Label(loop_id) = self.expect_full_def(id) { - hir::LoopIdResult::Ok(self.lower_node_id(loop_id)) + hir::LoopIdResult::Ok(self.lower_node_id(loop_id).node_id) } else { hir::LoopIdResult::Err(hir::LoopIdError::UnresolvedLabel) }; @@ -548,7 +567,7 @@ impl<'a> LoweringContext<'a> { hir::Destination { ident: None, target_id: hir::ScopeTarget::Loop( - loop_id.map(|id| Ok(self.lower_node_id(id))) + loop_id.map(|id| Ok(self.lower_node_id(id).node_id)) .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)) .into()) } @@ -571,7 +590,7 @@ impl<'a> LoweringContext<'a> { fn lower_ty_binding(&mut self, b: &TypeBinding) -> hir::TypeBinding { hir::TypeBinding { - id: self.lower_node_id(b.id), + id: self.lower_node_id(b.id).node_id, name: self.lower_ident(b.ident), ty: self.lower_ty(&b.ty), span: b.span, @@ -608,7 +627,7 @@ impl<'a> LoweringContext<'a> { return self.lower_ty(ty); } TyKind::Path(ref qself, ref path) => { - let id = self.lower_node_id(t.id); + let id = self.lower_node_id(t.id).node_id; let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit); return self.ty_path(id, t.span, qpath); } @@ -658,7 +677,7 @@ impl<'a> LoweringContext<'a> { }; P(hir::Ty { - id: self.lower_node_id(t.id), + id: self.lower_node_id(t.id).node_id, node: kind, span: t.span, }) @@ -770,7 +789,7 @@ impl<'a> LoweringContext<'a> { // Otherwise, the base path is an implicit `Self` type path, // e.g. `Vec` in `Vec::new` or `::Item` in // `::Item::default`. - let new_id = self.next_id(); + let new_id = self.next_id().node_id; self.ty_path(new_id, p.span, hir::QPath::Resolved(qself, path)) }; @@ -794,7 +813,7 @@ impl<'a> LoweringContext<'a> { } // Wrap the associated extension in another type node. - let new_id = self.next_id(); + let new_id = self.next_id().node_id; ty = self.ty_path(new_id, p.span, qpath); } @@ -898,8 +917,10 @@ impl<'a> LoweringContext<'a> { } fn lower_local(&mut self, l: &Local) -> P { + let LoweredNodeId { node_id, hir_id } = self.lower_node_id(l.id); P(hir::Local { - id: self.lower_node_id(l.id), + id: node_id, + hir_id, ty: l.ty.as_ref().map(|t| self.lower_ty(t)), pat: self.lower_pat(&l.pat), init: l.init.as_ref().map(|e| P(self.lower_expr(e))), @@ -917,8 +938,10 @@ impl<'a> LoweringContext<'a> { } fn lower_arg(&mut self, arg: &Arg) -> hir::Arg { + let LoweredNodeId { node_id, hir_id } = self.lower_node_id(arg.id); hir::Arg { - id: self.lower_node_id(arg.id), + id: node_id, + hir_id, pat: self.lower_pat(&arg.pat), } } @@ -981,7 +1004,7 @@ impl<'a> LoweringContext<'a> { } hir::TyParam { - id: self.lower_node_id(tp.id), + id: self.lower_node_id(tp.id).node_id, name, bounds, default: tp.default.as_ref().map(|x| self.lower_ty(x)), @@ -999,7 +1022,7 @@ impl<'a> LoweringContext<'a> { fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime { hir::Lifetime { - id: self.lower_node_id(l.id), + id: self.lower_node_id(l.id).node_id, name: self.lower_ident(l.ident), span: l.span, } @@ -1071,7 +1094,7 @@ impl<'a> LoweringContext<'a> { fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause { hir::WhereClause { - id: self.lower_node_id(wc.id), + id: self.lower_node_id(wc.id).node_id, predicates: wc.predicates .iter() .map(|predicate| self.lower_where_predicate(predicate)) @@ -1110,7 +1133,7 @@ impl<'a> LoweringContext<'a> { ref rhs_ty, span}) => { hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - id: self.lower_node_id(id), + id: self.lower_node_id(id).node_id, lhs_ty: self.lower_ty(lhs_ty), rhs_ty: self.lower_ty(rhs_ty), span, @@ -1126,16 +1149,16 @@ impl<'a> LoweringContext<'a> { .enumerate() .map(|f| self.lower_struct_field(f)) .collect(), - self.lower_node_id(id)) + self.lower_node_id(id).node_id) } VariantData::Tuple(ref fields, id) => { hir::VariantData::Tuple(fields.iter() .enumerate() .map(|f| self.lower_struct_field(f)) .collect(), - self.lower_node_id(id)) + self.lower_node_id(id).node_id) } - VariantData::Unit(id) => hir::VariantData::Unit(self.lower_node_id(id)), + VariantData::Unit(id) => hir::VariantData::Unit(self.lower_node_id(id).node_id), } } @@ -1146,7 +1169,7 @@ impl<'a> LoweringContext<'a> { }; hir::TraitRef { path, - ref_id: self.lower_node_id(p.ref_id), + ref_id: self.lower_node_id(p.ref_id).node_id, } } @@ -1161,7 +1184,7 @@ impl<'a> LoweringContext<'a> { fn lower_struct_field(&mut self, (index, f): (usize, &StructField)) -> hir::StructField { hir::StructField { span: f.span, - id: self.lower_node_id(f.id), + id: self.lower_node_id(f.id).node_id, name: self.lower_ident(match f.ident { Some(ident) => ident, // FIXME(jseyfried) positional field hygiene @@ -1210,8 +1233,11 @@ impl<'a> LoweringContext<'a> { } } + let LoweredNodeId { node_id, hir_id } = self.lower_node_id(b.id); + P(hir::Block { - id: self.lower_node_id(b.id), + id: node_id, + hir_id, stmts: stmts.into(), expr, rules: self.lower_block_check_mode(&b.rules), @@ -1261,7 +1287,7 @@ impl<'a> LoweringContext<'a> { hir::Visibility::Restricted { path: path.clone(), // We are allocating a new NodeId here - id: this.next_id(), + id: this.next_id().node_id, } } }; @@ -1399,7 +1425,7 @@ impl<'a> LoweringContext<'a> { fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { self.with_parent_def(i.id, |this| { hir::TraitItem { - id: this.lower_node_id(i.id), + id: this.lower_node_id(i.id).node_id, name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), node: match i.node { @@ -1460,7 +1486,7 @@ impl<'a> LoweringContext<'a> { fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem { self.with_parent_def(i.id, |this| { hir::ImplItem { - id: this.lower_node_id(i.id), + id: this.lower_node_id(i.id).node_id, name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), vis: this.lower_visibility(&i.vis, None), @@ -1552,7 +1578,7 @@ impl<'a> LoweringContext<'a> { }); Some(hir::Item { - id: self.lower_node_id(i.id), + id: self.lower_node_id(i.id).node_id, name, attrs, node, @@ -1564,7 +1590,7 @@ impl<'a> LoweringContext<'a> { fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem { self.with_parent_def(i.id, |this| { hir::ForeignItem { - id: this.lower_node_id(i.id), + id: this.lower_node_id(i.id).node_id, name: i.ident.name, attrs: this.lower_attrs(&i.attrs), node: match i.node { @@ -1642,8 +1668,11 @@ impl<'a> LoweringContext<'a> { } fn lower_pat(&mut self, p: &Pat) -> P { + let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id); + P(hir::Pat { - id: self.lower_node_id(p.id), + id: node_id, + hir_id, node: match p.node { PatKind::Wild => hir::PatKind::Wild, PatKind::Ident(ref binding_mode, pth1, ref sub) => { @@ -1825,7 +1854,7 @@ impl<'a> LoweringContext<'a> { let call_move_val_init = hir::StmtSemi( make_call(self, &move_val_init, hir_vec![ptr, pop_unsafe_expr]), - self.next_id()); + self.next_id().node_id); let call_move_val_init = respan(e.span, call_move_val_init); let place = self.expr_ident(e.span, place_ident, place_binding); @@ -1895,11 +1924,15 @@ impl<'a> LoweringContext<'a> { // wrap the if-let expr in a block let span = els.span; let els = P(self.lower_expr(els)); - let id = self.next_id(); + let LoweredNodeId { + node_id, + hir_id, + } = self.next_id(); let blk = P(hir::Block { stmts: hir_vec![], expr: Some(els), - id, + id: node_id, + hir_id, rules: hir::DefaultBlock, span, targeted_by_break: false, @@ -2009,8 +2042,11 @@ impl<'a> LoweringContext<'a> { let struct_path = self.std_path(unstable_span, &struct_path, is_unit); let struct_path = hir::QPath::Resolved(None, P(struct_path)); + let LoweredNodeId { node_id, hir_id } = self.lower_node_id(e.id); + return hir::Expr { - id: self.lower_node_id(e.id), + id: node_id, + hir_id, node: if is_unit { hir::ExprPath(struct_path) } else { @@ -2265,7 +2301,7 @@ impl<'a> LoweringContext<'a> { hir::MatchSource::ForLoopDesugar), ThinVec::new())) }; - let match_stmt = respan(e.span, hir::StmtExpr(match_expr, self.next_id())); + let match_stmt = respan(e.span, hir::StmtExpr(match_expr, self.next_id().node_id)); let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id)); @@ -2285,7 +2321,7 @@ impl<'a> LoweringContext<'a> { let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); let body_expr = P(self.expr_block(body_block, ThinVec::new())); - let body_stmt = respan(e.span, hir::StmtExpr(body_expr, self.next_id())); + let body_stmt = respan(e.span, hir::StmtExpr(body_expr, self.next_id().node_id)); let loop_block = P(self.block_all(e.span, hir_vec![next_let, @@ -2297,8 +2333,10 @@ impl<'a> LoweringContext<'a> { // `[opt_ident]: loop { ... }` let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident), hir::LoopSource::ForLoop); + let LoweredNodeId { node_id, hir_id } = self.lower_node_id(e.id); let loop_expr = P(hir::Expr { - id: self.lower_node_id(e.id), + id: node_id, + hir_id, node: loop_expr, span: e.span, attrs: ThinVec::new(), @@ -2437,8 +2475,11 @@ impl<'a> LoweringContext<'a> { ExprKind::Mac(_) => panic!("Shouldn't exist here"), }; + let LoweredNodeId { node_id, hir_id } = self.lower_node_id(e.id); + hir::Expr { - id: self.lower_node_id(e.id), + id: node_id, + hir_id, node: kind, span: e.span, attrs: e.attrs.clone(), @@ -2451,7 +2492,7 @@ impl<'a> LoweringContext<'a> { node: hir::StmtDecl(P(Spanned { node: hir::DeclLocal(self.lower_local(l)), span: s.span, - }), self.lower_node_id(s.id)), + }), self.lower_node_id(s.id).node_id), span: s.span, }, StmtKind::Item(ref it) => { @@ -2462,22 +2503,22 @@ impl<'a> LoweringContext<'a> { node: hir::DeclItem(item_id), span: s.span, }), id.take() - .map(|id| self.lower_node_id(id)) - .unwrap_or_else(|| self.next_id())), + .map(|id| self.lower_node_id(id).node_id) + .unwrap_or_else(|| self.next_id().node_id)), span: s.span, }).collect(); } StmtKind::Expr(ref e) => { Spanned { node: hir::StmtExpr(P(self.lower_expr(e)), - self.lower_node_id(s.id)), + self.lower_node_id(s.id).node_id), span: s.span, } } StmtKind::Semi(ref e) => { Spanned { node: hir::StmtSemi(P(self.lower_expr(e)), - self.lower_node_id(s.id)), + self.lower_node_id(s.id).node_id), span: s.span, } } @@ -2508,9 +2549,9 @@ impl<'a> LoweringContext<'a> { hir::Visibility::Restricted { path: P(self.lower_path(id, path, ParamMode::Explicit, true)), id: if let Some(owner) = explicit_owner { - self.lower_node_id_with_owner(id, owner) + self.lower_node_id_with_owner(id, owner).node_id } else { - self.lower_node_id(id) + self.lower_node_id(id).node_id } } } @@ -2652,8 +2693,10 @@ impl<'a> LoweringContext<'a> { } fn expr(&mut self, span: Span, node: hir::Expr_, attrs: ThinVec) -> hir::Expr { + let LoweredNodeId { node_id, hir_id } = self.next_id(); hir::Expr { - id: self.next_id(), + id: node_id, + hir_id, node, span, attrs, @@ -2666,17 +2709,20 @@ impl<'a> LoweringContext<'a> { pat: P, source: hir::LocalSource) -> hir::Stmt { + let LoweredNodeId { node_id, hir_id } = self.next_id(); + let local = P(hir::Local { pat, ty: None, init: ex, - id: self.next_id(), + id: node_id, + hir_id, span: sp, attrs: ThinVec::new(), source, }); let decl = respan(sp, hir::DeclLocal(local)); - respan(sp, hir::StmtDecl(P(decl), self.next_id())) + respan(sp, hir::StmtDecl(P(decl), self.next_id().node_id)) } fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P) @@ -2696,10 +2742,13 @@ impl<'a> LoweringContext<'a> { fn block_all(&mut self, span: Span, stmts: hir::HirVec, expr: Option>) -> hir::Block { + let LoweredNodeId { node_id, hir_id } = self.next_id(); + hir::Block { stmts, expr, - id: self.next_id(), + id: node_id, + hir_id, rules: hir::DefaultBlock, span, targeted_by_break: false, @@ -2743,18 +2792,22 @@ impl<'a> LoweringContext<'a> { fn pat_ident_binding_mode(&mut self, span: Span, name: Name, bm: hir::BindingAnnotation) -> P { - let id = self.next_id(); + let LoweredNodeId { node_id, hir_id } = self.next_id(); let parent_def = self.parent_def.unwrap(); let def_id = { let defs = self.resolver.definitions(); let def_path_data = DefPathData::Binding(name); - let def_index = defs - .create_def_with_parent(parent_def, id, def_path_data, REGULAR_SPACE, Mark::root()); + let def_index = defs.create_def_with_parent(parent_def, + node_id, + def_path_data, + REGULAR_SPACE, + Mark::root()); DefId::local(def_index) }; P(hir::Pat { - id, + id: node_id, + hir_id, node: hir::PatKind::Binding(bm, def_id, Spanned { @@ -2771,8 +2824,10 @@ impl<'a> LoweringContext<'a> { } fn pat(&mut self, span: Span, pat: hir::PatKind) -> P { + let LoweredNodeId { node_id, hir_id } = self.next_id(); P(hir::Pat { - id: self.next_id(), + id: node_id, + hir_id, node: pat, span, }) @@ -2801,11 +2856,13 @@ impl<'a> LoweringContext<'a> { rule: hir::BlockCheckMode, attrs: ThinVec) -> hir::Expr { - let id = self.next_id(); + let LoweredNodeId { node_id, hir_id } = self.next_id(); + let block = P(hir::Block { rules: rule, span, - id, + id: node_id, + hir_id, stmts, expr: Some(expr), targeted_by_break: false, @@ -2830,7 +2887,7 @@ impl<'a> LoweringContext<'a> { // The original ID is taken by the `PolyTraitRef`, // so the `Ty` itself needs a different one. - id = self.next_id(); + id = self.next_id().node_id; hir::TyTraitObject(hir_vec![principal], self.elided_lifetime(span)) } else { @@ -2844,7 +2901,7 @@ impl<'a> LoweringContext<'a> { fn elided_lifetime(&mut self, span: Span) -> hir::Lifetime { hir::Lifetime { - id: self.next_id(), + id: self.next_id().node_id, span, name: keywords::Invalid.name() } diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index cdd5a6e3da7f..b371366bc5d5 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -434,18 +434,22 @@ impl Definitions { DefPath::make(LOCAL_CRATE, index, |p| self.def_key(p)) } + #[inline] pub fn opt_def_index(&self, node: ast::NodeId) -> Option { self.node_to_def_index.get(&node).cloned() } + #[inline] pub fn opt_local_def_id(&self, node: ast::NodeId) -> Option { self.opt_def_index(node).map(DefId::local) } + #[inline] pub fn local_def_id(&self, node: ast::NodeId) -> DefId { self.opt_local_def_id(node).unwrap() } + #[inline] pub fn as_local_node_id(&self, def_id: DefId) -> Option { if def_id.krate == LOCAL_CRATE { let space_index = def_id.index.address_space().index(); @@ -461,10 +465,27 @@ impl Definitions { } } + #[inline] pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId { self.node_to_hir_id[node_id] } + pub fn find_node_for_hir_id(&self, hir_id: hir::HirId) -> ast::NodeId { + self.node_to_hir_id + .iter() + .position(|x| *x == hir_id) + .map(|idx| ast::NodeId::new(idx)) + .unwrap() + } + + #[inline] + pub fn def_index_to_hir_id(&self, def_index: DefIndex) -> hir::HirId { + let space_index = def_index.address_space().index(); + let array_index = def_index.as_array_index(); + let node_id = self.def_index_to_node[space_index][array_index]; + self.node_to_hir_id[node_id] + } + /// Add a definition with a parent definition. pub fn create_root_def(&mut self, crate_name: &str, diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index f4ca536d370b..072da14cdeb2 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -250,7 +250,7 @@ pub struct Map<'hir> { pub forest: &'hir Forest, /// Same as the dep_graph in forest, just available with one fewer - /// deref. This is a gratuitious micro-optimization. + /// deref. This is a gratuitous micro-optimization. pub dep_graph: DepGraph, /// NodeIds are sequential integers from 0, so we can be @@ -359,6 +359,7 @@ impl<'hir> Map<'hir> { } } + #[inline] pub fn definitions(&self) -> &Definitions { &self.definitions } @@ -379,6 +380,7 @@ impl<'hir> Map<'hir> { self.definitions.def_path(def_id.index) } + #[inline] pub fn local_def_id(&self, node: NodeId) -> DefId { self.opt_local_def_id(node).unwrap_or_else(|| { bug!("local_def_id: no entry for `{}`, which has a map of `{:?}`", @@ -386,14 +388,31 @@ impl<'hir> Map<'hir> { }) } + #[inline] pub fn opt_local_def_id(&self, node: NodeId) -> Option { self.definitions.opt_local_def_id(node) } + #[inline] pub fn as_local_node_id(&self, def_id: DefId) -> Option { self.definitions.as_local_node_id(def_id) } + #[inline] + pub fn node_to_hir_id(&self, node_id: NodeId) -> HirId { + self.definitions.node_to_hir_id(node_id) + } + + #[inline] + pub fn def_index_to_hir_id(&self, def_index: DefIndex) -> HirId { + self.definitions.def_index_to_hir_id(def_index) + } + + #[inline] + pub fn def_index_to_node_id(&self, def_index: DefIndex) -> NodeId { + self.definitions.as_local_node_id(DefId::local(def_index)).unwrap() + } + fn entry_count(&self) -> usize { self.map.len() } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 10d34e49da49..3704bc36e0be 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -129,9 +129,11 @@ pub const CRATE_HIR_ID: HirId = HirId { pub const DUMMY_HIR_ID: HirId = HirId { owner: CRATE_DEF_INDEX, - local_id: ItemLocalId(!0) + local_id: DUMMY_ITEM_LOCAL_ID, }; +pub const DUMMY_ITEM_LOCAL_ID: ItemLocalId = ItemLocalId(!0); + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)] pub struct Lifetime { pub id: NodeId, @@ -496,7 +498,7 @@ impl Crate { &self.impl_items[&id] } - /// Visits all items in the crate in some determinstic (but + /// Visits all items in the crate in some deterministic (but /// unspecified) order. If you just need to process every item, /// but don't care about nesting, this method is the best choice. /// @@ -547,6 +549,7 @@ pub struct Block { /// without a semicolon, if any pub expr: Option>, pub id: NodeId, + pub hir_id: HirId, /// Distinguishes between `unsafe { ... }` and `{ ... }` pub rules: BlockCheckMode, pub span: Span, @@ -560,6 +563,7 @@ pub struct Block { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub struct Pat { pub id: NodeId, + pub hir_id: HirId, pub node: PatKind, pub span: Span, } @@ -897,6 +901,7 @@ pub struct Local { /// Initializer expression to set the value, if any pub init: Option>, pub id: NodeId, + pub hir_id: HirId, pub span: Span, pub attrs: ThinVec, pub source: LocalSource, @@ -987,6 +992,7 @@ pub struct Expr { pub span: Span, pub node: Expr_, pub attrs: ThinVec, + pub hir_id: HirId, } impl fmt::Debug for Expr { @@ -1430,6 +1436,7 @@ pub struct InlineAsm { pub struct Arg { pub pat: P, pub id: NodeId, + pub hir_id: HirId, } /// Represents the header (not the body) of a function declaration diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index 8ce1b39d934d..218483232d67 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -14,7 +14,7 @@ use hir::map::DefPathHash; use ich::{self, CachingCodemapView}; use session::config::DebugInfoLevel::NoDebugInfo; use ty; -use util::nodemap::NodeMap; +use util::nodemap::{NodeMap, ItemLocalMap}; use std::hash as std_hash; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -358,6 +358,18 @@ pub fn hash_stable_nodemap<'a, 'tcx, 'gcx, V, W>( }); } +pub fn hash_stable_itemlocalmap<'a, 'tcx, 'gcx, V, W>( + hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>, + hasher: &mut StableHasher, + map: &ItemLocalMap) + where V: HashStable>, + W: StableHasherResult, +{ + hash_stable_hashmap(hcx, hasher, map, |_, local_id| { + *local_id + }); +} + pub fn hash_stable_btreemap<'a, 'tcx, 'gcx, K, V, SK, F, W>( hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>, diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index ea3f04ff8c2a..96bfbbaf8703 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -359,6 +359,7 @@ impl<'a, 'gcx, 'tcx> HashStable> for hir::B ref stmts, ref expr, id, + hir_id: _, rules, span, targeted_by_break, @@ -423,6 +424,7 @@ impl<'a, 'gcx, 'tcx> HashStable> for hir::P let hir::Pat { id, + hir_id: _, ref node, ref span } = *self; @@ -504,6 +506,7 @@ impl_stable_hash_for!(struct hir::Local { ty, init, id, + hir_id, span, attrs, source @@ -551,6 +554,7 @@ impl<'a, 'gcx, 'tcx> HashStable> for hir::E hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Expr { id, + hir_id: _, ref span, ref node, ref attrs @@ -1023,7 +1027,8 @@ impl_stable_hash_for!(enum hir::Stmt_ { impl_stable_hash_for!(struct hir::Arg { pat, - id + id, + hir_id }); impl_stable_hash_for!(struct hir::Body { diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 22364c2c43a3..9416427f05f9 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -11,7 +11,7 @@ //! This module contains `HashStable` implementations for various data types //! from rustc::ty in no particular order. -use ich::{self, StableHashingContext, NodeIdHashingMode}; +use ich::StableHashingContext; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; use std::hash as std_hash; @@ -624,68 +624,6 @@ impl_stable_hash_for!(struct ty::ExistentialProjection<'tcx> { ty }); - -impl<'a, 'gcx, 'tcx> HashStable> -for ty::TypeckTables<'gcx> { - fn hash_stable(&self, - hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>, - hasher: &mut StableHasher) { - let ty::TypeckTables { - ref type_dependent_defs, - ref node_types, - ref node_substs, - ref adjustments, - ref pat_binding_modes, - ref upvar_capture_map, - ref closure_tys, - ref closure_kinds, - ref generator_interiors, - ref generator_sigs, - ref liberated_fn_sigs, - ref fru_field_types, - - ref cast_kinds, - - ref used_trait_imports, - tainted_by_errors, - ref free_region_map, - } = *self; - - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - ich::hash_stable_nodemap(hcx, hasher, type_dependent_defs); - ich::hash_stable_nodemap(hcx, hasher, node_types); - ich::hash_stable_nodemap(hcx, hasher, node_substs); - ich::hash_stable_nodemap(hcx, hasher, adjustments); - ich::hash_stable_nodemap(hcx, hasher, pat_binding_modes); - ich::hash_stable_hashmap(hcx, hasher, upvar_capture_map, |hcx, up_var_id| { - let ty::UpvarId { - var_id, - closure_expr_id - } = *up_var_id; - - let var_def_id = hcx.tcx().hir.local_def_id(var_id); - let closure_def_id = hcx.tcx().hir.local_def_id(closure_expr_id); - (hcx.def_path_hash(var_def_id), hcx.def_path_hash(closure_def_id)) - }); - - ich::hash_stable_nodemap(hcx, hasher, closure_tys); - ich::hash_stable_nodemap(hcx, hasher, closure_kinds); - ich::hash_stable_nodemap(hcx, hasher, generator_interiors); - ich::hash_stable_nodemap(hcx, hasher, generator_sigs); - ich::hash_stable_nodemap(hcx, hasher, liberated_fn_sigs); - ich::hash_stable_nodemap(hcx, hasher, fru_field_types); - ich::hash_stable_nodemap(hcx, hasher, cast_kinds); - - ich::hash_stable_hashset(hcx, hasher, used_trait_imports, |hcx, def_id| { - hcx.def_path_hash(*def_id) - }); - - tainted_by_errors.hash_stable(hcx, hasher); - free_region_map.hash_stable(hcx, hasher); - }) - } -} - impl_stable_hash_for!(enum ty::fast_reject::SimplifiedType { BoolSimplifiedType, CharSimplifiedType, diff --git a/src/librustc/ich/mod.rs b/src/librustc/ich/mod.rs index 5b2380908505..dcf84be0eeb3 100644 --- a/src/librustc/ich/mod.rs +++ b/src/librustc/ich/mod.rs @@ -14,7 +14,7 @@ pub use self::fingerprint::Fingerprint; pub use self::caching_codemap_view::CachingCodemapView; pub use self::hcx::{StableHashingContext, NodeIdHashingMode, hash_stable_hashmap, hash_stable_hashset, hash_stable_nodemap, - hash_stable_btreemap}; + hash_stable_btreemap, hash_stable_itemlocalmap}; mod fingerprint; mod caching_codemap_view; mod hcx; diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs index 756e0b5f9fb6..3fd7ee276729 100644 --- a/src/librustc/infer/at.rs +++ b/src/librustc/infer/at.rs @@ -169,7 +169,7 @@ impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> { } /// Sets the "trace" values that will be used for - /// error-repporting, but doesn't actually perform any operation + /// error-reporting, but doesn't actually perform any operation /// yet (this is useful when you want to set the trace using /// distinct values from those you wish to operate upon). pub fn trace(self, diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 9f70b4834ddc..b5390da7e852 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -913,7 +913,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } infer::UpvarRegion(ref upvar_id, _) => { format!(" for capture of `{}` by closure", - self.tcx.local_var_name_str(upvar_id.var_id).to_string()) + self.tcx.local_var_name_str_def_index(upvar_id.var_id)) } }; diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index a684881c0912..22d9a9e313b7 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -8,13 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use hir::{self, Local, Pat, Body}; +use hir::{self, Local, Pat, Body, HirId}; use hir::intravisit::{self, Visitor, NestedVisitorMap}; use infer::InferCtxt; use infer::type_variable::TypeVariableOrigin; use ty::{self, Ty, TyInfer, TyVar}; - -use syntax::ast::NodeId; use syntax_pos::Span; struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { @@ -26,7 +24,7 @@ struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { - fn node_matches_type(&mut self, node_id: NodeId) -> bool { + fn node_matches_type(&mut self, node_id: HirId) -> bool { let ty_opt = self.infcx.in_progress_tables.and_then(|tables| { tables.borrow().node_id_to_type_opt(node_id) }); @@ -56,7 +54,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { } fn visit_local(&mut self, local: &'gcx Local) { - if self.found_local_pattern.is_none() && self.node_matches_type(local.id) { + if self.found_local_pattern.is_none() && self.node_matches_type(local.hir_id) { self.found_local_pattern = Some(&*local.pat); } intravisit::walk_local(self, local); @@ -64,7 +62,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { fn visit_body(&mut self, body: &'gcx Body) { for argument in &body.arguments { - if self.found_arg_pattern.is_none() && self.node_matches_type(argument.id) { + if self.found_arg_pattern.is_none() && self.node_matches_type(argument.hir_id) { self.found_arg_pattern = Some(&*argument.pat); } } diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs index 963c14c48c82..87047d0df144 100644 --- a/src/librustc/infer/error_reporting/note.rs +++ b/src/librustc/infer/error_reporting/note.rs @@ -45,8 +45,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { err.span_note(span, &format!("...so that closure can access `{}`", self.tcx - .local_var_name_str(upvar_id.var_id) - .to_string())); + .local_var_name_str_def_index(upvar_id.var_id))); } infer::InfStackClosure(span) => { err.span_note(span, "...so that closure does not outlive its stack frame"); @@ -176,18 +175,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { E0313, "lifetime of borrowed pointer outlives lifetime \ of captured variable `{}`...", - self.tcx.local_var_name_str(upvar_id.var_id)); + self.tcx + .local_var_name_str_def_index(upvar_id.var_id)); self.tcx.note_and_explain_region(&mut err, "...the borrowed pointer is valid for ", sub, "..."); self.tcx - .note_and_explain_region(&mut err, - &format!("...but `{}` is only valid for ", - self.tcx - .local_var_name_str(upvar_id.var_id)), - sup, - ""); + .note_and_explain_region( + &mut err, + &format!("...but `{}` is only valid for ", + self.tcx.local_var_name_str_def_index(upvar_id.var_id)), + sup, + ""); err } infer::InfStackClosure(span) => { diff --git a/src/librustc/infer/error_reporting/util.rs b/src/librustc/infer/error_reporting/util.rs index 14fe8e699c7f..c8f78367420e 100644 --- a/src/librustc/infer/error_reporting/util.rs +++ b/src/librustc/infer/error_reporting/util.rs @@ -46,7 +46,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .iter() .enumerate() .filter_map(|(index, arg)| { - let ty = tables.borrow().node_id_to_type(arg.id); + let ty = tables.borrow().node_id_to_type(arg.hir_id); let mut found_anon_region = false; let new_arg_ty = self.tcx .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region { diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 9ecc8b0e66b9..0d02420457e6 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -589,7 +589,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { (result, map) } - /// Searches the region constriants created since `snapshot` was started + /// Searches the region constraints created since `snapshot` was started /// and checks to determine whether any of the skolemized regions created /// in `skol_map` would "escape" -- meaning that they are related to /// other regions in some way. If so, the higher-ranked subtyping doesn't diff --git a/src/librustc/infer/lattice.rs b/src/librustc/infer/lattice.rs index d4d090f0153d..d5c1163cfc1b 100644 --- a/src/librustc/infer/lattice.rs +++ b/src/librustc/infer/lattice.rs @@ -46,7 +46,7 @@ pub trait LatticeDir<'f, 'gcx: 'f+'tcx, 'tcx: 'f> : TypeRelation<'f, 'gcx, 'tcx> // the LUB/GLB of `a` and `b` as appropriate. // // Subtle hack: ordering *may* be significant here. This method - // relates `v` to `a` first, which may help us to avoid unecessary + // relates `v` to `a` first, which may help us to avoid unnecessary // type variable obligations. See caller for details. fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>; } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 6b60a2482671..11f3da3c2081 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -358,8 +358,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> { impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { /// Used only by `rustc_typeck` during body type-checking/inference, /// will initialize `in_progress_tables` with fresh `TypeckTables`. - pub fn with_fresh_in_progress_tables(mut self) -> Self { - self.fresh_tables = Some(RefCell::new(ty::TypeckTables::empty())); + pub fn with_fresh_in_progress_tables(mut self, table_owner: DefId) -> Self { + self.fresh_tables = Some(RefCell::new(ty::TypeckTables::empty(Some(table_owner)))); self } @@ -1331,9 +1331,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { if let Some(tables) = self.in_progress_tables { if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { + let hir_id = self.tcx.hir.node_to_hir_id(id); return tables.borrow() - .closure_kinds - .get(&id) + .closure_kinds() + .get(hir_id) .cloned() .map(|(kind, _)| kind); } @@ -1353,7 +1354,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> { if let Some(tables) = self.in_progress_tables { if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - if let Some(&ty) = tables.borrow().closure_tys.get(&id) { + let hir_id = self.tcx.hir.node_to_hir_id(id); + if let Some(&ty) = tables.borrow().closure_tys().get(hir_id) { return ty; } } diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 57f2f748b246..5588b6d9add1 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -128,7 +128,7 @@ pub enum UndoLogEntry<'tcx> { /// We added the given `given` AddGiven(Region<'tcx>, ty::RegionVid), - /// We added a GLB/LUB "combinaton variable" + /// We added a GLB/LUB "combination variable" AddCombination(CombineMapType, TwoRegions<'tcx>), /// During skolemization, we sometimes purge entries from the undo diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 2e8b843d07b3..6a1f8f1d0692 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -111,8 +111,10 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for FullTypeResolver<'a, 'gcx, 'tcx> } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.needs_infer() { + if !t.needs_infer() && !ty::keep_local(&t) { t // micro-optimize -- if there is nothing in this type that this fold affects... + // ^ we need to have the `keep_local` check to un-default + // defaulted tuples. } else { let t = self.infcx.shallow_resolve(t); match t.sty { @@ -131,6 +133,12 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for FullTypeResolver<'a, 'gcx, 'tcx> ty::TyInfer(_) => { bug!("Unexpected type in full type resolver: {:?}", t); } + ty::TyTuple(tys, true) => { + // Un-default defaulted tuples - we are going to a + // different infcx, and the default will just cause + // pollution. + self.tcx().intern_tup(tys, false) + } _ => { t.super_fold_with(self) } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 6ee06dc0a816..40d78d0138bb 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -69,7 +69,7 @@ pub struct LintStore { /// is true if the lint group was added by a plugin. lint_groups: FxHashMap<&'static str, (Vec, bool)>, - /// Extra info for future incompatibility lints, descibing the + /// Extra info for future incompatibility lints, describing the /// issue or RFC that caused the incompatibility. future_incompatible: FxHashMap, } @@ -986,7 +986,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut cx = LateContext { tcx, - tables: &ty::TypeckTables::empty(), + tables: &ty::TypeckTables::empty(None), param_env: ty::ParamEnv::empty(Reveal::UserFacing), access_levels, lint_sess: LintSession::new(&tcx.sess.lint_store), diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 4e08bc90c7c3..8a2b115e58d3 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -94,8 +94,8 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } } - fn lookup_and_handle_method(&mut self, id: ast::NodeId) { - self.check_def_id(self.tables.type_dependent_defs[&id].def_id()); + fn lookup_and_handle_method(&mut self, id: hir::HirId) { + self.check_def_id(self.tables.type_dependent_defs()[id].def_id()); } fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) { @@ -119,7 +119,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, def: Def, pats: &[codemap::Spanned]) { - let variant = match self.tables.node_id_to_type(lhs.id).sty { + let variant = match self.tables.node_id_to_type(lhs.hir_id).sty { ty::TyAdt(adt, _) => adt.variant_of_def(def), _ => span_bug!(lhs.span, "non-ADT in struct pattern") }; @@ -235,11 +235,11 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr) { match expr.node { hir::ExprPath(ref qpath @ hir::QPath::TypeRelative(..)) => { - let def = self.tables.qpath_def(qpath, expr.id); + let def = self.tables.qpath_def(qpath, expr.hir_id); self.handle_definition(def); } hir::ExprMethodCall(..) => { - self.lookup_and_handle_method(expr.id); + self.lookup_and_handle_method(expr.hir_id); } hir::ExprField(ref lhs, ref name) => { self.handle_field_access(&lhs, name.node); @@ -282,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { self.handle_field_pattern_match(pat, path.def, fields); } PatKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { - let def = self.tables.qpath_def(qpath, pat.id); + let def = self.tables.qpath_def(qpath, pat.hir_id); self.handle_definition(def); } _ => () @@ -425,7 +425,7 @@ fn find_live<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, - tables: &ty::TypeckTables::empty(), + tables: &ty::TypeckTables::empty(None), live_symbols: box FxHashSet(), struct_has_extern_repr: false, ignore_non_const_paths: false, diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index fcf366788b22..98934d607032 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -165,7 +165,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr) { match expr.node { hir::ExprMethodCall(..) => { - let def_id = self.tables.type_dependent_defs[&expr.id].def_id(); + let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id(); let sig = self.tcx.fn_sig(def_id); debug!("effect: method call case, signature is {:?}", sig); @@ -262,7 +262,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut visitor = EffectCheckVisitor { tcx, - tables: &ty::TypeckTables::empty(), + tables: &ty::TypeckTables::empty(None), body_id: hir::BodyId { node_id: ast::CRATE_NODE_ID }, unsafe_context: UnsafeContext::new(SafeContext), }; diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 1ea5b29b15ba..e36e1f470eb6 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -296,7 +296,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { debug!("consume_body(body={:?})", body); for arg in &body.arguments { - let arg_ty = return_if_err!(self.mc.node_ty(arg.pat.id)); + let arg_ty = return_if_err!(self.mc.node_ty(arg.pat.hir_id)); let fn_body_scope_r = self.tcx().node_scope_region(body.value.id); let arg_cmt = self.mc.cat_rvalue( @@ -541,7 +541,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { } ty::TyError => { } _ => { - let def_id = self.mc.tables.type_dependent_defs[&call.id].def_id(); + let def_id = self.mc.tables.type_dependent_defs()[call.hir_id].def_id(); match OverloadedCallType::from_method_id(self.tcx(), def_id) { FnMutOverloadedCall => { let call_scope_r = self.tcx().node_scope_region(call.id); @@ -801,7 +801,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { pat); return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |cmt_pat, pat| { if let PatKind::Binding(..) = pat.node { - let bm = *self.mc.tables.pat_binding_modes.get(&pat.id) + let bm = *self.mc.tables.pat_binding_modes().get(pat.hir_id) .expect("missing binding mode"); match bm { ty::BindByReference(..) => @@ -827,10 +827,11 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |cmt_pat, pat| { if let PatKind::Binding(_, def_id, ..) = pat.node { debug!("binding cmt_pat={:?} pat={:?} match_mode={:?}", cmt_pat, pat, match_mode); - let bm = *mc.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode"); + let bm = *mc.tables.pat_binding_modes().get(pat.hir_id) + .expect("missing binding mode"); // pat_ty: the type of the binding being produced. - let pat_ty = return_if_err!(mc.node_ty(pat.id)); + let pat_ty = return_if_err!(mc.node_ty(pat.hir_id)); // Each match binding is effectively an assignment to the // binding being produced. @@ -867,7 +868,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { PatKind::Struct(ref qpath, ..) => qpath, _ => return }; - let def = mc.tables.qpath_def(qpath, pat.id); + let def = mc.tables.qpath_def(qpath, pat.hir_id); match def { Def::Variant(variant_did) | Def::VariantCtor(variant_did, ..) => { @@ -891,10 +892,13 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { self.tcx().with_freevars(closure_expr.id, |freevars| { for freevar in freevars { - let def_id = freevar.def.def_id(); - let id_var = self.tcx().hir.as_local_node_id(def_id).unwrap(); - let upvar_id = ty::UpvarId { var_id: id_var, - closure_expr_id: closure_expr.id }; + let var_def_id = freevar.def.def_id(); + debug_assert!(var_def_id.is_local()); + let closure_def_id = self.tcx().hir.local_def_id(closure_expr.id); + let upvar_id = ty::UpvarId { + var_id: var_def_id.index, + closure_expr_id: closure_def_id.index + }; let upvar_capture = self.mc.tables.upvar_capture(upvar_id); let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id, fn_decl_span, @@ -927,8 +931,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { -> mc::McResult> { // Create the cmt for the variable being borrowed, from the // caller's perspective - let var_id = self.tcx().hir.as_local_node_id(upvar_def.def_id()).unwrap(); - let var_ty = self.mc.node_ty(var_id)?; + let var_node_id = self.tcx().hir.as_local_node_id(upvar_def.def_id()).unwrap(); + let var_hir_id = self.tcx().hir.node_to_hir_id(var_node_id); + let var_ty = self.mc.node_ty(var_hir_id)?; self.mc.cat_def(closure_id, closure_span, var_ty, upvar_def) } } diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index d29622b4a815..0a4e5094cde7 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -146,13 +146,13 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr) { let def = if let hir::ExprPath(ref qpath) = expr.node { - self.tables.qpath_def(qpath, expr.id) + self.tables.qpath_def(qpath, expr.hir_id) } else { Def::Err }; if let Def::Fn(did) = def { if self.def_id_is_transmute(did) { - let typ = self.tables.node_id_to_type(expr.id); + let typ = self.tables.node_id_to_type(expr.hir_id); let sig = typ.fn_sig(self.tcx); let from = sig.inputs().skip_binder()[0]; let to = *sig.output().skip_binder(); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 3abd63fccdb3..354930be441f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -70,7 +70,7 @@ pub use self::Note::*; use self::Aliasability::*; use middle::region::RegionMaps; -use hir::def_id::DefId; +use hir::def_id::{DefId, DefIndex}; use hir::map as hir_map; use infer::InferCtxt; use hir::def::{Def, CtorKind}; @@ -190,7 +190,7 @@ pub type cmt<'tcx> = Rc>; pub enum ImmutabilityBlame<'tcx> { ImmLocal(ast::NodeId), - ClosureEnv(ast::NodeId), + ClosureEnv(DefIndex), LocalDeref(ast::NodeId), AdtFieldDeref(&'tcx ty::AdtDef, &'tcx ty::FieldDef) } @@ -334,7 +334,9 @@ impl MutabilityCategory { let ret = match tcx.hir.get(id) { hir_map::NodeLocal(p) => match p.node { PatKind::Binding(..) => { - let bm = *tables.pat_binding_modes.get(&p.id).expect("missing binding mode"); + let bm = *tables.pat_binding_modes() + .get(p.hir_id) + .expect("missing binding mode"); if bm == ty::BindByValue(hir::MutMutable) { McDeclared } else { @@ -435,7 +437,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } fn resolve_type_vars_or_error(&self, - id: ast::NodeId, + id: hir::HirId, ty: Option>) -> McResult> { match ty { @@ -451,33 +453,41 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // FIXME None if self.is_tainted_by_errors() => Err(()), None => { + let id = self.tcx.hir.definitions().find_node_for_hir_id(id); bug!("no type for node {}: {} in mem_categorization", id, self.tcx.hir.node_to_string(id)); } } } - pub fn node_ty(&self, id: ast::NodeId) -> McResult> { - self.resolve_type_vars_or_error(id, self.tables.node_id_to_type_opt(id)) + pub fn node_ty(&self, + hir_id: hir::HirId) + -> McResult> { + self.resolve_type_vars_or_error(hir_id, + self.tables.node_id_to_type_opt(hir_id)) } pub fn expr_ty(&self, expr: &hir::Expr) -> McResult> { - self.resolve_type_vars_or_error(expr.id, self.tables.expr_ty_opt(expr)) + self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_opt(expr)) } pub fn expr_ty_adjusted(&self, expr: &hir::Expr) -> McResult> { - self.resolve_type_vars_or_error(expr.id, self.tables.expr_ty_adjusted_opt(expr)) + self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_adjusted_opt(expr)) } fn pat_ty(&self, pat: &hir::Pat) -> McResult> { - let base_ty = self.node_ty(pat.id)?; + let base_ty = self.node_ty(pat.hir_id)?; // FIXME (Issue #18207): This code detects whether we are // looking at a `ref x`, and if so, figures out what the type // *being borrowed* is. But ideally we would put in a more // fundamental fix to this conflated use of the node id. let ret_ty = match pat.node { PatKind::Binding(..) => { - let bm = *self.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode"); + let bm = *self.tables + .pat_binding_modes() + .get(pat.hir_id) + .expect("missing binding mode"); + if let ty::BindByReference(_) = bm { // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. @@ -604,7 +614,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } hir::ExprPath(ref qpath) => { - let def = self.tables.qpath_def(qpath, expr.id); + let def = self.tables.qpath_def(qpath, expr.hir_id); self.cat_def(expr.id, expr.span, expr_ty, def) } @@ -643,7 +653,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { Ok(self.cat_rvalue_node(id, span, expr_ty)) } - Def::Static(_, mutbl) => { + Def::Static(def_id, mutbl) => { + // `#[thread_local]` statics may not outlive the current function. + for attr in &self.tcx.get_attrs(def_id)[..] { + if attr.check_name("thread_local") { + return Ok(self.cat_rvalue_node(id, span, expr_ty)); + } + } Ok(Rc::new(cmt_ { id:id, span:span, @@ -684,6 +700,8 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { fn_node_id: ast::NodeId) -> McResult> { + let fn_hir_id = self.tcx.hir.node_to_hir_id(fn_node_id); + // An upvar can have up to 3 components. We translate first to a // `Categorization::Upvar`, which is itself a fiction -- it represents the reference to the // field from the environment. @@ -707,7 +725,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // FnMut | copied -> &'env mut | upvar -> &'env mut -> &'up bk // FnOnce | copied | upvar -> &'up bk - let kind = match self.tables.closure_kinds.get(&fn_node_id) { + let kind = match self.tables.closure_kinds().get(fn_hir_id) { Some(&(kind, _)) => kind, None => { let ty = self.node_ty(fn_node_id)?; @@ -718,9 +736,15 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } }; - let upvar_id = ty::UpvarId { var_id, - closure_expr_id: fn_node_id }; - let var_ty = self.node_ty(var_id)?; + let closure_expr_def_index = self.tcx.hir.local_def_id(fn_node_id).index; + let var_def_index = self.tcx.hir.local_def_id(var_id).index; + + let upvar_id = ty::UpvarId { + var_id: var_def_index, + closure_expr_id: closure_expr_def_index + }; + let var_hir_id = self.tcx.hir.node_to_hir_id(var_id); + let var_ty = self.node_ty(var_hir_id)?; // Mutability of original variable itself let var_mutbl = MutabilityCategory::from_local(self.tcx, self.tables, var_id); @@ -755,8 +779,6 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // If this is a by-ref capture, then the upvar we loaded is // actually a reference, so we have to add an implicit deref // for that. - let upvar_id = ty::UpvarId { var_id, - closure_expr_id: fn_node_id }; let upvar_capture = self.tables.upvar_capture(upvar_id); let cmt_result = match upvar_capture { ty::UpvarCapture::ByValue => { @@ -794,7 +816,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // The environment of a closure is guaranteed to // outlive any bindings introduced in the body of the // closure itself. - scope: self.tcx.hir.local_def_id(upvar_id.closure_expr_id), + scope: DefId::local(upvar_id.closure_expr_id), bound_region: ty::BrEnv })); @@ -1130,7 +1152,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { match pat.node { PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => { - let def = self.tables.qpath_def(qpath, pat.id); + let def = self.tables.qpath_def(qpath, pat.hir_id); let (cmt, expected_len) = match def { Def::Err => { debug!("access to unresolvable pattern {:?}", pat); @@ -1167,7 +1189,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { PatKind::Struct(ref qpath, ref field_pats, _) => { // {f1: p1, ..., fN: pN} - let def = self.tables.qpath_def(qpath, pat.id); + let def = self.tables.qpath_def(qpath, pat.hir_id); let cmt = match def { Def::Err => { debug!("access to unresolvable pattern {:?}", pat); diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index df828c8d8e71..666f71cca06b 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -107,10 +107,10 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr) { let def = match expr.node { hir::ExprPath(ref qpath) => { - Some(self.tables.qpath_def(qpath, expr.id)) + Some(self.tables.qpath_def(qpath, expr.hir_id)) } hir::ExprMethodCall(..) => { - Some(self.tables.type_dependent_defs[&expr.id]) + Some(self.tables.type_dependent_defs()[expr.hir_id]) } _ => None }; @@ -296,6 +296,9 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { hir::ImplItemKind::Type(_) => {} } } + hir_map::NodeExpr(&hir::Expr { node: hir::ExprClosure(.., body, _), .. }) => { + self.visit_nested_body(body); + } // Nothing to recurse on for these hir_map::NodeForeignItem(_) | hir_map::NodeVariant(_) | @@ -375,7 +378,7 @@ fn reachable_set<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) -> }); let mut reachable_context = ReachableContext { tcx, - tables: &ty::TypeckTables::empty(), + tables: &ty::TypeckTables::empty(None), reachable_symbols: NodeSet(), worklist: Vec::new(), any_library, diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 6484572d2243..abfff023e892 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -764,7 +764,7 @@ impl<'tcx> Debug for TerminatorKind<'tcx> { impl<'tcx> TerminatorKind<'tcx> { /// Write the "head" part of the terminator; that is, its name and the data it uses to pick the - /// successor basic block, if any. The only information not inlcuded is the list of possible + /// successor basic block, if any. The only information not included is the list of possible /// successors, which may be rendered differently between the text and the graphviz format. pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { use self::TerminatorKind::*; diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index be39f95b9889..3aea0722d0e2 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -112,7 +112,7 @@ pub struct Session { /// Map from imported macro spans (which consist of /// the localized span for the macro body) to the - /// macro name and defintion span in the source crate. + /// macro name and definition span in the source crate. pub imported_macro_spans: RefCell>, incr_comp_session: RefCell, @@ -828,7 +828,7 @@ pub fn compile_result_from_err_count(err_count: usize) -> CompileResult { #[inline(never)] pub fn bug_fmt(file: &'static str, line: u32, args: fmt::Arguments) -> ! { // this wrapper mostly exists so I don't have to write a fully - // qualified path of None:: inside the bug!() macro defintion + // qualified path of None:: inside the bug!() macro definition opt_span_bug_fmt(file, line, None::, args); } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index e4042c48bf33..c147c9d56368 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -111,8 +111,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - // returns if `cond` not occuring implies that `error` does not occur - i.e. that - // `error` occuring implies that `cond` occurs. + // returns if `cond` not occurring implies that `error` does not occur - i.e. that + // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) @@ -683,7 +683,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Additional context information explaining why the closure only implements // a particular trait. if let Some(tables) = self.in_progress_tables { - match tables.borrow().closure_kinds.get(&node_id) { + let tables = tables.borrow(); + let closure_hir_id = self.tcx.hir.node_to_hir_id(node_id); + match tables.closure_kinds().get(closure_hir_id) { Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) => { err.span_note(span, &format!( "closure is `FnOnce` because it moves the \ diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 71f4c8441b2b..ebb6466aa32d 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -1422,7 +1422,7 @@ impl<'tcx> ProjectionCache<'tcx> { } /// Try to start normalize `key`; returns an error if - /// normalization already occured (this error corresponds to a + /// normalization already occurred (this error corresponds to a /// cache hit, so it's actually a good thing). fn try_start(&mut self, key: ty::ProjectionTy<'tcx>) -> Result<(), ProjectionCacheEntry<'tcx>> { diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index f80caeec460f..8b31cb599e45 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -31,7 +31,7 @@ use util::nodemap::{DefIdMap, FxHashMap}; /// /// - Parent extraction. In particular, the graph can give you the *immediate* /// parents of a given specializing impl, which is needed for extracting -/// default items amongst other thigns. In the simple "chain" rule, every impl +/// default items amongst other things. In the simple "chain" rule, every impl /// has at most one parent. pub struct Graph { // all impls have a parent; the "root" impls have as their parent the def_id @@ -95,7 +95,7 @@ impl<'a, 'gcx, 'tcx> Children { } /// Attempt to insert an impl into this set of children, while comparing for - /// specialiation relationships. + /// specialization relationships. fn insert(&mut self, tcx: TyCtxt<'a, 'gcx, 'tcx>, impl_def_id: DefId, @@ -206,7 +206,7 @@ impl<'a, 'gcx, 'tcx> Graph { // if the reference itself contains an earlier error (e.g., due to a // resolution failure), then we just insert the impl at the top level of - // the graph and claim that there's no overlap (in order to supress + // the graph and claim that there's no overlap (in order to suppress // bogus errors). if trait_ref.references_error() { debug!("insert: inserting dummy node for erroneous TraitRef {:?}, \ diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index 62d137475f90..514366607795 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -29,7 +29,7 @@ use ty::subst::Substs; /// by `autoref`, to either a raw or borrowed pointer. In these cases unsize is /// `false`. /// -/// 2. A thin-to-fat coercon involves unsizing the underlying data. We start +/// 2. A thin-to-fat coercion involves unsizing the underlying data. We start /// with a thin pointer, deref a number of times, unsize the underlying data, /// then autoref. The 'unsize' phase may change a fixed length array to a /// dynamically sized one, a concrete object to a trait object, or statically @@ -52,7 +52,7 @@ use ty::subst::Substs; /// that case, we have the pointer we need coming in, so there are no /// autoderefs, and no autoref. Instead we just do the `Unsize` transformation. /// At some point, of course, `Box` should move out of the compiler, in which -/// case this is analogous to transformating a struct. E.g., Box<[i32; 4]> -> +/// case this is analogous to transforming a struct. E.g., Box<[i32; 4]> -> /// Box<[i32]> is an `Adjust::Unsize` with the target `Box<[i32]>`. #[derive(Clone, RustcEncodable, RustcDecodable)] pub struct Adjustment<'tcx> { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index c2c52e5fa21b..18478f7c61d7 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -14,12 +14,13 @@ use dep_graph::DepGraph; use errors::DiagnosticBuilder; use session::Session; use middle; -use hir::TraitMap; +use hir::{TraitMap}; use hir::def::{Def, ExportMap}; use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use hir::map as hir_map; use hir::map::DefPathHash; use lint::{self, Lint}; +use ich::{self, StableHashingContext, NodeIdHashingMode}; use middle::free_region::FreeRegionMap; use middle::lang_items; use middle::resolve_lifetime; @@ -42,15 +43,18 @@ use ty::inhabitedness::DefIdForest; use ty::maps; use ty::steal::Steal; use ty::BindingMode; -use util::nodemap::{NodeMap, NodeSet, DefIdSet}; +use util::nodemap::{NodeMap, NodeSet, DefIdSet, ItemLocalMap}; use util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::accumulate_vec::AccumulateVec; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, + StableHasherResult}; use arena::{TypedArena, DroplessArena}; use rustc_data_structures::indexed_vec::IndexVec; use std::borrow::Borrow; use std::cell::{Cell, RefCell}; use std::cmp::Ordering; +use std::collections::hash_map::{self, Entry}; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; @@ -207,58 +211,155 @@ pub struct CommonTypes<'tcx> { pub re_erased: Region<'tcx>, } +pub struct LocalTableInContext<'a, V: 'a> { + local_id_root: Option, + data: &'a ItemLocalMap +} + +/// Validate that the given HirId (respectively its `local_id` part) can be +/// safely used as a key in the tables of a TypeckTable. For that to be +/// the case, the HirId must have the same `owner` as all the other IDs in +/// this table (signified by `local_id_root`). Otherwise the HirId +/// would be in a different frame of reference and using its `local_id` +/// would result in lookup errors, or worse, in silently wrong data being +/// stored/returned. +fn validate_hir_id_for_typeck_tables(local_id_root: Option, + hir_id: hir::HirId, + mut_access: bool) { + if cfg!(debug_assertions) { + if let Some(local_id_root) = local_id_root { + if hir_id.owner != local_id_root.index { + ty::tls::with(|tcx| { + let node_id = tcx.hir + .definitions() + .find_node_for_hir_id(hir_id); + + bug!("node {} with HirId::owner {:?} cannot be placed in \ + TypeckTables with local_id_root {:?}", + tcx.hir.node_to_string(node_id), + DefId::local(hir_id.owner), + local_id_root) + }); + } + } else { + // We use "Null Object" TypeckTables in some of the analysis passes. + // These are just expected to be empty and their `local_id_root` is + // `None`. Therefore we cannot verify whether a given `HirId` would + // be a valid key for the given table. Instead we make sure that + // nobody tries to write to such a Null Object table. + if mut_access { + bug!("access to invalid TypeckTables") + } + } + } +} + +impl<'a, V> LocalTableInContext<'a, V> { + pub fn contains_key(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + self.data.contains_key(&id.local_id) + } + + pub fn get(&self, id: hir::HirId) -> Option<&V> { + validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + self.data.get(&id.local_id) + } + + pub fn iter(&self) -> hash_map::Iter { + self.data.iter() + } +} + +impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { + type Output = V; + + fn index(&self, key: hir::HirId) -> &V { + self.get(key).expect("LocalTableInContext: key not found") + } +} + +pub struct LocalTableInContextMut<'a, V: 'a> { + local_id_root: Option, + data: &'a mut ItemLocalMap +} + +impl<'a, V> LocalTableInContextMut<'a, V> { + pub fn get_mut(&mut self, id: hir::HirId) -> Option<&mut V> { + validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + self.data.get_mut(&id.local_id) + } + + pub fn entry(&mut self, id: hir::HirId) -> Entry { + validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + self.data.entry(id.local_id) + } + + pub fn insert(&mut self, id: hir::HirId, val: V) -> Option { + validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + self.data.insert(id.local_id, val) + } + + pub fn remove(&mut self, id: hir::HirId) -> Option { + validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + self.data.remove(&id.local_id) + } +} + #[derive(RustcEncodable, RustcDecodable)] pub struct TypeckTables<'tcx> { + /// The HirId::owner all ItemLocalIds in this table are relative to. + pub local_id_root: Option, + /// Resolved definitions for `::X` associated paths and /// method calls, including those of overloaded operators. - pub type_dependent_defs: NodeMap, + type_dependent_defs: ItemLocalMap, /// Stores the types for various nodes in the AST. Note that this table /// is not guaranteed to be populated until after typeck. See /// typeck::check::fn_ctxt for details. - pub node_types: NodeMap>, + node_types: ItemLocalMap>, /// Stores the type parameters which were substituted to obtain the type /// of this node. This only applies to nodes that refer to entities /// parameterized by type parameters, such as generic fns, types, or /// other items. - pub node_substs: NodeMap<&'tcx Substs<'tcx>>, + node_substs: ItemLocalMap<&'tcx Substs<'tcx>>, - pub adjustments: NodeMap>>, + adjustments: ItemLocalMap>>, // Stores the actual binding mode for all instances of hir::BindingAnnotation. - pub pat_binding_modes: NodeMap, + pat_binding_modes: ItemLocalMap, /// Borrows pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, /// Records the type of each closure. - pub closure_tys: NodeMap>, + closure_tys: ItemLocalMap>, /// Records the kind of each closure and the span and name of the variable /// that caused the closure to be this kind. - pub closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>, + closure_kinds: ItemLocalMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>, - pub generator_sigs: NodeMap>>, + pub generator_sigs: ItemLocalMap>>, - pub generator_interiors: NodeMap>, + pub generator_interiors: ItemLocalMap>, /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions /// (including late-bound regions) are replaced with free /// equivalents. This table is not used in trans (since regions /// are erased there) and hence is not serialized to metadata. - pub liberated_fn_sigs: NodeMap>, + liberated_fn_sigs: ItemLocalMap>, /// For each FRU expression, record the normalized types of the fields /// of the struct - this is needed because it is non-trivial to /// normalize while preserving regions. This table is used only in /// MIR construction and hence is not serialized to metadata. - pub fru_field_types: NodeMap>>, + fru_field_types: ItemLocalMap>>, /// Maps a cast expression to its kind. This is keyed on the /// *from* expression of the cast, not the cast itself. - pub cast_kinds: NodeMap, + cast_kinds: ItemLocalMap, /// Set of trait imports actually used in the method resolution. /// This is used for warning unused imports. @@ -275,21 +376,22 @@ pub struct TypeckTables<'tcx> { } impl<'tcx> TypeckTables<'tcx> { - pub fn empty() -> TypeckTables<'tcx> { + pub fn empty(local_id_root: Option) -> TypeckTables<'tcx> { TypeckTables { - type_dependent_defs: NodeMap(), - node_types: FxHashMap(), - node_substs: NodeMap(), - adjustments: NodeMap(), - pat_binding_modes: NodeMap(), + local_id_root, + type_dependent_defs: ItemLocalMap(), + node_types: ItemLocalMap(), + node_substs: ItemLocalMap(), + adjustments: ItemLocalMap(), + pat_binding_modes: ItemLocalMap(), upvar_capture_map: FxHashMap(), - generator_sigs: NodeMap(), - generator_interiors: NodeMap(), - closure_tys: NodeMap(), - closure_kinds: NodeMap(), - liberated_fn_sigs: NodeMap(), - fru_field_types: NodeMap(), - cast_kinds: NodeMap(), + generator_sigs: ItemLocalMap(), + generator_interiors: ItemLocalMap(), + closure_tys: ItemLocalMap(), + closure_kinds: ItemLocalMap(), + liberated_fn_sigs: ItemLocalMap(), + fru_field_types: ItemLocalMap(), + cast_kinds: ItemLocalMap(), used_trait_imports: DefIdSet(), tainted_by_errors: false, free_region_map: FreeRegionMap::new(), @@ -297,41 +399,87 @@ impl<'tcx> TypeckTables<'tcx> { } /// Returns the final resolution of a `QPath` in an `Expr` or `Pat` node. - pub fn qpath_def(&self, qpath: &hir::QPath, id: NodeId) -> Def { + pub fn qpath_def(&self, qpath: &hir::QPath, id: hir::HirId) -> Def { match *qpath { hir::QPath::Resolved(_, ref path) => path.def, hir::QPath::TypeRelative(..) => { - self.type_dependent_defs.get(&id).cloned().unwrap_or(Def::Err) + validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + self.type_dependent_defs.get(&id.local_id).cloned().unwrap_or(Def::Err) } } } - pub fn node_id_to_type(&self, id: NodeId) -> Ty<'tcx> { + pub fn type_dependent_defs(&self) -> LocalTableInContext { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.type_dependent_defs + } + } + + pub fn type_dependent_defs_mut(&mut self) -> LocalTableInContextMut { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.type_dependent_defs + } + } + + pub fn node_types(&self) -> LocalTableInContext> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.node_types + } + } + + pub fn node_types_mut(&mut self) -> LocalTableInContextMut> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.node_types + } + } + + pub fn node_id_to_type(&self, id: hir::HirId) -> Ty<'tcx> { match self.node_id_to_type_opt(id) { Some(ty) => ty, None => { bug!("node_id_to_type: no type for node `{}`", - tls::with(|tcx| tcx.hir.node_to_string(id))) + tls::with(|tcx| { + let id = tcx.hir.definitions().find_node_for_hir_id(id); + tcx.hir.node_to_string(id) + })) } } } - pub fn node_id_to_type_opt(&self, id: NodeId) -> Option> { - self.node_types.get(&id).cloned() + pub fn node_id_to_type_opt(&self, id: hir::HirId) -> Option> { + validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + self.node_types.get(&id.local_id).cloned() } - pub fn node_substs(&self, id: NodeId) -> &'tcx Substs<'tcx> { - self.node_substs.get(&id).cloned().unwrap_or(Substs::empty()) + pub fn node_substs_mut(&mut self) -> LocalTableInContextMut<&'tcx Substs<'tcx>> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.node_substs + } + } + + pub fn node_substs(&self, id: hir::HirId) -> &'tcx Substs<'tcx> { + validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + self.node_substs.get(&id.local_id).cloned().unwrap_or(Substs::empty()) + } + + pub fn node_substs_opt(&self, id: hir::HirId) -> Option<&'tcx Substs<'tcx>> { + validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + self.node_substs.get(&id.local_id).cloned() } // Returns the type of a pattern as a monotype. Like @expr_ty, this function // doesn't provide type parameter substitutions. pub fn pat_ty(&self, pat: &hir::Pat) -> Ty<'tcx> { - self.node_id_to_type(pat.id) + self.node_id_to_type(pat.hir_id) } pub fn pat_ty_opt(&self, pat: &hir::Pat) -> Option> { - self.node_id_to_type_opt(pat.id) + self.node_id_to_type_opt(pat.hir_id) } // Returns the type of an expression as a monotype. @@ -345,16 +493,32 @@ impl<'tcx> TypeckTables<'tcx> { // ask for the type of "id" in "id(3)", it will return "fn(&isize) -> isize" // instead of "fn(ty) -> T with T = isize". pub fn expr_ty(&self, expr: &hir::Expr) -> Ty<'tcx> { - self.node_id_to_type(expr.id) + self.node_id_to_type(expr.hir_id) } pub fn expr_ty_opt(&self, expr: &hir::Expr) -> Option> { - self.node_id_to_type_opt(expr.id) + self.node_id_to_type_opt(expr.hir_id) + } + + pub fn adjustments(&self) -> LocalTableInContext>> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.adjustments + } + } + + pub fn adjustments_mut(&mut self) + -> LocalTableInContextMut>> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.adjustments + } } pub fn expr_adjustments(&self, expr: &hir::Expr) -> &[ty::adjustment::Adjustment<'tcx>] { - self.adjustments.get(&expr.id).map_or(&[], |a| &a[..]) + validate_hir_id_for_typeck_tables(self.local_id_root, expr.hir_id, false); + self.adjustments.get(&expr.hir_id.local_id).map_or(&[], |a| &a[..]) } /// Returns the type of `expr`, considering any `Adjustment` @@ -379,15 +543,169 @@ impl<'tcx> TypeckTables<'tcx> { return false; } - match self.type_dependent_defs.get(&expr.id) { + match self.type_dependent_defs().get(expr.hir_id) { Some(&Def::Method(_)) => true, _ => false } } + pub fn pat_binding_modes(&self) -> LocalTableInContext { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.pat_binding_modes + } + } + + pub fn pat_binding_modes_mut(&mut self) + -> LocalTableInContextMut { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.pat_binding_modes + } + } + pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { self.upvar_capture_map[&upvar_id] } + + pub fn closure_tys(&self) -> LocalTableInContext> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.closure_tys + } + } + + pub fn closure_tys_mut(&mut self) + -> LocalTableInContextMut> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.closure_tys + } + } + + pub fn closure_kinds(&self) -> LocalTableInContext<(ty::ClosureKind, + Option<(Span, ast::Name)>)> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.closure_kinds + } + } + + pub fn closure_kinds_mut(&mut self) + -> LocalTableInContextMut<(ty::ClosureKind, Option<(Span, ast::Name)>)> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.closure_kinds + } + } + + pub fn liberated_fn_sigs(&self) -> LocalTableInContext> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.liberated_fn_sigs + } + } + + pub fn liberated_fn_sigs_mut(&mut self) -> LocalTableInContextMut> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.liberated_fn_sigs + } + } + + pub fn fru_field_types(&self) -> LocalTableInContext>> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.fru_field_types + } + } + + pub fn fru_field_types_mut(&mut self) -> LocalTableInContextMut>> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.fru_field_types + } + } + + pub fn cast_kinds(&self) -> LocalTableInContext { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.cast_kinds + } + } + + pub fn cast_kinds_mut(&mut self) -> LocalTableInContextMut { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.cast_kinds + } + } +} + +impl<'a, 'gcx, 'tcx> HashStable> for TypeckTables<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>, + hasher: &mut StableHasher) { + let ty::TypeckTables { + local_id_root, + ref type_dependent_defs, + ref node_types, + ref node_substs, + ref adjustments, + ref pat_binding_modes, + ref upvar_capture_map, + ref closure_tys, + ref closure_kinds, + ref liberated_fn_sigs, + ref fru_field_types, + + ref cast_kinds, + + ref used_trait_imports, + tainted_by_errors, + ref free_region_map, + } = *self; + + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + ich::hash_stable_itemlocalmap(hcx, hasher, type_dependent_defs); + ich::hash_stable_itemlocalmap(hcx, hasher, node_types); + ich::hash_stable_itemlocalmap(hcx, hasher, node_substs); + ich::hash_stable_itemlocalmap(hcx, hasher, adjustments); + ich::hash_stable_itemlocalmap(hcx, hasher, pat_binding_modes); + ich::hash_stable_hashmap(hcx, hasher, upvar_capture_map, |hcx, up_var_id| { + let ty::UpvarId { + var_id, + closure_expr_id + } = *up_var_id; + + let local_id_root = + local_id_root.expect("trying to hash invalid TypeckTables"); + + let var_def_id = DefId { + krate: local_id_root.krate, + index: var_id, + }; + let closure_def_id = DefId { + krate: local_id_root.krate, + index: closure_expr_id, + }; + (hcx.def_path_hash(var_def_id), hcx.def_path_hash(closure_def_id)) + }); + + ich::hash_stable_itemlocalmap(hcx, hasher, closure_tys); + ich::hash_stable_itemlocalmap(hcx, hasher, closure_kinds); + ich::hash_stable_itemlocalmap(hcx, hasher, liberated_fn_sigs); + ich::hash_stable_itemlocalmap(hcx, hasher, fru_field_types); + ich::hash_stable_itemlocalmap(hcx, hasher, cast_kinds); + + ich::hash_stable_hashset(hcx, hasher, used_trait_imports, |hcx, def_id| { + hcx.def_path_hash(*def_id) + }); + + tainted_by_errors.hash_stable(hcx, hasher); + free_region_map.hash_stable(hcx, hasher); + }) + } } impl<'tcx> CommonTypes<'tcx> { @@ -1201,7 +1519,7 @@ macro_rules! direct_interners { } } -fn keep_local<'tcx, T: ty::TypeFoldable<'tcx>>(x: &T) -> bool { +pub fn keep_local<'tcx, T: ty::TypeFoldable<'tcx>>(x: &T) -> bool { x.has_type_flags(ty::TypeFlags::KEEP_IN_LOCAL_TCX) } diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index f40e1d370a99..27b8d245396c 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -158,7 +158,10 @@ impl FlagComputation { self.add_ty(m.ty); } - &ty::TyTuple(ref ts, _) => { + &ty::TyTuple(ref ts, is_default) => { + if is_default { + self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX); + } self.add_tys(&ts[..]); } diff --git a/src/librustc/ty/inhabitedness/def_id_forest.rs b/src/librustc/ty/inhabitedness/def_id_forest.rs index 231600f95ac6..896682e2370e 100644 --- a/src/librustc/ty/inhabitedness/def_id_forest.rs +++ b/src/librustc/ty/inhabitedness/def_id_forest.rs @@ -24,7 +24,7 @@ use ty::{DefId, DefIdTree}; #[derive(Clone)] pub struct DefIdForest { /// The minimal set of DefIds required to represent the whole set. - /// If A and B are DefIds in the DefIdForest, and A is a desecendant + /// If A and B are DefIds in the DefIdForest, and A is a descendant /// of B, then only B will be in root_ids. /// We use a SmallVec here because (for its use for cacheing inhabitedness) /// its rare that this will contain even two ids. @@ -61,7 +61,7 @@ impl<'a, 'gcx, 'tcx> DefIdForest { self.root_ids.is_empty() } - /// Test whether the forest conains a given DefId. + /// Test whether the forest contains a given DefId. pub fn contains(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, id: DefId) -> bool diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 01640712a38b..3e6af27935de 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -125,7 +125,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// If possible, this pushes a global path resolving to `external_def_id` that is visible /// from at least one local module and returns true. If the crate defining `external_def_id` is - /// declared with an `extern crate`, the path is guarenteed to use the `extern crate`. + /// declared with an `extern crate`, the path is guaranteed to use the `extern crate`. pub fn try_push_visible_item_path(self, buffer: &mut T, external_def_id: DefId) -> bool where T: ItemPathBuffer { diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index ba3cd5ba3910..5f9c7b551250 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -594,7 +594,7 @@ macro_rules! define_maps { } // FIXME(eddyb) Get more valid Span's on queries. - // def_span guard is necesary to prevent a recursive loop, + // def_span guard is necessary to prevent a recursive loop, // default_span calls def_span query internally. if span == DUMMY_SP && stringify!($name) != "def_span" { span = key.default_span(tcx) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 0a3c4d5d8fa5..794db5b7dd9c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -77,7 +77,7 @@ pub use self::sty::TypeVariants::*; pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; -pub use self::context::{TyCtxt, GlobalArenas, tls}; +pub use self::context::{TyCtxt, GlobalArenas, tls, keep_local}; pub use self::context::{Lift, TypeckTables}; pub use self::instance::{Instance, InstanceDef}; @@ -575,8 +575,8 @@ impl Slice { /// by the upvar) and the id of the closure expression. #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct UpvarId { - pub var_id: NodeId, - pub closure_expr_id: NodeId, + pub var_id: DefIndex, + pub closure_expr_id: DefIndex, } #[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable, Copy)] @@ -1986,6 +1986,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + pub fn local_var_name_str_def_index(self, def_index: DefIndex) -> InternedString { + let node_id = self.hir.as_local_node_id(DefId::local(def_index)).unwrap(); + self.local_var_name_str(node_id) + } + pub fn expr_is_lval(self, expr: &hir::Expr) -> bool { match expr.node { hir::ExprPath(hir::QPath::Resolved(_, ref path)) => { diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index 244b7f359688..885be8464eb3 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -224,7 +224,7 @@ pub trait MemoizationMap { type Key: Clone; type Value: Clone; - /// If `key` is present in the map, return the valuee, + /// If `key` is present in the map, return the value, /// otherwise invoke `op` and store the value in the map. /// /// NB: if the receiver is a `DepTrackingMap`, special care is diff --git a/src/librustc/util/nodemap.rs b/src/librustc/util/nodemap.rs index b03011fcb216..c397371c5c76 100644 --- a/src/librustc/util/nodemap.rs +++ b/src/librustc/util/nodemap.rs @@ -13,6 +13,7 @@ #![allow(non_snake_case)] use hir::def_id::DefId; +use hir::ItemLocalId; use syntax::ast; pub use rustc_data_structures::fx::FxHashMap; @@ -20,12 +21,14 @@ pub use rustc_data_structures::fx::FxHashSet; pub type NodeMap = FxHashMap; pub type DefIdMap = FxHashMap; +pub type ItemLocalMap = FxHashMap; pub type NodeSet = FxHashSet; pub type DefIdSet = FxHashSet; pub fn NodeMap() -> NodeMap { FxHashMap() } pub fn DefIdMap() -> DefIdMap { FxHashMap() } +pub fn ItemLocalMap() -> ItemLocalMap { FxHashMap() } pub fn NodeSet() -> NodeSet { FxHashSet() } pub fn DefIdSet() -> DefIdSet { FxHashSet() } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index ed72f2429487..a5642467474b 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -905,9 +905,9 @@ impl<'tcx> fmt::Display for ty::TyS<'tcx> { impl fmt::Debug for ty::UpvarId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UpvarId({};`{}`;{})", + write!(f, "UpvarId({:?};`{}`;{:?})", self.var_id, - ty::tls::with(|tcx| tcx.local_var_name_str(self.var_id)), + ty::tls::with(|tcx| tcx.local_var_name_str_def_index(self.var_id)), self.closure_expr_id) } } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index 676c3c51ea2a..78b07a33389e 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -79,6 +79,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { format: MacroAttribute(Symbol::intern(name)), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); let span = Span { diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index e70b7f89a676..c34bf4c3d284 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -472,7 +472,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { if new_loan.span == old_loan.span { // Both borrows are happening in the same place - // Meaning the borrow is occuring in a loop + // Meaning the borrow is occurring in a loop err.span_label( new_loan.span, format!("mutable borrow starts here in previous \ diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index ec88bcf9c79c..90e38465dd46 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -153,7 +153,9 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> { } fn decl_without_init(&mut self, id: ast::NodeId, _span: Span) { - let ty = self.bccx.tables.node_id_to_type(id); + let ty = self.bccx + .tables + .node_id_to_type(self.bccx.tcx.hir.node_to_hir_id(id)); gather_moves::gather_decl(self.bccx, &self.move_data, id, ty); } } @@ -445,7 +447,8 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { } None } - LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => { + LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => { + let local_id = self.tcx().hir.def_index_to_node_id(var_id); self.tcx().used_mut_nodes.borrow_mut().insert(local_id); None } diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index cceb4a7b3cc2..bfd883be8487 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -93,11 +93,11 @@ fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &Vec { LpInterior(Option, InteriorKind), } -pub fn closure_to_block(closure_id: ast::NodeId, - tcx: TyCtxt) -> ast::NodeId { +fn closure_to_block(closure_id: DefIndex, + tcx: TyCtxt) -> ast::NodeId { + let closure_id = tcx.hir.def_index_to_node_id(closure_id); match tcx.hir.get(closure_id) { hir_map::NodeExpr(expr) => match expr.node { hir::ExprClosure(.., body_id, _, _) => { @@ -597,8 +598,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let need_note = match lp.ty.sty { ty::TypeVariants::TyClosure(id, _) => { let node_id = self.tcx.hir.as_local_node_id(id).unwrap(); + let hir_id = self.tcx.hir.node_to_hir_id(node_id); if let Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) = - self.tables.closure_kinds.get(&node_id) + self.tables.closure_kinds().get(hir_id) { err.span_note(span, &format!( "closure cannot be invoked more than once because \ @@ -1044,7 +1046,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } else { "consider changing this closure to take self by mutable reference" }; - err.span_help(self.tcx.hir.span(id), help); + let node_id = self.tcx.hir.def_index_to_node_id(id); + err.span_help(self.tcx.hir.span(node_id), help); err } _ => { @@ -1100,8 +1103,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { }; match pat.node { - hir::PatKind::Binding(..) => - *self.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode"), + hir::PatKind::Binding(..) => { + *self.tables + .pat_binding_modes() + .get(pat.hir_id) + .expect("missing binding mode") + } _ => bug!("local is not a binding: {:?}", pat) } } @@ -1248,7 +1255,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { _ => bug!() }; if kind == ty::ClosureKind::Fn { - db.span_help(self.tcx.hir.span(upvar_id.closure_expr_id), + let closure_node_id = + self.tcx.hir.def_index_to_node_id(upvar_id.closure_expr_id); + db.span_help(self.tcx.hir.span(closure_node_id), "consider changing this closure to take \ self by mutable reference"); } @@ -1281,7 +1290,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { loan_path: &LoanPath<'tcx>, out: &mut String) { match loan_path.kind { - LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) | + LpUpvar(ty::UpvarId { var_id: id, closure_expr_id: _ }) => { + out.push_str(&self.tcx.local_var_name_str_def_index(id)); + } LpVar(id) => { out.push_str(&self.tcx.local_var_name_str(id)); } @@ -1419,8 +1430,11 @@ impl<'tcx> fmt::Debug for LoanPath<'tcx> { } LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => { - let s = ty::tls::with(|tcx| tcx.hir.node_to_string(var_id)); - write!(f, "$({} captured by id={})", s, closure_expr_id) + let s = ty::tls::with(|tcx| { + let var_node_id = tcx.hir.def_index_to_node_id(var_id); + tcx.hir.node_to_string(var_node_id) + }); + write!(f, "$({} captured by id={:?})", s, closure_expr_id) } LpDowncast(ref lp, variant_def_id) => { @@ -1451,7 +1465,10 @@ impl<'tcx> fmt::Display for LoanPath<'tcx> { } LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => { - let s = ty::tls::with(|tcx| tcx.hir.node_to_user_string(var_id)); + let s = ty::tls::with(|tcx| { + let var_node_id = tcx.hir.def_index_to_node_id(var_id); + tcx.hir.node_to_string(var_node_id) + }); write!(f, "$({} captured by closure)", s) } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index a3f8aae472ca..ea7deef47242 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -188,7 +188,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { // Then, if the match has no arms, check whether the scrutinee // is uninhabited. - let pat_ty = self.tables.node_id_to_type(scrut.id); + let pat_ty = self.tables.node_id_to_type(scrut.hir_id); let module = self.tcx.hir.get_module_parent(scrut.id); if inlined_arms.is_empty() { let scrutinee_is_uninhabited = if self.tcx.sess.features.borrow().never_type { @@ -217,7 +217,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { .flat_map(|arm| &arm.0) .map(|pat| vec![pat.0]) .collect(); - let scrut_ty = self.tables.node_id_to_type(scrut.id); + let scrut_ty = self.tables.node_id_to_type(scrut.hir_id); check_exhaustive(cx, scrut_ty, scrut.span, &matrix); }) } @@ -269,7 +269,11 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { fn check_for_bindings_named_the_same_as_variants(cx: &MatchVisitor, pat: &Pat) { pat.walk(|p| { if let PatKind::Binding(_, _, name, None) = p.node { - let bm = *cx.tables.pat_binding_modes.get(&p.id).expect("missing binding mode"); + let bm = *cx.tables + .pat_binding_modes() + .get(p.hir_id) + .expect("missing binding mode"); + if bm != ty::BindByValue(hir::MutImmutable) { // Nothing to check. return true; @@ -458,7 +462,11 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor, let mut by_ref_span = None; for pat in pats { pat.each_binding(|_, id, span, _path| { - let bm = *cx.tables.pat_binding_modes.get(&id).expect("missing binding mode"); + let hir_id = cx.tcx.hir.node_to_hir_id(id); + let bm = *cx.tables + .pat_binding_modes() + .get(hir_id) + .expect("missing binding mode"); if let ty::BindByReference(..) = bm { by_ref_span = Some(span); } @@ -491,10 +499,13 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor, for pat in pats { pat.walk(|p| { if let PatKind::Binding(_, _, _, ref sub) = p.node { - let bm = *cx.tables.pat_binding_modes.get(&p.id).expect("missing binding mode"); + let bm = *cx.tables + .pat_binding_modes() + .get(p.hir_id) + .expect("missing binding mode"); match bm { ty::BindByValue(..) => { - let pat_ty = cx.tables.node_id_to_type(p.id); + let pat_ty = cx.tables.node_id_to_type(p.hir_id); if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) { check_move(p, sub.as_ref().map(|p| &**p)); } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index eb45fd9c0e0a..20ca218571c7 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -106,7 +106,7 @@ impl<'a, 'tcx> ConstContext<'a, 'tcx> { } /// Evaluate a constant expression in a context where the expression isn't - /// guaranteed to be evaluatable. + /// guaranteed to be evaluable. pub fn eval(&self, e: &Expr) -> EvalResult<'tcx> { if self.tables.tainted_by_errors { signal!(e, TypeckError); @@ -275,8 +275,8 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, } } hir::ExprPath(ref qpath) => { - let substs = cx.tables.node_substs(e.id).subst(tcx, cx.substs); - match cx.tables.qpath_def(qpath, e.id) { + let substs = cx.tables.node_substs(e.hir_id).subst(tcx, cx.substs); + match cx.tables.qpath_def(qpath, e.hir_id) { Def::Const(def_id) | Def::AssociatedConst(def_id) => { match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) { diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index f37a112a596a..d151e817040a 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -303,7 +303,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } pub fn lower_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> { - let mut ty = self.tables.node_id_to_type(pat.id); + let mut ty = self.tables.node_id_to_type(pat.hir_id); let kind = match pat.node { PatKind::Wild => PatternKind::Wild, @@ -321,7 +321,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.id, pat.id, pat.span); + return self.lower_path(qpath, pat.hir_id, pat.id, pat.span); } PatKind::Ref(ref subpattern, _) | @@ -330,7 +330,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } PatKind::Slice(ref prefix, ref slice, ref suffix) => { - let ty = self.tables.node_id_to_type(pat.id); + let ty = self.tables.node_id_to_type(pat.hir_id); match ty.sty { ty::TyRef(_, mt) => PatternKind::Deref { @@ -355,7 +355,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } PatKind::Tuple(ref subpatterns, ddpos) => { - let ty = self.tables.node_id_to_type(pat.id); + let ty = self.tables.node_id_to_type(pat.hir_id); match ty.sty { ty::TyTuple(ref tys, _) => { let subpatterns = @@ -376,13 +376,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatKind::Binding(_, def_id, ref ident, ref sub) => { let id = self.tcx.hir.as_local_node_id(def_id).unwrap(); - let var_ty = self.tables.node_id_to_type(pat.id); + let var_ty = self.tables.node_id_to_type(pat.hir_id); let region = match var_ty.sty { ty::TyRef(r, _) => Some(r), _ => None, }; - let bm = *self.tables.pat_binding_modes.get(&pat.id) - .expect("missing binding mode"); + let bm = *self.tables.pat_binding_modes().get(pat.hir_id) + .expect("missing binding mode"); let (mutability, mode) = match bm { ty::BindByValue(hir::MutMutable) => (Mutability::Mut, BindingMode::ByValue), @@ -417,7 +417,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } PatKind::TupleStruct(ref qpath, ref subpatterns, ddpos) => { - let def = self.tables.qpath_def(qpath, pat.id); + let def = self.tables.qpath_def(qpath, pat.hir_id); let adt_def = match ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"), @@ -436,7 +436,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } PatKind::Struct(ref qpath, ref fields, _) => { - let def = self.tables.qpath_def(qpath, pat.id); + let def = self.tables.qpath_def(qpath, pat.hir_id); let adt_def = match ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => { @@ -590,7 +590,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { fn lower_path(&mut self, qpath: &hir::QPath, - id: ast::NodeId, + id: hir::HirId, pat_id: ast::NodeId, span: Span) -> Pattern<'tcx> { @@ -695,8 +695,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { hir::ExprPath(ref qpath) => qpath, _ => bug!() }; - let ty = self.tables.node_id_to_type(callee.id); - let def = self.tables.qpath_def(qpath, callee.id); + let ty = self.tables.node_id_to_type(callee.hir_id); + let def = self.tables.qpath_def(qpath, callee.hir_id); match def { Def::Fn(..) | Def::Method(..) => self.lower_lit(expr), _ => { @@ -712,7 +712,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } hir::ExprStruct(ref qpath, ref fields, None) => { - let def = self.tables.qpath_def(qpath, expr.id); + let def = self.tables.qpath_def(qpath, expr.hir_id); let adt_def = match pat_ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => { @@ -755,7 +755,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } hir::ExprPath(ref qpath) => { - return self.lower_path(qpath, expr.id, pat_id, span); + return self.lower_path(qpath, expr.hir_id, pat_id, span); } _ => self.lower_lit(expr) diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 42f56aa07827..1d0e88ee3285 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -196,6 +196,16 @@ impl IndexVec { } } +impl IndexVec { + #[inline] + pub fn binary_search(&self, value: &T) -> Result { + match self.raw.binary_search(value) { + Ok(i) => Ok(Idx::new(i)), + Err(i) => Err(Idx::new(i)), + } + } +} + impl Index for IndexVec { type Output = T; diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 866261b4f63d..33c4a041cff8 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -193,7 +193,7 @@ impl Hasher for StableHasher { /// Something that implements `HashStable` can be hashed in a way that is -/// stable across multiple compiliation sessions. +/// stable across multiple compilation sessions. pub trait HashStable { fn hash_stable(&self, hcx: &mut CTX, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 7efa2846cd6f..6c673b2ea236 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![cfg_attr(not(feature="llvm"), allow(dead_code))] + use rustc::hir::{self, map as hir_map}; use rustc::hir::lowering::lower_crate; use rustc::ich::Fingerprint; @@ -19,8 +21,6 @@ use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; use rustc::lint; use rustc::middle::{self, stability, reachable}; -#[cfg(feature="llvm")] -use rustc::middle::dependency_format; use rustc::middle::privacy::AccessLevels; use rustc::mir::transform::{MIR_CONST, MIR_VALIDATED, MIR_OPTIMIZED, Passes}; use rustc::ty::{self, TyCtxt, Resolutions, GlobalArenas}; @@ -33,9 +33,7 @@ use rustc_incremental::{self, IncrementalHashesMap}; use rustc_resolve::{MakeGlobMap, Resolver}; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; -#[cfg(feature="llvm")] -use rustc_trans::back::{link, write}; -#[cfg(feature="llvm")] +use rustc_trans::back::write; use rustc_trans as trans; use rustc_typeck as typeck; use rustc_privacy; @@ -73,11 +71,7 @@ pub fn compile_input(sess: &Session, output: &Option, addl_plugins: Option>, control: &CompileController) -> CompileResult { - #[cfg(feature="llvm")] use rustc_trans::back::write::OngoingCrateTranslation; - #[cfg(not(feature="llvm"))] - type OngoingCrateTranslation = (); - macro_rules! controller_entry_point { ($point: ident, $tsess: expr, $make_state: expr, $phase_result: expr) => {{ let state = &mut $make_state; @@ -94,6 +88,23 @@ pub fn compile_input(sess: &Session, }} } + if cfg!(not(feature="llvm")) { + use rustc::session::config::CrateType; + if !sess.opts.debugging_opts.no_trans && sess.opts.output_types.should_trans() { + sess.err("LLVM is not supported by this rustc. Please use -Z no-trans to compile") + } + + if sess.opts.crate_types.iter().all(|&t|{ + t != CrateType::CrateTypeRlib && t != CrateType::CrateTypeExecutable + }) && !sess.opts.crate_types.is_empty() { + sess.err( + "LLVM is not supported by this rustc, so non rlib libraries are not supported" + ); + } + + sess.abort_if_errors(); + } + // We need nested scopes here, because the intermediate results can keep // large chunks of memory alive and we want to free them as soon as // possible to keep the peak memory usage low @@ -217,7 +228,6 @@ pub fn compile_input(sess: &Session, tcx.print_debug_stats(); } - #[cfg(feature="llvm")] let trans = phase_4_translate_to_llvm(tcx, analysis, incremental_hashes_map, &outputs); @@ -233,24 +243,13 @@ pub fn compile_input(sess: &Session, } } - #[cfg(not(feature="llvm"))] - { - let _ = incremental_hashes_map; - sess.err(&format!("LLVM is not supported by this rustc")); - sess.abort_if_errors(); - unreachable!(); - } - - #[cfg(feature="llvm")] Ok((outputs, trans)) })?? }; - #[cfg(not(feature="llvm"))] - { - let _ = outputs; - let _ = trans; - unreachable!(); + if cfg!(not(feature="llvm")) { + let (_, _) = (outputs, trans); + sess.fatal("LLVM is not supported by this rustc"); } #[cfg(feature="llvm")] @@ -315,7 +314,7 @@ pub fn source_name(input: &Input) -> String { /// This is a somewhat higher level controller than a Session - the Session /// controls what happens in each phase, whereas the CompileController controls /// whether a phase is run at all and whether other code (from outside the -/// the compiler) is run between phases. +/// compiler) is run between phases. /// /// Note that if compilation is set to stop and a callback is provided for a /// given entry point, the callback is called before compilation is stopped. @@ -393,7 +392,6 @@ pub struct CompileState<'a, 'tcx: 'a> { pub resolutions: Option<&'a Resolutions>, pub analysis: Option<&'a ty::CrateAnalysis>, pub tcx: Option>, - #[cfg(feature="llvm")] pub trans: Option<&'a trans::CrateTranslation>, } @@ -420,7 +418,6 @@ impl<'a, 'tcx> CompileState<'a, 'tcx> { resolutions: None, analysis: None, tcx: None, - #[cfg(feature="llvm")] trans: None, } } @@ -509,7 +506,6 @@ impl<'a, 'tcx> CompileState<'a, 'tcx> { } } - #[cfg(feature="llvm")] fn state_after_llvm(input: &'a Input, session: &'tcx Session, out_dir: &'a Option, @@ -523,7 +519,6 @@ impl<'a, 'tcx> CompileState<'a, 'tcx> { } } - #[cfg(feature="llvm")] fn state_when_compilation_done(input: &'a Input, session: &'tcx Session, out_dir: &'a Option, @@ -942,7 +937,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, mir::provide(&mut local_providers); reachable::provide(&mut local_providers); rustc_privacy::provide(&mut local_providers); - #[cfg(feature="llvm")] trans::provide(&mut local_providers); typeck::provide(&mut local_providers); ty::provide(&mut local_providers); @@ -955,7 +949,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, let mut extern_providers = ty::maps::Providers::default(); cstore::provide(&mut extern_providers); - #[cfg(feature="llvm")] trans::provide(&mut extern_providers); ty::provide_extern(&mut extern_providers); traits::provide_extern(&mut extern_providers); @@ -974,7 +967,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir); passes.push_pass(MIR_CONST, mir::transform::rustc_peek::SanityCheck); - // We compute "constant qualifications" betwen MIR_CONST and MIR_VALIDATED. + // We compute "constant qualifications" between MIR_CONST and MIR_VALIDATED. // What we need to run borrowck etc. passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants); @@ -1104,7 +1097,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, /// Run the translation phase to LLVM, after which the AST and analysis can /// be discarded. -#[cfg(feature="llvm")] pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, analysis: ty::CrateAnalysis, incremental_hashes_map: IncrementalHashesMap, @@ -1114,7 +1106,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, time(time_passes, "resolving dependency formats", - || dependency_format::calculate(tcx)); + || ::rustc::middle::dependency_format::calculate(tcx)); let translation = time(time_passes, @@ -1149,9 +1141,9 @@ pub fn phase_5_run_llvm_passes(sess: &Session, pub fn phase_6_link_output(sess: &Session, trans: &trans::CrateTranslation, outputs: &OutputFilenames) { - time(sess.time_passes(), - "linking", - || link::link_binary(sess, trans, outputs, &trans.crate_name.as_str())); + time(sess.time_passes(), "linking", || { + ::rustc_trans::back::link::link_binary(sess, trans, outputs, &trans.crate_name.as_str()) + }); } fn escape_dep_filename(filename: &str) -> String { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 91ba7ed1958e..f8cd2280cafe 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -28,15 +28,10 @@ #![feature(rustc_diagnostic_macros)] #![feature(set_stdio)] -#[cfg(not(feature="llvm"))] -extern crate ar; - extern crate arena; extern crate getopts; extern crate graphviz; extern crate env_logger; -#[cfg(not(feature="llvm"))] -extern crate owning_ref; extern crate libc; extern crate rustc; extern crate rustc_allocator; @@ -71,8 +66,6 @@ use pretty::{PpMode, UserIdentifiedItem}; use rustc_resolve as resolve; use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; -#[cfg(feature="llvm")] -use rustc_trans::back::write::{RELOC_MODEL_ARGS, CODE_GEN_MODEL_ARGS}; use rustc::dep_graph::DepGraph; use rustc::session::{self, config, Session, build_session, CompileResult}; use rustc::session::CompileIncomplete; @@ -81,13 +74,9 @@ use rustc::session::config::nightly_options; use rustc::session::{early_error, early_warn}; use rustc::lint::Lint; use rustc::lint; -#[cfg(not(feature="llvm"))] -use rustc::middle::cstore::MetadataLoader as MetadataLoaderTrait; use rustc_metadata::locator; use rustc_metadata::cstore::CStore; use rustc::util::common::{time, ErrorReported}; -#[cfg(not(feature="llvm"))] -use rustc_back::target::Target; use serialize::json::ToJson; @@ -100,8 +89,6 @@ use std::ffi::OsString; use std::io::{self, Read, Write}; use std::iter::repeat; use std::path::PathBuf; -#[cfg(not(feature="llvm"))] -use std::path::Path; use std::process::{self, Command, Stdio}; use std::rc::Rc; use std::str; @@ -114,15 +101,11 @@ use syntax::feature_gate::{GatedCfg, UnstableFeatures}; use syntax::parse::{self, PResult}; use syntax_pos::{DUMMY_SP, MultiSpan}; -#[cfg(not(feature="llvm"))] -use owning_ref::{OwningRef, ErasedBoxRef}; - #[cfg(test)] pub mod test; pub mod driver; pub mod pretty; -#[cfg(feature="llvm")] pub mod target_features; mod derive_registrar; @@ -169,48 +152,106 @@ pub fn run(run_compiler: F) -> isize } #[cfg(not(feature="llvm"))] -pub struct NoLLvmMetadataLoader; - -#[cfg(not(feature="llvm"))] -pub use NoLLvmMetadataLoader as MetadataLoader; +pub use no_llvm_metadata_loader::NoLLvmMetadataLoader as MetadataLoader; #[cfg(feature="llvm")] pub use rustc_trans::LlvmMetadataLoader as MetadataLoader; #[cfg(not(feature="llvm"))] -impl MetadataLoaderTrait for NoLLvmMetadataLoader { - fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result, String> { - use std::fs::File; - use std::io; - use self::ar::Archive; +mod no_llvm_metadata_loader { + extern crate ar; + extern crate owning_ref; - let file = File::open(filename).map_err(|e|format!("metadata file open err: {:?}", e))?; - let mut archive = Archive::new(file); + use rustc::middle::cstore::MetadataLoader as MetadataLoaderTrait; + use rustc_back::target::Target; + use std::io; + use std::fs::File; + use std::path::Path; - while let Some(entry_result) = archive.next_entry() { - let mut entry = entry_result.map_err(|e|format!("metadata section read err: {:?}", e))?; - if entry.header().identifier() == "rust.metadata.bin" { - let mut buf = Vec::new(); - io::copy(&mut entry, &mut buf).unwrap(); - let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); - return Ok(buf.map_owner_box().erase_owner()); + use self::ar::Archive; + use self::owning_ref::{OwningRef, ErasedBoxRef}; + + pub struct NoLLvmMetadataLoader; + + impl MetadataLoaderTrait for NoLLvmMetadataLoader { + fn get_rlib_metadata( + &self, + _: &Target, + filename: &Path + ) -> Result, String> { + let file = File::open(filename).map_err(|e| { + format!("metadata file open err: {:?}", e) + })?; + let mut archive = Archive::new(file); + + while let Some(entry_result) = archive.next_entry() { + let mut entry = entry_result.map_err(|e| { + format!("metadata section read err: {:?}", e) + })?; + if entry.header().identifier() == "rust.metadata.bin" { + let mut buf = Vec::new(); + io::copy(&mut entry, &mut buf).unwrap(); + let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); + return Ok(buf.map_owner_box().erase_owner()); + } } + + Err("Couldnt find metadata section".to_string()) } - Err("Couldnt find metadata section".to_string()) + fn get_dylib_metadata(&self, + _target: &Target, + _filename: &Path) + -> Result, String> { + panic!("Dylib metadata loading not supported without LLVM") + } + } +} + +#[cfg(not(feature="llvm"))] +mod rustc_trans { + use syntax_pos::symbol::Symbol; + use rustc::session::Session; + use rustc::session::config::{PrintRequest, OutputFilenames}; + use rustc::ty::{TyCtxt, CrateAnalysis}; + use rustc::ty::maps::Providers; + use rustc_incremental::IncrementalHashesMap; + + use self::back::write::OngoingCrateTranslation; + + pub fn init(_sess: &Session) {} + pub fn enable_llvm_debug() {} + pub fn provide(_providers: &mut Providers) {} + pub fn print_version() {} + pub fn print_passes() {} + pub fn print(_req: PrintRequest, _sess: &Session) {} + pub fn target_features(_sess: &Session) -> Vec { vec![] } + + pub fn trans_crate<'a, 'tcx>( + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _analysis: CrateAnalysis, + _incr_hashes_map: IncrementalHashesMap, + _output_filenames: &OutputFilenames + ) -> OngoingCrateTranslation { + OngoingCrateTranslation(()) } - fn get_dylib_metadata(&self, - _target: &Target, - _filename: &Path) - -> Result, String> { - panic!("Dylib metadata loading not supported without LLVM") + pub struct CrateTranslation(()); + + pub mod back { + pub mod write { + pub struct OngoingCrateTranslation(pub (in ::rustc_trans) ()); + + pub const RELOC_MODEL_ARGS: [(&'static str, ()); 0] = []; + pub const CODE_GEN_MODEL_ARGS: [(&'static str, ()); 0] = []; + } } + + __build_diagnostic_array! { librustc_trans, DIAGNOSTICS } } // Parse args and run the compiler. This is the primary entry point for rustc. // See comments on CompilerCalls below for details about the callbacks argument. // The FileLoader provides a way to load files from sources other than the file system. -#[cfg_attr(not(feature="llvm"), allow(unused_mut))] pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>, file_loader: Option>, @@ -232,7 +273,6 @@ pub fn run_compiler<'a>(args: &[String], let (sopts, cfg) = config::build_session_options_and_crate_config(&matches); if sopts.debugging_opts.debug_llvm { - #[cfg(feature="llvm")] rustc_trans::enable_llvm_debug(); } @@ -262,12 +302,10 @@ pub fn run_compiler<'a>(args: &[String], let mut sess = session::build_session_with_codemap( sopts, &dep_graph, input_file_path, descriptions, cstore.clone(), codemap, emitter_dest, ); - #[cfg(feature="llvm")] rustc_trans::init(&sess); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg); - #[cfg(feature="llvm")] target_features::add_configuration(&mut cfg, &sess); sess.parse_sess.config = cfg; @@ -520,7 +558,6 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { Compilation::Continue } - #[cfg_attr(not(feature="llvm"), allow(unused_mut))] fn no_input(&mut self, matches: &getopts::Matches, sopts: &config::Options, @@ -544,11 +581,9 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { None, descriptions.clone(), cstore.clone()); - #[cfg(feature="llvm")] rustc_trans::init(&sess); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg.clone()); - #[cfg(feature="llvm")] target_features::add_configuration(&mut cfg, &sess); sess.parse_sess.config = cfg; let should_stop = @@ -802,25 +837,20 @@ impl RustcDefaultCalls { } PrintRequest::RelocationModels => { println!("Available relocation models:"); - #[cfg(feature="llvm")] - for &(name, _) in RELOC_MODEL_ARGS.iter() { + for &(name, _) in rustc_trans::back::write::RELOC_MODEL_ARGS.iter() { println!(" {}", name); } println!(""); } PrintRequest::CodeModels => { println!("Available code models:"); - #[cfg(feature="llvm")] - for &(name, _) in CODE_GEN_MODEL_ARGS.iter(){ + for &(name, _) in rustc_trans::back::write::CODE_GEN_MODEL_ARGS.iter(){ println!(" {}", name); } println!(""); } PrintRequest::TargetCPUs | PrintRequest::TargetFeatures => { - #[cfg(feature="llvm")] rustc_trans::print(*req, sess); - #[cfg(not(feature="llvm"))] - panic!("LLVM not supported by this rustc") } } } @@ -859,7 +889,6 @@ pub fn version(binary: &str, matches: &getopts::Matches) { println!("commit-date: {}", unw(commit_date_str())); println!("host: {}", config::host_triple()); println!("release: {}", unw(release_str())); - #[cfg(feature="llvm")] rustc_trans::print_version(); } } @@ -1157,7 +1186,6 @@ pub fn handle_options(args: &[String]) -> Option { } if cg_flags.contains(&"passes=list".to_string()) { - #[cfg(feature="llvm")] rustc_trans::print_passes(); return None; } @@ -1285,7 +1313,6 @@ pub fn diagnostics_registry() -> errors::registry::Registry { all_errors.extend_from_slice(&rustc_borrowck::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); - #[cfg(feature="llvm")] all_errors.extend_from_slice(&rustc_trans::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_const_eval::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 269363fdd2f9..84d3ab65b1c1 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -44,6 +44,7 @@ use std::io::{self, Write}; use std::option; use std::path::Path; use std::str::FromStr; +use std::mem; use rustc::hir::map as hir_map; use rustc::hir::map::blocks; @@ -232,7 +233,7 @@ impl PpSourceMode { arenas, id, |tcx, _, _, _| { - let empty_tables = ty::TypeckTables::empty(); + let empty_tables = ty::TypeckTables::empty(None); let annotation = TypedAnnotation { tcx: tcx, tables: Cell::new(&empty_tables) @@ -618,52 +619,53 @@ impl UserIdentifiedItem { } } -struct ReplaceBodyWithLoop { +// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere. +pub struct ReplaceBodyWithLoop { within_static_or_const: bool, } impl ReplaceBodyWithLoop { - fn new() -> ReplaceBodyWithLoop { + pub fn new() -> ReplaceBodyWithLoop { ReplaceBodyWithLoop { within_static_or_const: false } } + + fn run R>(&mut self, is_const: bool, action: F) -> R { + let old_const = mem::replace(&mut self.within_static_or_const, is_const); + let ret = action(self); + self.within_static_or_const = old_const; + ret + } } impl fold::Folder for ReplaceBodyWithLoop { fn fold_item_kind(&mut self, i: ast::ItemKind) -> ast::ItemKind { - match i { - ast::ItemKind::Static(..) | - ast::ItemKind::Const(..) => { - self.within_static_or_const = true; - let ret = fold::noop_fold_item_kind(i, self); - self.within_static_or_const = false; - return ret; - } - _ => fold::noop_fold_item_kind(i, self), - } + let is_const = match i { + ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, + ast::ItemKind::Fn(_, _, ref constness, _, _, _) => + constness.node == ast::Constness::Const, + _ => false, + }; + self.run(is_const, |s| fold::noop_fold_item_kind(i, s)) } fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector { - match i.node { - ast::TraitItemKind::Const(..) => { - self.within_static_or_const = true; - let ret = fold::noop_fold_trait_item(i, self); - self.within_static_or_const = false; - return ret; - } - _ => fold::noop_fold_trait_item(i, self), - } + let is_const = match i.node { + ast::TraitItemKind::Const(..) => true, + ast::TraitItemKind::Method(ast::MethodSig { ref constness, .. }, _) => + constness.node == ast::Constness::Const, + _ => false, + }; + self.run(is_const, |s| fold::noop_fold_trait_item(i, s)) } fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector { - match i.node { - ast::ImplItemKind::Const(..) => { - self.within_static_or_const = true; - let ret = fold::noop_fold_impl_item(i, self); - self.within_static_or_const = false; - return ret; - } - _ => fold::noop_fold_impl_item(i, self), - } + let is_const = match i.node { + ast::ImplItemKind::Const(..) => true, + ast::ImplItemKind::Method(ast::MethodSig { ref constness, .. }, _) => + constness.node == ast::Constness::Const, + _ => false, + }; + self.run(is_const, |s| fold::noop_fold_impl_item(i, s)) } fn fold_block(&mut self, b: P) -> P { diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 7f65a8b97cd6..b187cdaa480e 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -14,7 +14,6 @@ use driver; use rustc::dep_graph::DepGraph; use rustc_lint; use rustc_resolve::MakeGlobMap; -#[cfg(feature="llvm")] use rustc_trans; use rustc::middle::lang_items; use rustc::middle::free_region::FreeRegionMap; @@ -114,7 +113,6 @@ fn test_env(source_string: &str, diagnostic_handler, Rc::new(CodeMap::new(FilePathMapping::empty())), cstore.clone()); - #[cfg(feature="llvm")] rustc_trans::init(&sess); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let input = config::Input::Str { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 88432e642903..3bfe2897de17 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -139,7 +139,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxPointers { } fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { - let ty = cx.tables.node_id_to_type(e.id); + let ty = cx.tables.node_id_to_type(e.hir_id); self.check_heap_type(cx, e.span, ty); } } @@ -195,12 +195,23 @@ impl LintPass for UnsafeCode { } } +impl UnsafeCode { + fn report_unsafe(&self, cx: &LateContext, span: Span, desc: &'static str) { + // This comes from a macro that has #[allow_internal_unsafe]. + if span.allows_unsafe() { + return; + } + + cx.span_lint(UNSAFE_CODE, span, desc); + } +} + impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { if let hir::ExprBlock(ref blk) = e.node { // Don't warn about generated blocks, that'll just pollute the output. if blk.rules == hir::UnsafeBlock(hir::UserProvided) { - cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block"); + self.report_unsafe(cx, blk.span, "usage of an `unsafe` block"); } } } @@ -208,11 +219,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { match it.node { hir::ItemTrait(hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait") + self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait") } hir::ItemImpl(hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait") + self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait") } _ => return, @@ -228,12 +239,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { _: ast::NodeId) { match fk { FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function") + self.report_unsafe(cx, span, "declaration of an `unsafe` function") } FnKind::Method(_, sig, ..) => { if sig.unsafety == hir::Unsafety::Unsafe { - cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method") + self.report_unsafe(cx, span, "implementation of an `unsafe` method") } } @@ -244,9 +255,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) { if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = item.node { if sig.unsafety == hir::Unsafety::Unsafe { - cx.span_lint(UNSAFE_CODE, - item.span, - "declaration of an `unsafe` method") + self.report_unsafe(cx, item.span, "declaration of an `unsafe` method") } } } @@ -896,7 +905,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { match cx.tcx.hir.get(id) { hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => { let def = if let hir::ExprPath(ref qpath) = callee.node { - cx.tables.qpath_def(qpath, callee.id) + cx.tables.qpath_def(qpath, callee.hir_id) } else { return false; }; @@ -934,8 +943,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { // Check for method calls and overloaded operators. if cx.tables.is_method_call(expr) { - let def_id = cx.tables.type_dependent_defs[&id].def_id(); - let substs = cx.tables.node_substs(id); + let hir_id = cx.tcx.hir.definitions().node_to_hir_id(id); + let def_id = cx.tables.type_dependent_defs()[hir_id].def_id(); + let substs = cx.tables.node_substs(hir_id); if method_call_refers_to_method(cx, method, def_id, substs, id) { return true; } @@ -945,13 +955,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { match expr.node { hir::ExprCall(ref callee, _) => { let def = if let hir::ExprPath(ref qpath) = callee.node { - cx.tables.qpath_def(qpath, callee.id) + cx.tables.qpath_def(qpath, callee.hir_id) } else { return false; }; match def { Def::Method(def_id) => { - let substs = cx.tables.node_substs(callee.id); + let substs = cx.tables.node_substs(callee.hir_id); method_call_refers_to_method(cx, method, def_id, substs, id) } _ => false, @@ -1179,7 +1189,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes { expr: &hir::Expr) -> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> { let def = if let hir::ExprPath(ref qpath) = expr.node { - cx.tables.qpath_def(qpath, expr.id) + cx.tables.qpath_def(qpath, expr.hir_id) } else { return None; }; @@ -1187,7 +1197,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes { if !def_id_is_transmute(cx, did) { return None; } - let sig = cx.tables.node_id_to_type(expr.id).fn_sig(cx.tcx); + let sig = cx.tables.node_id_to_type(expr.hir_id).fn_sig(cx.tcx); let from = sig.inputs().skip_binder()[0]; let to = *sig.output().skip_binder(); return Some((&from.sty, &to.sty)); diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 31aa8c0316aa..daf7d743e123 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -92,7 +92,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } if binop.node.is_shift() { - let opt_ty_bits = match cx.tables.node_id_to_type(l.id).sty { + let opt_ty_bits = match cx.tables.node_id_to_type(l.hir_id).sty { ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)), ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)), _ => None, @@ -135,7 +135,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } } hir::ExprLit(ref lit) => { - match cx.tables.node_id_to_type(e.id).sty { + match cx.tables.node_id_to_type(e.hir_id).sty { ty::TyInt(t) => { match lit.node { ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | @@ -285,7 +285,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { // Normalize the binop so that the literal is always on the RHS in // the comparison let norm_binop = if swap { rev_binop(binop) } else { binop }; - match cx.tables.node_id_to_type(expr.id).sty { + match cx.tables.node_id_to_type(expr.hir_id).sty { ty::TyInt(int_ty) => { let (min, max) = int_ty_range(int_ty); let lit_val: i128 = match lit.node { diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index ba17df4cdca4..195bd2acce0f 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -45,7 +45,8 @@ impl UnusedMut { let mut mutables = FxHashMap(); for p in pats { p.each_binding(|_, id, span, path1| { - let bm = match cx.tables.pat_binding_modes.get(&id) { + let hir_id = cx.tcx.hir.node_to_hir_id(id); + let bm = match cx.tables.pat_binding_modes().get(hir_id) { Some(&bm) => bm, None => span_bug!(span, "missing binding mode"), }; @@ -146,7 +147,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { let t = cx.tables.expr_ty(&expr); let ty_warned = match t.sty { - ty::TyAdt(def, _) => check_must_use(cx, def.did, s.span, ""), + ty::TyTuple(ref tys, _) if tys.is_empty() => return, + ty::TyNever => return, + ty::TyAdt(def, _) => { + if def.variants.is_empty() { + return; + } else { + check_must_use(cx, def.did, s.span, "") + } + }, _ => false, }; @@ -154,12 +163,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { let maybe_def = match expr.node { hir::ExprCall(ref callee, _) => { match callee.node { - hir::ExprPath(ref qpath) => Some(cx.tables.qpath_def(qpath, callee.id)), + hir::ExprPath(ref qpath) => Some(cx.tables.qpath_def(qpath, callee.hir_id)), _ => None } }, hir::ExprMethodCall(..) => { - cx.tables.type_dependent_defs.get(&expr.id).cloned() + cx.tables.type_dependent_defs().get(expr.hir_id).cloned() }, _ => { None } }; diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 96d66ac9986f..cb3ad6743a7d 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -90,7 +90,8 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t } else if let MirSource::Fn(id) = src { // fetch the fully liberated fn signature (that is, all bound // types/lifetimes replaced) - let fn_sig = cx.tables().liberated_fn_sigs[&id].clone(); + let fn_hir_id = tcx.hir.node_to_hir_id(id); + let fn_sig = cx.tables().liberated_fn_sigs()[fn_hir_id].clone(); let ty = tcx.type_of(tcx.hir.local_def_id(id)); let mut abi = fn_sig.abi; @@ -215,7 +216,8 @@ pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, closure_expr_id: ast::NodeId, body_id: hir::BodyId) -> Ty<'tcx> { - let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_id); + let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id); + let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id); let closure_def_id = tcx.hir.local_def_id(closure_expr_id); let region = ty::ReFree(ty::FreeRegion { @@ -382,10 +384,12 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, // Gather the upvars of a closure, if any. let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| { freevars.iter().map(|fv| { - let var_id = tcx.hir.as_local_node_id(fv.def.def_id()).unwrap(); + let var_def_id = fv.def.def_id(); + let var_node_id = tcx.hir.as_local_node_id(var_def_id).unwrap(); + let closure_expr_id = tcx.hir.local_def_id(fn_id).index; let capture = hir.tables().upvar_capture(ty::UpvarId { - var_id: var_id, - closure_expr_id: fn_id + var_id: var_def_id.index, + closure_expr_id, }); let by_ref = match capture { ty::UpvarCapture::ByValue => false, @@ -395,7 +399,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, debug_name: keywords::Invalid.name(), by_ref: by_ref }; - if let Some(hir::map::NodeLocal(pat)) = tcx.hir.find(var_id) { + if let Some(hir::map::NodeLocal(pat)) = tcx.hir.find(var_node_id) { if let hir::PatKind::Binding(_, _, ref ident, _) = pat.node { decl.debug_name = ident.node; } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index f530cac9397d..4ef4733c1c78 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -113,7 +113,7 @@ pub struct Scope<'tcx> { /// for unwinding, for several reasons: /// * clang doesn't emit llvm.lifetime.end for C++ unwinding /// * LLVM's memory dependency analysis can't handle it atm - /// * pollutting the cleanup MIR with StorageDead creates + /// * polluting the cleanup MIR with StorageDead creates /// landing pads even though there's no actual destructors /// * freeing up stack space has no effect during unwinding needs_cleanup: bool, diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index d68e64abe4f2..41019799e41d 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -213,7 +213,7 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'tcx> { /// you if an l-value *might* be uninitialized at a given point in the /// control flow. But `MovingOutStatements` also includes the added /// data of *which* particular statement causing the deinitialization -/// that the borrow checker's error meessage may need to report. +/// that the borrow checker's error message may need to report. #[allow(dead_code)] pub struct MovingOutStatements<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 6392bde9eaeb..eeb9d14807d9 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -333,11 +333,11 @@ pub trait BitDenotation { /// basic block) according to the effects of evaluating statement. /// /// This is used, in particular, for building up the - /// "transfer-function" represnting the overall-effect of the + /// "transfer-function" representing the overall-effect of the /// block, represented via GEN and KILL sets. /// /// The statement is identified as `bb_data[idx_stmt]`, where - /// `bb_data` is the sequence of statements identifed by `bb` in + /// `bb_data` is the sequence of statements identified by `bb` in /// the MIR. fn statement_effect(&self, sets: &mut BlockSets, @@ -349,7 +349,7 @@ pub trait BitDenotation { /// the terminator. /// /// This is used, in particular, for building up the - /// "transfer-function" represnting the overall-effect of the + /// "transfer-function" representing the overall-effect of the /// block, represented via GEN and KILL sets. /// /// The effects applied here cannot depend on which branch the diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 6530b356e33f..34170a6609c4 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -442,4 +442,5 @@ static A : &'static u32 = &S.a; // ok! register_diagnostics! { E0526, // shuffle indices are not constant + E0625, // thread-local statics cannot be accessed at compile-time } diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index c91326f6d874..fa54925c6e75 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -95,7 +95,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, pub fn to_expr_ref<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, block: &'tcx hir::Block) -> ExprRef<'tcx> { - let block_ty = cx.tables().node_id_to_type(block.id); + let block_ty = cx.tables().node_id_to_type(block.hir_id); let temp_lifetime = cx.region_maps.temporary_scope(block.id); let expr = Expr { ty: block_ty, diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 231e2fb68e65..c4eb0008df80 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -217,7 +217,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, None }; if let Some((adt_def, index)) = adt_data { - let substs = cx.tables().node_substs(fun.id); + let substs = cx.tables().node_substs(fun.hir_id); let field_refs = args.iter() .enumerate() .map(|(idx, e)| { @@ -236,7 +236,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } else { ExprKind::Call { - ty: cx.tables().node_id_to_type(fun.id), + ty: cx.tables().node_id_to_type(fun.hir_id), fun: fun.to_ref(), args: args.to_ref(), } @@ -389,7 +389,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, base: base.as_ref().map(|base| { FruInfo { base: base.to_ref(), - field_types: cx.tables().fru_field_types[&expr.id].clone(), + field_types: cx.tables() + .fru_field_types()[expr.hir_id] + .clone(), } }), } @@ -452,7 +454,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprPath(ref qpath) => { - let def = cx.tables().qpath_def(qpath, expr.id); + let def = cx.tables().qpath_def(qpath, expr.hir_id); convert_path_expr(cx, expr, def) } @@ -550,7 +552,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprCast(ref source, _) => { // Check to see if this cast is a "coercion cast", where the cast is actually done // using a coercion (or is a no-op). - if let Some(&TyCastKind::CoercionCast) = cx.tables().cast_kinds.get(&source.id) { + if let Some(&TyCastKind::CoercionCast) = cx.tables() + .cast_kinds() + .get(source.hir_id) { // Convert the lexpr to a vexpr. ExprKind::Use { source: source.to_ref() } } else { @@ -583,8 +587,8 @@ fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, -> Expr<'tcx> { let temp_lifetime = cx.region_maps.temporary_scope(expr.id); let (def_id, substs) = custom_callee.unwrap_or_else(|| { - (cx.tables().type_dependent_defs[&expr.id].def_id(), - cx.tables().node_substs(expr.id)) + (cx.tables().type_dependent_defs()[expr.hir_id].def_id(), + cx.tables().node_substs(expr.hir_id)) }); Expr { temp_lifetime: temp_lifetime, @@ -622,7 +626,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr, def: Def) -> ExprKind<'tcx> { - let substs = cx.tables().node_substs(expr.id); + let substs = cx.tables().node_substs(expr.hir_id); match def { // A regular function, constructor function or a constant. Def::Fn(def_id) | @@ -644,7 +648,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, Def::StructCtor(def_id, CtorKind::Const) | Def::VariantCtor(def_id, CtorKind::Const) => { - match cx.tables().node_id_to_type(expr.id).sty { + match cx.tables().node_id_to_type(expr.hir_id).sty { // A unit struct/variant which is used as a value. // We return a completely different ExprKind here to account for this special case. ty::TyAdt(adt_def, substs) => { @@ -680,16 +684,18 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ExprKind::VarRef { id: node_id } } - Def::Upvar(def_id, index, closure_expr_id) => { - let id_var = cx.tcx.hir.as_local_node_id(def_id).unwrap(); + Def::Upvar(var_def_id, index, closure_expr_id) => { + let id_var = cx.tcx.hir.as_local_node_id(var_def_id).unwrap(); debug!("convert_var(upvar({:?}, {:?}, {:?}))", id_var, index, closure_expr_id); - let var_ty = cx.tables().node_id_to_type(id_var); + let var_ty = cx.tables() + .node_id_to_type(cx.tcx.hir.node_to_hir_id(id_var)); // FIXME free regions in closures are not right - let closure_ty = cx.tables().node_id_to_type(closure_expr_id); + let closure_ty = cx.tables() + .node_id_to_type(cx.tcx.hir.node_to_hir_id(closure_expr_id)); // FIXME we're just hard-coding the idea that the // signature will be &self or &mut self and hence will @@ -771,8 +777,8 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, // ...but the upvar might be an `&T` or `&mut T` capture, at which // point we need an implicit deref let upvar_id = ty::UpvarId { - var_id: id_var, - closure_expr_id: closure_expr_id, + var_id: var_def_id.index, + closure_expr_id: closure_def_id.index, }; match cx.tables().upvar_capture(upvar_id) { ty::UpvarCapture::ByValue => field_kind, @@ -883,14 +889,16 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, freevar: &hir::Freevar, freevar_ty: Ty<'tcx>) -> ExprRef<'tcx> { - let id_var = cx.tcx.hir.as_local_node_id(freevar.def.def_id()).unwrap(); + let var_def_id = freevar.def.def_id(); + let var_node_id = cx.tcx.hir.as_local_node_id(var_def_id).unwrap(); let upvar_id = ty::UpvarId { - var_id: id_var, - closure_expr_id: closure_expr.id, + var_id: var_def_id.index, + closure_expr_id: cx.tcx.hir.local_def_id(closure_expr.id).index, }; let upvar_capture = cx.tables().upvar_capture(upvar_id); let temp_lifetime = cx.region_maps.temporary_scope(closure_expr.id); - let var_ty = cx.tables().node_id_to_type(id_var); + let var_ty = cx.tables() + .node_id_to_type(cx.tcx.hir.node_to_hir_id(var_node_id)); let captured_var = Expr { temp_lifetime: temp_lifetime, ty: var_ty, diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 073b32ae7fa3..9bd5df16a14e 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -80,7 +80,7 @@ pub enum StmtKind<'tcx> { /// The Hair trait implementor translates their expressions (`&'tcx H::Expr`) /// into instances of this `Expr` enum. This translation can be done -/// basically as lazilly or as eagerly as desired: every recursive +/// basically as lazily or as eagerly as desired: every recursive /// reference to an expression in this enum is an `ExprRef<'tcx>`, which /// may in turn be another instance of this enum (boxed), or else an /// untranslated `&'tcx H::Expr`. Note that instances of `Expr` are very diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index baf0522896c9..89de847231c8 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! This pass erases all early-bound regions from the types occuring in the MIR. +//! This pass erases all early-bound regions from the types occurring in the MIR. //! We want to do this once just before trans, so trans does not have to take //! care erasing regions all over the place. //! NOTE: We do NOT erase regions of statements that are relevant for diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 53e3a7ff5a23..24e9f4c6c345 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -486,8 +486,20 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } }, - Lvalue::Static(_) => { + Lvalue::Static(ref global) => { self.add(Qualif::STATIC); + + if self.mode != Mode::Fn { + for attr in &self.tcx.get_attrs(global.def_id)[..] { + if attr.check_name("thread_local") { + span_err!(self.tcx.sess, self.span, E0625, + "thread-local statics cannot be \ + accessed at compile-time"); + return; + } + } + } + if self.mode == Mode::Const || self.mode == Mode::ConstFn { span_err!(self.tcx.sess, self.span, E0013, "{}s cannot refer to statics, use \ @@ -1001,6 +1013,12 @@ impl MirPass for QualifyAndPromoteConstants { // Statics must be Sync. if mode == Mode::Static { + // `#[thread_local]` statics don't have to be `Sync`. + for attr in &tcx.get_attrs(def_id)[..] { + if attr.check_name("thread_local") { + return; + } + } let ty = mir.return_ty; tcx.infer_ctxt().enter(|infcx| { let param_env = ty::ParamEnv::empty(Reveal::UserFacing); diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 2643ed2a3c07..b22f8112d7a3 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -94,10 +94,25 @@ impl<'a> AstValidator<'a> { } } - /// matches '-' lit | lit (cf. parser::Parser::parse_pat_literal_maybe_minus) - fn check_expr_within_pat(&self, expr: &Expr) { + /// matches '-' lit | lit (cf. parser::Parser::parse_pat_literal_maybe_minus), + /// or path for ranges. + /// + /// FIXME: do we want to allow expr -> pattern conversion to create path expressions? + /// That means making this work: + /// + /// ```rust,ignore (FIXME) + /// struct S; + /// macro_rules! m { + /// ($a:expr) => { + /// let $a = S; + /// } + /// } + /// m!(S); + /// ``` + fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) { match expr.node { - ExprKind::Lit(..) | ExprKind::Path(..) => {} + ExprKind::Lit(..) => {} + ExprKind::Path(..) if allow_paths => {} ExprKind::Unary(UnOp::Neg, ref inner) if match inner.node { ExprKind::Lit(_) => true, _ => false } => {} _ => self.err_handler().span_err(expr.span, "arbitrary expressions aren't allowed \ @@ -332,11 +347,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_pat(&mut self, pat: &'a Pat) { match pat.node { PatKind::Lit(ref expr) => { - self.check_expr_within_pat(expr); + self.check_expr_within_pat(expr, false); } PatKind::Range(ref start, ref end, _) => { - self.check_expr_within_pat(start); - self.check_expr_within_pat(end); + self.check_expr_within_pat(start, true); + self.check_expr_within_pat(end, true); } _ => {} } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index d0322e27d66b..d53c4ef45d14 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -219,7 +219,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { let outer = self.promotable; self.promotable = true; - let node_ty = self.tables.node_id_to_type(ex.id); + let node_ty = self.tables.node_id_to_type(ex.hir_id); check_expr(self, ex, node_ty); check_adjustments(self, ex); @@ -297,7 +297,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node v.promotable = false; } hir::ExprUnary(op, ref inner) => { - match v.tables.node_id_to_type(inner.id).sty { + match v.tables.node_id_to_type(inner.hir_id).sty { ty::TyRawPtr(_) => { assert!(op == hir::UnDeref); @@ -307,7 +307,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } } hir::ExprBinary(op, ref lhs, _) => { - match v.tables.node_id_to_type(lhs.id).sty { + match v.tables.node_id_to_type(lhs.hir_id).sty { ty::TyRawPtr(_) => { assert!(op.node == hir::BiEq || op.node == hir::BiNe || op.node == hir::BiLe || op.node == hir::BiLt || @@ -320,7 +320,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } hir::ExprCast(ref from, _) => { debug!("Checking const cast(id={})", from.id); - match v.tables.cast_kinds.get(&from.id) { + match v.tables.cast_kinds().get(from.hir_id) { None => span_bug!(e.span, "no kind for cast"), Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => { v.promotable = false; @@ -329,7 +329,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } } hir::ExprPath(ref qpath) => { - let def = v.tables.qpath_def(qpath, e.id); + let def = v.tables.qpath_def(qpath, e.hir_id); match def { Def::VariantCtor(..) | Def::StructCtor(..) | Def::Fn(..) | Def::Method(..) => {} @@ -365,7 +365,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } // The callee is an arbitrary expression, it doesn't necessarily have a definition. let def = if let hir::ExprPath(ref qpath) = callee.node { - v.tables.qpath_def(qpath, callee.id) + v.tables.qpath_def(qpath, callee.hir_id) } else { Def::Err }; @@ -387,7 +387,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } } hir::ExprMethodCall(..) => { - let def_id = v.tables.type_dependent_defs[&e.id].def_id(); + let def_id = v.tables.type_dependent_defs()[e.hir_id].def_id(); match v.tcx.associated_item(def_id).container { ty::ImplContainer(_) => v.handle_const_fn_call(def_id, node_ty), ty::TraitContainer(_) => v.promotable = false @@ -474,7 +474,7 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.hir.krate().visit_all_item_likes(&mut CheckCrateVisitor { tcx: tcx, - tables: &ty::TypeckTables::empty(), + tables: &ty::TypeckTables::empty(None), in_fn: false, promotable: false, mut_rvalue_borrows: NodeSet(), diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs index 29fac5463e55..c6bc045f0de3 100644 --- a/src/librustc_passes/hir_stats.rs +++ b/src/librustc_passes/hir_stats.rs @@ -125,6 +125,11 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { self.visit_impl_item(nested_impl_item) } + fn visit_nested_body(&mut self, body_id: hir::BodyId) { + let nested_body = self.krate.unwrap().body(body_id); + self.visit_body(nested_body) + } + fn visit_item(&mut self, i: &'v hir::Item) { self.record("Item", Id::Node(i.id), i); hir_visit::walk_item(self, i) diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index 3027489d65be..aac21f2af0d9 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -102,9 +102,19 @@ impl<'a> Registry<'a> { panic!("user-defined macros may not be named `macro_rules`"); } self.syntax_exts.push((name, match extension { - NormalTT(ext, _, allow_internal_unstable) => { + NormalTT { + expander, + def_info: _, + allow_internal_unstable, + allow_internal_unsafe + } => { let nid = ast::CRATE_NODE_ID; - NormalTT(ext, Some((nid, self.krate_span)), allow_internal_unstable) + NormalTT { + expander, + def_info: Some((nid, self.krate_span)), + allow_internal_unstable, + allow_internal_unsafe + } } IdentTT(ext, _, allow_internal_unstable) => { IdentTT(ext, Some(self.krate_span), allow_internal_unstable) @@ -134,8 +144,12 @@ impl<'a> Registry<'a> { /// It builds for you a `NormalTT` that calls `expander`, /// and also takes care of interning the macro's name. pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) { - self.register_syntax_extension(Symbol::intern(name), - NormalTT(Box::new(expander), None, false)); + self.register_syntax_extension(Symbol::intern(name), NormalTT { + expander: Box::new(expander), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); } /// Register a compiler lint pass. diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 9fa5fea20d91..983f478e07d6 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -448,6 +448,7 @@ impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), ty::TyProjection(ref proj) => Some(proj.item_def_id), ty::TyFnDef(def_id, ..) | + ty::TyClosure(def_id, ..) | ty::TyAnon(def_id, _) => Some(def_id), _ => None }; @@ -473,6 +474,7 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, tables: &'a ty::TypeckTables<'tcx>, current_item: ast::NodeId, + empty_tables: &'a ty::TypeckTables<'tcx>, } impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { @@ -489,6 +491,22 @@ impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { } } +// Set the correct TypeckTables for the given `item_id` (or an empty table if +// there is no TypeckTables for the item). +fn update_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + item_id: ast::NodeId, + tables: &mut &'a ty::TypeckTables<'tcx>, + empty_tables: &'a ty::TypeckTables<'tcx>) + -> &'a ty::TypeckTables<'tcx> { + let def_id = tcx.hir.local_def_id(item_id); + + if tcx.has_typeck_tables(def_id) { + replace(tables, tcx.typeck_tables_of(def_id)) + } else { + replace(tables, empty_tables) + } +} + impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { /// We want to visit items in the context of their containing /// module and so forth, so supply a crate for doing a deep walk. @@ -505,14 +523,28 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item) { let orig_current_item = replace(&mut self.current_item, item.id); + let orig_tables = update_tables(self.tcx, item.id, &mut self.tables, self.empty_tables); intravisit::walk_item(self, item); self.current_item = orig_current_item; + self.tables = orig_tables; + } + + fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { + let orig_tables = update_tables(self.tcx, ti.id, &mut self.tables, self.empty_tables); + intravisit::walk_trait_item(self, ti); + self.tables = orig_tables; + } + + fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { + let orig_tables = update_tables(self.tcx, ii.id, &mut self.tables, self.empty_tables); + intravisit::walk_impl_item(self, ii); + self.tables = orig_tables; } fn visit_expr(&mut self, expr: &'tcx hir::Expr) { match expr.node { hir::ExprStruct(ref qpath, ref fields, ref base) => { - let def = self.tables.qpath_def(qpath, expr.id); + let def = self.tables.qpath_def(qpath, expr.hir_id); let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); let variant = adt.variant_of_def(def); if let Some(ref base) = *base { @@ -539,7 +571,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { fn visit_pat(&mut self, pat: &'tcx hir::Pat) { match pat.node { PatKind::Struct(ref qpath, ref fields, _) => { - let def = self.tables.qpath_def(qpath, pat.id); + let def = self.tables.qpath_def(qpath, pat.hir_id); let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap(); let variant = adt.variant_of_def(def); for field in fields { @@ -564,6 +596,7 @@ struct TypePrivacyVisitor<'a, 'tcx: 'a> { tables: &'a ty::TypeckTables<'tcx>, current_item: DefId, span: Span, + empty_tables: &'a ty::TypeckTables<'tcx>, } impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { @@ -607,7 +640,7 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { } // Take node ID of an expression or pattern and check its type for privacy. - fn check_expr_pat_type(&mut self, id: ast::NodeId, span: Span) -> bool { + fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool { self.span = span; if let Some(ty) = self.tables.node_id_to_type_opt(id) { if ty.visit_with(self) { @@ -617,7 +650,7 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { if self.tables.node_substs(id).visit_with(self) { return true; } - if let Some(adjustments) = self.tables.adjustments.get(&id) { + if let Some(adjustments) = self.tables.adjustments().get(id) { for adjustment in adjustments { if adjustment.target.visit_with(self) { return true; @@ -703,20 +736,20 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { // Check types of expressions fn visit_expr(&mut self, expr: &'tcx hir::Expr) { - if self.check_expr_pat_type(expr.id, expr.span) { + if self.check_expr_pat_type(expr.hir_id, expr.span) { // Do not check nested expressions if the error already happened. return; } match expr.node { hir::ExprAssign(.., ref rhs) | hir::ExprMatch(ref rhs, ..) => { // Do not report duplicate errors for `x = y` and `match x { ... }`. - if self.check_expr_pat_type(rhs.id, rhs.span) { + if self.check_expr_pat_type(rhs.hir_id, rhs.span) { return; } } hir::ExprMethodCall(_, span, _) => { // Method calls have to be checked specially. - let def_id = self.tables.type_dependent_defs[&expr.id].def_id(); + let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id(); self.span = span; if self.tcx.type_of(def_id).visit_with(self) { return; @@ -732,7 +765,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { // Inherent associated constants don't have self type in substs, // we have to check it additionally. if let hir::QPath::TypeRelative(..) = *qpath { - if let Some(def) = self.tables.type_dependent_defs.get(&id).cloned() { + let hir_id = self.tcx.hir.node_to_hir_id(id); + if let Some(def) = self.tables.type_dependent_defs().get(hir_id).cloned() { if let Some(assoc_item) = self.tcx.opt_associated_item(def.def_id()) { if let ty::ImplContainer(impl_def_id) = assoc_item.container { if self.tcx.type_of(impl_def_id).visit_with(self) { @@ -748,7 +782,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { // Check types of patterns fn visit_pat(&mut self, pattern: &'tcx hir::Pat) { - if self.check_expr_pat_type(pattern.id, pattern.span) { + if self.check_expr_pat_type(pattern.hir_id, pattern.span) { // Do not check nested patterns if the error already happened. return; } @@ -758,7 +792,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { fn visit_local(&mut self, local: &'tcx hir::Local) { if let Some(ref init) = local.init { - if self.check_expr_pat_type(init.id, init.span) { + if self.check_expr_pat_type(init.hir_id, init.span) { // Do not report duplicate errors for `let x = y`. return; } @@ -770,6 +804,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { // Check types in item interfaces fn visit_item(&mut self, item: &'tcx hir::Item) { let orig_current_item = self.current_item; + let orig_tables = update_tables(self.tcx, + item.id, + &mut self.tables, + self.empty_tables); match item.node { hir::ItemExternCrate(..) | hir::ItemMod(..) | @@ -829,8 +867,21 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { self.current_item = self.tcx.hir.local_def_id(item.id); intravisit::walk_item(self, item); + self.tables = orig_tables; self.current_item = orig_current_item; } + + fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { + let orig_tables = update_tables(self.tcx, ti.id, &mut self.tables, self.empty_tables); + intravisit::walk_trait_item(self, ti); + self.tables = orig_tables; + } + + fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { + let orig_tables = update_tables(self.tcx, ii.id, &mut self.tables, self.empty_tables); + intravisit::walk_impl_item(self, ii); + self.tables = orig_tables; + } } impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { @@ -1605,12 +1656,14 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, assert_eq!(krate, LOCAL_CRATE); let krate = tcx.hir.krate(); + let empty_tables = ty::TypeckTables::empty(None); // Check privacy of names not checked in previous compilation stages. let mut visitor = NamePrivacyVisitor { tcx: tcx, - tables: &ty::TypeckTables::empty(), + tables: &empty_tables, current_item: CRATE_NODE_ID, + empty_tables: &empty_tables, }; intravisit::walk_crate(&mut visitor, krate); @@ -1618,9 +1671,10 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // inferred types of expressions and patterns. let mut visitor = TypePrivacyVisitor { tcx: tcx, - tables: &ty::TypeckTables::empty(), + tables: &empty_tables, current_item: DefId::local(CRATE_DEF_INDEX), span: krate.span, + empty_tables: &empty_tables, }; intravisit::walk_crate(&mut visitor, krate); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index b4f9ba4e8f78..da170e7fe22c 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1621,7 +1621,7 @@ impl<'a> Resolver<'a> { return Some(module.parent.unwrap()); } - let mut module_expansion = module.expansion.modern(); // for backward compatability + let mut module_expansion = module.expansion.modern(); // for backward compatibility while let Some(parent) = module.parent { let parent_expansion = parent.expansion.modern(); if module_expansion.is_descendant_of(parent_expansion) && diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 98eaa056177e..7572c4aa6802 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -313,7 +313,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn check_unused_macros(&self) { for did in self.unused_macros.iter() { let id_span = match *self.macro_map[did] { - SyntaxExtension::NormalTT(_, isp, _) => isp, + SyntaxExtension::NormalTT { def_info, .. } => def_info, SyntaxExtension::DeclMacro(.., osp) => osp, _ => None, }; diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 4740f9a0d5a5..658b6dbc4e35 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -27,7 +27,6 @@ use rustc::hir::def::Def as HirDef; use rustc::hir::def_id::DefId; use rustc::hir::map::Node; -use rustc::session::Session; use rustc::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxHashSet; @@ -62,7 +61,6 @@ macro_rules! down_cast_data { pub struct DumpVisitor<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> { save_ctxt: SaveContext<'l, 'tcx>, - sess: &'l Session, tcx: TyCtxt<'l, 'tcx, 'tcx>, dumper: &'ll mut JsonDumper, @@ -84,7 +82,6 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { -> DumpVisitor<'l, 'tcx, 'll, O> { let span_utils = SpanUtils::new(&save_ctxt.tcx.sess); DumpVisitor { - sess: &save_ctxt.tcx.sess, tcx: save_ctxt.tcx, save_ctxt: save_ctxt, dumper: dumper, @@ -147,39 +144,15 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { // For each prefix, we return the span for the last segment in the prefix and // a str representation of the entire prefix. fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> { - let spans = self.span.spans_for_path_segments(path); let segments = &path.segments[if path.is_global() { 1 } else { 0 }..]; - // Paths to enums seem to not match their spans - the span includes all the - // variants too. But they seem to always be at the end, so I hope we can cope with - // always using the first ones. So, only error out if we don't have enough spans. - // What could go wrong...? - if spans.len() < segments.len() { - if generated_code(path.span) { - return vec![]; - } - error!("Mis-calculated spans for path '{}'. Found {} spans, expected {}. Found spans:", - path_to_string(path), - spans.len(), - segments.len()); - for s in &spans { - let loc = self.sess.codemap().lookup_char_pos(s.lo); - error!(" '{}' in {}, line {}", - self.span.snippet(*s), - loc.file.name, - loc.line); - } - error!(" master span: {:?}: `{}`", path.span, self.span.snippet(path.span)); - return vec![]; - } - - let mut result: Vec<(Span, String)> = vec![]; + let mut result = Vec::with_capacity(segments.len()); let mut segs = vec![]; - for (i, (seg, span)) in segments.iter().zip(&spans).enumerate() { + for (i, seg) in segments.iter().enumerate() { segs.push(seg.clone()); let sub_path = ast::Path { - span: *span, // span for the last segment + span: seg.span, // span for the last segment segments: segs, }; let qualname = if i == 0 && path.is_global() { @@ -187,7 +160,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } else { path_to_string(&sub_path) }; - result.push((*span, qualname)); + result.push((seg.span, qualname)); segs = sub_path.segments; } @@ -345,7 +318,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { collector.visit_pat(&arg.pat); let span_utils = self.span.clone(); for &(id, ref p, ..) in &collector.collected_paths { - let typ = match self.save_ctxt.tables.node_types.get(&id) { + let hir_id = self.tcx.hir.node_to_hir_id(id); + let typ = match self.save_ctxt.tables.node_id_to_type_opt(hir_id) { Some(s) => s.to_string(), None => continue, }; @@ -436,13 +410,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { full_span: Span, prefix: &str, id: NodeId) { - // We can't only use visit_generics since we don't have spans for param - // bindings, so we reparse the full_span to get those sub spans. - // However full span is the entire enum/fn/struct block, so we only want - // the first few to match the number of generics we're looking for. - let param_sub_spans = self.span.spans_for_ty_params(full_span, - (generics.ty_params.len() as isize)); - for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans) { + for param in &generics.ty_params { + let param_ss = param.span; let name = escape(self.span.snippet(param_ss)); // Append $id to name to make sure each one is unique let qualname = format!("{}::{}${}", @@ -499,12 +468,14 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { item: &'l ast::Item, typ: &'l ast::Ty, expr: &'l ast::Expr) { - if let Some(var_data) = self.save_ctxt.get_item_data(item) { - down_cast_data!(var_data, DefData, item.span); - self.dumper.dump_def(item.vis == ast::Visibility::Public, var_data); - } - self.visit_ty(&typ); - self.visit_expr(expr); + self.nest_tables(item.id, |v| { + if let Some(var_data) = v.save_ctxt.get_item_data(item) { + down_cast_data!(var_data, DefData, item.span); + v.dumper.dump_def(item.vis == ast::Visibility::Public, var_data); + } + v.visit_ty(&typ); + v.visit_expr(expr); + }); } fn process_assoc_const(&mut self, @@ -893,7 +864,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { match p.node { PatKind::Struct(ref _path, ref fields, _) => { // FIXME do something with _path? - let adt = match self.save_ctxt.tables.node_id_to_type_opt(p.id) { + let hir_id = self.tcx.hir.node_to_hir_id(p.id); + let adt = match self.save_ctxt.tables.node_id_to_type_opt(hir_id) { Some(ty) => ty.ty_adt_def().unwrap(), None => { visit::walk_pat(self, p); @@ -935,7 +907,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { ast::Mutability::Immutable => value.to_string(), _ => String::new(), }; - let typ = match self.save_ctxt.tables.node_types.get(&id) { + let hir_id = self.tcx.hir.node_to_hir_id(id); + let typ = match self.save_ctxt.tables.node_id_to_type_opt(hir_id) { Some(typ) => { let typ = typ.to_string(); if !value.is_empty() { @@ -1466,8 +1439,12 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc } else { "".to_string() }; - let typ = self.save_ctxt.tables.node_types - .get(&id).map(|t| t.to_string()).unwrap_or(String::new()); + let hir_id = self.tcx.hir.node_to_hir_id(id); + let typ = self.save_ctxt + .tables + .node_id_to_type_opt(hir_id) + .map(|t| t.to_string()) + .unwrap_or(String::new()); value.push_str(": "); value.push_str(&typ); diff --git a/src/librustc_save_analysis/json_dumper.rs b/src/librustc_save_analysis/json_dumper.rs index 30a698e63514..8dd191f4375e 100644 --- a/src/librustc_save_analysis/json_dumper.rs +++ b/src/librustc_save_analysis/json_dumper.rs @@ -109,7 +109,7 @@ impl<'b, O: DumpOutput + 'b> JsonDumper { } if data.kind == DefKind::Mod && data.span.file_name.to_str().unwrap() != data.value { // If the module is an out-of-line defintion, then we'll make the - // defintion the first character in the module's file and turn the + // definition the first character in the module's file and turn the // the declaration into a reference to it. let rf = Ref { kind: RefKind::Mod, diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 1dd0df4108fc..c3d071d27d6e 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -550,7 +550,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } ast::ExprKind::MethodCall(..) => { - let method_id = self.tables.type_dependent_defs[&expr.id].def_id(); + let expr_hir_id = self.tcx.hir.definitions().node_to_hir_id(expr.id); + let method_id = self.tables.type_dependent_defs()[expr_hir_id].def_id(); let (def_id, decl_id) = match self.tcx.associated_item(method_id).container { ty::ImplContainer(_) => (Some(method_id), None), ty::TraitContainer(_) => (None, Some(method_id)), @@ -586,7 +587,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { Node::NodePat(&hir::Pat { node: hir::PatKind::Path(ref qpath), .. }) | Node::NodePat(&hir::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }) | Node::NodePat(&hir::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }) => { - self.tables.qpath_def(qpath, id) + let hir_id = self.tcx.hir.node_to_hir_id(id); + self.tables.qpath_def(qpath, hir_id) } Node::NodeLocal(&hir::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }) => { @@ -975,7 +977,7 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>, let save_ctxt = SaveContext { tcx: tcx, - tables: &ty::TypeckTables::empty(), + tables: &ty::TypeckTables::empty(None), analysis: analysis, span_utils: SpanUtils::new(&tcx.sess), config: find_config(config), diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index 3557b4752e7e..6ef499694aa4 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -387,7 +387,7 @@ impl Sig for ast::Item { sig.text.push('('); for i in &decl.inputs { - // FIXME shoudl descend into patterns to add defs. + // FIXME should descend into patterns to add defs. sig.text.push_str(&pprust::pat_to_string(&i.pat)); sig.text.push_str(": "); let nested = i.ty.make(offset + sig.text.len(), Some(i.id), scx)?; @@ -922,7 +922,7 @@ fn make_method_signature(id: NodeId, sig.text.push('('); for i in &m.decl.inputs { - // FIXME shoudl descend into patterns to add defs. + // FIXME should descend into patterns to add defs. sig.text.push_str(&pprust::pat_to_string(&i.pat)); sig.text.push_str(": "); let nested = i.ty.make(sig.text.len(), Some(i.id), scx)?; diff --git a/src/librustc_save_analysis/span_utils.rs b/src/librustc_save_analysis/span_utils.rs index e771da2ed4ce..631907b28f8b 100644 --- a/src/librustc_save_analysis/span_utils.rs +++ b/src/librustc_save_analysis/span_utils.rs @@ -16,7 +16,6 @@ use std::cell::Cell; use std::env; use std::path::Path; -use syntax::ast; use syntax::parse::lexer::{self, StringReader}; use syntax::parse::token::{self, Token}; use syntax::symbol::keywords; @@ -207,75 +206,6 @@ impl<'a> SpanUtils<'a> { result } - // Reparse span and return an owned vector of sub spans of the first limit - // identifier tokens in the given nesting level. - // example with Foo, Bar> - // Nesting = 0: all idents outside of angle brackets: [Foo] - // Nesting = 1: idents within one level of angle brackets: [Bar, Bar] - pub fn spans_with_brackets(&self, span: Span, nesting: isize, limit: isize) -> Vec { - let mut result: Vec = vec![]; - - let mut toks = self.retokenise_span(span); - // We keep track of how many brackets we're nested in - let mut angle_count: isize = 0; - let mut bracket_count: isize = 0; - let mut found_ufcs_sep = false; - loop { - let ts = toks.real_token(); - if ts.tok == token::Eof { - if angle_count != 0 || bracket_count != 0 { - if generated_code(span) { - return vec![]; - } - let loc = self.sess.codemap().lookup_char_pos(span.lo); - span_bug!(span, - "Mis-counted brackets when breaking path? \ - Parsing '{}' in {}, line {}", - self.snippet(span), - loc.file.name, - loc.line); - } - return result - } - if (result.len() as isize) == limit { - return result; - } - bracket_count += match ts.tok { - token::OpenDelim(token::Bracket) => 1, - token::CloseDelim(token::Bracket) => -1, - _ => 0, - }; - if bracket_count > 0 { - continue; - } - angle_count += match ts.tok { - token::Lt => 1, - token::Gt => -1, - token::BinOp(token::Shl) => 2, - token::BinOp(token::Shr) => -2, - _ => 0, - }; - - // Ignore the `>::` in `::AssocTy`. - - // The root cause of this hack is that the AST representation of - // qpaths is horrible. It treats ::C as a path with two - // segments, B and C and notes that there is also a self type A at - // position 0. Because we don't have spans for individual idents, - // only the whole path, we have to iterate over the tokens in the - // path, trying to pull out the non-nested idents (e.g., avoiding 'a - // in `>::C`). So we end up with a span for `B>::C` from - // the start of the first ident to the end of the path. - if !found_ufcs_sep && angle_count == -1 { - found_ufcs_sep = true; - angle_count += 1; - } - if ts.tok.is_ident() && angle_count == nesting { - result.push(ts.sp); - } - } - } - pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option { let mut toks = self.retokenise_span(span); let mut prev = toks.real_token(); @@ -330,21 +260,6 @@ impl<'a> SpanUtils<'a> { } } - - // Returns a list of the spans of idents in a path. - // E.g., For foo::bar::baz, we return [foo, bar, baz] (well, their spans) - pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec { - self.spans_with_brackets(path.span, 0, -1) - } - - // Return an owned vector of the subspans of the param identifier - // tokens found in span. - pub fn spans_for_ty_params(&self, span: Span, number: isize) -> Vec { - // Type params are nested within one level of brackets: - // i.e. we want Vec from Foo> - self.spans_with_brackets(span, 1, number) - } - // // Return the name for a macro definition (identifier after first `!`) // pub fn span_for_macro_def_name(&self, span: Span) -> Option { // let mut toks = self.retokenise_span(span); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 8cbc5155ddeb..b4a2891f94c2 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -191,7 +191,7 @@ pub fn compare_simd_types<'a, 'tcx>( /// adjustment. /// /// The `old_info` argument is a bit funny. It is intended for use -/// in an upcast, where the new vtable for an object will be drived +/// in an upcast, where the new vtable for an object will be derived /// from the old one. pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>, source: Ty<'tcx>, @@ -488,7 +488,7 @@ impl Lifetime { // on), and `ptr` is nonzero-sized, then extracts the size of `ptr` // and the intrinsic for `lt` and passes them to `emit`, which is in // charge of generating code to call the passed intrinsic on whatever - // block of generated code is targetted for the intrinsic. + // block of generated code is targeted for the intrinsic. // // If LLVM lifetime intrinsic support is disabled (i.e. optimizations // off) or `ptr` is zero-sized, then no-op (does not call `emit`). diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index 826b4c09cc2d..5dd1c15fd2d6 100644 --- a/src/librustc_trans/debuginfo/type_names.rs +++ b/src/librustc_trans/debuginfo/type_names.rs @@ -36,7 +36,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, qualified: bool, output: &mut String) { - // When targeting MSVC, emit C++ style type names for compatability with + // When targeting MSVC, emit C++ style type names for compatibility with // .natvis visualizers (and perhaps other existing native debuggers?) let cpp_like_names = cx.sess().target.target.options.is_like_msvc; diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 9956c28e6412..033ef988571d 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -246,7 +246,11 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let val = if fn_ty.args[1].is_indirect() { bcx.load(llargs[1], None) } else { - from_immediate(bcx, llargs[1]) + if !type_is_zero_size(ccx, tp_ty) { + from_immediate(bcx, llargs[1]) + } else { + C_nil(ccx) + } }; let ptr = bcx.pointercast(llargs[0], val_ty(val).ptr_to()); let store = bcx.volatile_store(val, ptr); diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 3c38619cf5dd..3899295da40b 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -135,7 +135,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { (scope, source_info.span) } else { // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occurr + // We also stop at the function body level because no line stepping can occur // at the level above that. let mut span = source_info.span; while span.ctxt != NO_EXPANSION && span.ctxt != self.mir.span.ctxt { diff --git a/src/librustc_trans_utils/link.rs b/src/librustc_trans_utils/link.rs index 29bb062d34cf..264158f0de9e 100644 --- a/src/librustc_trans_utils/link.rs +++ b/src/librustc_trans_utils/link.rs @@ -8,34 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::session::config::{self, /*NoDebugInfo,*/ OutputFilenames, Input, OutputType}; -/*use rustc::session::filesearch; -use rustc::session::search_paths::PathKind; -*/use rustc::session::Session; -use rustc::middle::cstore;/*::{self, LinkMeta, NativeLibrary, LibSource, LinkagePreference, - NativeLibraryKind};*/ -/*use rustc::middle::dependency_format::Linkage; -use rustc::util::common::time; -use rustc::util::fs::fix_windows_verbatim_for_gcc; -use rustc::dep_graph::{DepKind, DepNode}; -use rustc::hir::def_id::CrateNum; -use rustc::hir::svh::Svh; -use rustc_back::tempdir::TempDir; -use rustc_back::{PanicStrategy, RelroLevel}; -use rustc_incremental::IncrementalHashesMap;*/ - -/*use std::ascii; -use std::char; -use std::env; -use std::ffi::OsString; -use std::fs; -use std::io::{self, Read, Write}; -use std::mem; -*/use std::path::PathBuf;/*{Path, PathBuf}; -use std::process::Command; -use std::str;*/ +use rustc::session::config::{self, OutputFilenames, Input, OutputType}; +use rustc::session::Session; +use rustc::middle::cstore; +use std::path::PathBuf; use syntax::ast; -//use syntax::attr; use syntax_pos::Span; pub fn find_crate_name(sess: Option<&Session>, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index eaff8e7b8ace..b49b9377e8c5 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -118,8 +118,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // identical to what could be scraped from the HIR, but this will change with // default binding modes (#42640). let bm = ty::BindingMode::convert(ba); - self.inh.tables.borrow_mut().pat_binding_modes.insert(pat.id, bm); - + self.inh + .tables + .borrow_mut() + .pat_binding_modes_mut() + .insert(pat.hir_id, bm); let typ = self.local_ty(pat.span, pat.id); match bm { ty::BindByReference(mutbl) => { @@ -323,7 +326,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - self.write_ty(pat.id, ty); + self.write_ty(pat.hir_id, ty); // (*) In most of the cases above (literals and constants being // the exception), we relate types using strict equality, evewn diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index a0801a748665..460e2858b22e 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -222,7 +222,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let hir::ExprCall(ref expr, _) = call_expr.node { let def = if let hir::ExprPath(ref qpath) = expr.node { - self.tables.borrow().qpath_def(qpath, expr.id) + self.tables.borrow().qpath_def(qpath, expr.hir_id) } else { Def::Err }; @@ -314,7 +314,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { TupleArgumentsFlag::TupleArguments, expected); - self.write_method_call(call_expr.id, method_callee); + self.write_method_call(call_expr.hir_id, method_callee); output_type } } @@ -364,7 +364,8 @@ impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> { adjustments.extend(autoref); fcx.apply_adjustments(self.callee_expr, adjustments); - fcx.write_method_call(self.call_expr.id, method_callee); + fcx.write_method_call(self.call_expr.hir_id, + method_callee); } None => { span_bug!(self.call_expr.span, diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 5f256eab9a9c..b18b11f3d906 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -330,12 +330,13 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { } else if self.try_coercion_cast(fcx) { self.trivial_cast_lint(fcx); debug!(" -> CoercionCast"); - fcx.tables.borrow_mut().cast_kinds.insert(self.expr.id, CastKind::CoercionCast); + fcx.tables.borrow_mut().cast_kinds_mut().insert(self.expr.hir_id, + CastKind::CoercionCast); } else { match self.do_check(fcx) { Ok(k) => { debug!(" -> {:?}", k); - fcx.tables.borrow_mut().cast_kinds.insert(self.expr.id, k); + fcx.tables.borrow_mut().cast_kinds_mut().insert(self.expr.hir_id, k); } Err(e) => self.report_cast_error(fcx, e), }; diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 7e7e1018f977..8f8dc71899fc 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -111,12 +111,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { sig, opt_kind); - self.tables.borrow_mut().closure_tys.insert(expr.id, sig); - match opt_kind { - Some(kind) => { - self.tables.borrow_mut().closure_kinds.insert(expr.id, (kind, None)); + { + let mut tables = self.tables.borrow_mut(); + tables.closure_tys_mut().insert(expr.hir_id, sig); + match opt_kind { + Some(kind) => { + tables.closure_kinds_mut().insert(expr.hir_id, (kind, None)); + } + None => {} } - None => {} } closure_type diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 934a4f9b2968..53f3d811fae4 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -844,7 +844,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // First try to coerce the new expression to the type of the previous ones, // but only if the new expression has no coercion already applied to it. let mut first_error = None; - if !self.tables.borrow().adjustments.contains_key(&new.id) { + if !self.tables.borrow().adjustments().contains_key(new.hir_id) { let result = self.commit_if_ok(|_| coerce.coerce(new_ty, prev_ty)); match result { Ok(ok) => { @@ -866,7 +866,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Adjustment { kind: Adjust::Deref(_), .. }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl_adj)), .. } ] => { - match self.node_ty(expr.id).sty { + match self.node_ty(expr.hir_id).sty { ty::TyRef(_, mt_orig) => { // Reborrow that we can safely ignore, because // the next adjustment can only be a Deref diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 7bb8a251354e..fc241c023cda 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -67,7 +67,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); None - }, + } Err(e) => { Some(self.report_mismatched_types(cause, expected, actual, e)) } @@ -82,7 +82,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Checks that the type of `expr` can be coerced to `expected`. // - // NB: This code relies on `self.diverges` to be accurate. In + // NB: This code relies on `self.diverges` to be accurate. In // particular, assignments to `!` will be permitted if the // diverges flag is currently "always". pub fn demand_coerce_diag(&self, diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index ed22cd1333e9..72ff9eb6f5b0 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -22,7 +22,7 @@ use util::nodemap::FxHashSet; use syntax_pos::Span; -/// check_drop_impl confirms that the Drop implementation identfied by +/// check_drop_impl confirms that the Drop implementation identified by /// `drop_impl_did` is not any more specialized than the type it is /// attached to (Issue #8142). /// diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index b6a5ce0a6ce5..355e6cdbbe07 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -445,11 +445,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { // Fix up the autoderefs. Autorefs can only occur immediately preceding // overloaded lvalue ops, and will be fixed by them in order to get // the correct region. - let mut source = self.node_ty(expr.id); + let mut source = self.node_ty(expr.hir_id); // Do not mutate adjustments in place, but rather take them, // and replace them after mutating them, to avoid having the // tables borrowed during (`deref_mut`) method resolution. - let previous_adjustments = self.tables.borrow_mut().adjustments.remove(&expr.id); + let previous_adjustments = self.tables + .borrow_mut() + .adjustments_mut() + .remove(expr.hir_id); if let Some(mut adjustments) = previous_adjustments { let pref = LvaluePreference::PreferMutLvalue; for adjustment in &mut adjustments { @@ -466,12 +469,12 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { } source = adjustment.target; } - self.tables.borrow_mut().adjustments.insert(expr.id, adjustments); + self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); } match expr.node { hir::ExprIndex(ref base_expr, ref index_expr) => { - let index_expr_ty = self.node_ty(index_expr.id); + let index_expr_ty = self.node_ty(index_expr.hir_id); self.convert_lvalue_op_to_mutable( LvalueOp::Index, expr, base_expr, &[index_expr_ty]); } @@ -498,7 +501,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { } let base_ty = self.tables.borrow().expr_adjustments(base_expr).last() - .map_or_else(|| self.node_ty(expr.id), |adj| adj.target); + .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); let base_ty = self.resolve_type_vars_if_possible(&base_ty); // Need to deref because overloaded lvalue ops take self by-reference. @@ -513,7 +516,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed") }; debug!("convert_lvalue_op_to_mutable: method={:?}", method); - self.write_method_call(expr.id, method); + self.write_method_call(expr.hir_id, method); let (region, mutbl) = if let ty::TyRef(r, mt) = method.sig.inputs()[0].sty { (r, mt.mutbl) @@ -523,8 +526,11 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { // Convert the autoref in the base expr to mutable with the correct // region and mutability. - let base_expr_ty = self.node_ty(base_expr.id); - if let Some(adjustments) = self.tables.borrow_mut().adjustments.get_mut(&base_expr.id) { + let base_expr_ty = self.node_ty(base_expr.hir_id); + if let Some(adjustments) = self.tables + .borrow_mut() + .adjustments_mut() + .get_mut(base_expr.hir_id) { let mut source = base_expr_ty; for adjustment in &mut adjustments[..] { if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 1ccb1e64a98e..a1c987f22e05 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -219,7 +219,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// `lookup_method_in_trait` is used for overloaded operators. /// It does a very narrow slice of what the normal probe/confirm path does. /// In particular, it doesn't really do any probing: it simply constructs - /// an obligation for aparticular trait with the given self-type and checks + /// an obligation for a particular trait with the given self-type and checks /// whether that trait is implemented. /// /// FIXME(#18741) -- It seems likely that we can consolidate some of this diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 6669e75d5a01..a55f4878ac6e 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -822,7 +822,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let closure_id = match step.self_ty.sty { ty::TyClosure(def_id, _) => { if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - id + self.tcx.hir.node_to_hir_id(id) } else { continue; } @@ -830,11 +830,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { _ => continue, }; - let closure_kinds = &self.tables.borrow().closure_kinds; - let closure_kind = match closure_kinds.get(&closure_id) { - Some(&(k, _)) => k, - None => { - return Err(MethodError::ClosureAmbiguity(trait_def_id)); + let closure_kind = { + match self.tables.borrow().closure_kinds().get(closure_id) { + Some(&(k, _)) => k, + None => { + return Err(MethodError::ClosureAmbiguity(trait_def_id)); + } } }; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b22cb0bafe82..ff96f3db7ebb 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -534,7 +534,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { /// `foo(return)`; we warn on the `foo()` expression. (We then /// update the flag to `WarnedAlways` to suppress duplicate /// reports.) Similarly, if we traverse to a fresh statement (or - /// tail expression) from a `Always` setting, we will isssue a + /// tail expression) from a `Always` setting, we will issue a /// warning. This corresponds to something like `{return; /// foo();}` or `{return; 22}`, where we would warn on the /// `foo()` or `22`. @@ -543,7 +543,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { /// (including the "return slot") of type `!`. This is allowed /// if **either** the type of value being assigned is `!`, which /// means the current code is dead, **or** the expression's - /// divering flag is true, which means that a divering value was + /// diverging flag is true, which means that a diverging value was /// wrapped (e.g., `let x: ! = foo(return)`). /// /// To repeat the last point: an expression represents dead-code @@ -577,8 +577,16 @@ pub struct InheritedBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { pub fn build(tcx: TyCtxt<'a, 'gcx, 'gcx>, def_id: DefId) -> InheritedBuilder<'a, 'gcx, 'tcx> { + let hir_id_root = if def_id.is_local() { + let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); + let hir_id = tcx.hir.definitions().node_to_hir_id(node_id); + DefId::local(hir_id.owner) + } else { + def_id + }; + InheritedBuilder { - infcx: tcx.infer_ctxt().with_fresh_in_progress_tables(), + infcx: tcx.infer_ctxt().with_fresh_in_progress_tables(hir_id_root), def_id, } } @@ -749,7 +757,8 @@ fn closure_kind<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::ClosureKind { let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); - tcx.typeck_tables_of(def_id).closure_kinds[&node_id].0 + let hir_id = tcx.hir.node_to_hir_id(node_id); + tcx.typeck_tables_of(def_id).closure_kinds()[hir_id].0 } fn adt_destructor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -854,7 +863,7 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }); let body = tcx.hir.body(body_id); - Inherited::build(tcx, def_id).enter(|inh| { + let tables = Inherited::build(tcx, def_id).enter(|inh| { let param_env = tcx.param_env(def_id); let fcx = if let Some(decl) = fn_decl { let fn_sig = tcx.fn_sig(def_id); @@ -902,7 +911,13 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } fcx.resolve_type_vars_in_body(body) - }) + }); + + // Consistency check our TypeckTables instance can hold all ItemLocalIds + // it will need to hold. + assert_eq!(tables.local_id_root, + Some(DefId::local(tcx.hir.definitions().node_to_hir_id(id).owner))); + tables } fn check_abi<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, span: Span, abi: Abi) { @@ -1032,7 +1047,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, fcx.require_type_is_sized(arg_ty, decl.output.span(), traits::MiscObligation); } - fcx.write_ty(arg.id, arg_ty); + fcx.write_ty(arg.hir_id, arg_ty); } let gen_ty = if can_be_generator && body.is_generator { @@ -1040,20 +1055,20 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, yield_ty: fcx.yield_ty.unwrap(), return_ty: ret_ty, }; - inherited.tables.borrow_mut().generator_sigs.insert(fn_id, Some(gen_sig)); + inherited.tables.borrow_mut().generator_sigs.insert(fn_hir_id, Some(gen_sig)); let witness = fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span)); fcx.deferred_generator_interiors.borrow_mut().push((body.id(), witness)); let interior = ty::GeneratorInterior::new(witness); - inherited.tables.borrow_mut().generator_interiors.insert(fn_id, interior); + inherited.tables.borrow_mut().generator_interiors.insert(fn_hir_id, interior); Some(interior) } else { - inherited.tables.borrow_mut().generator_sigs.insert(fn_id, None); + inherited.tables.borrow_mut().generator_sigs.insert(fn_hir_id, None); None }; - inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig); + inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_hir_id, fn_sig); fcx.check_return_expr(&body.value); @@ -1834,10 +1849,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } #[inline] - pub fn write_ty(&self, node_id: ast::NodeId, ty: Ty<'tcx>) { - debug!("write_ty({}, {:?}) in fcx {}", - node_id, self.resolve_type_vars_if_possible(&ty), self.tag()); - self.tables.borrow_mut().node_types.insert(node_id, ty); + pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { + debug!("write_ty({:?}, {:?}) in fcx {}", + id, self.resolve_type_vars_if_possible(&ty), self.tag()); + self.tables.borrow_mut().node_types_mut().insert(id, ty); if ty.references_error() { self.has_errors.set(true); @@ -1845,19 +1860,26 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - pub fn write_method_call(&self, node_id: ast::NodeId, method: MethodCallee<'tcx>) { - self.tables.borrow_mut().type_dependent_defs.insert(node_id, Def::Method(method.def_id)); - self.write_substs(node_id, method.substs); + // The NodeId and the ItemLocalId must identify the same item. We just pass + // both of them for consistency checking. + pub fn write_method_call(&self, + hir_id: hir::HirId, + method: MethodCallee<'tcx>) { + self.tables + .borrow_mut() + .type_dependent_defs_mut() + .insert(hir_id, Def::Method(method.def_id)); + self.write_substs(hir_id, method.substs); } - pub fn write_substs(&self, node_id: ast::NodeId, substs: &'tcx Substs<'tcx>) { + pub fn write_substs(&self, node_id: hir::HirId, substs: &'tcx Substs<'tcx>) { if !substs.is_noop() { - debug!("write_substs({}, {:?}) in fcx {}", + debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); - self.tables.borrow_mut().node_substs.insert(node_id, substs); + self.tables.borrow_mut().node_substs_mut().insert(node_id, substs); } } @@ -1868,7 +1890,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { return; } - match self.tables.borrow_mut().adjustments.entry(expr.id) { + match self.tables.borrow_mut().adjustments_mut().entry(expr.hir_id) { Entry::Vacant(entry) => { entry.insert(adj); }, Entry::Occupied(mut entry) => { debug!(" - composing on top of {:?}", entry.get()); @@ -1936,7 +1958,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { value.fold_with(&mut BottomUpFolder { tcx: self.tcx, fldop: |ty| { if let ty::TyAnon(def_id, substs) = ty.sty { // Use the same type variable if the exact same TyAnon appears more - // than once in the return type (e.g. if it's pased to a type alias). + // than once in the return type (e.g. if it's passed to a type alias). let id = self.tcx.hir.as_local_node_id(def_id).unwrap(); if let Some(ty_var) = self.anon_types.borrow().get(&id) { return ty_var; @@ -2021,13 +2043,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { t } - pub fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> { - match self.tables.borrow().node_types.get(&id) { + pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { + match self.tables.borrow().node_types().get(id) { Some(&t) => t, None if self.err_count_since_creation() != 0 => self.tcx.types.err, None => { + let node_id = self.tcx.hir.definitions().find_node_for_hir_id(id); bug!("no type for node {}: {} in fcx {}", - id, self.tcx.hir.node_to_string(id), + node_id, self.tcx.hir.node_to_string(node_id), self.tag()); } } @@ -2302,7 +2325,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } self.apply_adjustments(base_expr, adjustments); - self.write_method_call(expr.id, method); + self.write_method_call(expr.hir_id, method); (input_ty, self.make_overloaded_lvalue_return_type(method).ty) }); if result.is_some() { @@ -2691,7 +2714,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. if ty.is_never() { - assert!(!self.tables.borrow().adjustments.contains_key(&expr.id), + assert!(!self.tables.borrow().adjustments().contains_key(expr.hir_id), "expression with never type wound up being adjusted"); let adj_ty = self.next_diverging_ty_var( TypeVariableOrigin::AdjustmentType(expr.span)); @@ -2842,7 +2865,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr, rcvr) { Ok(method) => { - self.write_method_call(expr.id, method); + self.write_method_call(expr.hir_id, method); Ok(method) } Err(error) => { @@ -3416,7 +3439,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let fru_field_types = adt.struct_variant().fields.iter().map(|f| { self.normalize_associated_types_in(expr.span, &f.ty(self.tcx, substs)) }).collect(); - self.tables.borrow_mut().fru_field_types.insert(expr.id, fru_field_types); + + self.tables + .borrow_mut() + .fru_field_types_mut() + .insert(expr.hir_id, fru_field_types); } _ => { span_err!(self.tcx.sess, base_expr.span, E0436, @@ -3474,7 +3501,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Record the type, which applies it effects. // We need to do this after the warning above, so that // we don't warn for the diverging expression itself. - self.write_ty(expr.id, ty); + self.write_ty(expr.hir_id, ty); // Combine the diverging and has_error flags. self.diverges.set(self.diverges.get() | old_diverges); @@ -3547,7 +3574,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }]); } oprnd_t = self.make_overloaded_lvalue_return_type(method).ty; - self.write_method_call(expr.id, method); + self.write_method_call(expr.hir_id, method); } else { type_error_struct!(tcx.sess, expr.span, oprnd_t, E0614, "type `{}` cannot be dereferenced", @@ -3625,7 +3652,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // We always require that the type provided as the value for // a type parameter outlives the moment of instantiation. - let substs = self.tables.borrow().node_substs(expr.id); + let substs = self.tables.borrow().node_substs(expr.hir_id); self.add_wf_bounds(substs, expr); ty @@ -4057,7 +4084,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty, def, segment); // Write back the new resolution. - self.tables.borrow_mut().type_dependent_defs.insert(node_id, def); + let hir_id = self.tcx.hir.node_to_hir_id(node_id); + self.tables.borrow_mut().type_dependent_defs_mut().insert(hir_id, def); (def, ty) } @@ -4098,7 +4126,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; // Write back the new resolution. - self.tables.borrow_mut().type_dependent_defs.insert(node_id, def); + let hir_id = self.tcx.hir.node_to_hir_id(node_id); + self.tables.borrow_mut().type_dependent_defs_mut().insert(hir_id, def); (def, Some(ty), slice::ref_slice(&**item_segment)) } @@ -4130,19 +4159,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn check_decl_local(&self, local: &'gcx hir::Local) { let t = self.local_ty(local.span, local.id); - self.write_ty(local.id, t); + self.write_ty(local.hir_id, t); if let Some(ref init) = local.init { let init_ty = self.check_decl_initializer(local, &init); if init_ty.references_error() { - self.write_ty(local.id, init_ty); + self.write_ty(local.hir_id, init_ty); } } self.check_pat(&local.pat, t); - let pat_ty = self.node_ty(local.pat.id); + let pat_ty = self.node_ty(local.pat.hir_id); if pat_ty.references_error() { - self.write_ty(local.id, pat_ty); + self.write_ty(local.hir_id, pat_ty); } } @@ -4294,14 +4323,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty = self.tcx.types.err } - self.write_ty(blk.id, ty); + self.write_ty(blk.hir_id, ty); *self.ps.borrow_mut() = prev; ty } /// Given a `NodeId`, return the `FnDecl` of the method it is enclosed by and whether a - /// suggetion can be made, `None` otherwise. + /// suggestion can be made, `None` otherwise. pub fn get_fn_decl(&self, blk_id: ast::NodeId) -> Option<(hir::FnDecl, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. @@ -4369,7 +4398,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// ``` /// /// This routine checks if the return expression in a block would make sense on its own as a - /// statement and the return type has been left as defaultor has been specified as `()`. If so, + /// statement and the return type has been left as default or has been specified as `()`. If so, /// it suggests adding a semicolon. fn suggest_missing_semicolon(&self, err: &mut DiagnosticBuilder<'tcx>, @@ -4472,7 +4501,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::StmtSemi(ref e, _) => e, _ => return, }; - let last_expr_ty = self.node_ty(last_expr.id); + let last_expr_ty = self.node_ty(last_expr.hir_id); if self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() { return; } @@ -4615,7 +4644,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let nid = self.tcx.hir.as_local_node_id(def_id).unwrap(); let ty = self.local_ty(span, nid); let ty = self.normalize_associated_types_in(span, &ty); - self.write_ty(node_id, ty); + self.write_ty(self.tcx.hir.node_to_hir_id(node_id), ty); return ty; } _ => {} @@ -4747,7 +4776,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { debug!("instantiate_value_path: type of {:?} is {:?}", node_id, ty_substituted); - self.write_substs(node_id, substs); + self.write_substs(self.tcx.hir.node_to_hir_id(node_id), substs); ty_substituted } diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index c1711491ee48..a4e1fdaf39e2 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -210,11 +210,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // some cases applied on the RHS, on top of which we need // to autoref, which is not allowed by apply_adjustments. // self.apply_adjustments(rhs_expr, vec![autoref]); - self.tables.borrow_mut().adjustments.entry(rhs_expr.id) - .or_insert(vec![]).push(autoref); + self.tables + .borrow_mut() + .adjustments_mut() + .entry(rhs_expr.hir_id) + .or_insert(vec![]) + .push(autoref); } } - self.write_method_call(expr.id, method); + self.write_method_call(expr.hir_id, method); method.sig.output() } @@ -340,7 +344,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { assert!(op.is_by_value()); match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) { Ok(method) => { - self.write_method_call(ex.id, method); + self.write_method_call(ex.hir_id, method); method.sig.output() } Err(()) => { diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 3a39800ea912..baaeae93e08c 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -284,7 +284,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } /// Try to resolve the type for the given node. - fn resolve_node_type(&self, id: ast::NodeId) -> Ty<'tcx> { + fn resolve_node_type(&self, id: hir::HirId) -> Ty<'tcx> { let t = self.node_ty(id); self.resolve_type(t) } @@ -309,8 +309,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let old_call_site_scope = self.set_call_site_scope(Some(call_site)); let fn_sig = { - let fn_sig_map = &self.tables.borrow().liberated_fn_sigs; - match fn_sig_map.get(&id) { + let fn_hir_id = self.tcx.hir.node_to_hir_id(id); + match self.tables.borrow().liberated_fn_sigs().get(fn_hir_id) { Some(f) => f.clone(), None => { bug!("No fn-sig entry for id={}", id); @@ -338,8 +338,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("visit_fn_body body.id {:?} call_site_scope: {:?}", body.id(), call_site_scope); let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope)); + let body_hir_id = self.tcx.hir.node_to_hir_id(body_id.node_id); self.type_of_node_must_outlive(infer::CallReturn(span), - body_id.node_id, + body_hir_id, call_site_region); self.region_bound_pairs.truncate(old_region_bounds_pairs_len); @@ -613,9 +614,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let var_region = self.tcx.mk_region(ty::ReScope(var_scope)); let origin = infer::BindingTypeIsNotValidAtDecl(span); - self.type_of_node_must_outlive(origin, id, var_region); + let hir_id = self.tcx.hir.node_to_hir_id(id); + self.type_of_node_must_outlive(origin, hir_id, var_region); - let typ = self.resolve_node_type(id); + let typ = self.resolve_node_type(hir_id); let _ = dropck::check_safety_of_destructor_if_necessary( self, typ, span, var_scope); }) @@ -664,7 +666,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { // No matter what, the type of each expression must outlive the // scope of that expression. This also guarantees basic WF. - let expr_ty = self.resolve_node_type(expr.id); + let expr_ty = self.resolve_node_type(expr.hir_id); // the region corresponding to this expression let expr_region = self.tcx.node_scope_region(expr.id); self.type_must_outlive(infer::ExprTypeIsNotInScope(expr_ty, expr.span), @@ -686,7 +688,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { infer::ParameterOrigin::OverloadedOperator }; - let substs = self.tables.borrow().node_substs(expr.id); + let substs = self.tables.borrow().node_substs(expr.hir_id); self.substs_wf_in_scope(origin, substs, expr.span, expr_region); // Arguments (sub-expressions) are checked via `constrain_call`, below. } @@ -709,7 +711,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { expr, self.repeating_scope); match expr.node { hir::ExprPath(_) => { - let substs = self.tables.borrow().node_substs(expr.id); + let substs = self.tables.borrow().node_substs(expr.hir_id); let origin = infer::ParameterOrigin::Path; self.substs_wf_in_scope(origin, substs, expr.span, expr_region); } @@ -718,7 +720,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { if is_method_call { self.constrain_call(expr, Some(&callee), args.iter().map(|e| &*e)); } else { - self.constrain_callee(callee.id, expr, &callee); + self.constrain_callee(&callee); self.constrain_call(expr, None, args.iter().map(|e| &*e)); } @@ -812,7 +814,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { // adjustments*. // // FIXME(#6268) nested method calls requires that this rule change - let ty0 = self.resolve_node_type(expr.id); + let ty0 = self.resolve_node_type(expr.hir_id); self.type_must_outlive(infer::AddrOf(expr.span), ty0, expr_region); intravisit::walk_expr(self, expr); } @@ -849,7 +851,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { ret_expr.id, call_site_scope); let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope.unwrap())); self.type_of_node_must_outlive(infer::CallReturn(ret_expr.span), - ret_expr.id, + ret_expr.hir_id, call_site_region); intravisit::walk_expr(self, expr); } @@ -870,8 +872,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { cast_expr, source_expr); - let source_ty = self.resolve_node_type(source_expr.id); - let target_ty = self.resolve_node_type(cast_expr.id); + let source_ty = self.resolve_node_type(source_expr.hir_id); + let target_ty = self.resolve_node_type(cast_expr.hir_id); self.walk_cast(cast_expr, source_ty, target_ty); } @@ -915,11 +917,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.set_repeating_scope(repeating_scope); } - fn constrain_callee(&mut self, - callee_id: ast::NodeId, - _call_expr: &hir::Expr, - _callee_expr: &hir::Expr) { - let callee_ty = self.resolve_node_type(callee_id); + fn constrain_callee(&mut self, callee_expr: &hir::Expr) { + let callee_ty = self.resolve_node_type(callee_expr.hir_id); match callee_ty.sty { ty::TyFnDef(..) | ty::TyFnPtr(_) => { } _ => { @@ -962,14 +961,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // ensure that any regions appearing in the argument type are // valid for at least the lifetime of the function: self.type_of_node_must_outlive(infer::CallArg(arg_expr.span), - arg_expr.id, callee_region); + arg_expr.hir_id, + callee_region); } // as loop above, but for receiver if let Some(r) = receiver { debug!("receiver: {:?}", r); self.type_of_node_must_outlive(infer::CallRcvr(r.span), - r.id, callee_region); + r.hir_id, + callee_region); } } @@ -1038,7 +1039,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // // FIXME(#6268) remove to support nested method calls self.type_of_node_must_outlive(infer::AutoBorrow(expr.span), - expr.id, expr_region); + expr.hir_id, + expr_region); } cmt = self.with_mc(|mc| mc.cat_expr_adjusted(expr, cmt, &adjustment))?; @@ -1109,21 +1111,25 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// adjustments) are valid for at least `minimum_lifetime` fn type_of_node_must_outlive(&mut self, origin: infer::SubregionOrigin<'tcx>, - id: ast::NodeId, + hir_id: hir::HirId, minimum_lifetime: ty::Region<'tcx>) { // Try to resolve the type. If we encounter an error, then typeck // is going to fail anyway, so just stop here and let typeck // report errors later on in the writeback phase. - let ty0 = self.resolve_node_type(id); - let ty = self.tables.borrow().adjustments.get(&id) - .and_then(|adj| adj.last()) - .map_or(ty0, |adj| adj.target); + let ty0 = self.resolve_node_type(hir_id); + + let ty = self.tables + .borrow() + .adjustments() + .get(hir_id) + .and_then(|adj| adj.last()) + .map_or(ty0, |adj| adj.target); let ty = self.resolve_type(ty); debug!("constrain_regions_in_type_of_node(\ - ty={}, ty0={}, id={}, minimum_lifetime={:?})", + ty={}, ty0={}, id={:?}, minimum_lifetime={:?})", ty, ty0, - id, minimum_lifetime); + hir_id, minimum_lifetime); self.type_must_outlive(origin, ty, minimum_lifetime); } @@ -1137,7 +1143,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("link_addr_of: cmt={:?}", cmt); - self.link_region_from_node_type(expr.span, expr.id, mutability, cmt); + self.link_region_from_node_type(expr.span, expr.hir_id, mutability, cmt); } /// Computes the guarantors for any ref bindings in a `let` and @@ -1173,7 +1179,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { fn link_fn_args(&self, body_scope: CodeExtent, args: &[hir::Arg]) { debug!("regionck::link_fn_args(body_scope={:?})", body_scope); for arg in args { - let arg_ty = self.node_ty(arg.id); + let arg_ty = self.node_ty(arg.hir_id); let re_scope = self.tcx.mk_region(ty::ReScope(body_scope)); let arg_cmt = self.with_mc(|mc| { mc.cat_rvalue(arg.id, arg.pat.span, re_scope, arg_ty) @@ -1197,10 +1203,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { match sub_pat.node { // `ref x` pattern PatKind::Binding(..) => { - let bm = *mc.tables.pat_binding_modes.get(&sub_pat.id) - .expect("missing binding mode"); + let bm = *mc.tables.pat_binding_modes().get(sub_pat.hir_id) + .expect("missing binding mode"); if let ty::BindByReference(mutbl) = bm { - self.link_region_from_node_type(sub_pat.span, sub_pat.id, + self.link_region_from_node_type(sub_pat.span, sub_pat.hir_id, mutbl, sub_cmt); } } @@ -1236,7 +1242,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// which must be some reference (`&T`, `&str`, etc). fn link_region_from_node_type(&self, span: Span, - id: ast::NodeId, + id: hir::HirId, mutbl: hir::Mutability, cmt_borrowed: mc::cmt<'tcx>) { debug!("link_region_from_node_type(id={:?}, mutbl={:?}, cmt_borrowed={:?})", diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 70325ee3a933..77e597b30c8e 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -50,8 +50,9 @@ use rustc::infer::UpvarRegion; use syntax::ast; use syntax_pos::Span; use rustc::hir; +use rustc::hir::def_id::DefIndex; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use rustc::util::nodemap::NodeMap; +use rustc::util::nodemap::FxHashMap; use std::collections::hash_map::Entry; @@ -78,7 +79,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for InferBorrowKindVisitor<'a, 'gcx, 'tcx> { hir::ExprClosure(cc, _, body_id, _, is_generator) => { let body = self.fcx.tcx.hir.body(body_id); self.visit_body(body); - self.fcx.analyze_closure(expr.id, expr.span, body, cc, + self.fcx.analyze_closure((expr.id, expr.hir_id), + expr.span, + body, + cc, is_generator); } @@ -91,7 +95,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for InferBorrowKindVisitor<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn analyze_closure(&self, - id: ast::NodeId, + (closure_node_id, closure_hir_id): (ast::NodeId, hir::HirId), span: Span, body: &hir::Body, capture_clause: hir::CaptureClause, @@ -100,25 +104,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { * Analysis starting point. */ - debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id()); + debug!("analyze_closure(id={:?}, body.id={:?})", closure_node_id, body.id()); - let infer_kind = if gen { false } else { - match self.tables.borrow_mut().closure_kinds.entry(id) { + let infer_kind = if gen { + false + } else { + match self.tables + .borrow_mut() + .closure_kinds_mut() + .entry(closure_hir_id) { Entry::Occupied(_) => false, Entry::Vacant(entry) => { - debug!("check_closure: adding closure {:?} as Fn", id); + debug!("check_closure: adding closure {:?} as Fn", closure_node_id); entry.insert((ty::ClosureKind::Fn, None)); true } } }; - self.tcx.with_freevars(id, |freevars| { + let closure_def_id = self.tcx.hir.local_def_id(closure_node_id); + + self.tcx.with_freevars(closure_node_id, |freevars| { for freevar in freevars { - let def_id = freevar.def.def_id(); - let var_node_id = self.tcx.hir.as_local_node_id(def_id).unwrap(); - let upvar_id = ty::UpvarId { var_id: var_node_id, - closure_expr_id: id }; + let var_def_id = freevar.def.def_id(); + let upvar_id = ty::UpvarId { + var_id: var_def_id.index, + closure_expr_id: closure_def_id.index, + }; debug!("seed upvar_id {:?}", upvar_id); let capture_kind = match capture_clause { @@ -143,7 +155,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let region_maps = &self.tcx.region_maps(body_owner_def_id); let mut delegate = InferBorrowKind { fcx: self, - adjust_closure_kinds: NodeMap(), + adjust_closure_kinds: FxHashMap(), adjust_upvar_captures: ty::UpvarCaptureMap::default(), }; euv::ExprUseVisitor::with_infer(&mut delegate, @@ -155,8 +167,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Write the adjusted values back into the main tables. if infer_kind { - if let Some(kind) = delegate.adjust_closure_kinds.remove(&id) { - self.tables.borrow_mut().closure_kinds.insert(id, kind); + if let Some(kind) = delegate.adjust_closure_kinds + .remove(&closure_def_id.index) { + self.tables + .borrow_mut() + .closure_kinds_mut() + .insert(closure_hir_id, kind); } } self.tables.borrow_mut().upvar_capture_map.extend( @@ -176,21 +192,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // inference algorithm will reject it). // Extract the type variables UV0...UVn. - let (def_id, closure_substs) = match self.node_ty(id).sty { + let (def_id, closure_substs) = match self.node_ty(closure_hir_id).sty { ty::TyClosure(def_id, substs) | ty::TyGenerator(def_id, substs, _) => (def_id, substs), ref t => { span_bug!( span, "type of closure expr {:?} is not a closure {:?}", - id, t); + closure_node_id, t); } }; // Equate the type variables with the actual types. - let final_upvar_tys = self.final_upvar_tys(id); + let final_upvar_tys = self.final_upvar_tys(closure_node_id); debug!("analyze_closure: id={:?} closure_substs={:?} final_upvar_tys={:?}", - id, closure_substs, final_upvar_tys); + closure_node_id, closure_substs, final_upvar_tys); for (upvar_ty, final_upvar_ty) in closure_substs.upvar_tys(def_id, self.tcx).zip(final_upvar_tys) { @@ -200,7 +216,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // If we are also inferred the closure kind here, // process any deferred resolutions. if infer_kind { - let closure_def_id = self.tcx.hir.local_def_id(id); let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); for deferred_call_resolution in deferred_call_resolutions { @@ -217,19 +232,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // This may change if abstract return types of some sort are // implemented. let tcx = self.tcx; + let closure_def_index = tcx.hir.local_def_id(closure_id).index; + tcx.with_freevars(closure_id, |freevars| { freevars.iter().map(|freevar| { - let def_id = freevar.def.def_id(); - let var_id = tcx.hir.as_local_node_id(def_id).unwrap(); - let freevar_ty = self.node_ty(var_id); + let var_def_id = freevar.def.def_id(); + let var_node_id = tcx.hir.as_local_node_id(var_def_id).unwrap(); + let freevar_ty = self.node_ty(tcx.hir.node_to_hir_id(var_node_id)); let upvar_id = ty::UpvarId { - var_id: var_id, - closure_expr_id: closure_id + var_id: var_def_id.index, + closure_expr_id: closure_def_index, }; let capture = self.tables.borrow().upvar_capture(upvar_id); debug!("var_id={:?} freevar_ty={:?} capture={:?}", - var_id, freevar_ty, capture); + var_node_id, freevar_ty, capture); match capture { ty::UpvarCapture::ByValue => freevar_ty, @@ -247,7 +264,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { struct InferBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - adjust_closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>, + adjust_closure_kinds: FxHashMap)>, adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>, } @@ -286,7 +303,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnOnce, guarantor.span, - tcx.hir.name(upvar_id.var_id)); + var_name(tcx, upvar_id.var_id)); self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue); } @@ -300,7 +317,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnOnce, guarantor.span, - tcx.hir.name(upvar_id.var_id)); + var_name(tcx, upvar_id.var_id)); } mc::NoteNone => { } @@ -405,7 +422,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnMut, cmt.span, - tcx.hir.name(upvar_id.var_id)); + var_name(tcx, upvar_id.var_id)); true } @@ -416,7 +433,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnMut, cmt.span, - tcx.hir.name(upvar_id.var_id)); + var_name(tcx, upvar_id.var_id)); true } @@ -465,17 +482,21 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { } fn adjust_closure_kind(&mut self, - closure_id: ast::NodeId, + closure_id: DefIndex, new_kind: ty::ClosureKind, upvar_span: Span, var_name: ast::Name) { - debug!("adjust_closure_kind(closure_id={}, new_kind={:?}, upvar_span={:?}, var_name={})", + debug!("adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})", closure_id, new_kind, upvar_span, var_name); let closure_kind = self.adjust_closure_kinds.get(&closure_id).cloned() - .or_else(|| self.fcx.tables.borrow().closure_kinds.get(&closure_id).cloned()); + .or_else(|| { + let closure_id = self.fcx.tcx.hir.def_index_to_hir_id(closure_id); + self.fcx.tables.borrow().closure_kinds().get(closure_id).cloned() + }); + if let Some((existing_kind, _)) = closure_kind { - debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}", + debug!("adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}", closure_id, existing_kind, new_kind); match (existing_kind, new_kind) { @@ -565,3 +586,8 @@ impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'gcx, 'tcx> { self.adjust_upvar_borrow_kind_for_mut(assignee_cmt); } } + +fn var_name(tcx: ty::TyCtxt, var_def_index: DefIndex) -> ast::Name { + let var_node_id = tcx.hir.def_index_to_node_id(var_def_index); + tcx.hir.name(var_node_id) +} diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index cf5882bb9bdb..31e14a6b630d 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -255,7 +255,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { // We want to ensure: // // 1) that there are no items contained within - // the trait defintion + // the trait definition // // 2) that the definition doesn't violate the no-super trait rule // for auto traits. diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index c18a070300a2..54be3507774d 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -14,6 +14,7 @@ use check::FnCtxt; use rustc::hir; +use rustc::hir::def_id::{DefId, DefIndex}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::infer::{InferCtxt}; use rustc::ty::{self, Ty, TyCtxt}; @@ -34,7 +35,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut wbcx = WritebackCx::new(self, body); for arg in &body.arguments { - wbcx.visit_node_id(arg.pat.span, arg.id); + wbcx.visit_node_id(arg.pat.span, arg.hir_id); } wbcx.visit_body(body); wbcx.visit_upvar_borrow_map(); @@ -76,10 +77,13 @@ struct WritebackCx<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>, body: &'gcx hir::Body) - -> WritebackCx<'cx, 'gcx, 'tcx> { + -> WritebackCx<'cx, 'gcx, 'tcx> + { + let owner = fcx.tcx.hir.definitions().node_to_hir_id(body.id().node_id); + WritebackCx { fcx: fcx, - tables: ty::TypeckTables::empty(), + tables: ty::TypeckTables::empty(Some(DefId::local(owner.owner))), body: body } } @@ -88,10 +92,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { self.fcx.tcx } - fn write_ty_to_tables(&mut self, node_id: ast::NodeId, ty: Ty<'gcx>) { - debug!("write_ty_to_tables({}, {:?})", node_id, ty); + fn write_ty_to_tables(&mut self, hir_id: hir::HirId, ty: Ty<'gcx>) { + debug!("write_ty_to_tables({:?}, {:?})", hir_id, ty); assert!(!ty.needs_infer()); - self.tables.node_types.insert(node_id, ty); + self.tables.node_types_mut().insert(hir_id, ty); } // Hacky hack: During type-checking, we treat *all* operators @@ -102,37 +106,38 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { match e.node { hir::ExprUnary(hir::UnNeg, ref inner) | hir::ExprUnary(hir::UnNot, ref inner) => { - let inner_ty = self.fcx.node_ty(inner.id); + let inner_ty = self.fcx.node_ty(inner.hir_id); let inner_ty = self.fcx.resolve_type_vars_if_possible(&inner_ty); if inner_ty.is_scalar() { let mut tables = self.fcx.tables.borrow_mut(); - tables.type_dependent_defs.remove(&e.id); - tables.node_substs.remove(&e.id); + tables.type_dependent_defs_mut().remove(e.hir_id); + tables.node_substs_mut().remove(e.hir_id); } } hir::ExprBinary(ref op, ref lhs, ref rhs) | hir::ExprAssignOp(ref op, ref lhs, ref rhs) => { - let lhs_ty = self.fcx.node_ty(lhs.id); + let lhs_ty = self.fcx.node_ty(lhs.hir_id); let lhs_ty = self.fcx.resolve_type_vars_if_possible(&lhs_ty); - let rhs_ty = self.fcx.node_ty(rhs.id); + let rhs_ty = self.fcx.node_ty(rhs.hir_id); let rhs_ty = self.fcx.resolve_type_vars_if_possible(&rhs_ty); if lhs_ty.is_scalar() && rhs_ty.is_scalar() { let mut tables = self.fcx.tables.borrow_mut(); - tables.type_dependent_defs.remove(&e.id); - tables.node_substs.remove(&e.id); + tables.type_dependent_defs_mut().remove(e.hir_id); + tables.node_substs_mut().remove(e.hir_id); match e.node { hir::ExprBinary(..) => { if !op.node.is_by_value() { - tables.adjustments.get_mut(&lhs.id).map(|a| a.pop()); - tables.adjustments.get_mut(&rhs.id).map(|a| a.pop()); + let mut adjustments = tables.adjustments_mut(); + adjustments.get_mut(lhs.hir_id).map(|a| a.pop()); + adjustments.get_mut(rhs.hir_id).map(|a| a.pop()); } }, hir::ExprAssignOp(..) => { - tables.adjustments.get_mut(&lhs.id).map(|a| a.pop()); + tables.adjustments_mut().get_mut(lhs.hir_id).map(|a| a.pop()); }, _ => {}, } @@ -159,13 +164,13 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { fn visit_expr(&mut self, e: &'gcx hir::Expr) { self.fix_scalar_builtin_expr(e); - self.visit_node_id(e.span, e.id); + self.visit_node_id(e.span, e.hir_id); if let hir::ExprClosure(_, _, body, _, _) = e.node { let body = self.fcx.tcx.hir.body(body); // FIXME: Why visit the args here? for arg in &body.arguments { - self.visit_node_id(e.span, arg.id); + self.visit_node_id(e.span, arg.hir_id); } self.visit_body(body); @@ -175,21 +180,25 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { } fn visit_block(&mut self, b: &'gcx hir::Block) { - self.visit_node_id(b.span, b.id); + self.visit_node_id(b.span, b.hir_id); intravisit::walk_block(self, b); } fn visit_pat(&mut self, p: &'gcx hir::Pat) { match p.node { hir::PatKind::Binding(..) => { - let bm = *self.fcx.tables.borrow().pat_binding_modes.get(&p.id) - .expect("missing binding mode"); - self.tables.pat_binding_modes.insert(p.id, bm); + let bm = *self.fcx + .tables + .borrow() + .pat_binding_modes() + .get(p.hir_id) + .expect("missing binding mode"); + self.tables.pat_binding_modes_mut().insert(p.hir_id, bm); } _ => {} }; - self.visit_node_id(p.span, p.id); + self.visit_node_id(p.span, p.hir_id); intravisit::walk_pat(self, p); } @@ -197,7 +206,7 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { intravisit::walk_local(self, l); let var_ty = self.fcx.local_ty(l.span, l.id); let var_ty = self.resolve(&var_ty, &l.span); - self.write_ty_to_tables(l.id, var_ty); + self.write_ty_to_tables(l.hir_id, var_ty); } } @@ -221,19 +230,42 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } fn visit_closures(&mut self) { - for (&id, closure_ty) in self.fcx.tables.borrow().closure_tys.iter() { - let closure_ty = self.resolve(closure_ty, &id); - self.tables.closure_tys.insert(id, closure_ty); + let fcx_tables = self.fcx.tables.borrow(); + debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + let common_local_id_root = fcx_tables.local_id_root.unwrap(); + + for (&id, closure_ty) in fcx_tables.closure_tys().iter() { + let hir_id = hir::HirId { + owner: common_local_id_root.index, + local_id: id, + }; + let closure_ty = self.resolve(closure_ty, &hir_id); + self.tables.closure_tys_mut().insert(hir_id, closure_ty); } - for (&id, &closure_kind) in self.fcx.tables.borrow().closure_kinds.iter() { - self.tables.closure_kinds.insert(id, closure_kind); + for (&id, &closure_kind) in fcx_tables.closure_kinds().iter() { + let hir_id = hir::HirId { + owner: common_local_id_root.index, + local_id: id, + }; + self.tables.closure_kinds_mut().insert(hir_id, closure_kind); } } fn visit_cast_types(&mut self) { - self.tables.cast_kinds.extend( - self.fcx.tables.borrow().cast_kinds.iter().map(|(&key, &value)| (key, value))); + let fcx_tables = self.fcx.tables.borrow(); + let fcx_cast_kinds = fcx_tables.cast_kinds(); + debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + let mut self_cast_kinds = self.tables.cast_kinds_mut(); + let common_local_id_root = fcx_tables.local_id_root.unwrap(); + + for (&local_id, &cast_kind) in fcx_cast_kinds.iter() { + let hir_id = hir::HirId { + owner: common_local_id_root.index, + local_id, + }; + self_cast_kinds.insert(hir_id, cast_kind); + } } fn visit_free_region_map(&mut self) { @@ -276,45 +308,54 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } }); - self.tables.node_types.insert(node_id, outside_ty); + let hir_id = self.tcx().hir.node_to_hir_id(node_id); + self.tables.node_types_mut().insert(hir_id, outside_ty); } } - fn visit_node_id(&mut self, span: Span, node_id: ast::NodeId) { + fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) { // Export associated path extensions and method resultions. - if let Some(def) = self.fcx.tables.borrow_mut().type_dependent_defs.remove(&node_id) { - self.tables.type_dependent_defs.insert(node_id, def); + if let Some(def) = self.fcx + .tables + .borrow_mut() + .type_dependent_defs_mut() + .remove(hir_id) { + self.tables.type_dependent_defs_mut().insert(hir_id, def); } // Resolve any borrowings for the node with id `node_id` - self.visit_adjustments(span, node_id); + self.visit_adjustments(span, hir_id); // Resolve the type of the node with id `node_id` - let n_ty = self.fcx.node_ty(node_id); + let n_ty = self.fcx.node_ty(hir_id); let n_ty = self.resolve(&n_ty, &span); - self.write_ty_to_tables(node_id, n_ty); - debug!("Node {} has type {:?}", node_id, n_ty); + self.write_ty_to_tables(hir_id, n_ty); + debug!("Node {:?} has type {:?}", hir_id, n_ty); // Resolve any substitutions - if let Some(&substs) = self.fcx.tables.borrow().node_substs.get(&node_id) { + if let Some(substs) = self.fcx.tables.borrow().node_substs_opt(hir_id) { let substs = self.resolve(&substs, &span); - debug!("write_substs_to_tcx({}, {:?})", node_id, substs); + debug!("write_substs_to_tcx({:?}, {:?})", hir_id, substs); assert!(!substs.needs_infer()); - self.tables.node_substs.insert(node_id, substs); + self.tables.node_substs_mut().insert(hir_id, substs); } } - fn visit_adjustments(&mut self, span: Span, node_id: ast::NodeId) { - let adjustment = self.fcx.tables.borrow_mut().adjustments.remove(&node_id); + fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) { + let adjustment = self.fcx + .tables + .borrow_mut() + .adjustments_mut() + .remove(hir_id); match adjustment { None => { - debug!("No adjustments for node {}", node_id); + debug!("No adjustments for node {:?}", hir_id); } Some(adjustment) => { let resolved_adjustment = self.resolve(&adjustment, &span); - debug!("Adjustments for node {}: {:?}", node_id, resolved_adjustment); - self.tables.adjustments.insert(node_id, resolved_adjustment); + debug!("Adjustments for node {:?}: {:?}", hir_id, resolved_adjustment); + self.tables.adjustments_mut().insert(hir_id, resolved_adjustment); } } } @@ -337,16 +378,32 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } fn visit_liberated_fn_sigs(&mut self) { - for (&node_id, fn_sig) in self.fcx.tables.borrow().liberated_fn_sigs.iter() { - let fn_sig = self.resolve(fn_sig, &node_id); - self.tables.liberated_fn_sigs.insert(node_id, fn_sig.clone()); + let fcx_tables = self.fcx.tables.borrow(); + debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + let common_local_id_root = fcx_tables.local_id_root.unwrap(); + + for (&local_id, fn_sig) in fcx_tables.liberated_fn_sigs().iter() { + let hir_id = hir::HirId { + owner: common_local_id_root.index, + local_id, + }; + let fn_sig = self.resolve(fn_sig, &hir_id); + self.tables.liberated_fn_sigs_mut().insert(hir_id, fn_sig.clone()); } } fn visit_fru_field_types(&mut self) { - for (&node_id, ftys) in self.fcx.tables.borrow().fru_field_types.iter() { - let ftys = self.resolve(ftys, &node_id); - self.tables.fru_field_types.insert(node_id, ftys); + let fcx_tables = self.fcx.tables.borrow(); + debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + let common_local_id_root = fcx_tables.local_id_root.unwrap(); + + for (&local_id, ftys) in fcx_tables.fru_field_types().iter() { + let hir_id = hir::HirId { + owner: common_local_id_root.index, + local_id, + }; + let ftys = self.resolve(ftys, &hir_id); + self.tables.fru_field_types_mut().insert(hir_id, ftys); } } @@ -376,6 +433,20 @@ impl Locatable for ast::NodeId { fn to_span(&self, tcx: &TyCtxt) -> Span { tcx.hir.span(*self) } } +impl Locatable for DefIndex { + fn to_span(&self, tcx: &TyCtxt) -> Span { + let node_id = tcx.hir.def_index_to_node_id(*self); + tcx.hir.span(node_id) + } +} + +impl Locatable for hir::HirId { + fn to_span(&self, tcx: &TyCtxt) -> Span { + let node_id = tcx.hir.definitions().find_node_for_hir_id(*self); + tcx.hir.span(node_id) + } +} + /////////////////////////////////////////////////////////////////////////// // The Resolver. This is the type folding engine that detects // unresolved types and so forth. diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 0ce5c96da763..6060312f51e8 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1191,7 +1191,8 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, NodeTy(&hir::Ty { node: TyImplTrait(..), .. }) => { let owner = tcx.hir.get_parent_did(node_id); - tcx.typeck_tables_of(owner).node_id_to_type(node_id) + let hir_id = tcx.hir.node_to_hir_id(node_id); + tcx.typeck_tables_of(owner).node_id_to_type(hir_id) } x => { @@ -1242,8 +1243,8 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, )) } - NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => { - tcx.typeck_tables_of(def_id).closure_tys[&node_id] + NodeExpr(&hir::Expr { node: hir::ExprClosure(..), hir_id, .. }) => { + tcx.typeck_tables_of(def_id).closure_tys()[hir_id] } x => { diff --git a/src/librustc_typeck/constrained_type_params.rs b/src/librustc_typeck/constrained_type_params.rs index 7742194dfe6e..37cb1f9280b6 100644 --- a/src/librustc_typeck/constrained_type_params.rs +++ b/src/librustc_typeck/constrained_type_params.rs @@ -98,7 +98,7 @@ pub fn identify_constrained_type_params<'tcx>(tcx: ty::TyCtxt, /// Order the predicates in `predicates` such that each parameter is /// constrained before it is used, if that is possible, and add the -/// paramaters so constrained to `input_parameters`. For example, +/// parameters so constrained to `input_parameters`. For example, /// imagine the following impl: /// /// impl> Trait for U diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index aa334bc570a7..ba2a8594ec0e 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -51,7 +51,7 @@ pub struct Constraint<'a> { pub variance: &'a VarianceTerm<'a>, } -/// To build constriants, we visit one item (type, trait) at a time +/// To build constraints, we visit one item (type, trait) at a time /// and look at its contents. So e.g. if we have /// /// struct Foo { diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs new file mode 100644 index 000000000000..da8c3a5cf206 --- /dev/null +++ b/src/librustdoc/clean/cfg.rs @@ -0,0 +1,889 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Representation of a `#[doc(cfg(...))]` attribute. + +// FIXME: Once RFC #1868 is implemented, switch to use those structures instead. + +use std::mem; +use std::fmt::{self, Write}; +use std::ops; +use std::ascii::AsciiExt; + +use syntax::symbol::Symbol; +use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind, LitKind}; +use syntax::parse::ParseSess; +use syntax::feature_gate::Features; + +use syntax_pos::Span; + +use html::escape::Escape; + +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, PartialEq)] +pub enum Cfg { + /// Accepts all configurations. + True, + /// Denies all configurations. + False, + /// A generic configration option, e.g. `test` or `target_os = "linux"`. + Cfg(Symbol, Option), + /// Negate a configuration requirement, i.e. `not(x)`. + Not(Box), + /// Union of a list of configuration requirements, i.e. `any(...)`. + Any(Vec), + /// Intersection of a list of configuration requirements, i.e. `all(...)`. + All(Vec), +} + +#[derive(PartialEq, Debug)] +pub struct InvalidCfgError { + pub msg: &'static str, + pub span: Span, +} + +impl Cfg { + /// Parses a `NestedMetaItem` into a `Cfg`. + fn parse_nested(nested_cfg: &NestedMetaItem) -> Result { + match nested_cfg.node { + NestedMetaItemKind::MetaItem(ref cfg) => Cfg::parse(cfg), + NestedMetaItemKind::Literal(ref lit) => Err(InvalidCfgError { + msg: "unexpected literal", + span: lit.span, + }), + } + } + + /// Parses a `MetaItem` into a `Cfg`. + /// + /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g. `unix` or + /// `target_os = "redox"`. + /// + /// If the content is not properly formatted, it will return an error indicating what and where + /// the error is. + pub fn parse(cfg: &MetaItem) -> Result { + let name = cfg.name(); + match cfg.node { + MetaItemKind::Word => Ok(Cfg::Cfg(name, None)), + MetaItemKind::NameValue(ref lit) => match lit.node { + LitKind::Str(value, _) => Ok(Cfg::Cfg(name, Some(value))), + _ => Err(InvalidCfgError { + // FIXME: if the main #[cfg] syntax decided to support non-string literals, + // this should be changed as well. + msg: "value of cfg option should be a string literal", + span: lit.span, + }), + }, + MetaItemKind::List(ref items) => { + let mut sub_cfgs = items.iter().map(Cfg::parse_nested); + match &*name.as_str() { + "all" => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)), + "any" => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)), + "not" => if sub_cfgs.len() == 1 { + Ok(!sub_cfgs.next().unwrap()?) + } else { + Err(InvalidCfgError { + msg: "expected 1 cfg-pattern", + span: cfg.span, + }) + }, + _ => Err(InvalidCfgError { + msg: "invalid predicate", + span: cfg.span, + }), + } + } + } + } + + /// Checks whether the given configuration can be matched in the current session. + /// + /// Equivalent to `attr::cfg_matches`. + // FIXME: Actually make use of `features`. + pub fn matches(&self, parse_sess: &ParseSess, features: Option<&Features>) -> bool { + match *self { + Cfg::False => false, + Cfg::True => true, + Cfg::Not(ref child) => !child.matches(parse_sess, features), + Cfg::All(ref sub_cfgs) => { + sub_cfgs.iter().all(|sub_cfg| sub_cfg.matches(parse_sess, features)) + }, + Cfg::Any(ref sub_cfgs) => { + sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(parse_sess, features)) + }, + Cfg::Cfg(name, value) => parse_sess.config.contains(&(name, value)), + } + } + + /// Whether the configuration consists of just `Cfg` or `Not`. + fn is_simple(&self) -> bool { + match *self { + Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true, + Cfg::All(..) | Cfg::Any(..) => false, + } + } + + /// Whether the configuration consists of just `Cfg`, `Not` or `All`. + fn is_all(&self) -> bool { + match *self { + Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true, + Cfg::Any(..) => false, + } + } + + /// Renders the configuration for human display, as a short HTML description. + pub(crate) fn render_short_html(&self) -> String { + let mut msg = Html(self).to_string(); + if self.should_capitalize_first_letter() { + if let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) { + msg[i .. i+1].make_ascii_uppercase(); + } + } + msg + } + + /// Renders the configuration for long display, as a long HTML description. + pub(crate) fn render_long_html(&self) -> String { + let mut msg = format!("This is supported on {}", Html(self)); + if self.should_append_only_to_description() { + msg.push_str(" only"); + } + msg.push('.'); + msg + } + + fn should_capitalize_first_letter(&self) -> bool { + match *self { + Cfg::False | Cfg::True | Cfg::Not(..) => true, + Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => { + sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false) + }, + Cfg::Cfg(name, _) => match &*name.as_str() { + "debug_assertions" | "target_endian" => true, + _ => false, + }, + } + } + + fn should_append_only_to_description(&self) -> bool { + match *self { + Cfg::False | Cfg::True => false, + Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true, + Cfg::Not(ref child) => match **child { + Cfg::Cfg(..) => true, + _ => false, + } + } + } +} + +impl ops::Not for Cfg { + type Output = Cfg; + fn not(self) -> Cfg { + match self { + Cfg::False => Cfg::True, + Cfg::True => Cfg::False, + Cfg::Not(cfg) => *cfg, + s => Cfg::Not(Box::new(s)), + } + } +} + +impl ops::BitAndAssign for Cfg { + fn bitand_assign(&mut self, other: Cfg) { + match (self, other) { + (&mut Cfg::False, _) | (_, Cfg::True) => {}, + (s, Cfg::False) => *s = Cfg::False, + (s @ &mut Cfg::True, b) => *s = b, + (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => a.append(b), + (&mut Cfg::All(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)), + (s, Cfg::All(mut a)) => { + let b = mem::replace(s, Cfg::True); + a.push(b); + *s = Cfg::All(a); + }, + (s, b) => { + let a = mem::replace(s, Cfg::True); + *s = Cfg::All(vec![a, b]); + }, + } + } +} + +impl ops::BitAnd for Cfg { + type Output = Cfg; + fn bitand(mut self, other: Cfg) -> Cfg { + self &= other; + self + } +} + +impl ops::BitOrAssign for Cfg { + fn bitor_assign(&mut self, other: Cfg) { + match (self, other) { + (&mut Cfg::True, _) | (_, Cfg::False) => {}, + (s, Cfg::True) => *s = Cfg::True, + (s @ &mut Cfg::False, b) => *s = b, + (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => a.append(b), + (&mut Cfg::Any(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)), + (s, Cfg::Any(mut a)) => { + let b = mem::replace(s, Cfg::True); + a.push(b); + *s = Cfg::Any(a); + }, + (s, b) => { + let a = mem::replace(s, Cfg::True); + *s = Cfg::Any(vec![a, b]); + }, + } + } +} + +impl ops::BitOr for Cfg { + type Output = Cfg; + fn bitor(mut self, other: Cfg) -> Cfg { + self |= other; + self + } +} + +struct Html<'a>(&'a Cfg); + +fn write_with_opt_paren( + fmt: &mut fmt::Formatter, + has_paren: bool, + obj: T, +) -> fmt::Result { + if has_paren { + fmt.write_char('(')?; + } + obj.fmt(fmt)?; + if has_paren { + fmt.write_char(')')?; + } + Ok(()) +} + + +impl<'a> fmt::Display for Html<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self.0 { + Cfg::Not(ref child) => match **child { + Cfg::Any(ref sub_cfgs) => { + let separator = if sub_cfgs.iter().all(Cfg::is_simple) { + " nor " + } else { + ", nor " + }; + for (i, sub_cfg) in sub_cfgs.iter().enumerate() { + fmt.write_str(if i == 0 { "neither " } else { separator })?; + write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?; + } + Ok(()) + } + ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Html(simple)), + ref c => write!(fmt, "not ({})", Html(c)), + }, + + Cfg::Any(ref sub_cfgs) => { + let separator = if sub_cfgs.iter().all(Cfg::is_simple) { + " or " + } else { + ", or " + }; + for (i, sub_cfg) in sub_cfgs.iter().enumerate() { + if i != 0 { + fmt.write_str(separator)?; + } + write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?; + } + Ok(()) + }, + + Cfg::All(ref sub_cfgs) => { + for (i, sub_cfg) in sub_cfgs.iter().enumerate() { + if i != 0 { + fmt.write_str(" and ")?; + } + write_with_opt_paren(fmt, !sub_cfg.is_simple(), Html(sub_cfg))?; + } + Ok(()) + }, + + Cfg::True => fmt.write_str("everywhere"), + Cfg::False => fmt.write_str("nowhere"), + + Cfg::Cfg(name, value) => { + let n = &*name.as_str(); + let human_readable = match (n, value) { + ("unix", None) => "Unix", + ("windows", None) => "Windows", + ("debug_assertions", None) => "debug-assertions enabled", + ("target_os", Some(os)) => match &*os.as_str() { + "android" => "Android", + "bitrig" => "Bitrig", + "dragonfly" => "DragonFly BSD", + "emscripten" => "Emscripten", + "freebsd" => "FreeBSD", + "fuchsia" => "Fuchsia", + "haiku" => "Haiku", + "ios" => "iOS", + "l4re" => "L4Re", + "linux" => "Linux", + "macos" => "macOS", + "nacl" => "NaCl", + "netbsd" => "NetBSD", + "openbsd" => "OpenBSD", + "redox" => "Redox", + "solaris" => "Solaris", + "windows" => "Windows", + _ => "", + }, + ("target_arch", Some(arch)) => match &*arch.as_str() { + "aarch64" => "AArch64", + "arm" => "ARM", + "asmjs" => "asm.js", + "mips" => "MIPS", + "mips64" => "MIPS-64", + "msp430" => "MSP430", + "powerpc" => "PowerPC", + "powerpc64" => "PowerPC-64", + "s390x" => "s390x", + "sparc64" => "SPARC64", + "wasm32" => "WebAssembly", + "x86" => "x86", + "x86_64" => "x86-64", + _ => "", + }, + ("target_vendor", Some(vendor)) => match &*vendor.as_str() { + "apple" => "Apple", + "pc" => "PC", + "rumprun" => "Rumprun", + "sun" => "Sun", + _ => "" + }, + ("target_env", Some(env)) => match &*env.as_str() { + "gnu" => "GNU", + "msvc" => "MSVC", + "musl" => "musl", + "newlib" => "Newlib", + "uclibc" => "uClibc", + _ => "", + }, + ("target_endian", Some(endian)) => return write!(fmt, "{}-endian", endian), + ("target_pointer_width", Some(bits)) => return write!(fmt, "{}-bit", bits), + _ => "", + }; + if !human_readable.is_empty() { + fmt.write_str(human_readable) + } else if let Some(v) = value { + write!(fmt, "{}=\"{}\"", Escape(n), Escape(&*v.as_str())) + } else { + write!(fmt, "{}", Escape(n)) + } + } + } + } +} + +#[cfg(test)] +mod test { + use super::Cfg; + + use syntax::symbol::Symbol; + use syntax::ast::*; + use syntax::codemap::dummy_spanned; + use syntax_pos::DUMMY_SP; + + fn word_cfg(s: &str) -> Cfg { + Cfg::Cfg(Symbol::intern(s), None) + } + + fn name_value_cfg(name: &str, value: &str) -> Cfg { + Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value))) + } + + #[test] + fn test_cfg_not() { + assert_eq!(!Cfg::False, Cfg::True); + assert_eq!(!Cfg::True, Cfg::False); + assert_eq!(!word_cfg("test"), Cfg::Not(Box::new(word_cfg("test")))); + assert_eq!( + !Cfg::All(vec![word_cfg("a"), word_cfg("b")]), + Cfg::Not(Box::new(Cfg::All(vec![word_cfg("a"), word_cfg("b")]))) + ); + assert_eq!( + !Cfg::Any(vec![word_cfg("a"), word_cfg("b")]), + Cfg::Not(Box::new(Cfg::Any(vec![word_cfg("a"), word_cfg("b")]))) + ); + assert_eq!(!Cfg::Not(Box::new(word_cfg("test"))), word_cfg("test")); + } + + #[test] + fn test_cfg_and() { + let mut x = Cfg::False; + x &= Cfg::True; + assert_eq!(x, Cfg::False); + + x = word_cfg("test"); + x &= Cfg::False; + assert_eq!(x, Cfg::False); + + x = word_cfg("test2"); + x &= Cfg::True; + assert_eq!(x, word_cfg("test2")); + + x = Cfg::True; + x &= word_cfg("test3"); + assert_eq!(x, word_cfg("test3")); + + x &= word_cfg("test4"); + assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")])); + + x &= word_cfg("test5"); + assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")])); + + x &= Cfg::All(vec![word_cfg("test6"), word_cfg("test7")]); + assert_eq!(x, Cfg::All(vec![ + word_cfg("test3"), + word_cfg("test4"), + word_cfg("test5"), + word_cfg("test6"), + word_cfg("test7"), + ])); + + let mut y = Cfg::Any(vec![word_cfg("a"), word_cfg("b")]); + y &= x; + assert_eq!(y, Cfg::All(vec![ + word_cfg("test3"), + word_cfg("test4"), + word_cfg("test5"), + word_cfg("test6"), + word_cfg("test7"), + Cfg::Any(vec![word_cfg("a"), word_cfg("b")]), + ])); + + assert_eq!( + word_cfg("a") & word_cfg("b") & word_cfg("c"), + Cfg::All(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")]) + ); + } + + #[test] + fn test_cfg_or() { + let mut x = Cfg::True; + x |= Cfg::False; + assert_eq!(x, Cfg::True); + + x = word_cfg("test"); + x |= Cfg::True; + assert_eq!(x, Cfg::True); + + x = word_cfg("test2"); + x |= Cfg::False; + assert_eq!(x, word_cfg("test2")); + + x = Cfg::False; + x |= word_cfg("test3"); + assert_eq!(x, word_cfg("test3")); + + x |= word_cfg("test4"); + assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")])); + + x |= word_cfg("test5"); + assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")])); + + x |= Cfg::Any(vec![word_cfg("test6"), word_cfg("test7")]); + assert_eq!(x, Cfg::Any(vec![ + word_cfg("test3"), + word_cfg("test4"), + word_cfg("test5"), + word_cfg("test6"), + word_cfg("test7"), + ])); + + let mut y = Cfg::All(vec![word_cfg("a"), word_cfg("b")]); + y |= x; + assert_eq!(y, Cfg::Any(vec![ + word_cfg("test3"), + word_cfg("test4"), + word_cfg("test5"), + word_cfg("test6"), + word_cfg("test7"), + Cfg::All(vec![word_cfg("a"), word_cfg("b")]), + ])); + + assert_eq!( + word_cfg("a") | word_cfg("b") | word_cfg("c"), + Cfg::Any(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")]) + ); + } + + #[test] + fn test_parse_ok() { + let mi = MetaItem { + name: Symbol::intern("all"), + node: MetaItemKind::Word, + span: DUMMY_SP, + }; + assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all"))); + + let mi = MetaItem { + name: Symbol::intern("all"), + node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str( + Symbol::intern("done"), + StrStyle::Cooked, + ))), + span: DUMMY_SP, + }; + assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done"))); + + let mi = MetaItem { + name: Symbol::intern("all"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("b"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b"))); + + let mi = MetaItem { + name: Symbol::intern("any"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("b"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b"))); + + let mi = MetaItem { + name: Symbol::intern("not"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a"))); + + let mi = MetaItem { + name: Symbol::intern("not"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("any"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("all"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("b"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("c"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c"))))); + + let mi = MetaItem { + name: Symbol::intern("all"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("b"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("c"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c"))); + } + + #[test] + fn test_parse_err() { + let mi = MetaItem { + name: Symbol::intern("foo"), + node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))), + span: DUMMY_SP, + }; + assert!(Cfg::parse(&mi).is_err()); + + let mi = MetaItem { + name: Symbol::intern("not"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("b"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert!(Cfg::parse(&mi).is_err()); + + let mi = MetaItem { + name: Symbol::intern("not"), + node: MetaItemKind::List(vec![]), + span: DUMMY_SP, + }; + assert!(Cfg::parse(&mi).is_err()); + + let mi = MetaItem { + name: Symbol::intern("foo"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert!(Cfg::parse(&mi).is_err()); + + let mi = MetaItem { + name: Symbol::intern("all"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("foo"), + node: MetaItemKind::List(vec![]), + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("b"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert!(Cfg::parse(&mi).is_err()); + + let mi = MetaItem { + name: Symbol::intern("any"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("a"), + node: MetaItemKind::Word, + span: DUMMY_SP, + })), + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("foo"), + node: MetaItemKind::List(vec![]), + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert!(Cfg::parse(&mi).is_err()); + + let mi = MetaItem { + name: Symbol::intern("not"), + node: MetaItemKind::List(vec![ + dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { + name: Symbol::intern("foo"), + node: MetaItemKind::List(vec![]), + span: DUMMY_SP, + })), + ]), + span: DUMMY_SP, + }; + assert!(Cfg::parse(&mi).is_err()); + } + + #[test] + fn test_render_short_html() { + assert_eq!( + word_cfg("unix").render_short_html(), + "Unix" + ); + assert_eq!( + name_value_cfg("target_os", "macos").render_short_html(), + "macOS" + ); + assert_eq!( + name_value_cfg("target_pointer_width", "16").render_short_html(), + "16-bit" + ); + assert_eq!( + name_value_cfg("target_endian", "little").render_short_html(), + "Little-endian" + ); + assert_eq!( + (!word_cfg("windows")).render_short_html(), + "Non-Windows" + ); + assert_eq!( + (word_cfg("unix") & word_cfg("windows")).render_short_html(), + "Unix and Windows" + ); + assert_eq!( + (word_cfg("unix") | word_cfg("windows")).render_short_html(), + "Unix or Windows" + ); + assert_eq!( + ( + word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions") + ).render_short_html(), + "Unix and Windows and debug-assertions enabled" + ); + assert_eq!( + ( + word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions") + ).render_short_html(), + "Unix or Windows or debug-assertions enabled" + ); + assert_eq!( + ( + !(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")) + ).render_short_html(), + "Neither Unix nor Windows nor debug-assertions enabled" + ); + assert_eq!( + ( + (word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) | + (word_cfg("windows") & name_value_cfg("target_pointer_width", "64")) + ).render_short_html(), + "Unix and x86-64, or Windows and 64-bit" + ); + assert_eq!( + (!(word_cfg("unix") & word_cfg("windows"))).render_short_html(), + "Not (Unix and Windows)" + ); + assert_eq!( + ( + (word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix") + ).render_short_html(), + "(Debug-assertions enabled or Windows) and Unix" + ); + } + + #[test] + fn test_render_long_html() { + assert_eq!( + word_cfg("unix").render_long_html(), + "This is supported on Unix only." + ); + assert_eq!( + name_value_cfg("target_os", "macos").render_long_html(), + "This is supported on macOS only." + ); + assert_eq!( + name_value_cfg("target_pointer_width", "16").render_long_html(), + "This is supported on 16-bit only." + ); + assert_eq!( + name_value_cfg("target_endian", "little").render_long_html(), + "This is supported on little-endian only." + ); + assert_eq!( + (!word_cfg("windows")).render_long_html(), + "This is supported on non-Windows only." + ); + assert_eq!( + (word_cfg("unix") & word_cfg("windows")).render_long_html(), + "This is supported on Unix and Windows only." + ); + assert_eq!( + (word_cfg("unix") | word_cfg("windows")).render_long_html(), + "This is supported on Unix or Windows only." + ); + assert_eq!( + ( + word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions") + ).render_long_html(), + "This is supported on Unix and Windows and debug-assertions enabled \ + only." + ); + assert_eq!( + ( + word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions") + ).render_long_html(), + "This is supported on Unix or Windows or debug-assertions enabled \ + only." + ); + assert_eq!( + ( + !(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")) + ).render_long_html(), + "This is supported on neither Unix nor Windows nor debug-assertions \ + enabled." + ); + assert_eq!( + ( + (word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) | + (word_cfg("windows") & name_value_cfg("target_pointer_width", "64")) + ).render_long_html(), + "This is supported on Unix and x86-64, or Windows and 64-bit only." + ); + assert_eq!( + (!(word_cfg("unix") & word_cfg("windows"))).render_long_html(), + "This is supported on not (Unix and Windows)." + ); + assert_eq!( + ( + (word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix") + ).render_long_html(), + "This is supported on (debug-assertions enabled or Windows) and Unix \ + only." + ); + } +} \ No newline at end of file diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index fa5a999adf19..9b4c4e479d04 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -495,7 +495,7 @@ fn build_static(cx: &DocContext, did: DefId, mutable: bool) -> clean::Static { /// A trait's generics clause actually contains all of the predicates for all of /// its associated types as well. We specifically move these clauses to the -/// associated types instead when displaying, so when we're genering the +/// associated types instead when displaying, so when we're generating the /// generics for the trait itself we need to be sure to remove them. /// We also need to remove the implied "recursive" Self: Trait bound. /// diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 75ae2f9fe9ad..e849f93a6bb7 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -52,8 +52,11 @@ use visit_ast; use html::item_type::ItemType; pub mod inline; +pub mod cfg; mod simplify; +use self::cfg::Cfg; + // extract the stability index for a node from tcx, if possible fn get_stability(cx: &DocContext, def_id: DefId) -> Option { cx.tcx.lookup_stability(def_id).clean(cx) @@ -536,31 +539,67 @@ impl> NestedAttributesExt for I { pub struct Attributes { pub doc_strings: Vec, pub other_attrs: Vec, + pub cfg: Option>, pub span: Option, } impl Attributes { - pub fn from_ast(attrs: &[ast::Attribute]) -> Attributes { - let mut doc_strings = vec![]; - let mut sp = None; - let other_attrs = attrs.iter().filter_map(|attr| { - attr.with_desugared_doc(|attr| { - if let Some(value) = attr.value_str() { - if attr.check_name("doc") { - doc_strings.push(value.to_string()); - if sp.is_none() { - sp = Some(attr.span); + /// Extracts the content from an attribute `#[doc(cfg(content))]`. + fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> { + use syntax::ast::NestedMetaItemKind::MetaItem; + + if let ast::MetaItemKind::List(ref nmis) = mi.node { + if nmis.len() == 1 { + if let MetaItem(ref cfg_mi) = nmis[0].node { + if cfg_mi.check_name("cfg") { + if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.node { + if cfg_nmis.len() == 1 { + if let MetaItem(ref content_mi) = cfg_nmis[0].node { + return Some(content_mi); + } + } } - return None; } } + } + } + None + } + + pub fn from_ast(diagnostic: &::errors::Handler, attrs: &[ast::Attribute]) -> Attributes { + let mut doc_strings = vec![]; + let mut sp = None; + let mut cfg = Cfg::True; + + let other_attrs = attrs.iter().filter_map(|attr| { + attr.with_desugared_doc(|attr| { + if attr.check_name("doc") { + if let Some(mi) = attr.meta() { + if let Some(value) = mi.value_str() { + // Extracted #[doc = "..."] + doc_strings.push(value.to_string()); + if sp.is_none() { + sp = Some(attr.span); + } + return None; + } else if let Some(cfg_mi) = Attributes::extract_cfg(&mi) { + // Extracted #[doc(cfg(...))] + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg &= new_cfg, + Err(e) => diagnostic.span_err(e.span, e.msg), + } + return None; + } + } + } Some(attr.clone()) }) }).collect(); Attributes { - doc_strings: doc_strings, - other_attrs: other_attrs, + doc_strings, + other_attrs, + cfg: if cfg == Cfg::True { None } else { Some(Rc::new(cfg)) }, span: sp, } } @@ -579,8 +618,8 @@ impl AttributesExt for Attributes { } impl Clean for [ast::Attribute] { - fn clean(&self, _cx: &DocContext) -> Attributes { - Attributes::from_ast(self) + fn clean(&self, cx: &DocContext) -> Attributes { + Attributes::from_ast(cx.sess().diagnostic(), self) } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index e101e29fc6fb..9bb7e4e3a09d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -10,6 +10,7 @@ use rustc_lint; use rustc_driver::{driver, target_features, abort_on_err}; +use rustc_driver::pretty::ReplaceBodyWithLoop; use rustc::dep_graph::DepGraph; use rustc::session::{self, config}; use rustc::hir::def_id::DefId; @@ -26,6 +27,7 @@ use rustc_metadata::cstore::CStore; use syntax::{ast, codemap}; use syntax::feature_gate::UnstableFeatures; +use syntax::fold::Folder; use errors; use errors::emitter::ColorConfig; @@ -158,6 +160,7 @@ pub fn run_core(search_paths: SearchPaths, let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input)); + let krate = ReplaceBodyWithLoop::new().fold_crate(krate); let name = link::find_crate_name(Some(&sess), &krate.attrs, &input); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 735c9d8af7a8..82c252ae4d72 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -190,8 +190,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { .map(|l| map_line(l).for_code()) .collect::>().join("\n"); let krate = krate.as_ref().map(|s| &**s); - let test = test::maketest(&test, krate, false, - &Default::default()); + let test = test::make_test(&test, krate, false, + &Default::default()); let channel = if test.contains("#![feature(") { "&version=nightly" } else { @@ -584,8 +584,8 @@ pub fn render(w: &mut fmt::Formatter, .map(|l| map_line(l).for_code()) .collect::>().join("\n"); let krate = krate.as_ref().map(|s| &**s); - let test = test::maketest(&test, krate, false, - &Default::default()); + let test = test::make_test(&test, krate, false, + &Default::default()); let channel = if test.contains("#![feature(") { "&version=nightly" } else { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 563c5618759b..4e3181759f99 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -762,6 +762,7 @@ fn write_shared(cx: &Context, } }; + let mut have_impls = false; let mut implementors = format!(r#"implementors["{}"] = ["#, krate.name); for imp in imps { // If the trait and implementation are in the same crate, then @@ -769,10 +770,21 @@ fn write_shared(cx: &Context, // going on). If they're in different crates then the crate defining // the trait will be interested in our implementation. if imp.def_id.krate == did.krate { continue } + // If the implementation is from another crate then that crate + // should add it. + if !imp.def_id.is_local() { continue } + have_impls = true; write!(implementors, "{},", as_json(&imp.impl_.to_string())).unwrap(); } implementors.push_str("];"); + // Only create a js file if we have impls to add to it. If the trait is + // documented locally though we always create the file to avoid dead + // links. + if !have_impls && !cache.paths.contains_key(&did) { + continue; + } + let mut mydst = dst.clone(); for part in &remote_path[..remote_path.len() - 1] { mydst.push(part); @@ -1950,6 +1962,14 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec{}", text)) } + if let Some(ref cfg) = item.attrs.cfg { + stability.push(format!("
{}
", if show_reason { + cfg.render_long_html() + } else { + cfg.render_short_html() + })); + } + stability } diff --git a/src/librustdoc/html/static/styles/main.css b/src/librustdoc/html/static/styles/main.css index 034c5307fc08..08bf5a10fe9d 100644 --- a/src/librustdoc/html/static/styles/main.css +++ b/src/librustdoc/html/static/styles/main.css @@ -152,6 +152,7 @@ a.test-arrow { .stab.unstable { background: #FFF5D6; border-color: #FFC600; } .stab.deprecated { background: #F3DFFF; border-color: #7F0087; } +.stab.portability { background: #C4ECFF; border-color: #7BA5DB; } #help > div { background: #e9e9e9; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 64240d26894d..9264015ed9ed 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -26,6 +26,7 @@ #![feature(test)] #![feature(unicode)] #![feature(vec_remove_item)] +#![feature(ascii_ctype)] extern crate arena; extern crate getopts; diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 41fd9efe61e0..146629486fab 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -33,6 +33,9 @@ pub use self::strip_priv_imports::strip_priv_imports; mod unindent_comments; pub use self::unindent_comments::unindent_comments; +mod propagate_doc_cfg; +pub use self::propagate_doc_cfg::propagate_doc_cfg; + type Pass = (&'static str, // name fn(clean::Crate) -> plugins::PluginResult, // fn &'static str); // description @@ -49,6 +52,8 @@ pub const PASSES: &'static [Pass] = &[ implies strip-priv-imports"), ("strip-priv-imports", strip_priv_imports, "strips all private import statements (`use`, `extern crate`) from a crate"), + ("propagate-doc-cfg", propagate_doc_cfg, + "propagates `#[doc(cfg(...))]` to child items"), ]; pub const DEFAULT_PASSES: &'static [&'static str] = &[ @@ -56,6 +61,7 @@ pub const DEFAULT_PASSES: &'static [&'static str] = &[ "strip-private", "collapse-docs", "unindent-comments", + "propagate-doc-cfg", ]; diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs new file mode 100644 index 000000000000..9e65fff5e2ac --- /dev/null +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -0,0 +1,47 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::rc::Rc; + +use clean::{Crate, Item}; +use clean::cfg::Cfg; +use fold::DocFolder; +use plugins::PluginResult; + +pub fn propagate_doc_cfg(cr: Crate) -> PluginResult { + CfgPropagator { parent_cfg: None }.fold_crate(cr) +} + +struct CfgPropagator { + parent_cfg: Option>, +} + +impl DocFolder for CfgPropagator { + fn fold_item(&mut self, mut item: Item) -> Option { + let old_parent_cfg = self.parent_cfg.clone(); + + let new_cfg = match (self.parent_cfg.take(), item.attrs.cfg.take()) { + (None, None) => None, + (Some(rc), None) | (None, Some(rc)) => Some(rc), + (Some(mut a), Some(b)) => { + let b = Rc::try_unwrap(b).unwrap_or_else(|rc| Cfg::clone(&rc)); + *Rc::make_mut(&mut a) &= b; + Some(a) + } + }; + self.parent_cfg = new_cfg.clone(); + item.attrs.cfg = new_cfg; + + let result = self.fold_item_recur(item); + self.parent_cfg = old_parent_cfg; + + result + } +} diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index b1e92b5190f3..b1fb343b8bb9 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -32,6 +32,7 @@ use rustc_back::dynamic_lib::DynamicLibrary; use rustc_back::tempdir::TempDir; use rustc_driver::{self, driver, Compilation}; use rustc_driver::driver::phase_2_configure_and_expand; +use rustc_driver::pretty::ReplaceBodyWithLoop; use rustc_metadata::cstore::CStore; use rustc_resolve::MakeGlobMap; use rustc_trans; @@ -39,6 +40,7 @@ use rustc_trans::back::link; use syntax::ast; use syntax::codemap::CodeMap; use syntax::feature_gate::UnstableFeatures; +use syntax::fold::Folder; use syntax_pos::{BytePos, DUMMY_SP, Pos, Span}; use errors; use errors::emitter::ColorConfig; @@ -72,6 +74,7 @@ pub fn run(input: &str, crate_types: vec![config::CrateTypeDylib], externs: externs.clone(), unstable_features: UnstableFeatures::from_environment(), + lint_cap: Some(::rustc::lint::Level::Allow), actually_rustdoc: true, ..config::basic_options().clone() }; @@ -94,6 +97,7 @@ pub fn run(input: &str, let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input)); + let krate = ReplaceBodyWithLoop::new().fold_crate(krate); let driver::ExpansionResult { defs, mut hir_forest, .. } = { phase_2_configure_and_expand( &sess, &cstore, krate, None, "rustdoc-test", None, MakeGlobMap::No, |_| Ok(()) @@ -121,6 +125,7 @@ pub fn run(input: &str, let map = hir::map::map_crate(&mut hir_forest, defs); let krate = map.krate(); let mut hir_collector = HirCollector { + sess: &sess, collector: &mut collector, map: &map }; @@ -169,16 +174,16 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions { opts } -fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, - externs: Externs, - should_panic: bool, no_run: bool, as_test_harness: bool, - compile_fail: bool, mut error_codes: Vec, opts: &TestOptions, - maybe_sysroot: Option) { +fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec, libs: SearchPaths, + externs: Externs, + should_panic: bool, no_run: bool, as_test_harness: bool, + compile_fail: bool, mut error_codes: Vec, opts: &TestOptions, + maybe_sysroot: Option) { // the test harness wants its own `main` & top level functions, so // never wrap the test in `fn main() { ... }` - let test = maketest(test, Some(cratename), as_test_harness, opts); + let test = make_test(test, Some(cratename), as_test_harness, opts); let input = config::Input::Str { - name: driver::anon_src(), + name: filename.to_owned(), input: test.to_owned(), }; let outputs = OutputTypes::new(&[(OutputType::Exe, None)]); @@ -315,8 +320,11 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, } } -pub fn maketest(s: &str, cratename: Option<&str>, dont_insert_main: bool, - opts: &TestOptions) -> String { +pub fn make_test(s: &str, + cratename: Option<&str>, + dont_insert_main: bool, + opts: &TestOptions) + -> String { let (crate_attrs, everything_else) = partition_source(s); let mut prog = String::new(); @@ -500,18 +508,19 @@ impl Collector { rustc_driver::in_rustc_thread(move || { io::set_panic(panic); io::set_print(print); - runtest(&test, - &cratename, - cfgs, - libs, - externs, - should_panic, - no_run, - as_test_harness, - compile_fail, - error_codes, - &opts, - maybe_sysroot) + run_test(&test, + &cratename, + &filename, + cfgs, + libs, + externs, + should_panic, + no_run, + as_test_harness, + compile_fail, + error_codes, + &opts, + maybe_sysroot) }) } { Ok(()) => (), @@ -574,6 +583,7 @@ impl Collector { } struct HirCollector<'a, 'hir: 'a> { + sess: &'a session::Session, collector: &'a mut Collector, map: &'a hir::map::Map<'hir> } @@ -583,12 +593,18 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { name: String, attrs: &[ast::Attribute], nested: F) { + let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs); + if let Some(ref cfg) = attrs.cfg { + if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features.borrow())) { + return; + } + } + let has_name = !name.is_empty(); if has_name { self.collector.names.push(name); } - let mut attrs = Attributes::from_ast(attrs); attrs.collapse_doc_comments(); attrs.unindent_doc_comments(); if let Some(doc) = attrs.doc_value() { diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 062186ef7086..1e692abaff2f 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -657,7 +657,7 @@ impl OpenOptions { /// This function will return an error under a number of different /// circumstances. Some of these error conditions are listed here, together /// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of - /// the compatiblity contract of the function, especially the `Other` kind + /// the compatibility contract of the function, especially the `Other` kind /// might change to more specific kinds in the future. /// /// * [`NotFound`]: The specified file does not exist and neither `create` diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index f7748aa3f041..880caa2ade5d 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -243,6 +243,7 @@ #![feature(allocator_api)] #![feature(alloc_system)] #![feature(allocator_internals)] +#![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm)] #![feature(box_syntax)] @@ -314,6 +315,7 @@ #![feature(untagged_unions)] #![feature(unwind_attributes)] #![feature(vec_push_all)] +#![feature(doc_cfg)] #![cfg_attr(test, feature(update_panic_count))] #![default_lib_allocator] diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 5e88a46ecc34..c426bf8086ee 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -461,9 +461,26 @@ pub mod builtin { /// /// # Examples /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// let secret_key = include_str!("secret-key.ascii"); + /// Assume there are two files in the same directory with the following + /// contents: + /// + /// File 'spanish.in': + /// + /// ```text + /// adiós /// ``` + /// + /// File 'main.rs': + /// + /// ```ignore (cannot-doctest-external-file-dependency) + /// fn main() { + /// let my_str = include_str!("spanish.in"); + /// assert_eq!(my_str, "adiós\n"); + /// print!("{}", my_str); + /// } + /// ``` + /// + /// Compiling 'main.rs' and running the resulting binary will print "adiós". #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) } @@ -478,9 +495,26 @@ pub mod builtin { /// /// # Examples /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// let secret_key = include_bytes!("secret-key.bin"); + /// Assume there are two files in the same directory with the following + /// contents: + /// + /// File 'spanish.in': + /// + /// ```text + /// adiós /// ``` + /// + /// File 'main.rs': + /// + /// ```ignore (cannot-doctest-external-file-dependency) + /// fn main() { + /// let bytes = include_bytes!("spanish.in"); + /// assert_eq!(bytes, b"adi\xc3\xb3s\n"); + /// print!("{}", String::from_utf8_lossy(bytes)); + /// } + /// ``` + /// + /// Compiling 'main.rs' and running the resulting binary will print "adiós". #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) } @@ -545,23 +579,28 @@ pub mod builtin { /// Assume there are two files in the same directory with the following /// contents: /// - /// File 'my_str.in': + /// File 'monkeys.in': /// /// ```ignore (only-for-syntax-highlight) - /// "Hello World!" + /// ['🙈', '🙊', '🙉'] + /// .iter() + /// .cycle() + /// .take(6) + /// .collect::() /// ``` /// /// File 'main.rs': /// /// ```ignore (cannot-doctest-external-file-dependency) /// fn main() { - /// let my_str = include!("my_str.in"); - /// println!("{}", my_str); + /// let my_string = include!("monkeys.in"); + /// assert_eq!("🙈🙊🙉🙈🙊🙉", my_string); + /// println!("{}", my_string); /// } /// ``` /// - /// Compiling 'main.rs' and running the resulting binary will print "Hello - /// World!". + /// Compiling 'main.rs' and running the resulting binary will print + /// "🙈🙊🙉🙈🙊🙉". #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) } diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index fdeca8bc5cac..2eabb46441b3 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -147,7 +147,7 @@ impl TcpStream { /// connection request. /// /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html - #[unstable(feature = "tcpstream_connect_timeout", issue = "43709")] + #[unstable(feature = "tcpstream_connect_timeout", issue = "43079")] pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { net_imp::TcpStream::connect_timeout(addr, timeout).map(TcpStream) } @@ -351,7 +351,7 @@ impl TcpStream { self.0.write_timeout() } - /// Receives data on the socket from the remote adress to which it is + /// Receives data on the socket from the remote address to which it is /// connected, without removing that data from the queue. On success, /// returns the number of bytes peeked. /// diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 80151dc2b445..9aff98978853 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -622,7 +622,7 @@ impl UdpSocket { self.0.recv(buf) } - /// Receives data on the socket from the remote adress to which it is + /// Receives data on the socket from the remote address to which it is /// connected, without removing that data from the queue. On success, /// returns the number of bytes peeked. /// diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index e45af8670558..72eed549f62a 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -13,26 +13,36 @@ #![stable(feature = "os", since = "1.0.0")] #![allow(missing_docs, bad_style, missing_debug_implementations)] -#[cfg(any(target_os = "redox", unix))] +#[cfg(all(not(dox), any(target_os = "redox", unix)))] #[stable(feature = "rust1", since = "1.0.0")] pub use sys::ext as unix; -#[cfg(windows)] +#[cfg(all(not(dox), windows))] #[stable(feature = "rust1", since = "1.0.0")] pub use sys::ext as windows; -#[cfg(target_os = "android")] pub mod android; -#[cfg(target_os = "bitrig")] pub mod bitrig; -#[cfg(target_os = "dragonfly")] pub mod dragonfly; -#[cfg(target_os = "freebsd")] pub mod freebsd; -#[cfg(target_os = "haiku")] pub mod haiku; -#[cfg(target_os = "ios")] pub mod ios; -#[cfg(target_os = "linux")] pub mod linux; -#[cfg(target_os = "macos")] pub mod macos; -#[cfg(target_os = "nacl")] pub mod nacl; -#[cfg(target_os = "netbsd")] pub mod netbsd; -#[cfg(target_os = "openbsd")] pub mod openbsd; -#[cfg(target_os = "solaris")] pub mod solaris; -#[cfg(target_os = "emscripten")] pub mod emscripten; -#[cfg(target_os = "fuchsia")] pub mod fuchsia; +#[cfg(dox)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use sys::unix_ext as unix; +#[cfg(dox)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use sys::windows_ext as windows; + +#[cfg(any(dox, target_os = "linux"))] +#[doc(cfg(target_os = "linux"))] +pub mod linux; + +#[cfg(all(not(dox), target_os = "android"))] pub mod android; +#[cfg(all(not(dox), target_os = "bitrig"))] pub mod bitrig; +#[cfg(all(not(dox), target_os = "dragonfly"))] pub mod dragonfly; +#[cfg(all(not(dox), target_os = "freebsd"))] pub mod freebsd; +#[cfg(all(not(dox), target_os = "haiku"))] pub mod haiku; +#[cfg(all(not(dox), target_os = "ios"))] pub mod ios; +#[cfg(all(not(dox), target_os = "macos"))] pub mod macos; +#[cfg(all(not(dox), target_os = "nacl"))] pub mod nacl; +#[cfg(all(not(dox), target_os = "netbsd"))] pub mod netbsd; +#[cfg(all(not(dox), target_os = "openbsd"))] pub mod openbsd; +#[cfg(all(not(dox), target_os = "solaris"))] pub mod solaris; +#[cfg(all(not(dox), target_os = "emscripten"))] pub mod emscripten; +#[cfg(all(not(dox), target_os = "fuchsia"))] pub mod fuchsia; pub mod raw; diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 58356bc43eeb..97b09b7e2ad9 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -37,7 +37,7 @@ pub use panicking::{take_hook, set_hook, PanicInfo, Location}; /// In Rust a function can "return" early if it either panics or calls a /// function which transitively panics. This sort of control flow is not always /// anticipated, and has the possibility of causing subtle bugs through a -/// combination of two cricial components: +/// combination of two critical components: /// /// 1. A data structure is in a temporarily invalid state when the thread /// panics. diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 04e1a579decd..99567bd08bbf 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -171,7 +171,7 @@ pub fn take_hook() -> Box { /// use std::panic; /// /// panic::set_hook(Box::new(|panic_info| { -/// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); +/// println!("panic occurred: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); /// })); /// /// panic!("Normal panic"); @@ -196,7 +196,7 @@ impl<'a> PanicInfo<'a> { /// use std::panic; /// /// panic::set_hook(Box::new(|panic_info| { - /// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); + /// println!("panic occurred: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); /// })); /// /// panic!("Normal panic"); @@ -221,9 +221,10 @@ impl<'a> PanicInfo<'a> { /// /// panic::set_hook(Box::new(|panic_info| { /// if let Some(location) = panic_info.location() { - /// println!("panic occured in file '{}' at line {}", location.file(), location.line()); + /// println!("panic occurred in file '{}' at line {}", location.file(), + /// location.line()); /// } else { - /// println!("panic occured but can't get location information..."); + /// println!("panic occurred but can't get location information..."); /// } /// })); /// @@ -249,9 +250,9 @@ impl<'a> PanicInfo<'a> { /// /// panic::set_hook(Box::new(|panic_info| { /// if let Some(location) = panic_info.location() { -/// println!("panic occured in file '{}' at line {}", location.file(), location.line()); +/// println!("panic occurred in file '{}' at line {}", location.file(), location.line()); /// } else { -/// println!("panic occured but can't get location information..."); +/// println!("panic occurred but can't get location information..."); /// } /// })); /// @@ -275,9 +276,9 @@ impl<'a> Location<'a> { /// /// panic::set_hook(Box::new(|panic_info| { /// if let Some(location) = panic_info.location() { - /// println!("panic occured in file '{}'", location.file()); + /// println!("panic occurred in file '{}'", location.file()); /// } else { - /// println!("panic occured but can't get location information..."); + /// println!("panic occurred but can't get location information..."); /// } /// })); /// @@ -297,9 +298,9 @@ impl<'a> Location<'a> { /// /// panic::set_hook(Box::new(|panic_info| { /// if let Some(location) = panic_info.location() { - /// println!("panic occured at line {}", location.line()); + /// println!("panic occurred at line {}", location.line()); /// } else { - /// println!("panic occured but can't get location information..."); + /// println!("panic occurred but can't get location information..."); /// } /// })); /// @@ -320,9 +321,9 @@ impl<'a> Location<'a> { /// /// panic::set_hook(Box::new(|panic_info| { /// if let Some(location) = panic_info.location() { - /// println!("panic occured at column {}", location.column()); + /// println!("panic occurred at column {}", location.column()); /// } else { - /// println!("panic occured but can't get location information..."); + /// println!("panic occurred but can't get location information..."); /// } /// })); /// diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 7be319d1954e..c52899db4373 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -906,7 +906,7 @@ mod prim_ref { } /// These markers can be combined, so `unsafe extern "stdcall" fn()` is a valid type. /// /// Like references in rust, function pointers are assumed to not be null, so if you want to pass a -/// function pointer over FFI and be able to accomodate null pointers, make your type +/// function pointer over FFI and be able to accommodate null pointers, make your type /// `Option` with your required signature. /// /// Function pointers implement the following traits: diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index ef4dc365dbef..d91c2073a23a 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -45,3 +45,33 @@ mod imp; #[cfg(target_os = "redox")] #[path = "redox/mod.rs"] mod imp; + + +// Import essential modules from both platforms when documenting. + +#[cfg(all(dox, not(unix)))] +use os::linux as platform; + +#[cfg(all(dox, not(any(unix, target_os = "redox"))))] +#[path = "unix/ext/mod.rs"] +pub mod unix_ext; + +#[cfg(all(dox, any(unix, target_os = "redox")))] +pub use self::ext as unix_ext; + + +#[cfg(all(dox, not(windows)))] +#[macro_use] +#[path = "windows/compat.rs"] +mod compat; + +#[cfg(all(dox, not(windows)))] +#[path = "windows/c.rs"] +mod c; + +#[cfg(all(dox, not(windows)))] +#[path = "windows/ext/mod.rs"] +pub mod windows_ext; + +#[cfg(all(dox, windows))] +pub use self::ext as windows_ext; diff --git a/src/libstd/sys/redox/ext/fs.rs b/src/libstd/sys/redox/ext/fs.rs index 9a0d1e06da32..5d4edc2cf92c 100644 --- a/src/libstd/sys/redox/ext/fs.rs +++ b/src/libstd/sys/redox/ext/fs.rs @@ -121,7 +121,7 @@ pub trait OpenOptionsExt { #[stable(feature = "fs_ext", since = "1.1.0")] fn mode(&mut self, mode: u32) -> &mut Self; - /// Pass custom flags to the `flags` agument of `open`. + /// Pass custom flags to the `flags` argument of `open`. /// /// The bits that define the access mode are masked out with `O_ACCMODE`, to /// ensure they do not interfere with the access mode set by Rusts options. diff --git a/src/libstd/sys/redox/ext/mod.rs b/src/libstd/sys/redox/ext/mod.rs index 0c1bf9e95576..259cda5bcb3e 100644 --- a/src/libstd/sys/redox/ext/mod.rs +++ b/src/libstd/sys/redox/ext/mod.rs @@ -28,6 +28,7 @@ //! ``` #![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(target_os = "redox"))] pub mod ffi; pub mod fs; diff --git a/src/libstd/sys/redox/ext/process.rs b/src/libstd/sys/redox/ext/process.rs index c59524974bf2..e68e180acf1c 100644 --- a/src/libstd/sys/redox/ext/process.rs +++ b/src/libstd/sys/redox/ext/process.rs @@ -47,7 +47,7 @@ pub trait CommandExt { /// # Notes /// /// This closure will be run in the context of the child process after a - /// `fork`. This primarily means that any modificatons made to memory on + /// `fork`. This primarily means that any modifications made to memory on /// behalf of this closure will **not** be visible to the parent process. /// This is often a very constrained environment where normal operations /// like `malloc` or acquiring a mutex are not guaranteed to work (due to diff --git a/src/libstd/sys/redox/net/dns/mod.rs b/src/libstd/sys/redox/net/dns/mod.rs index 43c4fe7ac9d9..49cde89dc054 100644 --- a/src/libstd/sys/redox/net/dns/mod.rs +++ b/src/libstd/sys/redox/net/dns/mod.rs @@ -102,6 +102,7 @@ impl Dns { } pub fn parse(data: &[u8]) -> Result { + let name_ind = 0b11000000; let mut i = 0; macro_rules! pop_u8 { @@ -147,9 +148,15 @@ impl Dns { () => { { let mut name = String::new(); + let old_i = i; loop { let name_len = pop_u8!(); + if name_len & name_ind == name_ind { + i -= 1; + i = (pop_n16!() - ((name_ind as u16) << 8)) as usize; + continue; + } if name_len == 0 { break; } @@ -161,6 +168,10 @@ impl Dns { } } + if i <= old_i { + i = old_i + 2; + } + name } }; @@ -184,21 +195,8 @@ impl Dns { let mut answers = Vec::new(); for _answer_i in 0..answers_len { - let name_ind = 0b11000000; - let name_test = pop_u8!(); - i -= 1; - answers.push(DnsAnswer { - name: if name_test & name_ind == name_ind { - let name_off = pop_n16!() - ((name_ind as u16) << 8); - let old_i = i; - i = name_off as usize; - let name = pop_name!(); - i = old_i; - name - } else { - pop_name!() - }, + name: pop_name!(), a_type: pop_n16!(), a_class: pop_n16!(), ttl_a: pop_n16!(), diff --git a/src/libstd/sys/redox/syscall/call.rs b/src/libstd/sys/redox/syscall/call.rs index ec9005c2cc3b..9fc809eb821d 100644 --- a/src/libstd/sys/redox/syscall/call.rs +++ b/src/libstd/sys/redox/syscall/call.rs @@ -40,7 +40,7 @@ pub unsafe fn brk(addr: usize) -> Result { /// /// * `EACCES` - permission is denied for one of the components of `path`, or `path` /// * `EFAULT` - `path` does not point to the process's addressible memory -/// * `EIO` - an I/O error occured +/// * `EIO` - an I/O error occurred /// * `ENOENT` - `path` does not exit /// * `ENOTDIR` - `path` is not a directory pub fn chdir(path: &str) -> Result { @@ -290,7 +290,7 @@ pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result /// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block /// * `EBADF` - the file descriptor is not valid or is not open for writing /// * `EFAULT` - `buf` does not point to the process's addressible memory -/// * `EIO` - an I/O error occured +/// * `EIO` - an I/O error occurred /// * `ENOSPC` - the device containing the file descriptor has no room for data /// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed pub fn write(fd: usize, buf: &[u8]) -> Result { diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 26710bf61d51..a94585723a1e 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -46,7 +46,7 @@ pub trait FileExt { /// /// The current file cursor is not affected by this function. /// - /// When writing beyond the end of the file, the file is appropiately + /// When writing beyond the end of the file, the file is appropriately /// extended and the intermediate bytes are initialized with the value 0. /// /// Note that similar to `File::write`, it is not an error to return a @@ -168,7 +168,7 @@ pub trait OpenOptionsExt { #[stable(feature = "fs_ext", since = "1.1.0")] fn mode(&mut self, mode: u32) -> &mut Self; - /// Pass custom flags to the `flags` agument of `open`. + /// Pass custom flags to the `flags` argument of `open`. /// /// The bits that define the access mode are masked out with `O_ACCMODE`, to /// ensure they do not interfere with the access mode set by Rusts options. diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index 1be9f11b92c7..67fe46cc9c7a 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -28,6 +28,7 @@ //! ``` #![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(unix))] pub mod io; pub mod ffi; diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 7701ae25b418..278534271281 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -12,8 +12,19 @@ //! Unix-specific networking functionality +#[cfg(unix)] use libc; +// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? +#[cfg(not(unix))] +mod libc { + pub use libc::c_int; + pub type socklen_t = u32; + pub struct sockaddr; + #[derive(Clone)] + pub struct sockaddr_un; +} + use ascii; use ffi::OsStr; use fmt; @@ -327,7 +338,7 @@ impl UnixStream { /// /// The returned `UnixStream` is a reference to the same stream that this /// object references. Both handles will read and write the same stream of - /// data, and options set on one stream will be propogated to the other + /// data, and options set on one stream will be propagated to the other /// stream. /// /// # Examples diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index 2961c4ec5824..cde21b089a20 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -47,7 +47,7 @@ pub trait CommandExt { /// # Notes /// /// This closure will be run in the context of the child process after a - /// `fork`. This primarily means that any modificatons made to memory on + /// `fork`. This primarily means that any modifications made to memory on /// behalf of this closure will **not** be visible to the parent process. /// This is often a very constrained environment where normal operations /// like `malloc` or acquiring a mutex are not guaranteed to work (due to diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 46e5acdf3d22..4393aedf162a 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -13,20 +13,21 @@ use io::{self, ErrorKind}; use libc; -#[cfg(target_os = "android")] pub use os::android as platform; -#[cfg(target_os = "bitrig")] pub use os::bitrig as platform; -#[cfg(target_os = "dragonfly")] pub use os::dragonfly as platform; -#[cfg(target_os = "freebsd")] pub use os::freebsd as platform; -#[cfg(target_os = "haiku")] pub use os::haiku as platform; -#[cfg(target_os = "ios")] pub use os::ios as platform; -#[cfg(target_os = "linux")] pub use os::linux as platform; -#[cfg(target_os = "macos")] pub use os::macos as platform; -#[cfg(target_os = "nacl")] pub use os::nacl as platform; -#[cfg(target_os = "netbsd")] pub use os::netbsd as platform; -#[cfg(target_os = "openbsd")] pub use os::openbsd as platform; -#[cfg(target_os = "solaris")] pub use os::solaris as platform; -#[cfg(target_os = "emscripten")] pub use os::emscripten as platform; -#[cfg(target_os = "fuchsia")] pub use os::fuchsia as platform; +#[cfg(any(dox, target_os = "linux"))] pub use os::linux as platform; + +#[cfg(all(not(dox), target_os = "android"))] pub use os::android as platform; +#[cfg(all(not(dox), target_os = "bitrig"))] pub use os::bitrig as platform; +#[cfg(all(not(dox), target_os = "dragonfly"))] pub use os::dragonfly as platform; +#[cfg(all(not(dox), target_os = "freebsd"))] pub use os::freebsd as platform; +#[cfg(all(not(dox), target_os = "haiku"))] pub use os::haiku as platform; +#[cfg(all(not(dox), target_os = "ios"))] pub use os::ios as platform; +#[cfg(all(not(dox), target_os = "macos"))] pub use os::macos as platform; +#[cfg(all(not(dox), target_os = "nacl"))] pub use os::nacl as platform; +#[cfg(all(not(dox), target_os = "netbsd"))] pub use os::netbsd as platform; +#[cfg(all(not(dox), target_os = "openbsd"))] pub use os::openbsd as platform; +#[cfg(all(not(dox), target_os = "solaris"))] pub use os::solaris as platform; +#[cfg(all(not(dox), target_os = "emscripten"))] pub use os::emscripten as platform; +#[cfg(all(not(dox), target_os = "fuchsia"))] pub use os::fuchsia as platform; #[macro_use] pub mod weak; diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 4785cefd6b4b..ba54ca6ea182 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -301,7 +301,7 @@ pub const PIPE_READMODE_BYTE: DWORD = 0x00000000; pub const FD_SETSIZE: usize = 64; #[repr(C)] -#[cfg(target_arch = "x86")] +#[cfg(not(target_pointer_width = "64"))] pub struct WSADATA { pub wVersion: WORD, pub wHighVersion: WORD, @@ -312,7 +312,7 @@ pub struct WSADATA { pub lpVendorInfo: *mut u8, } #[repr(C)] -#[cfg(target_arch = "x86_64")] +#[cfg(target_pointer_width = "64")] pub struct WSADATA { pub wVersion: WORD, pub wHighVersion: WORD, @@ -768,6 +768,14 @@ pub struct FLOATING_SAVE_AREA { _Dummy: [u8; 512] // FIXME: Fill this out } +// FIXME(#43348): This structure is used for backtrace only, and a fake +// definition is provided here only to allow rustdoc to pass type-check. This +// will not appear in the final documentation. This should be also defined for +// other architectures supported by Windows such as ARM, and for historical +// interest, maybe MIPS and PowerPC as well. +#[cfg(all(dox, not(any(target_arch = "x86_64", target_arch = "x86"))))] +pub enum CONTEXT {} + #[repr(C)] pub struct SOCKADDR_STORAGE_LH { pub ss_family: ADDRESS_FAMILY, diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index a1c63e335884..d58a3505154d 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -66,7 +66,7 @@ pub trait FileExt { /// from the current cursor. The current cursor **is** affected by this /// function, it is set to the end of the write. /// - /// When writing beyond the end of the file, the file is appropiately + /// When writing beyond the end of the file, the file is appropriately /// extended and the intermediate bytes are left uninitialized. /// /// Note that similar to `File::write`, it is not an error to return a diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs index 11b1337a8aec..4b458d293bce 100644 --- a/src/libstd/sys/windows/ext/mod.rs +++ b/src/libstd/sys/windows/ext/mod.rs @@ -17,6 +17,7 @@ //! platform-agnostic idioms would not normally support. #![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(windows))] pub mod ffi; pub mod fs; diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 0172f89e05b6..48f611a34394 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -91,13 +91,13 @@ pub struct LocalKey { // // Note that the thunk is itself unsafe because the returned lifetime of the // slot where data lives, `'static`, is not actually valid. The lifetime - // here is actually `'thread`! + // here is actually slightly shorter than the currently running thread! // // Although this is an extra layer of indirection, it should in theory be // trivially devirtualizable by LLVM because the value of `inner` never // changes and the constant should be readonly within a crate. This mainly // only runs into problems when TLS statics are exported across crates. - inner: fn() -> Option<&'static UnsafeCell>>, + inner: unsafe fn() -> Option<&'static UnsafeCell>>, // initialization routine to invoke to create a value init: fn() -> T, @@ -157,12 +157,13 @@ macro_rules! thread_local { issue = "0")] #[macro_export] #[allow_internal_unstable] +#[cfg_attr(not(stage0), allow_internal_unsafe)] macro_rules! __thread_local_inner { ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { $(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> = { fn __init() -> $t { $init } - fn __getit() -> $crate::option::Option< + unsafe fn __getit() -> $crate::option::Option< &'static $crate::cell::UnsafeCell< $crate::option::Option<$t>>> { @@ -178,7 +179,9 @@ macro_rules! __thread_local_inner { __KEY.get() } - $crate::thread::LocalKey::new(__getit, __init) + unsafe { + $crate::thread::LocalKey::new(__getit, __init) + } }; } } @@ -252,8 +255,8 @@ impl LocalKey { #[unstable(feature = "thread_local_internals", reason = "recently added to create a key", issue = "0")] - pub const fn new(inner: fn() -> Option<&'static UnsafeCell>>, - init: fn() -> T) -> LocalKey { + pub const unsafe fn new(inner: unsafe fn() -> Option<&'static UnsafeCell>>, + init: fn() -> T) -> LocalKey { LocalKey { inner: inner, init: init, @@ -391,6 +394,7 @@ pub mod fast { } } + #[cfg(stage0)] unsafe impl ::marker::Sync for Key { } impl Key { @@ -402,14 +406,12 @@ pub mod fast { } } - pub fn get(&'static self) -> Option<&'static UnsafeCell>> { - unsafe { - if mem::needs_drop::() && self.dtor_running.get() { - return None - } - self.register_dtor(); + pub unsafe fn get(&self) -> Option<&'static UnsafeCell>> { + if mem::needs_drop::() && self.dtor_running.get() { + return None } - Some(&self.inner) + self.register_dtor(); + Some(&*(&self.inner as *const _)) } unsafe fn register_dtor(&self) { @@ -478,26 +480,24 @@ pub mod os { } } - pub fn get(&'static self) -> Option<&'static UnsafeCell>> { - unsafe { - let ptr = self.os.get() as *mut Value; - if !ptr.is_null() { - if ptr as usize == 1 { - return None - } - return Some(&(*ptr).value); + pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell>> { + let ptr = self.os.get() as *mut Value; + if !ptr.is_null() { + if ptr as usize == 1 { + return None } - - // If the lookup returned null, we haven't initialized our own - // local copy, so do that now. - let ptr: Box> = box Value { - key: self, - value: UnsafeCell::new(None), - }; - let ptr = Box::into_raw(ptr); - self.os.set(ptr as *mut u8); - Some(&(*ptr).value) + return Some(&(*ptr).value); } + + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. + let ptr: Box> = box Value { + key: self, + value: UnsafeCell::new(None), + }; + let ptr = Box::into_raw(ptr); + self.os.set(ptr as *mut u8); + Some(&(*ptr).value) } } diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index cbd019c2c0e6..80eb8ba93f75 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -531,7 +531,7 @@ pub fn current() -> Thread { /// /// However programmers will usually prefer to use, [`channel`]s, [`Condvar`]s, /// [`Mutex`]es or [`join`] for their synchronisation routines, as they avoid -/// thinking about thread schedulling. +/// thinking about thread scheduling. /// /// Note that [`channel`]s for example are implemented using this primitive. /// Indeed when you call `send` or `recv`, which are blocking, they will yield diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index cce428cad6df..14578179b435 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -563,8 +563,8 @@ pub enum PatKind { TupleStruct(Path, Vec>, Option), /// A possibly qualified path pattern. - /// Unquailfied path patterns `A::B::C` can legally refer to variants, structs, constants - /// or associated constants. Quailfied path patterns `
::B::C`/`::B::C` can + /// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants + /// or associated constants. Qualified path patterns `::B::C`/`::B::C` can /// only legally refer to associated constants. Path(Option, Path), @@ -1841,7 +1841,7 @@ pub struct Item { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum ItemKind { - /// An`extern crate` item, with optional original crate name. + /// An `extern crate` item, with optional original crate name. /// /// E.g. `extern crate foo` or `extern crate foo_bar as foo` ExternCrate(Option), diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index a247fe7f8a56..d5caf458fd76 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -435,7 +435,7 @@ pub fn mk_attr_inner(span: Span, id: AttrId, item: MetaItem) -> Attribute { mk_spanned_attr_inner(span, id, item) } -/// Returns an innter attribute with the given value and span. +/// Returns an inner attribute with the given value and span. pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute { Attribute { id: id, diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index bfdcae7641dd..6c48b4cadd84 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -761,7 +761,7 @@ mod tests { } /// Given a string like " ~~~~~~~~~~~~ ", produces a span - /// coverting that range. The idea is that the string has the same + /// converting that range. The idea is that the string has the same /// length as the input, and we uncover the byte positions. Note /// that this can span lines and so on. fn span_from_selection(input: &str, selection: &str) -> Span { @@ -771,7 +771,7 @@ mod tests { Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), ctxt: NO_EXPANSION } } - /// Test span_to_snippet and span_to_lines for a span coverting 3 + /// Test span_to_snippet and span_to_lines for a span converting 3 /// lines in the middle of a file. #[test] fn span_to_snippet_and_lines_spanning_multiple_lines() { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 194d30e25d41..72b2552f64fc 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -532,10 +532,16 @@ pub enum SyntaxExtension { /// A normal, function-like syntax extension. /// /// `bytes!` is a `NormalTT`. - /// - /// The `bool` dictates whether the contents of the macro can - /// directly use `#[unstable]` things (true == yes). - NormalTT(Box, Option<(ast::NodeId, Span)>, bool), + NormalTT { + expander: Box, + def_info: Option<(ast::NodeId, Span)>, + /// Whether the contents of the macro can + /// directly use `#[unstable]` things (true == yes). + allow_internal_unstable: bool, + /// Whether the contents of the macro can use `unsafe` + /// without triggering the `unsafe_code` lint. + allow_internal_unsafe: bool, + }, /// A function-like syntax extension that has an extra ident before /// the block. @@ -562,7 +568,7 @@ impl SyntaxExtension { pub fn kind(&self) -> MacroKind { match *self { SyntaxExtension::DeclMacro(..) | - SyntaxExtension::NormalTT(..) | + SyntaxExtension::NormalTT { .. } | SyntaxExtension::IdentTT(..) | SyntaxExtension::ProcMacro(..) => MacroKind::Bang, diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index e7c5d8278d97..38715f7275de 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -64,6 +64,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path] format: ExpnFormat::MacroAttribute(Symbol::intern(&pretty_name)), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, }, }); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4843a66a750f..9625602fa4a5 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -411,6 +411,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(Symbol::intern(&format!("{}", attr.path))), span: None, allow_internal_unstable: false, + allow_internal_unsafe: false, } }); @@ -458,7 +459,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let path = &mac.node.path; let ident = ident.unwrap_or_else(|| keywords::Invalid.ident()); - let validate_and_set_expn_info = |def_site_span, allow_internal_unstable| { + let validate_and_set_expn_info = |def_site_span, + allow_internal_unstable, + allow_internal_unsafe| { if ident.name != keywords::Invalid.name() { return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident)); } @@ -467,7 +470,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { callee: NameAndSpan { format: MacroBang(Symbol::intern(&format!("{}", path))), span: def_site_span, - allow_internal_unstable: allow_internal_unstable, + allow_internal_unstable, + allow_internal_unsafe, }, }); Ok(()) @@ -476,20 +480,26 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let opt_expanded = match *ext { DeclMacro(ref expand, def_span) => { if let Err(msg) = validate_and_set_expn_info(def_span.map(|(_, s)| s), - false) { + false, false) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } kind.make_from(expand.expand(self.cx, span, mac.node.stream())) } - NormalTT(ref expandfun, def_info, allow_internal_unstable) => { + NormalTT { + ref expander, + def_info, + allow_internal_unstable, + allow_internal_unsafe + } => { if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s), - allow_internal_unstable) { + allow_internal_unstable, + allow_internal_unsafe) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } - kind.make_from(expandfun.expand(self.cx, span, mac.node.stream())) + kind.make_from(expander.expand(self.cx, span, mac.node.stream())) } IdentTT(ref expander, tt_span, allow_internal_unstable) => { @@ -504,7 +514,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { callee: NameAndSpan { format: MacroBang(Symbol::intern(&format!("{}", path))), span: tt_span, - allow_internal_unstable: allow_internal_unstable, + allow_internal_unstable, + allow_internal_unsafe: false, } }); @@ -540,6 +551,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { span: None, // FIXME probably want to follow macro_rules macros here. allow_internal_unstable: false, + allow_internal_unsafe: false, }, }); @@ -578,6 +590,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(pretty_name), span: None, allow_internal_unstable: false, + allow_internal_unsafe: false, } }; diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index b293aa8de38b..95fe41be1225 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -193,13 +193,14 @@ pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::Toke // resolve a file-system path to an absolute file-system path (if it // isn't already) fn res_rel_file(cx: &mut ExtCtxt, sp: syntax_pos::Span, arg: &Path) -> PathBuf { - // NB: relative paths are resolved relative to the compilation unit + // Relative paths are resolved relative to the file in which they are found + // after macro expansion (that is, they are unhygienic). if !arg.is_absolute() { let callsite = sp.source_callsite(); - let mut cu = PathBuf::from(&cx.codemap().span_to_filename(callsite)); - cu.pop(); - cu.push(arg); - cu + let mut path = PathBuf::from(&cx.codemap().span_to_filename(callsite)); + path.pop(); + path.push(arg); + path } else { arg.to_path_buf() } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 80b6794d1e3c..7b3fe2bd993a 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -269,7 +269,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()]) } - let exp: Box<_> = Box::new(MacroRulesMacroExpander { + let expander: Box<_> = Box::new(MacroRulesMacroExpander { name: def.ident, lhses: lhses, rhses: rhses, @@ -278,9 +278,15 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) if body.legacy { let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable"); - NormalTT(exp, Some((def.id, def.span)), allow_internal_unstable) + let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe"); + NormalTT { + expander, + def_info: Some((def.id, def.span)), + allow_internal_unstable, + allow_internal_unsafe + } } else { - SyntaxExtension::DeclMacro(exp, Some((def.id, def.span))) + SyntaxExtension::DeclMacro(expander, Some((def.id, def.span))) } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index cb0cc2aafb07..dd64bc3072e6 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -194,6 +194,14 @@ declare_features! ( // rustc internal (active, allow_internal_unstable, "1.0.0", None), + // Allows the use of #[allow_internal_unsafe]. This is an + // attribute on macro_rules! and can't use the attribute handling + // below (it has to be checked before expansion possibly makes + // macros disappear). + // + // rustc internal + (active, allow_internal_unsafe, "1.0.0", None), + // #23121. Array patterns have some hazards yet. (active, slice_patterns, "1.0.0", Some(23121)), @@ -368,6 +376,9 @@ declare_features! ( // global allocators and their internals (active, global_allocator, "1.20.0", None), (active, allocator_internals, "1.20.0", None), + + // #[doc(cfg(...))] + (active, doc_cfg, "1.21.0", Some(43781)), ); declare_features! ( @@ -739,6 +750,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG EXPLAIN_ALLOW_INTERNAL_UNSTABLE, cfg_fn!(allow_internal_unstable))), + ("allow_internal_unsafe", Normal, Gated(Stability::Unstable, + "allow_internal_unsafe", + EXPLAIN_ALLOW_INTERNAL_UNSAFE, + cfg_fn!(allow_internal_unsafe))), + ("fundamental", Whitelisted, Gated(Stability::Unstable, "fundamental", "the `#[fundamental]` attribute \ @@ -1049,6 +1065,8 @@ pub const EXPLAIN_TRACE_MACROS: &'static str = "`trace_macros` is not stable enough for use and is subject to change"; pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str = "allow_internal_unstable side-steps feature gating and stability checks"; +pub const EXPLAIN_ALLOW_INTERNAL_UNSAFE: &'static str = + "allow_internal_unsafe side-steps the unsafe_code lint"; pub const EXPLAIN_CUSTOM_DERIVE: &'static str = "`#[derive]` for custom traits is deprecated and will be removed in the future."; @@ -1161,6 +1179,16 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { self.context.check_attribute(attr, false); } + if attr.check_name("doc") { + if let Some(content) = attr.meta_item_list() { + if content.len() == 1 && content[0].check_name("cfg") { + gate_feature_post!(&self, doc_cfg, attr.span, + "#[doc(cfg(...))] is experimental" + ); + } + } + } + if self.context.features.proc_macro && attr::is_known(attr) { return } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 893bada2670d..957164bab79a 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -124,7 +124,7 @@ pub fn parse_expr_from_source_str(name: String, source: String, sess: &ParseSess /// Parses an item. /// -/// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and`Err` +/// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and `Err` /// when a syntax error occurred. pub fn parse_item_from_source_str(name: String, source: String, sess: &ParseSess) -> PResult>> { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index df1bae523a5c..245452dcb39c 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3595,7 +3595,7 @@ impl<'a> Parser<'a> { /// Parse a local variable declaration fn parse_local(&mut self, attrs: ThinVec) -> PResult<'a, P> { - let lo = self.span; + let lo = self.prev_span; let pat = self.parse_pat()?; let ty = if self.eat(&token::Colon) { diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index d9ed96f293a8..430976e7d3ce 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -28,6 +28,7 @@ fn ignored_span(sp: Span) -> Span { format: MacroAttribute(Symbol::intern("std_inject")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); Span { ctxt: SyntaxContext::empty().apply_mark(mark), ..sp } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 887479a24724..c05e166e0133 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -291,6 +291,7 @@ fn generate_test_harness(sess: &ParseSess, format: MacroAttribute(Symbol::intern("test")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 5eab81dd28fc..439538a8b5ee 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -64,7 +64,12 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, macro_rules! register { ($( $name:ident: $f:expr, )*) => { $( register(Symbol::intern(stringify!($name)), - NormalTT(Box::new($f as MacroExpanderFn), None, false)); + NormalTT { + expander: Box::new($f as MacroExpanderFn), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); )* } } @@ -112,7 +117,12 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, // format_args uses `unstable` things internally. register(Symbol::intern("format_args"), - NormalTT(Box::new(format::expand_format_args), None, true)); + NormalTT { + expander: Box::new(format::expand_format_args), + def_info: None, + allow_internal_unstable: true, + allow_internal_unsafe: false, + }); for (name, ext) in user_exts { register(name, ext); diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index ab6d73e5061a..700386f68fee 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -368,6 +368,7 @@ fn mk_registrar(cx: &mut ExtCtxt, format: MacroAttribute(Symbol::intern("proc_macro")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); let span = Span { ctxt: SyntaxContext::empty().apply_mark(mark), ..DUMMY_SP }; diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 804b91ab09e3..514cc26666e3 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -310,6 +310,9 @@ pub struct NameAndSpan { /// features internally without forcing the whole crate to opt-in /// to them. pub allow_internal_unstable: bool, + /// Whether the macro is allowed to use `unsafe` internally + /// even if the user crate has `#![forbid(unsafe_code)]`. + pub allow_internal_unsafe: bool, /// The span of the macro definition itself. The macro may not /// have a sensible definition span (e.g. something defined /// completely inside libsyntax) in which case this is None. diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 7006f45455e3..e162bc26412f 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -153,6 +153,16 @@ impl Span { } } + /// Check if a span is "internal" to a macro in which `unsafe` + /// can be used without triggering the `unsafe_code` lint + // (that is, a macro marked with `#[allow_internal_unsafe]`). + pub fn allows_unsafe(&self) -> bool { + match self.ctxt.outer().expn_info() { + Some(info) => info.callee.allow_internal_unsafe, + None => false, + } + } + pub fn macro_backtrace(mut self) -> Vec { let mut prev_span = DUMMY_SP; let mut result = vec![]; diff --git a/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs b/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs new file mode 100644 index 000000000000..590dc619f2f1 --- /dev/null +++ b/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// gate-test-allow_internal_unsafe + +#![allow(unused_macros)] + +macro_rules! bar { + () => { + // more layers don't help: + #[allow_internal_unsafe] //~ ERROR allow_internal_unsafe side-steps + macro_rules! baz { + () => {} + } + } +} + +bar!(); + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-doc_cfg.rs b/src/test/compile-fail/feature-gate-doc_cfg.rs new file mode 100644 index 000000000000..1a77d9180145 --- /dev/null +++ b/src/test/compile-fail/feature-gate-doc_cfg.rs @@ -0,0 +1,12 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[doc(cfg(unix))] //~ ERROR: #[doc(cfg(...))] is experimental +fn main() {} diff --git a/src/test/compile-fail/issue-17954.rs b/src/test/compile-fail/issue-17954.rs new file mode 100644 index 000000000000..4befe3ebc865 --- /dev/null +++ b/src/test/compile-fail/issue-17954.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(thread_local)] + +#[thread_local] +static FOO: u8 = 3; + +fn main() { + let a = &FOO; + //~^ ERROR borrowed value does not live long enough + //~| does not live long enough + //~| NOTE borrowed value must be valid for the static lifetime + + std::thread::spawn(move || { + println!("{}", a); + }); +} //~ temporary value only lives until here diff --git a/src/test/compile-fail/issue-43250.rs b/src/test/compile-fail/issue-43250.rs new file mode 100644 index 000000000000..e1d34f339dc6 --- /dev/null +++ b/src/test/compile-fail/issue-43250.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let mut y; + const C: u32 = 0; + macro_rules! m { + ($a:expr) => { + let $a = 0; + } + } + m!(y); + //~^ ERROR arbitrary expressions aren't allowed in patterns + m!(C); + //~^ ERROR arbitrary expressions aren't allowed in patterns +} diff --git a/src/test/compile-fail/issue-43733-2.rs b/src/test/compile-fail/issue-43733-2.rs new file mode 100644 index 000000000000..3dff34c2ebb1 --- /dev/null +++ b/src/test/compile-fail/issue-43733-2.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn, drop_types_in_const)] +#![feature(cfg_target_thread_local, thread_local_internals)] + +// On platforms *without* `#[thread_local]`, use +// a custom non-`Sync` type to fake the same error. +#[cfg(not(target_thread_local))] +struct Key { + _data: std::cell::UnsafeCell>, + _flag: std::cell::Cell, +} + +#[cfg(not(target_thread_local))] +impl Key { + const fn new() -> Self { + Key { + _data: std::cell::UnsafeCell::new(None), + _flag: std::cell::Cell::new(false), + } + } +} + +#[cfg(target_thread_local)] +use std::thread::__FastLocalKeyInner as Key; + +static __KEY: Key<()> = Key::new(); +//~^ ERROR `std::cell::UnsafeCell>: std::marker::Sync` is not satisfied +//~| ERROR `std::cell::Cell: std::marker::Sync` is not satisfied + +fn main() {} diff --git a/src/test/compile-fail/issue-43733.rs b/src/test/compile-fail/issue-43733.rs new file mode 100644 index 000000000000..a4aad21a9f83 --- /dev/null +++ b/src/test/compile-fail/issue-43733.rs @@ -0,0 +1,41 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn, drop_types_in_const)] +#![feature(cfg_target_thread_local, thread_local_internals)] + +type Foo = std::cell::RefCell; + +#[cfg(target_thread_local)] +static __KEY: std::thread::__FastLocalKeyInner = + std::thread::__FastLocalKeyInner::new(); + +#[cfg(not(target_thread_local))] +static __KEY: std::thread::__OsLocalKeyInner = + std::thread::__OsLocalKeyInner::new(); + +fn __getit() -> std::option::Option< + &'static std::cell::UnsafeCell< + std::option::Option>> +{ + __KEY.get() //~ ERROR invocation of unsafe method requires unsafe +} + +static FOO: std::thread::LocalKey = + std::thread::LocalKey::new(__getit, Default::default); +//~^ ERROR call to unsafe function requires unsafe + +fn main() { + FOO.with(|foo| println!("{}", foo.borrow())); + std::thread::spawn(|| { + FOO.with(|foo| *foo.borrow_mut() += "foo"); + }).join().unwrap(); + FOO.with(|foo| println!("{}", foo.borrow())); +} diff --git a/src/test/compile-fail/thread-local-in-ctfe.rs b/src/test/compile-fail/thread-local-in-ctfe.rs new file mode 100644 index 000000000000..720e15991c05 --- /dev/null +++ b/src/test/compile-fail/thread-local-in-ctfe.rs @@ -0,0 +1,38 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn, thread_local)] + +#[thread_local] +static A: u32 = 1; + +static B: u32 = A; +//~^ ERROR thread-local statics cannot be accessed at compile-time +//~| ERROR cannot refer to other statics by value +//~| WARN non-constant path in constant expression + +static C: &u32 = &A; +//~^ ERROR thread-local statics cannot be accessed at compile-time + +const D: u32 = A; +//~^ ERROR thread-local statics cannot be accessed at compile-time +//~| ERROR cannot refer to statics by value +//~| WARN non-constant path in constant expression + +const E: &u32 = &A; +//~^ ERROR thread-local statics cannot be accessed at compile-time + +const fn f() -> u32 { + A + //~^ ERROR thread-local statics cannot be accessed at compile-time + //~| ERROR cannot refer to statics by value +} + +fn main() {} diff --git a/src/test/run-make/issues-41478-43796/Makefile b/src/test/run-make/issues-41478-43796/Makefile new file mode 100644 index 000000000000..f9735253ab68 --- /dev/null +++ b/src/test/run-make/issues-41478-43796/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +all: + # Work in /tmp, because we need to create the `save-analysis-temp` folder. + cp a.rs $(TMPDIR)/ + cd $(TMPDIR) && $(RUSTC) -Zsave-analysis $(TMPDIR)/a.rs 2> $(TMPDIR)/stderr.txt || ( cat $(TMPDIR)/stderr.txt && exit 1 ) + [ ! -s $(TMPDIR)/stderr.txt ] || ( cat $(TMPDIR)/stderr.txt && exit 1 ) + [ -f $(TMPDIR)/save-analysis/liba.json ] || ( ls -la $(TMPDIR) && exit 1 ) diff --git a/src/test/run-make/issues-41478-43796/a.rs b/src/test/run-make/issues-41478-43796/a.rs new file mode 100644 index 000000000000..9d95f8b25852 --- /dev/null +++ b/src/test/run-make/issues-41478-43796/a.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] +pub struct V(S); +pub trait An { + type U; +} +pub trait F { +} +impl F for V<::U> { +} diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs index 134e36c587be..8da2ae8b29ab 100644 --- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs @@ -48,5 +48,10 @@ impl TTMacroExpander for Expander { pub fn plugin_registrar(reg: &mut Registry) { let args = reg.args().to_owned(); reg.register_syntax_extension(Symbol::intern("plugin_args"), - NormalTT(Box::new(Expander { args: args, }), None, false)); + NormalTT { + expander: Box::new(Expander { args: args, }), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); } diff --git a/src/test/run-pass/auxiliary/thread-local-extern-static.rs b/src/test/run-pass/auxiliary/thread-local-extern-static.rs index d1971a5e1aea..e9457886be80 100644 --- a/src/test/run-pass/auxiliary/thread-local-extern-static.rs +++ b/src/test/run-pass/auxiliary/thread-local-extern-static.rs @@ -8,10 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(thread_local)] -#![feature(cfg_target_thread_local)] +#![feature(cfg_target_thread_local, const_fn, thread_local)] #![crate_type = "lib"] +#[cfg(target_thread_local)] +use std::cell::Cell; + #[no_mangle] -#[cfg_attr(target_thread_local, thread_local)] -pub static FOO: u32 = 3; +#[cfg(target_thread_local)] +#[thread_local] +pub static FOO: Cell = Cell::new(3); diff --git a/src/test/run-pass/impl-trait/auxiliary/xcrate.rs b/src/test/run-pass/impl-trait/auxiliary/xcrate.rs index be353f6d563a..e9074f8c2309 100644 --- a/src/test/run-pass/impl-trait/auxiliary/xcrate.rs +++ b/src/test/run-pass/impl-trait/auxiliary/xcrate.rs @@ -13,3 +13,14 @@ pub fn fourway_add(a: i32) -> impl Fn(i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 { move |b| move |c| move |d| a + b + c + d } + +fn some_internal_fn() -> u32 { + 1 +} + +// See #40839 +pub fn return_closure_accessing_internal_fn() -> impl Fn() -> u32 { + || { + some_internal_fn() + 1 + } +} diff --git a/src/test/run-pass/impl-trait/xcrate.rs b/src/test/run-pass/impl-trait/xcrate.rs index fe3ed7b3465f..6d00c46fa350 100644 --- a/src/test/run-pass/impl-trait/xcrate.rs +++ b/src/test/run-pass/impl-trait/xcrate.rs @@ -14,4 +14,5 @@ extern crate xcrate; fn main() { assert_eq!(xcrate::fourway_add(1)(2)(3)(4), 10); + xcrate::return_closure_accessing_internal_fn()(); } diff --git a/src/test/run-pass/issue-30756.rs b/src/test/run-pass/issue-30756.rs index d21b42f8c876..621607e5f6fa 100644 --- a/src/test/run-pass/issue-30756.rs +++ b/src/test/run-pass/issue-30756.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(unsafe_code)] +#![forbid(unsafe_code)] thread_local!(static FOO: u8 = 1); diff --git a/src/test/run-pass/issue-39827.rs b/src/test/run-pass/issue-39827.rs new file mode 100644 index 000000000000..b753cf5844fa --- /dev/null +++ b/src/test/run-pass/issue-39827.rs @@ -0,0 +1,42 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(core_intrinsics)] + +use std::intrinsics::{ volatile_copy_memory, volatile_store, volatile_load, + volatile_copy_nonoverlapping_memory, + volatile_set_memory }; + +// +// This test ensures that volatile intrinsics can be specialised with +// zero-sized types and, in case of copy/set functions, can accept +// number of elements equal to zero. +// +fn main () { + let mut dst_pair = (1, 2); + let src_pair = (3, 4); + let mut dst_empty = (); + let src_empty = (); + + const COUNT_0: usize = 0; + const COUNT_100: usize = 100; + + unsafe { + volatile_copy_memory(&mut dst_pair, &dst_pair, COUNT_0); + volatile_copy_nonoverlapping_memory(&mut dst_pair, &src_pair, 0); + volatile_copy_memory(&mut dst_empty, &dst_empty, 100); + volatile_copy_nonoverlapping_memory(&mut dst_empty, &src_empty, + COUNT_100); + volatile_set_memory(&mut dst_empty, 0, COUNT_100); + volatile_set_memory(&mut dst_pair, 0, COUNT_0); + volatile_store(&mut dst_empty, ()); + volatile_store(&mut dst_empty, src_empty); + volatile_load(&src_empty); + } +} diff --git a/src/test/run-pass/issue-43853.rs b/src/test/run-pass/issue-43853.rs new file mode 100644 index 000000000000..f55d584ea24f --- /dev/null +++ b/src/test/run-pass/issue-43853.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::panic; + +fn test() { + wait(|| panic!()); +} + +fn wait T>(f: F) -> F::Output { + From::from(f()) +} + +fn main() { + let result = panic::catch_unwind(move || test()); + assert!(result.is_err()); +} diff --git a/src/test/run-pass/thread-local-extern-static.rs b/src/test/run-pass/thread-local-extern-static.rs index 87188db9dc0e..09c8b64776c7 100644 --- a/src/test/run-pass/thread-local-extern-static.rs +++ b/src/test/run-pass/thread-local-extern-static.rs @@ -11,18 +11,26 @@ // ignore-windows // aux-build:thread-local-extern-static.rs -#![feature(thread_local)] -#![feature(cfg_target_thread_local)] +#![feature(cfg_target_thread_local, thread_local)] +#[cfg(target_thread_local)] extern crate thread_local_extern_static; +#[cfg(target_thread_local)] +use std::cell::Cell; + +#[cfg(target_thread_local)] extern { - #[cfg_attr(target_thread_local, thread_local)] - static FOO: u32; + #[thread_local] + static FOO: Cell; } +#[cfg(target_thread_local)] fn main() { unsafe { - assert_eq!(FOO, 3); + assert_eq!(FOO.get(), 3); } } + +#[cfg(not(target_thread_local))] +fn main() {} diff --git a/src/test/rustdoc/doc-cfg.rs b/src/test/rustdoc/doc-cfg.rs new file mode 100644 index 000000000000..cfb37912fe75 --- /dev/null +++ b/src/test/rustdoc/doc-cfg.rs @@ -0,0 +1,47 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(doc_cfg)] + +// @has doc_cfg/struct.Portable.html +// @!has - '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' '' +// @has - '//*[@id="method.unix_and_arm_only_function"]' 'fn unix_and_arm_only_function()' +// @has - '//*[@class="stab portability"]' 'This is supported on Unix and ARM only.' +pub struct Portable; + +// @has doc_cfg/unix_only/index.html \ +// '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \ +// 'This is supported on Unix only.' +// @matches - '//*[@class=" module-item"]//*[@class="stab portability"]' '\AUnix\Z' +// @matches - '//*[@class=" module-item"]//*[@class="stab portability"]' '\AUnix and ARM\Z' +// @count - '//*[@class="stab portability"]' 3 +#[doc(cfg(unix))] +pub mod unix_only { + // @has doc_cfg/unix_only/fn.unix_only_function.html \ + // '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \ + // 'This is supported on Unix only.' + // @count - '//*[@class="stab portability"]' 1 + pub fn unix_only_function() { + content::should::be::irrelevant(); + } + + // @has doc_cfg/unix_only/trait.ArmOnly.html \ + // '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \ + // 'This is supported on Unix and ARM only.' + // @count - '//*[@class="stab portability"]' 2 + #[doc(cfg(target_arch = "arm"))] + pub trait ArmOnly { + fn unix_and_arm_only_function(); + } + + impl ArmOnly for super::Portable { + fn unix_and_arm_only_function() {} + } +} \ No newline at end of file diff --git a/src/test/rustdoc/issue-43153.rs b/src/test/rustdoc/issue-43153.rs new file mode 100644 index 000000000000..6ac341d8b022 --- /dev/null +++ b/src/test/rustdoc/issue-43153.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `include!` in a doc test searches relative to the directory in +// which the test is declared. + +// compile-flags:--test + +/// ```rust +/// include!("auxiliary/empty.rs"); +/// fn main() {} +/// ``` +pub struct Foo; diff --git a/src/test/rustdoc/issue-43701.rs b/src/test/rustdoc/issue-43701.rs new file mode 100644 index 000000000000..791d83195990 --- /dev/null +++ b/src/test/rustdoc/issue-43701.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +pub use std::vec::Vec; + +// @!has implementors/core/clone/trait.Clone.js diff --git a/src/test/ui/issue-43806.rs b/src/test/ui/issue-43806.rs new file mode 100644 index 000000000000..7757a503c7e8 --- /dev/null +++ b/src/test/ui/issue-43806.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-pass + +#![deny(unused_results)] + +enum Void {} + +fn foo() {} + +fn bar() -> ! { + loop {} +} + +fn baz() -> Void { + loop {} +} + +fn qux() { + foo(); + bar(); + baz(); +} + +fn main() {} diff --git a/src/tools/rustdoc/Cargo.toml b/src/tools/rustdoc/Cargo.toml index b6edb76d7f98..344f617ef95b 100644 --- a/src/tools/rustdoc/Cargo.toml +++ b/src/tools/rustdoc/Cargo.toml @@ -3,8 +3,11 @@ name = "rustdoc-tool" version = "0.0.0" authors = ["The Rust Project Developers"] +# Cargo adds a number of paths to the dylib search path on windows, which results in +# the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" +# rustdoc a different name. [[bin]] -name = "rustdoc" +name = "rustdoc-tool-binary" path = "main.rs" [dependencies]